816 lines
25 KiB
C
816 lines
25 KiB
C
|
|
/*++
|
|
|
|
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;
|
|
}
|
|
|