mirror of https://github.com/acidanthera/audk.git
484 lines
17 KiB
C
484 lines
17 KiB
C
/** @file
|
|
*
|
|
* Copyright (c) 2011-2012, ARM Limited. All rights reserved.
|
|
*
|
|
* This program and the accompanying materials
|
|
* are licensed and made available under the terms and conditions of the BSD License
|
|
* which accompanies this distribution. The full text of the license may be found at
|
|
* http://opensource.org/licenses/bsd-license.php
|
|
*
|
|
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
*
|
|
**/
|
|
|
|
#include "BdsInternal.h"
|
|
|
|
#include <Library/PcdLib.h>
|
|
#include <Library/PerformanceLib.h>
|
|
|
|
#include <Protocol/Bds.h>
|
|
|
|
#define EFI_SET_TIMER_TO_SECOND 10000000
|
|
|
|
EFI_HANDLE mImageHandle;
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
GetConsoleDevicePathFromVariable (
|
|
IN CHAR16* ConsoleVarName,
|
|
IN CHAR16* DefaultConsolePaths,
|
|
OUT EFI_DEVICE_PATH** DevicePaths
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Size;
|
|
EFI_DEVICE_PATH_PROTOCOL* DevicePathInstances;
|
|
EFI_DEVICE_PATH_PROTOCOL* DevicePathInstance;
|
|
CHAR16* DevicePathStr;
|
|
CHAR16* NextDevicePathStr;
|
|
EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *EfiDevicePathFromTextProtocol;
|
|
|
|
Status = GetEnvironmentVariable (ConsoleVarName, NULL, NULL, (VOID**)&DevicePathInstances);
|
|
if (EFI_ERROR(Status)) {
|
|
// In case no default console device path has been defined we assume a driver handles the console (eg: SimpleTextInOutSerial)
|
|
if ((DefaultConsolePaths == NULL) || (DefaultConsolePaths[0] == L'\0')) {
|
|
*DevicePaths = NULL;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);
|
|
ASSERT_EFI_ERROR(Status);
|
|
|
|
DevicePathInstances = NULL;
|
|
|
|
// Extract the Device Path instances from the multi-device path string
|
|
while ((DefaultConsolePaths != NULL) && (DefaultConsolePaths[0] != L'\0')) {
|
|
NextDevicePathStr = StrStr (DefaultConsolePaths, L";");
|
|
if (NextDevicePathStr == NULL) {
|
|
DevicePathStr = DefaultConsolePaths;
|
|
DefaultConsolePaths = NULL;
|
|
} else {
|
|
DevicePathStr = (CHAR16*)AllocateCopyPool ((NextDevicePathStr - DefaultConsolePaths + 1) * sizeof(CHAR16), DefaultConsolePaths);
|
|
*(DevicePathStr + (NextDevicePathStr - DefaultConsolePaths)) = L'\0';
|
|
DefaultConsolePaths = NextDevicePathStr;
|
|
if (DefaultConsolePaths[0] == L';') {
|
|
DefaultConsolePaths++;
|
|
}
|
|
}
|
|
|
|
DevicePathInstance = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath (DevicePathStr);
|
|
ASSERT(DevicePathInstance != NULL);
|
|
DevicePathInstances = AppendDevicePathInstance (DevicePathInstances, DevicePathInstance);
|
|
|
|
if (NextDevicePathStr != NULL) {
|
|
FreePool (DevicePathStr);
|
|
}
|
|
FreePool (DevicePathInstance);
|
|
}
|
|
|
|
// Set the environment variable with this device path multi-instances
|
|
Size = GetDevicePathSize (DevicePathInstances);
|
|
if (Size > 0) {
|
|
gRT->SetVariable (
|
|
ConsoleVarName,
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
|
|
Size,
|
|
DevicePathInstances
|
|
);
|
|
} else {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
*DevicePaths = DevicePathInstances;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
InitializeConsolePipe (
|
|
IN EFI_DEVICE_PATH *ConsoleDevicePaths,
|
|
IN EFI_GUID *Protocol,
|
|
OUT EFI_HANDLE *Handle,
|
|
OUT VOID* *Interface
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Size;
|
|
UINTN NoHandles;
|
|
EFI_HANDLE *Buffer;
|
|
EFI_DEVICE_PATH_PROTOCOL* DevicePath;
|
|
|
|
// Connect all the Device Path Consoles
|
|
while (ConsoleDevicePaths != NULL) {
|
|
DevicePath = GetNextDevicePathInstance (&ConsoleDevicePaths, &Size);
|
|
|
|
Status = BdsConnectDevicePath (DevicePath, Handle, NULL);
|
|
DEBUG_CODE_BEGIN();
|
|
if (EFI_ERROR(Status)) {
|
|
// We convert back to the text representation of the device Path
|
|
EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
|
|
CHAR16* DevicePathTxt;
|
|
EFI_STATUS Status;
|
|
|
|
Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
|
|
if (!EFI_ERROR(Status)) {
|
|
DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (DevicePath, TRUE, TRUE);
|
|
|
|
DEBUG((EFI_D_ERROR,"Fail to start the console with the Device Path '%s'. (Error '%r')\n", DevicePathTxt, Status));
|
|
|
|
FreePool (DevicePathTxt);
|
|
}
|
|
}
|
|
DEBUG_CODE_END();
|
|
|
|
// If the console splitter driver is not supported by the platform then use the first Device Path
|
|
// instance for the console interface.
|
|
if (!EFI_ERROR(Status) && (*Interface == NULL)) {
|
|
Status = gBS->HandleProtocol (*Handle, Protocol, Interface);
|
|
}
|
|
}
|
|
|
|
// No Device Path has been defined for this console interface. We take the first protocol implementation
|
|
if (*Interface == NULL) {
|
|
Status = gBS->LocateHandleBuffer (ByProtocol, Protocol, NULL, &NoHandles, &Buffer);
|
|
if (EFI_ERROR (Status)) {
|
|
BdsConnectAllDrivers();
|
|
Status = gBS->LocateHandleBuffer (ByProtocol, Protocol, NULL, &NoHandles, &Buffer);
|
|
}
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
*Handle = Buffer[0];
|
|
Status = gBS->HandleProtocol (*Handle, Protocol, Interface);
|
|
ASSERT_EFI_ERROR(Status);
|
|
}
|
|
FreePool (Buffer);
|
|
} else {
|
|
Status = EFI_SUCCESS;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
InitializeConsole (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_DEVICE_PATH* ConOutDevicePaths;
|
|
EFI_DEVICE_PATH* ConInDevicePaths;
|
|
EFI_DEVICE_PATH* ConErrDevicePaths;
|
|
|
|
// By getting the Console Device Paths from the environment variables before initializing the console pipe, we
|
|
// create the 3 environment variables (ConIn, ConOut, ConErr) that allows to initialize all the console interface
|
|
// of newly installed console drivers
|
|
Status = GetConsoleDevicePathFromVariable (L"ConOut", (CHAR16*)PcdGetPtr(PcdDefaultConOutPaths), &ConOutDevicePaths);
|
|
ASSERT_EFI_ERROR (Status);
|
|
Status = GetConsoleDevicePathFromVariable (L"ConIn", (CHAR16*)PcdGetPtr(PcdDefaultConInPaths), &ConInDevicePaths);
|
|
ASSERT_EFI_ERROR (Status);
|
|
Status = GetConsoleDevicePathFromVariable (L"ConErr", (CHAR16*)PcdGetPtr(PcdDefaultConOutPaths), &ConErrDevicePaths);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
// Initialize the Consoles
|
|
Status = InitializeConsolePipe (ConOutDevicePaths, &gEfiSimpleTextOutProtocolGuid, &gST->ConsoleOutHandle, (VOID **)&gST->ConOut);
|
|
ASSERT_EFI_ERROR (Status);
|
|
Status = InitializeConsolePipe (ConInDevicePaths, &gEfiSimpleTextInProtocolGuid, &gST->ConsoleInHandle, (VOID **)&gST->ConIn);
|
|
ASSERT_EFI_ERROR (Status);
|
|
Status = InitializeConsolePipe (ConErrDevicePaths, &gEfiSimpleTextOutProtocolGuid, &gST->StandardErrorHandle, (VOID **)&gST->StdErr);
|
|
if (EFI_ERROR(Status)) {
|
|
// In case of error, we reuse the console output for the error output
|
|
gST->StandardErrorHandle = gST->ConsoleOutHandle;
|
|
gST->StdErr = gST->ConOut;
|
|
}
|
|
|
|
// Free Memory allocated for reading the UEFI Variables
|
|
if (ConOutDevicePaths) {
|
|
FreePool (ConOutDevicePaths);
|
|
}
|
|
if (ConInDevicePaths) {
|
|
FreePool (ConInDevicePaths);
|
|
}
|
|
if (ConErrDevicePaths) {
|
|
FreePool (ConErrDevicePaths);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
DefineDefaultBootEntries (
|
|
VOID
|
|
)
|
|
{
|
|
BDS_LOAD_OPTION* BdsLoadOption;
|
|
UINTN Size;
|
|
EFI_STATUS Status;
|
|
EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL* EfiDevicePathFromTextProtocol;
|
|
EFI_DEVICE_PATH* BootDevicePath;
|
|
ARM_BDS_LOADER_ARGUMENTS* BootArguments;
|
|
ARM_BDS_LOADER_TYPE BootType;
|
|
EFI_DEVICE_PATH* InitrdPath;
|
|
UINTN CmdLineSize;
|
|
UINTN InitrdSize;
|
|
|
|
//
|
|
// If Boot Order does not exist then create a default entry
|
|
//
|
|
Size = 0;
|
|
Status = gRT->GetVariable (L"BootOrder", &gEfiGlobalVariableGuid, NULL, &Size, NULL);
|
|
if (Status == EFI_NOT_FOUND) {
|
|
if ((PcdGetPtr(PcdDefaultBootDevicePath) == NULL) || (StrLen ((CHAR16*)PcdGetPtr(PcdDefaultBootDevicePath)) == 0)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);
|
|
if (EFI_ERROR(Status)) {
|
|
// You must provide an implementation of DevicePathFromTextProtocol in your firmware (eg: DevicePathDxe)
|
|
DEBUG((EFI_D_ERROR,"Error: Bds requires DevicePathFromTextProtocol\n"));
|
|
return Status;
|
|
}
|
|
BootDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ((CHAR16*)PcdGetPtr(PcdDefaultBootDevicePath));
|
|
|
|
DEBUG_CODE_BEGIN();
|
|
// We convert back to the text representation of the device Path to see if the initial text is correct
|
|
EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
|
|
CHAR16* DevicePathTxt;
|
|
|
|
Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
|
|
ASSERT_EFI_ERROR(Status);
|
|
DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (BootDevicePath, TRUE, TRUE);
|
|
|
|
ASSERT (StrCmp ((CHAR16*)PcdGetPtr(PcdDefaultBootDevicePath), DevicePathTxt) == 0);
|
|
|
|
FreePool (DevicePathTxt);
|
|
DEBUG_CODE_END();
|
|
|
|
// Create the entry is the Default values are correct
|
|
if (BootDevicePath != NULL) {
|
|
BootType = (ARM_BDS_LOADER_TYPE)PcdGet32 (PcdDefaultBootType);
|
|
|
|
if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_FDT)) {
|
|
CmdLineSize = AsciiStrSize ((CHAR8*)PcdGetPtr(PcdDefaultBootArgument));
|
|
InitrdPath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ((CHAR16*)PcdGetPtr(PcdDefaultBootInitrdPath));
|
|
InitrdSize = GetDevicePathSize (InitrdPath);
|
|
|
|
BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool (sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize);
|
|
BootArguments->LinuxArguments.CmdLineSize = CmdLineSize;
|
|
BootArguments->LinuxArguments.InitrdSize = InitrdSize;
|
|
|
|
CopyMem ((VOID*)(BootArguments + 1), (CHAR8*)PcdGetPtr(PcdDefaultBootArgument), CmdLineSize);
|
|
CopyMem ((VOID*)((UINTN)(BootArguments + 1) + CmdLineSize), InitrdPath, InitrdSize);
|
|
} else {
|
|
BootArguments = NULL;
|
|
}
|
|
|
|
BootOptionCreate (LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_BOOT,
|
|
(CHAR16*)PcdGetPtr(PcdDefaultBootDescription),
|
|
BootDevicePath,
|
|
BootType,
|
|
BootArguments,
|
|
&BdsLoadOption
|
|
);
|
|
FreePool (BdsLoadOption);
|
|
} else {
|
|
Status = EFI_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
StartDefaultBootOnTimeout (
|
|
VOID
|
|
)
|
|
{
|
|
UINTN Size;
|
|
UINT16 Timeout;
|
|
UINT16 *TimeoutPtr;
|
|
EFI_EVENT WaitList[2];
|
|
UINTN WaitIndex;
|
|
UINT16 *BootOrder;
|
|
UINTN BootOrderSize;
|
|
UINTN Index;
|
|
CHAR16 BootVariableName[9];
|
|
EFI_STATUS Status;
|
|
EFI_INPUT_KEY Key;
|
|
|
|
Size = sizeof(UINT16);
|
|
Timeout = (UINT16)PcdGet16 (PcdPlatformBootTimeOut);
|
|
TimeoutPtr = &Timeout;
|
|
GetEnvironmentVariable (L"Timeout", &Timeout, &Size, (VOID**)&TimeoutPtr);
|
|
|
|
if (Timeout != 0xFFFF) {
|
|
if (Timeout > 0) {
|
|
// Create the waiting events (keystroke and 1sec timer)
|
|
gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &WaitList[0]);
|
|
gBS->SetTimer (WaitList[0], TimerPeriodic, EFI_SET_TIMER_TO_SECOND);
|
|
WaitList[1] = gST->ConIn->WaitForKey;
|
|
|
|
// Start the timer
|
|
WaitIndex = 0;
|
|
Print(L"The default boot selection will start in ");
|
|
while ((Timeout > 0) && (WaitIndex == 0)) {
|
|
Print(L"%3d seconds",Timeout);
|
|
gBS->WaitForEvent (2, WaitList, &WaitIndex);
|
|
if (WaitIndex == 0) {
|
|
Print(L"\b\b\b\b\b\b\b\b\b\b\b");
|
|
Timeout--;
|
|
}
|
|
}
|
|
// Discard key in the buffer
|
|
do {
|
|
Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
|
|
} while(!EFI_ERROR(Status));
|
|
gBS->CloseEvent (WaitList[0]);
|
|
Print(L"\n\r");
|
|
}
|
|
|
|
// In case of Timeout we start the default boot selection
|
|
if (Timeout == 0) {
|
|
// Get the Boot Option Order from the environment variable (a default value should have been created)
|
|
GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);
|
|
|
|
for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
|
|
UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BootOrder[Index]);
|
|
Status = BdsStartBootOption (BootVariableName);
|
|
if(!EFI_ERROR(Status)){
|
|
// Boot option returned successfully, hence don't need to start next boot option
|
|
break;
|
|
}
|
|
// In case of success, we should not return from this call.
|
|
}
|
|
FreePool (BootOrder);
|
|
}
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
This function uses policy data from the platform to determine what operating
|
|
system or system utility should be loaded and invoked. This function call
|
|
also optionally make the use of user input to determine the operating system
|
|
or system utility to be loaded and invoked. When the DXE Core has dispatched
|
|
all the drivers on the dispatch queue, this function is called. This
|
|
function will attempt to connect the boot devices required to load and invoke
|
|
the selected operating system or system utility. During this process,
|
|
additional firmware volumes may be discovered that may contain addition DXE
|
|
drivers that can be dispatched by the DXE Core. If a boot device cannot be
|
|
fully connected, this function calls the DXE Service Dispatch() to allow the
|
|
DXE drivers from any newly discovered firmware volumes to be dispatched.
|
|
Then the boot device connection can be attempted again. If the same boot
|
|
device connection operation fails twice in a row, then that boot device has
|
|
failed, and should be skipped. This function should never return.
|
|
|
|
@param This The EFI_BDS_ARCH_PROTOCOL instance.
|
|
|
|
@return None.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
BdsEntry (
|
|
IN EFI_BDS_ARCH_PROTOCOL *This
|
|
)
|
|
{
|
|
UINTN Size;
|
|
EFI_STATUS Status;
|
|
UINT16 *BootNext;
|
|
UINTN BootNextSize;
|
|
CHAR16 BootVariableName[9];
|
|
|
|
PERF_END (NULL, "DXE", NULL, 0);
|
|
|
|
//
|
|
// Declare the Firmware Vendor
|
|
//
|
|
if (FixedPcdGetPtr(PcdFirmwareVendor) != NULL) {
|
|
Size = 0x100;
|
|
gST->FirmwareVendor = AllocateRuntimePool (Size);
|
|
ASSERT (gST->FirmwareVendor != NULL);
|
|
UnicodeSPrint (gST->FirmwareVendor, Size, L"%a EFI %a %a", PcdGetPtr(PcdFirmwareVendor), __DATE__, __TIME__);
|
|
}
|
|
|
|
// If BootNext environment variable is defined then we just load it !
|
|
BootNextSize = sizeof(UINT16);
|
|
Status = GetEnvironmentVariable (L"BootNext", NULL, &BootNextSize, (VOID**)&BootNext);
|
|
if (!EFI_ERROR(Status)) {
|
|
ASSERT(BootNextSize == sizeof(UINT16));
|
|
|
|
// Generate the requested Boot Entry variable name
|
|
UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", *BootNext);
|
|
|
|
// Set BootCurrent variable
|
|
gRT->SetVariable (L"BootCurrent", &gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
|
|
BootNextSize, BootNext);
|
|
|
|
FreePool (BootNext);
|
|
|
|
// Start the requested Boot Entry
|
|
Status = BdsStartBootOption (BootVariableName);
|
|
if (Status != EFI_NOT_FOUND) {
|
|
// BootNext has not been succeeded launched
|
|
if (EFI_ERROR(Status)) {
|
|
Print(L"Fail to start BootNext.\n");
|
|
}
|
|
|
|
// Delete the BootNext environment variable
|
|
gRT->SetVariable (L"BootNext", &gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
|
|
0, NULL);
|
|
}
|
|
|
|
// Clear BootCurrent variable
|
|
gRT->SetVariable (L"BootCurrent", &gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
|
|
0, NULL);
|
|
}
|
|
|
|
// If Boot Order does not exist then create a default entry
|
|
DefineDefaultBootEntries ();
|
|
|
|
// Now we need to setup the EFI System Table with information about the console devices.
|
|
InitializeConsole ();
|
|
|
|
// Timer before initiating the default boot selection
|
|
StartDefaultBootOnTimeout ();
|
|
|
|
// Start the Boot Menu
|
|
Status = BootMenuMain ();
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
}
|
|
|
|
EFI_BDS_ARCH_PROTOCOL gBdsProtocol = {
|
|
BdsEntry,
|
|
};
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BdsInitialize (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
mImageHandle = ImageHandle;
|
|
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&ImageHandle,
|
|
&gEfiBdsArchProtocolGuid, &gBdsProtocol,
|
|
NULL
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
return Status;
|
|
}
|