/*++

Copyright (c) 1995-2001 Microsoft Corporation

Module Name:

    efisbent.c

Abstract:

    Contains the EFI OS boot entry and boot options
    abstraction implementation.

Author:

    Vijay Jayaseelan (vijayj@microsoft.com)  14 Feb 2001

Revision History:

    None.

--*/

#include <efisbent.h>
#include <ntosp.h>
#include <stdio.h>

//
// global variables
//
BOOLEAN PriviledgeSet = FALSE;

//
// EFI_OS_BOOT_ENTRY Methods
//

static
VOID
EFIOSBEInit(
    IN PEFI_OS_BOOT_ENTRY This
    )
{
    This->OsBootEntry.Delete = EFIOSBEDelete;
    This->OsBootEntry.Flush = EFIOSBEFlush;
}

static
POS_BOOT_ENTRY
EFIOSBECreate(
    IN PBOOT_ENTRY NtBootEntry,
    IN POS_BOOT_OPTIONS Container
    )
{    
    PEFI_OS_BOOT_ENTRY  Entry = NULL;

    if (NtBootEntry && Container) {        
        Entry = SBE_MALLOC(sizeof(EFI_OS_BOOT_ENTRY));

        if (Entry) {
            PWSTR       TempUniStr;
            NTSTATUS    Status = STATUS_SUCCESS;
            ULONG       Size;
            PFILE_PATH  FilePath;
                        
            memset(Entry, 0, sizeof(EFI_OS_BOOT_ENTRY));
            EFIOSBEInit(Entry);
            
            Entry->OsBootEntry.Id = NtBootEntry->Id;
            Entry->OsBootEntry.BootOptions = Container;

            //
            // If this is a Windows boot options set the windows attribute
            //
            if ( IS_BOOT_ENTRY_WINDOWS(NtBootEntry) ) {
                OSBE_SET_WINDOWS(Entry);
            }
            
            //
            // Get the friendly name
            //
            TempUniStr = ADD_OFFSET(NtBootEntry, FriendlyNameOffset);
            
            OSBESetFriendlyName((POS_BOOT_ENTRY)Entry, TempUniStr);

            //
            // Get the loader path
            //
            FilePath = ADD_OFFSET(NtBootEntry, BootFilePathOffset);            
            
            if (FilePath->Type != FILE_PATH_TYPE_NT) {            
                Size = 0;
                
                Status = NtTranslateFilePath(FilePath,
                                FILE_PATH_TYPE_NT,
                                NULL,
                                &Size);
                
                if (Size != 0) {
                    PFILE_PATH NtFilePath = SBE_MALLOC(Size);

                    if (NtFilePath) {                        
                        Status = NtTranslateFilePath(FilePath,
                                    FILE_PATH_TYPE_NT,
                                    NtFilePath,
                                    &Size);

                        if (NT_SUCCESS(Status)) {            
                            PWSTR   VolumeName = (PWSTR)(NtFilePath->FilePath);

                            OSBESetOsLoaderVolumeName((POS_BOOT_ENTRY)Entry,                                
                                    VolumeName);

                            OSBESetOsLoaderPath((POS_BOOT_ENTRY)Entry,
                                VolumeName + wcslen(VolumeName) + 1);
                        }

                        SBE_FREE(NtFilePath);
                    } else {
                        Status = STATUS_NO_MEMORY;
                    }                        
                }

                //
                // Its possible for some reason we didn't get NT path
                // for loader volume, for e.g. it may not be present at all
                // So ignore such cases
                //
                Status = STATUS_SUCCESS;
            } else {
                PWSTR   VolumeName = (PWSTR)(FilePath->FilePath);
                
                OSBESetOsLoaderVolumeName((POS_BOOT_ENTRY)Entry,                                
                        VolumeName);

                OSBESetOsLoaderPath((POS_BOOT_ENTRY)Entry,
                    VolumeName + wcslen(VolumeName) + 1);
            }
            
            if (NT_SUCCESS(Status)) {
                PWINDOWS_OS_OPTIONS OsOptions;

                //
                // Get the OsLoadOptions & Boot path if its windows
                // entry
                //
                OsOptions = (PWINDOWS_OS_OPTIONS)NtBootEntry->OsOptions;

                if (IS_BOOT_ENTRY_WINDOWS(NtBootEntry)) {
                    OSBESetOsLoadOptions((POS_BOOT_ENTRY)Entry,
                        OsOptions->OsLoadOptions);

                    FilePath = ADD_OFFSET(OsOptions, OsLoadPathOffset);

                    if (FilePath->Type != FILE_PATH_TYPE_NT) {            
                        Size = 0;
                        
                        Status = NtTranslateFilePath(FilePath,
                                        FILE_PATH_TYPE_NT,
                                        NULL,
                                        &Size);

                        if (Size != 0) {
                            PFILE_PATH NtFilePath = SBE_MALLOC(Size);

                            if (NtFilePath) {                                
                                Status = NtTranslateFilePath(FilePath,
                                            FILE_PATH_TYPE_NT,
                                            NtFilePath,
                                            &Size);

                                if (NT_SUCCESS(Status)) {
                                    PWSTR   VolumeName = (PWSTR)(NtFilePath->FilePath);

                                    OSBESetBootVolumeName((POS_BOOT_ENTRY)Entry,                                        
                                        VolumeName);

                                    OSBESetBootPath((POS_BOOT_ENTRY)Entry,
                                        VolumeName + wcslen(VolumeName) + 1);
                                }

                                SBE_FREE(NtFilePath);
                            } else {
                                Status = STATUS_NO_MEMORY;
                            }                        
                        }

                        //
                        // Its possible for some reason we didn't get NT path
                        // for Boot volume, for e.g. it may not be present at all
                        // So ignore such cases
                        //
                        Status = STATUS_SUCCESS;
                    } else {
                        PWSTR   VolumeName = (PWSTR)(FilePath->FilePath);
                        
                        OSBESetBootVolumeName((POS_BOOT_ENTRY)Entry,                                        
                            VolumeName);

                        OSBESetBootPath((POS_BOOT_ENTRY)Entry,
                            VolumeName + wcslen(VolumeName) + 1);
                    }                            
                }
            }

            if (!NT_SUCCESS(Status)) {
                SBE_FREE(Entry);
                Entry = NULL;
            }
        }
    }

    return (POS_BOOT_ENTRY)Entry;
}

static
BOOLEAN
EFIOSBEFillNtBootEntry(
    IN PEFI_OS_BOOT_ENTRY Entry
    )
{
    BOOLEAN Result = FALSE;

    if (Entry) {
        ULONG   RequiredLength;
        ULONG   OsOptionsOffset;
        ULONG   OsOptionsLength;
        ULONG   FriendlyNameOffset;
        ULONG   BootPathOffset;
        ULONG   BootPathLength;
        ULONG   LoaderPathOffset;
        ULONG   LoaderPathLength;
        ULONG   WinOsOptionsLength;
        POS_BOOT_ENTRY  BaseEntry = (POS_BOOT_ENTRY)Entry;
        
        if (Entry->NtBootEntry) {
            SBE_FREE(Entry->NtBootEntry);
        }                

        RequiredLength = FIELD_OFFSET(BOOT_ENTRY, OsOptions);

        //
        // TDB : What about non windows OS options ?
        //
        OsOptionsOffset = RequiredLength;
        RequiredLength += FIELD_OFFSET(WINDOWS_OS_OPTIONS, OsLoadOptions);
        RequiredLength += (wcslen(OSBEGetOsLoadOptions(BaseEntry)) + 1) * sizeof(WCHAR);                
        
        //
        // for boot path as part of windows OS options
        //
        RequiredLength = BootPathOffset = ALIGN_UP(RequiredLength, ULONG);
        RequiredLength += FIELD_OFFSET(FILE_PATH, FilePath);
        RequiredLength += (wcslen(OSBEGetBootVolumeName(BaseEntry)) + 1) * sizeof(WCHAR);
        RequiredLength += (wcslen(OSBEGetBootPath(BaseEntry)) + 1) * sizeof(WCHAR);
        BootPathLength = (RequiredLength - BootPathOffset);
        OsOptionsLength = (RequiredLength - OsOptionsOffset);

        //
        // for friendly name
        //
        RequiredLength = FriendlyNameOffset = ALIGN_UP(RequiredLength, ULONG);
        RequiredLength += (wcslen(OSBEGetFriendlyName(BaseEntry)) + 1) * sizeof(WCHAR);

        // 
        // for loader path
        //
        RequiredLength = LoaderPathOffset = ALIGN_UP(RequiredLength, ULONG);
        RequiredLength += FIELD_OFFSET(FILE_PATH, FilePath);
        RequiredLength += (wcslen(OSBEGetOsLoaderVolumeName(BaseEntry)) + 1) * sizeof(WCHAR);
        RequiredLength += (wcslen(OSBEGetOsLoaderPath(BaseEntry)) + 1) * sizeof(WCHAR);       
        LoaderPathLength = (RequiredLength - LoaderPathOffset);


        Entry->NtBootEntry = (PBOOT_ENTRY)SBE_MALLOC(RequiredLength);

        if (Entry->NtBootEntry) {
            PBOOT_ENTRY NtBootEntry = Entry->NtBootEntry;
            PFILE_PATH  BootPath = ADD_BYTE_OFFSET(NtBootEntry, BootPathOffset);
            PFILE_PATH  LoaderPath = ADD_BYTE_OFFSET(NtBootEntry, LoaderPathOffset);
            PWSTR       FriendlyName = (PWSTR)(ADD_BYTE_OFFSET(NtBootEntry, FriendlyNameOffset));
            PWINDOWS_OS_OPTIONS WindowsOptions = ADD_BYTE_OFFSET(NtBootEntry, OsOptionsOffset);
            PWSTR   TempStr;

            memset(NtBootEntry, 0, RequiredLength);
            
            //
            // Fill the base part
            //
            NtBootEntry->Version = BOOT_ENTRY_VERSION;
            NtBootEntry->Length = RequiredLength;
            NtBootEntry->Id = OSBEGetId(BaseEntry);
            NtBootEntry->Attributes = BOOT_ENTRY_ATTRIBUTE_ACTIVE | BOOT_ENTRY_ATTRIBUTE_WINDOWS;
            NtBootEntry->OsOptionsLength = OsOptionsLength;
            NtBootEntry->FriendlyNameOffset = (ULONG)((PUCHAR)FriendlyName - (PUCHAR)NtBootEntry);
            NtBootEntry->BootFilePathOffset = (ULONG)((PUCHAR)LoaderPath - (PUCHAR)NtBootEntry);
            
            //
            // Fill in the windows os options
            //
            strcpy(WindowsOptions->Signature, WINDOWS_OS_OPTIONS_SIGNATURE);
            WindowsOptions->Version = WINDOWS_OS_OPTIONS_VERSION;
            WindowsOptions->Length = OsOptionsLength;
            WindowsOptions->OsLoadPathOffset = (ULONG)((PUCHAR)BootPath - (PUCHAR)WindowsOptions);
            wcscpy(WindowsOptions->OsLoadOptions, OSBEGetOsLoadOptions(BaseEntry));

            //
            // Fill in the Boot path FILE_PATH
            //
            BootPath->Version = FILE_PATH_VERSION;
            BootPath->Length = BootPathLength;
            BootPath->Type = FILE_PATH_TYPE_NT;
            TempStr = (PWSTR)(BootPath->FilePath);
            wcscpy(TempStr, OSBEGetBootVolumeName(BaseEntry));
            TempStr += wcslen(TempStr) + 1;
            wcscpy(TempStr, OSBEGetBootPath(BaseEntry));

            //
            // Fill the friendly name
            //
            wcscpy(FriendlyName, OSBEGetFriendlyName(BaseEntry));

            //
            // Fill in the loader path FILE_PATH
            //
            LoaderPath->Version = FILE_PATH_VERSION;
            LoaderPath->Length = LoaderPathLength;
            LoaderPath->Type = FILE_PATH_TYPE_NT;
            TempStr = (PWSTR)(LoaderPath->FilePath);
            wcscpy(TempStr, OSBEGetOsLoaderVolumeName(BaseEntry));
            TempStr += wcslen(TempStr) + 1;
            wcscpy(TempStr, OSBEGetOsLoaderPath(BaseEntry));

            Result = TRUE;
        }
    }

    return Result;
}

static
VOID
EFIOSBEDelete(
    IN  POS_BOOT_ENTRY  Obj
    )
{
    PEFI_OS_BOOT_ENTRY  This = (PEFI_OS_BOOT_ENTRY)Obj;
    
    if (This) {
        if (This->NtBootEntry) {
            SBE_FREE(This->NtBootEntry);
        }
        
        SBE_FREE(This);
    }        
}

static
BOOLEAN
EFIOSBEFlush(
    IN  POS_BOOT_ENTRY  Obj
    )
{
    BOOLEAN Result = FALSE;
    PEFI_OS_BOOT_ENTRY  This = (PEFI_OS_BOOT_ENTRY)Obj;    

    if (This) {
        NTSTATUS Status = STATUS_SUCCESS;
        
        if (OSBE_IS_DIRTY(This)) {
            if (OSBE_IS_DELETED(This)) {
                //
                // Delete this entry
                //
                Status = NtDeleteBootEntry(This->OsBootEntry.Id);
            } else if (OSBE_IS_NEW(This)) {
                //
                // Add this as new boot entry
                //
                Status = EFIOSBEFillNtBootEntry(This);

                if (NT_SUCCESS(Status)) {
                    Status = NtAddBootEntry(This->NtBootEntry,
                                &(This->OsBootEntry.Id));
                }                                
            } else {
                //
                // Just change this boot entry
                //
                Status = EFIOSBEFillNtBootEntry(This);

                if (NT_SUCCESS(Status)) {
                    Status = NtModifyBootEntry(This->NtBootEntry);
                }                                
            }

            if (NT_SUCCESS(Status)) {
                OSBE_RESET_DIRTY(This);
                Result = TRUE;
            }             
        } else {
            Result = TRUE;  // nothing to flush
        }
    }

    return Result;
}

//
// EFI_OS_BOOT_OPTIONS Methods
//
static
VOID
EFIOSBOInit(
    IN PEFI_OS_BOOT_OPTIONS  This
    )
{
    This->OsBootOptions.Delete = EFIOSBODelete;
    This->OsBootOptions.Flush = EFIOSBOFlush;
    This->OsBootOptions.AddNewBootEntry = EFIOSBOAddNewBootEntry;
    This->OsBootOptions.DeleteBootEntry = OSBODeleteBootEntry;
}

POS_BOOT_OPTIONS
EFIOSBOCreate(
    VOID
    )
{
    PEFI_OS_BOOT_OPTIONS This = NULL;
    BOOLEAN WasEnabled = FALSE;

    if (PriviledgeSet || 
        NT_SUCCESS(RtlAdjustPrivilege(SE_SYSTEM_ENVIRONMENT_PRIVILEGE, 
                            TRUE,
                            FALSE,
                            &WasEnabled))) {
        PriviledgeSet = TRUE;    
        This = SBE_MALLOC(sizeof(EFI_OS_BOOT_OPTIONS));    
    } 
    
    if (This) {
        NTSTATUS    Status;
        ULONG       Length = 0;
        
        memset(This, 0, sizeof(EFI_OS_BOOT_OPTIONS));       
        EFIOSBOInit(This);

        //
        // Get hold of NT boot entries
        //
        Status = NtQueryBootOptions(NULL, &Length);

        if (Length) {
            This->NtBootOptions = SBE_MALLOC(Length);

            if (This->NtBootOptions) {
                Status = NtQueryBootOptions(This->NtBootOptions,
                                &Length);

                if (NT_SUCCESS(Status)) {
                    //
                    // save off the timeout period
                    //
                    This->OsBootOptions.Timeout = This->NtBootOptions->Timeout;

                    //
                    // enumerate all the boot entries
                    //
                    Length = 0;
                    Status = NtEnumerateBootEntries(NULL, &Length);

                    if (Length) {
                        This->NtBootEntries = SBE_MALLOC(Length);

                        if (This->NtBootEntries) {
                            Status = NtEnumerateBootEntries(This->NtBootEntries,
                                            &Length);
                        } else {
                            Status = STATUS_NO_MEMORY;
                        }                            
                    } 
                }
            } else {
                Status = STATUS_NO_MEMORY;
            }                
        }

        //
        // Convert the NT boot entries to our representation
        //
        if (NT_SUCCESS(Status) && (This->NtBootEntries)) {
            PBOOT_ENTRY_LIST    ListEntry = This->NtBootEntries;
            PBOOT_ENTRY         CurrentNtEntry = &(ListEntry->BootEntry);
            PEFI_OS_BOOT_ENTRY  CurrentOsEntry = NULL;
            PEFI_OS_BOOT_ENTRY  LastEntry = NULL;

            while (CurrentNtEntry) {
                //
                // Create the OS entry
                //
                CurrentOsEntry = (PEFI_OS_BOOT_ENTRY)EFIOSBECreate(CurrentNtEntry, 
                                                        (POS_BOOT_OPTIONS)This);

                if (!CurrentOsEntry)  {
                    Status = STATUS_NO_MEMORY;

                    break;
                }

                //
                // found one more valid entry
                //
                This->OsBootOptions.EntryCount++;
                CurrentOsEntry->OsBootEntry.BootOptions = (POS_BOOT_OPTIONS)This;

                //
                // If this is the first entry then setup the linked list head
                //
                if (!This->OsBootOptions.BootEntries) {
                    This->OsBootOptions.BootEntries = (POS_BOOT_ENTRY)(CurrentOsEntry);
                } 

                if (LastEntry) {
                    LastEntry->OsBootEntry.NextEntry = (POS_BOOT_ENTRY)CurrentOsEntry;
                }                    

                LastEntry = CurrentOsEntry;

                //
                // process the next entry, if available
                //
                if (ListEntry->NextEntryOffset) {
                    ListEntry = ADD_OFFSET(ListEntry, NextEntryOffset);
                    CurrentNtEntry = &(ListEntry->BootEntry);
                } else {
                    CurrentNtEntry = NULL;
                }                    
            }                                    
        }
        
        //
        // Now query the boot order
        //
        if (NT_SUCCESS(Status)) {
            Length = 0;

            Status = NtQueryBootEntryOrder(NULL,
                            &Length);

            if (Length) {
                PULONG  BootOrder = SBE_MALLOC(Length * sizeof(ULONG));

                if (BootOrder) {
                    memset(BootOrder, 0, Length);
                    This->OsBootOptions.BootOrder = BootOrder;
                    This->OsBootOptions.BootOrderCount = Length;

                    Status = NtQueryBootEntryOrder(BootOrder,
                                &Length);
                } else {
                    Status = STATUS_NO_MEMORY;
                }
            }
        }

        //
        // Now setup the valid entries
        //
        if (NT_SUCCESS(Status)) {
            ULONG FirstEntryId = OSBOGetBootEntryIdByOrder((POS_BOOT_OPTIONS)This,
                                        0);

            if (FirstEntryId != (-1)) {
                This->OsBootOptions.CurrentEntry = 
                        OSBOFindBootEntry((POS_BOOT_OPTIONS)This,
                                FirstEntryId);
            } else {
                This->OsBootOptions.CurrentEntry = NULL;
            }
        }
        
        if (!NT_SUCCESS(Status)) {
            EFIOSBODelete((POS_BOOT_OPTIONS)This);
            This = NULL;
        }
    }

    return (POS_BOOT_OPTIONS)This;
}

static        
VOID
EFIOSBODelete(
    IN POS_BOOT_OPTIONS Obj
    )
{
    PEFI_OS_BOOT_OPTIONS This = (PEFI_OS_BOOT_OPTIONS)Obj;
    
    if (This) {
        //
        // delete each boot entry 
        //
        ULONG Index = 0;
        POS_BOOT_ENTRY Entry = OSBOGetFirstBootEntry(Obj, &Index);
        POS_BOOT_ENTRY NextEntry;

        while (Entry) {
            NextEntry = Entry->NextEntry;
            OSBEDelete(Entry);
            Entry = NextEntry;
        }

        //
        // delete the options
        //
        if (This->NtBootOptions)
            SBE_FREE(This->NtBootOptions);

        SBE_FREE(This);
    }        
}

static
POS_BOOT_ENTRY
EFIOSBOAddNewBootEntry(
    IN POS_BOOT_OPTIONS This,
    IN PCWSTR            FriendlyName,
    IN PCWSTR            OsLoaderVolumeName,
    IN PCWSTR            OsLoaderPath,
    IN PCWSTR            BootVolumeName,
    IN PCWSTR            BootPath,
    IN PCWSTR            OsLoadOptions
    )
{
    PEFI_OS_BOOT_ENTRY  Entry = NULL;

    if (This && FriendlyName && OsLoaderVolumeName && OsLoaderPath &&
        BootVolumeName && BootPath) {
        Entry = SBE_MALLOC(sizeof(EFI_OS_BOOT_ENTRY));

        if (Entry) {
            memset(Entry, 0, sizeof(EFI_OS_BOOT_ENTRY));

            //
            // init core fields
            //
            EFIOSBEInit(Entry);            
            Entry->OsBootEntry.BootOptions = This;

            //
            // fill in the attributes
            //
            OSBESetFriendlyName((POS_BOOT_ENTRY)Entry, FriendlyName);
            OSBESetOsLoaderVolumeName((POS_BOOT_ENTRY)Entry, OsLoaderVolumeName);
            OSBESetOsLoaderPath((POS_BOOT_ENTRY)Entry, OsLoaderPath);
            OSBESetBootVolumeName((POS_BOOT_ENTRY)Entry, BootVolumeName);
            OSBESetBootPath((POS_BOOT_ENTRY)Entry, BootPath);            

            if (OsLoadOptions) {
                OSBESetOsLoadOptions((POS_BOOT_ENTRY)Entry, OsLoadOptions);
            }
            
            //
            // Set the attribute specifying that this is a Windows option
            //
            OSBE_SET_WINDOWS(Entry);

            //
            // mark it dirty and new for flushing
            //
            OSBE_SET_NEW(Entry);
            OSBE_SET_DIRTY(Entry);                    

            //
            // Flush the entry now to get a proper Id;
            //
            if (!OSBEFlush((POS_BOOT_ENTRY)Entry)) {
                SBE_FREE(Entry);
                Entry = NULL;
            } else {
                ULONG   OrderCount;
                PULONG  NewOrder;
                
                Entry->OsBootEntry.BootOptions = (POS_BOOT_OPTIONS)This;            
                Entry->OsBootEntry.NextEntry = This->BootEntries;
                This->BootEntries = (POS_BOOT_ENTRY)Entry;
                This->EntryCount++;

                //
                // Put the new entry at the end of the boot order
                //
                OrderCount = OSBOGetOrderedBootEntryCount(This);

                NewOrder = (PULONG)SBE_MALLOC((OrderCount + 1) * sizeof(ULONG));

                if (NewOrder) {
                    memset(NewOrder, 0, sizeof(ULONG) * (OrderCount + 1));

                    //
                    // copy over the old ordered list
                    //
                    memcpy(NewOrder, This->BootOrder, sizeof(ULONG) * OrderCount);
                    NewOrder[OrderCount] = OSBEGetId((POS_BOOT_ENTRY)Entry);
                    SBE_FREE(This->BootOrder);
                    This->BootOrder = NewOrder;
                    This->BootOrderCount = OrderCount + 1;
                } else {
                    SBE_FREE(Entry);
                    Entry = NULL;
                }                    
            }                
        }
    }        
    
    return (POS_BOOT_ENTRY)Entry;
}

static
BOOLEAN
EFIOSBOFlush(
    IN POS_BOOT_OPTIONS Obj
    )
{
    BOOLEAN Result = FALSE;
    PEFI_OS_BOOT_OPTIONS  This = (PEFI_OS_BOOT_OPTIONS)Obj;    

    if (This) { 
        ULONG Index;
        ULONG FieldsToChange = BOOT_OPTIONS_FIELD_COUNTDOWN |
                               BOOT_OPTIONS_FIELD_NEXT_BOOT_ENTRY_ID;
        ULONG OrderCount;                               
                               
        POS_BOOT_ENTRY  Entry = OSBOGetFirstBootEntry(Obj, &Index);

        //
        // First update the required entries
        //
        Result = TRUE;
        
        while (Entry) {
            if (!OSBE_IS_DELETED(Entry) && !OSBE_IS_NEW(Entry) &&
                !NT_SUCCESS(EFIOSBEFlush(Entry))) {
                Result = FALSE;
            }

            Entry = OSBOGetNextBootEntry(Obj, &Index);
        }

        if (Result) {
            Entry = OSBOGetFirstBootEntry(Obj, &Index);

            //
            // Next delete the required entries
            //
            Result = TRUE;
            
            while (Entry) {
                if (OSBE_IS_DELETED(Entry) && !NT_SUCCESS(EFIOSBEFlush(Entry))) {
                    Result = FALSE;
                }

                Entry = OSBOGetNextBootEntry(Obj, &Index);
            }
        }            

        if (Result) {
            POS_BOOT_ENTRY  Entry = OSBOGetFirstBootEntry(Obj, &Index);

            //
            // Now create the required entries
            //            
            while (Entry) {
                if (OSBE_IS_NEW(Entry) && !NT_SUCCESS(EFIOSBEFlush(Entry))) {
                    Result = FALSE;
                }

                Entry = OSBOGetNextBootEntry(Obj, &Index);
            }
        }

        //
        // Safety check
        //
        OrderCount = min(Obj->BootOrderCount, Obj->EntryCount);
        
        //
        // Write the boot entry order
        //        
        if (!NT_SUCCESS(NtSetBootEntryOrder(Obj->BootOrder,
                            OrderCount))) {
            Result = FALSE;
        }

        //
        // Write the other boot options
        //
        This->NtBootOptions->Timeout = Obj->Timeout;

        //
        // Make sure NextBootEntry points to the active boot entry
        // so that we can boot the active boot entry
        //                
        if (Obj->BootOrderCount) {
            This->NtBootOptions->NextBootEntryId = Obj->BootOrder[0];
        }            
            
        if (!NT_SUCCESS(NtSetBootOptions(This->NtBootOptions,
                            FieldsToChange))) {
            Result = FALSE;
        }            
    }

    return Result;
}