diff --git a/ArmPlatformPkg/ArmPlatformPkg.dec b/ArmPlatformPkg/ArmPlatformPkg.dec index 1d9c05a233..7a9223bc6c 100644 --- a/ArmPlatformPkg/ArmPlatformPkg.dec +++ b/ArmPlatformPkg/ArmPlatformPkg.dec @@ -71,3 +71,24 @@ gArmPlatformTokenSpaceGuid.PcdSP804FrequencyInMHz|1|UINT32|0x0000001D gArmPlatformTokenSpaceGuid.PcdSP804Timer0InterruptNum|0|UINT32|0x0000001E + # + # BDS - Boot Manager + # + gArmPlatformTokenSpaceGuid.PcdFirmwareVendor|"ARM Platform"|VOID*|0x00000019 + gArmPlatformTokenSpaceGuid.PcdDefaultBootDescription|L""|VOID*|0x0000000C + gArmPlatformTokenSpaceGuid.PcdDefaultBootDevicePath|L""|VOID*|0x0000000D + gArmPlatformTokenSpaceGuid.PcdDefaultBootArgument|""|VOID*|0x000000F + # PcdDefaultBootType define the type of the binary pointed by PcdDefaultBootDevicePath: + # - 0 = an EFI application + # - 1 = a Linux kernel with ATAG support + # - 2 = a Linux kernel with FDT support + gArmPlatformTokenSpaceGuid.PcdDefaultBootType|0|UINT32|0x00000010 + gArmPlatformTokenSpaceGuid.PcdFdtDevicePath|L""|VOID*|0x00000011 + + ## Timeout value for displaying progressing bar in before boot OS. + # According to UEFI 2.0 spec, the default TimeOut should be 0xffff. + gArmPlatformTokenSpaceGuid.PcdPlatformBootTimeOut|0xffff|UINT16|0x0000001A + + gArmPlatformTokenSpaceGuid.PcdDefaultConInPaths|L""|VOID*|0x0000001B + gArmPlatformTokenSpaceGuid.PcdDefaultConOutPaths|L""|VOID*|0x0000001C + diff --git a/ArmPlatformPkg/ArmRealViewEbPkg/ArmRealViewEb-RTSM-A8.dsc b/ArmPlatformPkg/ArmRealViewEbPkg/ArmRealViewEb-RTSM-A8.dsc index b64f784bc9..533acd215d 100644 --- a/ArmPlatformPkg/ArmRealViewEbPkg/ArmRealViewEb-RTSM-A8.dsc +++ b/ArmPlatformPkg/ArmRealViewEbPkg/ArmRealViewEb-RTSM-A8.dsc @@ -262,6 +262,8 @@ !endif [PcdsFixedAtBuild.common] + gArmPlatformTokenSpaceGuid.PcdFirmwareVendor|"ARM RealView Emulation Board" + gEmbeddedTokenSpaceGuid.PcdEmbeddedPrompt|"ArmRealViewEb-A8" gEmbeddedTokenSpaceGuid.PcdPrePiCpuMemorySize|32 gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize|0 @@ -381,11 +383,11 @@ # # ARM OS Loader # - # Versatile Express machine type (ARM VERSATILE EXPRESS = 2272) required for ARM Linux: - gArmTokenSpaceGuid.PcdArmMachineType|2272 - gArmTokenSpaceGuid.PcdLinuxKernelDP|L"VenHw(02118005-9DA7-443a-92D5-781F022AEDBB)/MemoryMapped(0,0x46000000,0x46400000)" - gArmTokenSpaceGuid.PcdLinuxAtag|"rdinit=/bin/ash debug earlyprintk console=ttyAMA0,38400 mem=1G" - gArmTokenSpaceGuid.PcdFdtDP|L"" + gArmTokenSpaceGuid.PcdArmMachineType|827 + gArmPlatformTokenSpaceGuid.PcdDefaultBootDescription|L"SemiHosting" + gArmPlatformTokenSpaceGuid.PcdDefaultBootDevicePath|L"VenHw(C5B9C74A-6D72-4719-99AB-C59F199091EB)/zImage-RTSM" + gArmPlatformTokenSpaceGuid.PcdDefaultBootArgument|"rdinit=/bin/ash debug earlyprintk console=ttyAMA0,38400 mem=128M" + gArmPlatformTokenSpaceGuid.PcdDefaultBootType|1 # # ARM L2x0 PCDs diff --git a/ArmPlatformPkg/ArmRealViewEbPkg/ArmRealViewEb-RTSM-A9x2.dsc b/ArmPlatformPkg/ArmRealViewEbPkg/ArmRealViewEb-RTSM-A9x2.dsc index 9254c01514..8245cc750e 100644 --- a/ArmPlatformPkg/ArmRealViewEbPkg/ArmRealViewEb-RTSM-A9x2.dsc +++ b/ArmPlatformPkg/ArmRealViewEbPkg/ArmRealViewEb-RTSM-A9x2.dsc @@ -264,6 +264,8 @@ !endif [PcdsFixedAtBuild.common] + gArmPlatformTokenSpaceGuid.PcdFirmwareVendor|"ARM RealView Emulation Board" + gEmbeddedTokenSpaceGuid.PcdEmbeddedPrompt|"ArmRealViewEb-A9x2" gEmbeddedTokenSpaceGuid.PcdPrePiCpuMemorySize|32 gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize|0 @@ -385,11 +387,11 @@ # # ARM OS Loader # - # Versatile Express machine type (ARM VERSATILE EXPRESS = 2272) required for ARM Linux: - gArmTokenSpaceGuid.PcdArmMachineType|2272 - gArmTokenSpaceGuid.PcdLinuxKernelDP|L"VenHw(02118005-9DA7-443a-92D5-781F022AEDBB)/MemoryMapped(0,0x46000000,0x46400000)" - gArmTokenSpaceGuid.PcdLinuxAtag|"rdinit=/bin/ash debug earlyprintk console=ttyAMA0,38400 mem=1G" - gArmTokenSpaceGuid.PcdFdtDP|L"" + gArmTokenSpaceGuid.PcdArmMachineType|827 + gArmPlatformTokenSpaceGuid.PcdDefaultBootDescription|L"SemiHosting" + gArmPlatformTokenSpaceGuid.PcdDefaultBootDevicePath|L"VenHw(C5B9C74A-6D72-4719-99AB-C59F199091EB)/zImage" + gArmPlatformTokenSpaceGuid.PcdDefaultBootArgument|"rdinit=/bin/ash debug earlyprintk console=ttyAMA0,38400 mem=128M" + gArmPlatformTokenSpaceGuid.PcdDefaultBootType|1 # # ARM L2x0 PCDs diff --git a/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-CTA9x4.dsc b/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-CTA9x4.dsc index 7f88fccc68..7dda73f9b2 100644 --- a/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-CTA9x4.dsc +++ b/ArmPlatformPkg/ArmVExpressPkg/ArmVExpress-CTA9x4.dsc @@ -285,6 +285,8 @@ !endif [PcdsFixedAtBuild.common] + gArmPlatformTokenSpaceGuid.PcdFirmwareVendor|"ARM Versatile Express" + gEmbeddedTokenSpaceGuid.PcdEmbeddedPrompt|"ArmVExpress" gEmbeddedTokenSpaceGuid.PcdPrePiCpuMemorySize|32 gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize|0 @@ -411,9 +413,10 @@ # # Versatile Express machine type (ARM VERSATILE EXPRESS = 2272) required for ARM Linux: gArmTokenSpaceGuid.PcdArmMachineType|2272 - gArmTokenSpaceGuid.PcdLinuxKernelDP|L"VenHw(02118005-9DA7-443a-92D5-781F022AEDBB)/MemoryMapped(0,0x46000000,0x46400000)" - gArmTokenSpaceGuid.PcdLinuxAtag|"rdinit=/bin/ash debug earlyprintk console=ttyAMA0,38400 mem=1G" - gArmTokenSpaceGuid.PcdFdtDP|L"" + gArmPlatformTokenSpaceGuid.PcdDefaultBootDescription|L"NorFlash" + gArmPlatformTokenSpaceGuid.PcdDefaultBootDevicePath|L"VenHw(1F15DA3C-37FF-4070-B471-BB4AF12A724A)/MemoryMapped(0x0,0x46000000,0x46400000)" + gArmPlatformTokenSpaceGuid.PcdDefaultBootArgument|"rdinit=/bin/ash debug earlyprintk console=ttyAMA0,38400 mem=1G" + gArmPlatformTokenSpaceGuid.PcdDefaultBootType|1 # # ARM L2x0 PCDs diff --git a/ArmPlatformPkg/Bds/Bds.c b/ArmPlatformPkg/Bds/Bds.c new file mode 100644 index 0000000000..415f0a9cb2 --- /dev/null +++ b/ArmPlatformPkg/Bds/Bds.c @@ -0,0 +1,398 @@ +/** @file +* +* Copyright (c) 2011, 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 +#include + +#include + +#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)) { + 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) { + Status = 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 EFI_SUCCESS; +} + +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 + do { + 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; + + ASSERT_EFI_ERROR(gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol)); + 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); + } + } while (ConsoleDevicePaths != NULL); + + // 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; + } + + 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; + + // + // 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) { + Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol); + ASSERT_EFI_ERROR(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) { + BootOptionCreate (LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_BOOT, + (CHAR16*)PcdGetPtr(PcdDefaultBootDescription), + BootDevicePath, + (BDS_LOADER_TYPE)PcdGet32 (PcdDefaultBootType), + (CHAR8*)PcdGetPtr(PcdDefaultBootArgument), + &BdsLoadOption + ); + FreePool (BdsLoadOption); + } + } + + 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; + + 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--; + } + } + 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. + } + } + } + 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; + + PERF_END (NULL, "DXE", NULL, 0); + + // + // Declare the Firmware Vendor + // + 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 ! + Status = BdsStartBootOption (L"BootNext"); + 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); + } + + // 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; +} diff --git a/ArmPlatformPkg/Bds/Bds.inf b/ArmPlatformPkg/Bds/Bds.inf index dbd26aedb4..5749e7c61b 100644 --- a/ArmPlatformPkg/Bds/Bds.inf +++ b/ArmPlatformPkg/Bds/Bds.inf @@ -2,7 +2,7 @@ # # Component discription file for NorFlashDxe module # -# Copyright (c) 2010, ARM Ltd. All rights reserved.
+# Copyright (c) 2011, ARM Ltd. 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 @@ -24,30 +24,49 @@ ENTRY_POINT = BdsInitialize [Sources.common] - BdsEntry.c + Bds.c + BdsHelper.c + BootMenu.c + BootOption.c + BootOptionSupport.c [Packages] MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec ArmPkg/ArmPkg.dec + ArmPlatformPkg/ArmPlatformPkg.dec [LibraryClasses] BdsLib TimerLib PerformanceLib UefiBootServicesTableLib + DxeServicesTableLib + UefiDriverEntryPoint DebugLib + PrintLib [Guids] + gEfiFileSystemInfoGuid [Protocols] gEfiBdsArchProtocolGuid + gEfiBlockIoProtocolGuid gEfiSimpleTextInProtocolGuid + gEfiPxeBaseCodeProtocolGuid + gEfiSimpleNetworkProtocolGuid gEfiDevicePathToTextProtocolGuid [Pcd] - gArmTokenSpaceGuid.PcdLinuxKernelDP - gArmTokenSpaceGuid.PcdLinuxAtag - gArmTokenSpaceGuid.PcdFdtDP - + gArmPlatformTokenSpaceGuid.PcdFirmwareVendor + gArmPlatformTokenSpaceGuid.PcdDefaultBootDescription + gArmPlatformTokenSpaceGuid.PcdDefaultBootDevicePath + gArmPlatformTokenSpaceGuid.PcdDefaultBootArgument + gArmPlatformTokenSpaceGuid.PcdDefaultBootType + gArmPlatformTokenSpaceGuid.PcdFdtDevicePath + gArmPlatformTokenSpaceGuid.PcdPlatformBootTimeOut + gArmPlatformTokenSpaceGuid.PcdDefaultConInPaths + gArmPlatformTokenSpaceGuid.PcdDefaultConOutPaths + [Depex] TRUE diff --git a/ArmPlatformPkg/Bds/BdsEntry.c b/ArmPlatformPkg/Bds/BdsEntry.c deleted file mode 100644 index 62e8236e84..0000000000 --- a/ArmPlatformPkg/Bds/BdsEntry.c +++ /dev/null @@ -1,332 +0,0 @@ -/** @file -* -* Copyright (c) 2011, 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#define MAX_CMD_LINE 256 - -VOID -EFIAPI -BdsEntry ( - IN EFI_BDS_ARCH_PROTOCOL *This - ); - -EFI_HANDLE mBdsImageHandle = NULL; -EFI_BDS_ARCH_PROTOCOL gBdsProtocol = { - BdsEntry, -}; - -EFI_STATUS GetEnvironmentVariable ( - IN CONST CHAR16* VariableName, - IN VOID* DefaultValue, - IN UINTN DefaultSize, - OUT VOID** Value) -{ - EFI_STATUS Status; - UINTN Size; - - // Try to get the variable size. - *Value = NULL; - Size = 0; - Status = gRT->GetVariable ((CHAR16 *) VariableName, &gEfiGlobalVariableGuid, NULL, &Size, *Value); - if (Status == EFI_NOT_FOUND) { - // If the environment variable does not exist yet then set it with the default value - Status = gRT->SetVariable ( - (CHAR16*)VariableName, - &gEfiGlobalVariableGuid, - EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, - DefaultSize, - DefaultValue - ); - *Value = DefaultValue; - } else if (Status == EFI_BUFFER_TOO_SMALL) { - // Get the environment variable value - *Value = AllocatePool (Size); - if (*Value == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - Status = gRT->GetVariable ((CHAR16 *)VariableName, &gEfiGlobalVariableGuid, NULL, &Size, *Value); - if (EFI_ERROR (Status)) { - FreePool(*Value); - return EFI_INVALID_PARAMETER; - } - } else { - *Value = DefaultValue; - return Status; - } - - return EFI_SUCCESS; -} - -EFI_STATUS -InitializeConsole ( - VOID - ) -{ - EFI_STATUS Status; - UINTN NoHandles; - EFI_HANDLE *Buffer; - BOOLEAN AllDriversConnected; - - AllDriversConnected = FALSE; - - // - // Now we need to setup the EFI System Table with information about the console devices. - // This code is normally in the console spliter driver on platforms that support multiple - // consoles at the same time - // - Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleTextOutProtocolGuid, NULL, &NoHandles, &Buffer); - if (EFI_ERROR (Status)) { - BdsConnectAllDrivers(); - AllDriversConnected = TRUE; - Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleTextOutProtocolGuid, NULL, &NoHandles, &Buffer); - } - - if (!EFI_ERROR (Status)) { - // Use the first SimpleTextOut we find and update the EFI System Table - gST->ConsoleOutHandle = Buffer[0]; - gST->StandardErrorHandle = Buffer[0]; - Status = gBS->HandleProtocol (Buffer[0], &gEfiSimpleTextOutProtocolGuid, (VOID **)&gST->ConOut); - ASSERT_EFI_ERROR (Status); - - gST->StdErr = gST->ConOut; - - FreePool (Buffer); - } else { - return Status; - } - - Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleTextInProtocolGuid, NULL, &NoHandles, &Buffer); - if (EFI_ERROR (Status) && !AllDriversConnected) { - BdsConnectAllDrivers(); - Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleTextInProtocolGuid, NULL, &NoHandles, &Buffer); - } - - if (!EFI_ERROR (Status)) { - // Use the first SimpleTextIn we find and update the EFI System Table - gST->ConsoleInHandle = Buffer[0]; - Status = gBS->HandleProtocol (Buffer[0], &gEfiSimpleTextInProtocolGuid, (VOID **)&gST->ConIn); - ASSERT_EFI_ERROR (Status); - - FreePool (Buffer); - } else { - return Status; - } - - return EFI_SUCCESS; -} - -EFI_STATUS -GetHIInputAscii ( - CHAR8 *CmdLine, - UINTN MaxCmdLine -) { - UINTN CmdLineIndex; - UINTN WaitIndex; - CHAR8 Char; - EFI_INPUT_KEY Key; - EFI_STATUS Status; - - CmdLine[0] = '\0'; - - for (CmdLineIndex = 0; CmdLineIndex < MaxCmdLine; ) { - Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &WaitIndex); - ASSERT_EFI_ERROR (Status); - - Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); - ASSERT_EFI_ERROR (Status); - - Char = (CHAR8)Key.UnicodeChar; - if ((Char == '\n') || (Char == '\r') || (Char == 0x7f)) { - CmdLine[CmdLineIndex] = '\0'; - AsciiPrint ("\n"); - return EFI_SUCCESS; - } else if ((Char == '\b') || (Key.ScanCode == SCAN_LEFT) || (Key.ScanCode == SCAN_DELETE)){ - if (CmdLineIndex != 0) { - CmdLineIndex--; - AsciiPrint ("\b \b"); - } - } else { - CmdLine[CmdLineIndex++] = Char; - AsciiPrint ("%c", Char); - } - } - - return EFI_SUCCESS; -} - -VOID -ListDevicePaths ( - IN BOOLEAN AllDrivers -) { - EFI_STATUS Status; - UINTN HandleCount; - EFI_HANDLE *HandleBuffer; - UINTN Index; - EFI_DEVICE_PATH_PROTOCOL* DevicePathProtocol; - CHAR16* String; - EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* EfiDevicePathToTextProtocol; - - if (AllDrivers) { - BdsConnectAllDrivers(); - } - - Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&EfiDevicePathToTextProtocol); - if (EFI_ERROR (Status)) { - AsciiPrint ("Did not find the DevicePathToTextProtocol.\n"); - return; - } - - Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiDevicePathProtocolGuid, NULL, &HandleCount, &HandleBuffer); - if (EFI_ERROR (Status)) { - AsciiPrint ("No device path found\n"); - return; - } - - for (Index = 0; Index < HandleCount; Index++) { - Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathProtocol); - String = EfiDevicePathToTextProtocol->ConvertDevicePathToText(DevicePathProtocol,TRUE,TRUE); - Print (L"\t- [%d] %s\n",Index, String); - } -} - -INTN BdsComparefile ( - IN CHAR16 *DeviceFilePath1, - IN CHAR16 *DeviceFilePath2, - VOID **FileImage1,VOID **FileImage2,UINTN* FileSize - ); - -/** - 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 - ) -{ - EFI_STATUS Status; - CHAR8 CmdLine[MAX_CMD_LINE]; - VOID* DefaultVariableValue; - UINTN DefaultVariableSize; - CHAR16 *LinuxKernelDP; - CHAR8 *LinuxAtag; - CHAR16 *FdtDP; - - PERF_END (NULL, "DXE", NULL, 0); - PERF_START (NULL, "BDS", NULL, 0); - - Status = InitializeConsole(); - ASSERT_EFI_ERROR(Status); - - while (1) { - // Get the Linux Kernel Device Path from Environment Variable - DefaultVariableValue = (VOID*)PcdGetPtr(PcdLinuxKernelDP); - DefaultVariableSize = StrSize((CHAR16*)DefaultVariableValue); - GetEnvironmentVariable(L"LinuxKernelDP",DefaultVariableValue,DefaultVariableSize,(VOID**)&LinuxKernelDP); - - // Get the Linux ATAG from Environment Variable - DefaultVariableValue = (VOID*)PcdGetPtr(PcdLinuxAtag); - DefaultVariableSize = AsciiStrSize((CHAR8*)DefaultVariableValue); - GetEnvironmentVariable(L"LinuxAtag",DefaultVariableValue,DefaultVariableSize,(VOID**)&LinuxAtag); - - // Get the FDT Device Path from Environment Variable - DefaultVariableValue = (VOID*)PcdGetPtr(PcdFdtDP); - DefaultVariableSize = StrSize((CHAR16*)DefaultVariableValue); - GetEnvironmentVariable(L"FdtDP",DefaultVariableValue,DefaultVariableSize,(VOID**)&FdtDP); - - AsciiPrint ("1. Start EBL\n\r"); - AsciiPrint ("2. List Device Paths of all the drivers\n"); - AsciiPrint ("3. Start Linux\n"); - Print (L"\t- Kernel: %s\n", LinuxKernelDP); - AsciiPrint ("\t- Atag: %a\n", LinuxAtag); - Print (L"\t- Fdt: %s\n", FdtDP); - AsciiPrint ("Choice: "); - - Status = GetHIInputAscii(CmdLine,MAX_CMD_LINE); - ASSERT_EFI_ERROR (Status); - if (AsciiStrCmp(CmdLine,"1") == 0) { - // Start EBL - Status = BdsLoadApplication(L"Ebl"); - if (Status == EFI_NOT_FOUND) { - AsciiPrint ("Error: EFI Application not found.\n"); - } else { - AsciiPrint ("Error: Status Code: 0x%X\n",(UINT32)Status); - } - } else if (AsciiStrCmp(CmdLine,"2") == 0) { - ListDevicePaths (TRUE); - } else if (AsciiStrCmp(CmdLine,"3") == 0) { - // Start Linux Kernel - Status = BdsBootLinux(LinuxKernelDP,LinuxAtag,FdtDP); - if (EFI_ERROR(Status)) { - AsciiPrint ("Error: Fail to start Linux (0x%X)\n",(UINT32)Status); - } - } else { - AsciiPrint ("Error: Invalid choice.\n"); - } - } -} - -EFI_STATUS -EFIAPI -BdsInitialize ( - IN EFI_HANDLE ImageHandle, - IN EFI_SYSTEM_TABLE *SystemTable - ) -{ - EFI_STATUS Status; - - mBdsImageHandle = ImageHandle; - - Status = gBS->InstallMultipleProtocolInterfaces ( - &mBdsImageHandle, - &gEfiBdsArchProtocolGuid, &gBdsProtocol, - NULL - ); - ASSERT_EFI_ERROR (Status); - - return Status; -} diff --git a/ArmPlatformPkg/Bds/BdsHelper.c b/ArmPlatformPkg/Bds/BdsHelper.c new file mode 100644 index 0000000000..2f0cb31d39 --- /dev/null +++ b/ArmPlatformPkg/Bds/BdsHelper.c @@ -0,0 +1,307 @@ +/** @file +* +* Copyright (c) 2011, 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" + +EFI_STATUS +GetEnvironmentVariable ( + IN CONST CHAR16* VariableName, + IN VOID* DefaultValue, + IN OUT UINTN* Size, + OUT VOID** Value + ) +{ + EFI_STATUS Status; + UINTN VariableSize; + + // Try to get the variable size. + *Value = NULL; + VariableSize = 0; + Status = gRT->GetVariable ((CHAR16 *) VariableName, &gEfiGlobalVariableGuid, NULL, &VariableSize, *Value); + if (Status == EFI_NOT_FOUND) { + if ((DefaultValue != NULL) && (Size != NULL) && (*Size != 0)) { + // If the environment variable does not exist yet then set it with the default value + Status = gRT->SetVariable ( + (CHAR16*)VariableName, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + *Size, + DefaultValue + ); + *Value = DefaultValue; + } else { + return EFI_NOT_FOUND; + } + } else if (Status == EFI_BUFFER_TOO_SMALL) { + // Get the environment variable value + *Value = AllocatePool (VariableSize); + if (*Value == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gRT->GetVariable ((CHAR16 *)VariableName, &gEfiGlobalVariableGuid, NULL, &VariableSize, *Value); + if (EFI_ERROR (Status)) { + FreePool(*Value); + return EFI_INVALID_PARAMETER; + } + + if (Size) { + *Size = VariableSize; + } + } else { + *Value = DefaultValue; + return Status; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EditHIInputAscii ( + IN OUT CHAR8 *CmdLine, + IN UINTN MaxCmdLine + ) +{ + UINTN CmdLineIndex; + UINTN WaitIndex; + CHAR8 Char; + EFI_INPUT_KEY Key; + EFI_STATUS Status; + + AsciiPrint (CmdLine); + + for (CmdLineIndex = AsciiStrLen(CmdLine); CmdLineIndex < MaxCmdLine; ) { + Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &WaitIndex); + ASSERT_EFI_ERROR (Status); + + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + ASSERT_EFI_ERROR (Status); + + // Unicode character is valid when Scancode is NUll + if (Key.ScanCode == SCAN_NULL) { + // Scan code is NUll, hence read Unicode character + Char = (CHAR8)Key.UnicodeChar; + } else { + Char = CHAR_NULL; + } + + if ((Char == CHAR_LINEFEED) || (Char == CHAR_CARRIAGE_RETURN) || (Char == 0x7f)) { + CmdLine[CmdLineIndex] = '\0'; + AsciiPrint ("\n\r"); + + return EFI_SUCCESS; + } else if ((Char == '\b') || (Key.ScanCode == SCAN_LEFT) || (Key.ScanCode == SCAN_DELETE)){ + if (CmdLineIndex != 0) { + CmdLineIndex--; + AsciiPrint ("\b \b"); + } + } else if ((Key.ScanCode == SCAN_ESC) || (Char == 0x1B) || (Char == 0x0)) { + return EFI_INVALID_PARAMETER; + } else { + CmdLine[CmdLineIndex++] = Char; + AsciiPrint ("%c", Char); + } + } + + return EFI_SUCCESS; +} + +EFI_STATUS +GetHIInputAscii ( + IN OUT CHAR8 *CmdLine, + IN UINTN MaxCmdLine + ) +{ + // For a new input just passed an empty string + CmdLine[0] = '\0'; + + return EditHIInputAscii (CmdLine,MaxCmdLine); +} + +EFI_STATUS +GetHIInputInteger ( + OUT UINTN *Integer + ) +{ + CHAR8 CmdLine[255]; + EFI_STATUS Status; + + CmdLine[0] = '\0'; + Status = EditHIInputAscii (CmdLine,255); + if (!EFI_ERROR(Status)) { + *Integer = AsciiStrDecimalToUintn (CmdLine); + } + + return Status; +} + +EFI_STATUS +GetHIInputIP ( + OUT EFI_IP_ADDRESS *Ip + ) +{ + CHAR8 CmdLine[255]; + CHAR8 *Str; + EFI_STATUS Status; + + CmdLine[0] = '\0'; + Status = EditHIInputAscii (CmdLine,255); + if (!EFI_ERROR(Status)) { + Str = CmdLine; + Ip->v4.Addr[0] = (UINT8)AsciiStrDecimalToUintn (Str); + + Str = AsciiStrStr (Str, "."); + if (Str == NULL) { + return EFI_INVALID_PARAMETER; + } + + Ip->v4.Addr[1] = (UINT8)AsciiStrDecimalToUintn (++Str); + + Str = AsciiStrStr (Str, "."); + if (Str == NULL) { + return EFI_INVALID_PARAMETER; + } + + Ip->v4.Addr[2] = (UINT8)AsciiStrDecimalToUintn (++Str); + + Str = AsciiStrStr (Str, "."); + if (Str == NULL) { + return EFI_INVALID_PARAMETER; + } + + Ip->v4.Addr[3] = (UINT8)AsciiStrDecimalToUintn (++Str); + } + + return Status; +} + +EFI_STATUS +GetHIInputBoolean ( + OUT BOOLEAN *Value + ) +{ + CHAR8 CmdBoolean[2]; + EFI_STATUS Status; + + while(1) { + Print (L"[y/n] "); + Status = GetHIInputAscii (CmdBoolean,2); + if (EFI_ERROR(Status)) { + return Status; + } else if ((CmdBoolean[0] == 'y') || (CmdBoolean[0] == 'Y')) { + if (Value) *Value = TRUE; + return EFI_SUCCESS; + } else if ((CmdBoolean[0] == 'n') || (CmdBoolean[0] == 'N')) { + if (Value) *Value = FALSE; + return EFI_SUCCESS; + } + } +} + +BOOLEAN +HasFilePathEfiExtension ( + IN CHAR16* FilePath + ) +{ + return (StrCmp (FilePath + (StrSize(FilePath)/sizeof(CHAR16)) - 5, L".efi") == 0); +} + +// Return the last non end-type Device Path Node from a Device Path +EFI_DEVICE_PATH* +GetLastDevicePathNode ( + IN EFI_DEVICE_PATH* DevicePath + ) +{ + EFI_DEVICE_PATH* PrevDevicePathNode; + + PrevDevicePathNode = DevicePath; + while (!IsDevicePathEndType (DevicePath)) { + PrevDevicePathNode = DevicePath; + DevicePath = NextDevicePathNode (DevicePath); + } + + return PrevDevicePathNode; +} + +EFI_STATUS +GenerateDeviceDescriptionName ( + IN EFI_HANDLE Handle, + IN OUT CHAR16* Description + ) +{ + EFI_STATUS Status; + EFI_COMPONENT_NAME_PROTOCOL* ComponentName2Protocol; + EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol; + EFI_DEVICE_PATH_PROTOCOL* DevicePathProtocol; + CHAR16* DriverName; + CHAR16* DevicePathTxt; + EFI_DEVICE_PATH* DevicePathNode; + + ComponentName2Protocol = NULL; + Status = gBS->HandleProtocol (Handle, &gEfiComponentName2ProtocolGuid, (VOID **)&ComponentName2Protocol); + if (!EFI_ERROR(Status)) { + //TODO: Fixme. we must find the best langague + Status = ComponentName2Protocol->GetDriverName (ComponentName2Protocol,"en",&DriverName); + if (!EFI_ERROR(Status)) { + StrnCpy (Description,DriverName,BOOT_DEVICE_DESCRIPTION_MAX); + } + } + + if (EFI_ERROR(Status)) { + // Use the lastest non null entry of the Device path as a description + Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathProtocol); + if (EFI_ERROR(Status)) { + return Status; + } + + // Convert the last non end-type Device Path Node in text for the description + DevicePathNode = GetLastDevicePathNode (DevicePathProtocol); + Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol); + ASSERT_EFI_ERROR(Status); + DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText(DevicePathNode,TRUE,TRUE); + StrnCpy (Description, DevicePathTxt, BOOT_DEVICE_DESCRIPTION_MAX); + FreePool (DevicePathTxt); + } + + return EFI_SUCCESS; +} + +EFI_STATUS +BdsStartBootOption ( + IN CHAR16* BootOption + ) +{ + EFI_STATUS Status; + EFI_LOAD_OPTION EfiLoadOption; + UINTN EfiLoadOptionSize; + BDS_LOAD_OPTION *BdsLoadOption; + + Status = GetEnvironmentVariable (BootOption, NULL, &EfiLoadOptionSize, (VOID**)&EfiLoadOption); + if (!EFI_ERROR(Status)) { + Status = BootOptionParseLoadOption (EfiLoadOption, EfiLoadOptionSize, &BdsLoadOption); + if (!EFI_ERROR(Status)) { + Status = BootOptionStart (BdsLoadOption); + FreePool (BdsLoadOption); + } + + if (!EFI_ERROR(Status)) { + Status = EFI_SUCCESS; + } else { + Status = EFI_NOT_STARTED; + } + } else { + Status = EFI_NOT_FOUND; + } + return Status; +} diff --git a/ArmPlatformPkg/Bds/BdsInternal.h b/ArmPlatformPkg/Bds/BdsInternal.h new file mode 100644 index 0000000000..9ef36a1ff6 --- /dev/null +++ b/ArmPlatformPkg/Bds/BdsInternal.h @@ -0,0 +1,214 @@ +/** @file +* +* Copyright (c) 2011, 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. +* +**/ + +#ifndef _BDSINTERNAL_H_ +#define _BDSINTERNAL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define BOOT_DEVICE_DESCRIPTION_MAX 100 +#define BOOT_DEVICE_FILEPATH_MAX 100 +#define BOOT_DEVICE_OPTION_MAX 100 +#define BOOT_DEVICE_ADDRESS_MAX 20 + +typedef enum { + BDS_LOADER_EFI_APPLICATION = 0, + BDS_LOADER_KERNEL_LINUX_ATAG, + BDS_LOADER_KERNEL_LINUX_FDT, +} BDS_LOADER_TYPE; + +typedef struct { + BDS_LOADER_TYPE LoaderType; + CHAR8 Arguments[]; +} BDS_LOADER_OPTIONAL_DATA; + +typedef enum { + BDS_DEVICE_FILESYSTEM = 0, + BDS_DEVICE_MEMMAP, + BDS_DEVICE_PXE, + BDS_DEVICE_TFTP, + BDS_DEVICE_MAX +} BDS_SUPPORTED_DEVICE_TYPE; + +typedef struct { + LIST_ENTRY Link; + CHAR16 Description[BOOT_DEVICE_DESCRIPTION_MAX]; + EFI_DEVICE_PATH_PROTOCOL* DevicePathProtocol; + struct _BDS_LOAD_OPTION_SUPPORT* Support; +} BDS_SUPPORTED_DEVICE; + +#define SUPPORTED_BOOT_DEVICE_FROM_LINK(a) BASE_CR(a, BDS_SUPPORTED_DEVICE, Link) + +typedef UINT8* EFI_LOAD_OPTION; + +typedef struct { + LIST_ENTRY Link; + + UINT16 LoadOptionIndex; + EFI_LOAD_OPTION LoadOption; + UINTN LoadOptionSize; + + UINT32 Attributes; + UINT16 FilePathListLength; + CHAR16 *Description; + EFI_DEVICE_PATH_PROTOCOL *FilePathList; + BDS_LOADER_OPTIONAL_DATA *OptionalData; +} BDS_LOAD_OPTION; + +typedef struct _BDS_LOAD_OPTION_SUPPORT { + BDS_SUPPORTED_DEVICE_TYPE Type; + EFI_STATUS (*ListDevices)(IN OUT LIST_ENTRY* BdsLoadOptionList); + BOOLEAN (*IsSupported)(IN BDS_LOAD_OPTION* BdsLoadOption); + EFI_STATUS (*CreateDevicePathNode)(IN BDS_SUPPORTED_DEVICE* BdsLoadOption, OUT EFI_DEVICE_PATH_PROTOCOL **DevicePathNode, OUT BDS_LOADER_TYPE *BootType, OUT UINT32 *Attributes); + EFI_STATUS (*UpdateDevicePathNode)(IN BDS_LOAD_OPTION *BootOption, OUT EFI_DEVICE_PATH_PROTOCOL** NewDevicePath, OUT BDS_LOADER_TYPE *BootType, OUT UINT32 *Attributes); +} BDS_LOAD_OPTION_SUPPORT; + +#define LOAD_OPTION_FROM_LINK(a) BASE_CR(a, BDS_LOAD_OPTION, Link) + +EFI_STATUS +GetEnvironmentVariable ( + IN CONST CHAR16* VariableName, + IN VOID* DefaultValue, + IN OUT UINTN* Size, + OUT VOID** Value + ); + +EFI_STATUS +BootDeviceListSupportedInit ( + IN OUT LIST_ENTRY *SupportedDeviceList + ); + +EFI_STATUS +BootDeviceListSupportedFree ( + IN LIST_ENTRY *SupportedDeviceList + ); + +EFI_STATUS +BootDeviceGetDeviceSupport ( + IN BDS_LOAD_OPTION *BootOption, + OUT BDS_LOAD_OPTION_SUPPORT** DeviceSupport + ); + +EFI_STATUS +GetHIInputAscii ( + IN OUT CHAR8 *CmdLine, + IN UINTN MaxCmdLine + ); + +EFI_STATUS +EditHIInputAscii ( + IN OUT CHAR8 *CmdLine, + IN UINTN MaxCmdLine + ); + +EFI_STATUS +GetHIInputInteger ( + IN OUT UINTN *Integer + ); + +EFI_STATUS +GetHIInputIP ( + OUT EFI_IP_ADDRESS *Ip + ); + +EFI_STATUS +GetHIInputBoolean ( + OUT BOOLEAN *Value + ); + +BOOLEAN +HasFilePathEfiExtension ( + IN CHAR16* FilePath + ); + +EFI_DEVICE_PATH* +GetLastDevicePathNode ( + IN EFI_DEVICE_PATH* DevicePath + ); + +EFI_STATUS +BdsStartBootOption ( + IN CHAR16* BootOption + ); + +EFI_STATUS +GenerateDeviceDescriptionName ( + IN EFI_HANDLE Handle, + IN OUT CHAR16* Description + ); + +EFI_STATUS +BootOptionList ( + IN OUT LIST_ENTRY *BootOptionList + ); + +EFI_STATUS +BootOptionParseLoadOption ( + IN EFI_LOAD_OPTION EfiLoadOption, + IN UINTN EfiLoadOptionSize, + OUT BDS_LOAD_OPTION **BdsLoadOption + ); + +EFI_STATUS +BootOptionStart ( + IN BDS_LOAD_OPTION *BootOption + ); + +EFI_STATUS +BootOptionCreate ( + IN UINT32 Attributes, + IN CHAR16* BootDescription, + IN EFI_DEVICE_PATH_PROTOCOL* DevicePath, + IN BDS_LOADER_TYPE BootType, + IN CHAR8* BootArguments, + OUT BDS_LOAD_OPTION **BdsLoadOption + ); + +EFI_STATUS +BootOptionUpdate ( + IN BDS_LOAD_OPTION *BdsLoadOption, + IN UINT32 Attributes, + IN CHAR16* BootDescription, + IN EFI_DEVICE_PATH_PROTOCOL* DevicePath, + IN BDS_LOADER_TYPE BootType, + IN CHAR8* BootArguments + ); + +EFI_STATUS +BootOptionDelete ( + IN BDS_LOAD_OPTION *BootOption + ); + +EFI_STATUS +BootMenuMain ( + VOID + ); + +#endif /* _BDSINTERNAL_H_ */ diff --git a/ArmPlatformPkg/Bds/BootMenu.c b/ArmPlatformPkg/Bds/BootMenu.c new file mode 100644 index 0000000000..a2360d14f5 --- /dev/null +++ b/ArmPlatformPkg/Bds/BootMenu.c @@ -0,0 +1,487 @@ +/** @file +* +* Copyright (c) 2011, 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" + +extern EFI_HANDLE mImageHandle; +extern BDS_LOAD_OPTION_SUPPORT *BdsLoadOptionSupportList; + +EFI_STATUS +BootMenuAddBootOption ( + IN LIST_ENTRY *BootOptionsList + ) +{ + EFI_STATUS Status; + LIST_ENTRY SupportedDeviceList; + UINTN SupportedDeviceCount; + BDS_SUPPORTED_DEVICE* SupportedBootDevice; + LIST_ENTRY* Entry; + UINTN SupportedDeviceSelected; + CHAR8 AsciiBootOption[BOOT_DEVICE_OPTION_MAX]; + CHAR8 AsciiBootDescription[BOOT_DEVICE_DESCRIPTION_MAX]; + CHAR16 *BootDescription; + UINT32 Attributes; + BDS_LOADER_TYPE BootType; + UINTN Index; + BDS_LOAD_OPTION *BdsLoadOption; + EFI_DEVICE_PATH* DevicePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; + + Attributes = 0; + + // + // List the Boot Devices supported + // + + // Start all the drivers first + BdsConnectAllDrivers (); + + // List the supported devices + Status = BootDeviceListSupportedInit (&SupportedDeviceList); + ASSERT_EFI_ERROR(Status); + + SupportedDeviceCount = 0; + for (Entry = GetFirstNode (&SupportedDeviceList); + !IsNull (&SupportedDeviceList,Entry); + Entry = GetNextNode (&SupportedDeviceList,Entry) + ) + { + SupportedBootDevice = SUPPORTED_BOOT_DEVICE_FROM_LINK(Entry); + Print(L"[%d] %s\n",SupportedDeviceCount+1,SupportedBootDevice->Description); + + DEBUG_CODE_BEGIN(); + CHAR16* DevicePathTxt; + EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol; + + Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol); + ASSERT_EFI_ERROR(Status); + DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText(SupportedBootDevice->DevicePathProtocol,TRUE,TRUE); + + Print(L"\t- %s\n",DevicePathTxt); + + FreePool(DevicePathTxt); + DEBUG_CODE_END(); + + SupportedDeviceCount++; + } + + if (SupportedDeviceCount == 0) { + Print(L"There is no supported device.\n"); + Status = EFI_ABORTED; + goto EXIT; + } + + // + // Select the Boot Device + // + SupportedDeviceSelected = 0; + while (SupportedDeviceSelected == 0) { + Print(L"Select the Boot Device: "); + Status = GetHIInputInteger (&SupportedDeviceSelected); + if (EFI_ERROR(Status)) { + Status = EFI_ABORTED; + goto EXIT; + } else if ((SupportedDeviceSelected == 0) || (SupportedDeviceSelected > SupportedDeviceCount)) { + Print(L"Invalid input (max %d)\n",SupportedDeviceSelected); + SupportedDeviceSelected = 0; + } + } + + // + // Get the Device Path for the selected boot device + // + Index = 1; + for (Entry = GetFirstNode (&SupportedDeviceList); + !IsNull (&SupportedDeviceList,Entry); + Entry = GetNextNode (&SupportedDeviceList,Entry) + ) + { + if (Index == SupportedDeviceSelected) { + SupportedBootDevice = SUPPORTED_BOOT_DEVICE_FROM_LINK(Entry); + break; + } + Index++; + } + + // Create the specific device path node + Status = SupportedBootDevice->Support->CreateDevicePathNode (SupportedBootDevice, &DevicePathNode, &BootType, &Attributes); + if (EFI_ERROR(Status)) { + Status = EFI_ABORTED; + goto EXIT; + } + // Append the Device Path node to the select device path + DevicePath = AppendDevicePathNode (SupportedBootDevice->DevicePathProtocol, (CONST EFI_DEVICE_PATH_PROTOCOL *)DevicePathNode); + + Print(L"Arguments to pass to the binary: "); + Status = GetHIInputAscii (AsciiBootOption,BOOT_DEVICE_OPTION_MAX); + if (EFI_ERROR(Status)) { + Status = EFI_ABORTED; + goto FREE_DEVICE_PATH; + } + + Print(L"Description for this new Entry: "); + Status = GetHIInputAscii (AsciiBootDescription,BOOT_DEVICE_DESCRIPTION_MAX); + if (EFI_ERROR(Status)) { + Status = EFI_ABORTED; + goto FREE_DEVICE_PATH; + } + + // Convert Ascii into Unicode + BootDescription = (CHAR16*)AllocatePool(AsciiStrSize(AsciiBootDescription) * sizeof(CHAR16)); + AsciiStrToUnicodeStr (AsciiBootDescription, BootDescription); + + // Create new entry + Status = BootOptionCreate (Attributes, BootDescription, DevicePath, BootType, AsciiBootOption, &BdsLoadOption); + if (!EFI_ERROR(Status)) { + InsertTailList (BootOptionsList,&BdsLoadOption->Link); + } + + FreePool (BootDescription); + +FREE_DEVICE_PATH: + FreePool (DevicePath); + +EXIT: + BootDeviceListSupportedFree (&SupportedDeviceList); + return Status; +} + +STATIC +EFI_STATUS +BootMenuSelectBootOption ( + IN LIST_ENTRY *BootOptionsList, + IN CONST CHAR16* InputStatement, + OUT BDS_LOAD_OPTION **BdsLoadOption + ) +{ + EFI_STATUS Status; + LIST_ENTRY* Entry; + BDS_LOAD_OPTION *BootOption; + UINTN BootOptionSelected; + UINTN BootOptionCount; + UINTN Index; + + // Display the list of supported boot devices + BootOptionCount = 1; + for (Entry = GetFirstNode (BootOptionsList); + !IsNull (BootOptionsList,Entry); + Entry = GetNextNode (BootOptionsList,Entry) + ) + { + BootOption = LOAD_OPTION_FROM_LINK(Entry); + Print(L"[%d] %s\n",BootOptionCount,BootOption->Description); + + DEBUG_CODE_BEGIN(); + CHAR16* DevicePathTxt; + EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol; + + Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol); + ASSERT_EFI_ERROR(Status); + DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText(BootOption->FilePathList,TRUE,TRUE); + + Print(L"\t- %s\n",DevicePathTxt); + if ((BootOption->OptionalData != NULL) && (BootOption->OptionalData->Arguments != NULL)) { + Print(L"\t- Arguments: %a\n",BootOption->OptionalData->Arguments); + } + + FreePool(DevicePathTxt); + DEBUG_CODE_END(); + + BootOptionCount++; + } + + // Get the index of the boot device to delete + BootOptionSelected = 0; + while (BootOptionSelected == 0) { + Print(InputStatement); + Status = GetHIInputInteger (&BootOptionSelected); + if (EFI_ERROR(Status)) { + return Status; + } else if ((BootOptionSelected == 0) || (BootOptionSelected >= BootOptionCount)) { + Print(L"Invalid input (max %d)\n",BootOptionCount); + BootOptionSelected = 0; + } + } + + // Get the structure of the Boot device to delete + Index = 1; + for (Entry = GetFirstNode (BootOptionsList); + !IsNull (BootOptionsList,Entry); + Entry = GetNextNode (BootOptionsList,Entry) + ) + { + if (Index == BootOptionSelected) { + *BdsLoadOption = LOAD_OPTION_FROM_LINK(Entry); + break; + } + Index++; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +BootMenuRemoveBootOption ( + IN LIST_ENTRY *BootOptionsList + ) +{ + EFI_STATUS Status; + BDS_LOAD_OPTION *BootOption; + + Status = BootMenuSelectBootOption (BootOptionsList,L"Delete entry: ",&BootOption); + if (EFI_ERROR(Status)) { + return Status; + } + + // Delete the BDS Load option structures + BootOptionDelete (BootOption); + + return EFI_SUCCESS; +} + +EFI_STATUS +BootMenuUpdateBootOption ( + IN LIST_ENTRY *BootOptionsList + ) +{ + EFI_STATUS Status; + BDS_LOAD_OPTION *BootOption; + BDS_LOAD_OPTION_SUPPORT* DeviceSupport; + CHAR8 AsciiBootOption[BOOT_DEVICE_OPTION_MAX]; + CHAR8 AsciiBootDescription[BOOT_DEVICE_DESCRIPTION_MAX]; + CHAR16 *BootDescription; + EFI_DEVICE_PATH* DevicePath; + UINT32 Attributes; + BDS_LOADER_TYPE BootType; + + Status = BootMenuSelectBootOption (BootOptionsList,L"Update entry: ",&BootOption); + if (EFI_ERROR(Status)) { + return Status; + } + + // Get the device support for this Boot Option + Status = BootDeviceGetDeviceSupport (BootOption,&DeviceSupport); + if (EFI_ERROR(Status)) { + Print(L"Impossible to retrieve the supported device for the update\n"); + return EFI_UNSUPPORTED; + } + + Status = DeviceSupport->UpdateDevicePathNode (BootOption,&DevicePath,&BootType,&Attributes); + if (EFI_ERROR(Status)) { + Status = EFI_ABORTED; + goto EXIT; + } + + Print(L"Arguments to pass to the binary: "); + if (BootOption->OptionalData) { + AsciiStrnCpy(AsciiBootOption,BootOption->OptionalData->Arguments,BOOT_DEVICE_FILEPATH_MAX); + } else { + AsciiBootOption[0] = '\0'; + } + Status = EditHIInputAscii (AsciiBootOption,BOOT_DEVICE_OPTION_MAX); + if (EFI_ERROR(Status)) { + Status = EFI_ABORTED; + goto FREE_DEVICE_PATH; + } + + Print(L"Description for this new Entry: "); + UnicodeStrToAsciiStr (BootOption->Description,AsciiBootDescription); + Status = EditHIInputAscii (AsciiBootDescription,BOOT_DEVICE_DESCRIPTION_MAX); + if (EFI_ERROR(Status)) { + Status = EFI_ABORTED; + goto FREE_DEVICE_PATH; + } + + // Convert Ascii into Unicode + BootDescription = (CHAR16*)AllocatePool(AsciiStrSize(AsciiBootDescription) * sizeof(CHAR16)); + AsciiStrToUnicodeStr (AsciiBootDescription, BootDescription); + + // Update the entry + Status = BootOptionUpdate (BootOption, Attributes, BootDescription, DevicePath, BootType, AsciiBootOption); + + FreePool (BootDescription); + +FREE_DEVICE_PATH: + FreePool (DevicePath); + +EXIT: + return Status; +} + +struct BOOT_MANAGER_ENTRY { + CONST CHAR16* Description; + EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList); +} BootManagerEntries[] = { + { L"Add Boot Device Entry", BootMenuAddBootOption }, + { L"Update Boot Device Entry", BootMenuUpdateBootOption }, + { L"Remove Boot Device Entry", BootMenuRemoveBootOption }, +}; + +EFI_STATUS +BootMenuManager ( + IN LIST_ENTRY *BootOptionsList + ) +{ + UINTN Index; + UINTN OptionSelected; + UINTN BootManagerEntryCount; + EFI_STATUS Status; + + BootManagerEntryCount = sizeof(BootManagerEntries) / sizeof(struct BOOT_MANAGER_ENTRY); + + while (TRUE) { + // Display Boot Manager menu + for (Index = 0; Index < BootManagerEntryCount; Index++) { + Print(L"[%d] %s\n",Index+1,BootManagerEntries[Index]); + } + Print(L"[%d] Return to main menu\n",Index+1); + + // Select which entry to call + Print(L"Choice: "); + Status = GetHIInputInteger (&OptionSelected); + if (EFI_ERROR(Status) || (OptionSelected == (BootManagerEntryCount+1))) { + return EFI_SUCCESS; + } else if ((OptionSelected > 0) && (OptionSelected <= BootManagerEntryCount)) { + Status = BootManagerEntries[OptionSelected-1].Callback (BootOptionsList); + } + } + + return EFI_SUCCESS; +} + +EFI_STATUS +BootEBL ( + IN LIST_ENTRY *BootOptionsList + ) +{ + EFI_STATUS Status; + + // Start EFI Shell + Status = BdsLoadApplication(mImageHandle, L"Ebl"); + if (Status == EFI_NOT_FOUND) { + Print (L"Error: EFI Application not found.\n"); + } else if (EFI_ERROR(Status)) { + Print (L"Error: Status Code: 0x%X\n",(UINT32)Status); + } + + return Status; +} + +struct BOOT_MAIN_ENTRY { + CONST CHAR16* Description; + EFI_STATUS (*Callback) (IN LIST_ENTRY *BootOptionsList); +} BootMainEntries[] = { + { L"EBL", BootEBL }, + { L"Boot Manager", BootMenuManager }, +}; + + +EFI_STATUS +BootMenuMain ( + VOID + ) +{ + LIST_ENTRY BootOptionsList; + UINTN OptionCount; + UINTN BootOptionCount; + EFI_STATUS Status; + LIST_ENTRY *Entry; + BDS_LOAD_OPTION *BootOption; + UINTN BootOptionSelected; + UINTN Index; + UINTN BootMainEntryCount; + + BootMainEntryCount = sizeof(BootMainEntries) / sizeof(struct BOOT_MAIN_ENTRY); + + // Get Boot#### list + BootOptionList (&BootOptionsList); + + while (TRUE) { + OptionCount = 1; + + // Display the Boot options + for (Entry = GetFirstNode (&BootOptionsList); + !IsNull (&BootOptionsList,Entry); + Entry = GetNextNode (&BootOptionsList,Entry) + ) + { + BootOption = LOAD_OPTION_FROM_LINK(Entry); + + Print(L"[%d] %s\n",OptionCount,BootOption->Description); + + DEBUG_CODE_BEGIN(); + CHAR16* DevicePathTxt; + EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol; + + Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol); + ASSERT_EFI_ERROR(Status); + DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText(BootOption->FilePathList,TRUE,TRUE); + + Print(L"\t- %s\n",DevicePathTxt); + if (BootOption->OptionalData != NULL) { + Print(L"\t- LoaderType: %d\n",BootOption->OptionalData->LoaderType); + if (BootOption->OptionalData->Arguments != NULL) { + Print(L"\t- Arguments: %a\n",BootOption->OptionalData->Arguments); + } + } + + FreePool(DevicePathTxt); + DEBUG_CODE_END(); + + OptionCount++; + } + BootOptionCount = OptionCount-1; + + // Display the hardcoded Boot entries + for (Index = 0; Index < BootMainEntryCount; Index++) { + Print(L"[%d] %s\n",OptionCount,BootMainEntries[Index]); + OptionCount++; + } + + // Request the boot entry from the user + BootOptionSelected = 0; + while (BootOptionSelected == 0) { + Print(L"Start: "); + Status = GetHIInputInteger (&BootOptionSelected); + if (EFI_ERROR(Status) || (BootOptionSelected == 0) || (BootOptionSelected > OptionCount)) { + Print(L"Invalid input (max %d)\n",(OptionCount-1)); + BootOptionSelected = 0; + } + } + + // Start the selected entry + if (BootOptionSelected > BootOptionCount) { + // Start the hardcoded entry + Status = BootMainEntries[BootOptionSelected - BootOptionCount - 1].Callback (&BootOptionsList); + } else { + // Find the selected entry from the Boot#### list + Index = 1; + for (Entry = GetFirstNode (&BootOptionsList); + !IsNull (&BootOptionsList,Entry); + Entry = GetNextNode (&BootOptionsList,Entry) + ) + { + if (Index == BootOptionSelected) { + BootOption = LOAD_OPTION_FROM_LINK(Entry); + break; + } + Index++; + } + + Status = BootOptionStart (BootOption); + } + } + + return Status; +} diff --git a/ArmPlatformPkg/Bds/BootOption.c b/ArmPlatformPkg/Bds/BootOption.c new file mode 100644 index 0000000000..a8ba23fe73 --- /dev/null +++ b/ArmPlatformPkg/Bds/BootOption.c @@ -0,0 +1,407 @@ +/** @file +* +* Copyright (c) 2011, 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" + +extern EFI_HANDLE mImageHandle; + +EFI_STATUS +BootOptionStart ( + IN BDS_LOAD_OPTION *BootOption + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH* FdtDevicePath; + EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *EfiDevicePathFromTextProtocol; + + Status = EFI_UNSUPPORTED; + + if (BootOption->OptionalData->LoaderType == BDS_LOADER_EFI_APPLICATION) { + // Need to connect every drivers to ensure no dependencies are missing for the application + BdsConnectAllDrivers(); + + Status = BdsStartEfiApplication (mImageHandle, BootOption->FilePathList); + } else if (BootOption->OptionalData->LoaderType == BDS_LOADER_KERNEL_LINUX_ATAG) { + Status = BdsBootLinux (BootOption->FilePathList, BootOption->OptionalData->Arguments, NULL); + } else if (BootOption->OptionalData->LoaderType == BDS_LOADER_KERNEL_LINUX_FDT) { + // Convert the FDT path into a Device Path + Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol); + ASSERT_EFI_ERROR(Status); + FdtDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ((CHAR16*)PcdGetPtr(PcdFdtDevicePath)); + + Status = BdsBootLinux (BootOption->FilePathList, BootOption->OptionalData->Arguments, FdtDevicePath); + FreePool(FdtDevicePath); + } + + return Status; +} + +EFI_STATUS +BootOptionParseLoadOption ( + IN EFI_LOAD_OPTION EfiLoadOption, + IN UINTN EfiLoadOptionSize, + OUT BDS_LOAD_OPTION **BdsLoadOption + ) +{ + BDS_LOAD_OPTION *LoadOption; + UINTN FilePathListLength; + UINTN DescriptionLength; + + if (EfiLoadOption == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (EfiLoadOptionSize < sizeof(UINT32) + sizeof(UINT16) + sizeof(CHAR16) + sizeof(EFI_DEVICE_PATH_PROTOCOL)) { + return EFI_BAD_BUFFER_SIZE; + } + + LoadOption = (BDS_LOAD_OPTION*)AllocatePool(sizeof(BDS_LOAD_OPTION)); + if (LoadOption == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + LoadOption->LoadOption = EfiLoadOption; + LoadOption->LoadOptionSize = EfiLoadOptionSize; + + LoadOption->Attributes = *(UINT32*)EfiLoadOption; + FilePathListLength = *(UINT16*)(EfiLoadOption + sizeof(UINT32)); + LoadOption->Description = (CHAR16*)(EfiLoadOption + sizeof(UINT32) + sizeof(UINT16)); + DescriptionLength = StrSize (LoadOption->Description); + LoadOption->FilePathList = (EFI_DEVICE_PATH_PROTOCOL*)(EfiLoadOption + sizeof(UINT32) + sizeof(UINT16) + DescriptionLength); + + if ((UINTN)((UINT8*)LoadOption->FilePathList + FilePathListLength - EfiLoadOption) == EfiLoadOptionSize) { + LoadOption->OptionalData = NULL; + } else { + LoadOption->OptionalData = (BDS_LOADER_OPTIONAL_DATA *)((UINT8*)LoadOption->FilePathList + FilePathListLength); + } + + *BdsLoadOption = LoadOption; + return EFI_SUCCESS; +} + +EFI_STATUS +BootOptionFromLoadOptionVariable ( + IN UINT16 LoadOptionIndex, + OUT BDS_LOAD_OPTION **BdsLoadOption + ) +{ + EFI_STATUS Status; + CHAR16 BootVariableName[9]; + EFI_LOAD_OPTION EfiLoadOption; + UINTN EfiLoadOptionSize; + + UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", LoadOptionIndex); + + Status = GetEnvironmentVariable (BootVariableName, NULL, &EfiLoadOptionSize, (VOID**)&EfiLoadOption); + if (!EFI_ERROR(Status)) { + Status = BootOptionParseLoadOption (EfiLoadOption,EfiLoadOptionSize,BdsLoadOption); + if (!EFI_ERROR(Status)) { + (*BdsLoadOption)->LoadOptionIndex = LoadOptionIndex; + } + } + + return Status; +} + +EFI_STATUS +BootOptionList ( + IN OUT LIST_ENTRY *BootOptionList + ) +{ + EFI_STATUS Status; + UINTN Index; + UINT16 *BootOrder; + UINTN BootOrderSize; + BDS_LOAD_OPTION *BdsLoadOption; + + InitializeListHead (BootOptionList); + + // Get the Boot Option Order from the environment variable + Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder); + if (EFI_ERROR(Status)) { + return Status; + } + + for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) { + Status = BootOptionFromLoadOptionVariable (BootOrder[Index],&BdsLoadOption); + if (!EFI_ERROR(Status)) { + InsertTailList (BootOptionList,&BdsLoadOption->Link); + } + } + + return EFI_SUCCESS; +} + +UINT16 +BootOptionAllocateBootIndex ( + VOID + ) +{ + EFI_STATUS Status; + UINTN Index; + UINT32 BootIndex; + UINT16 *BootOrder; + UINTN BootOrderSize; + BOOLEAN Found; + + // Get the Boot Option Order from the environment variable + Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder); + if (!EFI_ERROR(Status)) { + for (BootIndex = 0; BootIndex <= 0xFFFF; BootIndex++) { + Found = FALSE; + for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) { + if (BootOrder[Index] == BootIndex) { + Found = TRUE; + break; + } + } + if (!Found) { + return BootIndex; + } + } + } + // Return the first index + return 0; +} + +STATIC +EFI_STATUS +BootOptionSetFields ( + IN BDS_LOAD_OPTION *BootOption, + IN UINT32 Attributes, + IN CHAR16* BootDescription, + IN EFI_DEVICE_PATH_PROTOCOL* DevicePath, + IN BDS_LOADER_TYPE BootType, + IN CHAR8* BootArguments + ) +{ + EFI_LOAD_OPTION EfiLoadOption; + UINTN EfiLoadOptionSize; + UINTN BootDescriptionSize; + UINTN BootOptionalDataSize; + UINT16 FilePathListLength; + EFI_DEVICE_PATH_PROTOCOL* DevicePathNode; + UINTN NodeLength; + UINT8* EfiLoadOptionPtr; + + // If we are overwriting an existent Boot Option then we have to free previously allocated memory + if (BootOption->LoadOption) { + FreePool(BootOption->LoadOption); + } + + BootDescriptionSize = StrSize(BootDescription); + BootOptionalDataSize = sizeof(BDS_LOADER_OPTIONAL_DATA) + + (BootArguments == NULL ? 0 : AsciiStrSize(BootArguments)); + + // Compute the size of the FilePath list + FilePathListLength = 0; + DevicePathNode = DevicePath; + while (!IsDevicePathEndType (DevicePathNode)) { + FilePathListLength += DevicePathNodeLength (DevicePathNode); + DevicePathNode = NextDevicePathNode (DevicePathNode); + } + // Add the length of the DevicePath EndType + FilePathListLength += DevicePathNodeLength (DevicePathNode); + + // Allocate the memory for the EFI Load Option + EfiLoadOptionSize = sizeof(UINT32) + sizeof(UINT16) + BootDescriptionSize + FilePathListLength + BootOptionalDataSize; + EfiLoadOption = (EFI_LOAD_OPTION)AllocatePool(EfiLoadOptionSize); + EfiLoadOptionPtr = EfiLoadOption; + + // + // Populate the EFI Load Option and BDS Boot Option structures + // + + // Attributes fields + BootOption->Attributes = Attributes; + *(UINT32*)EfiLoadOptionPtr = Attributes; + EfiLoadOptionPtr += sizeof(UINT32); + + // FilePath List fields + BootOption->FilePathListLength = FilePathListLength; + *(UINT16*)EfiLoadOptionPtr = FilePathListLength; + EfiLoadOptionPtr += sizeof(UINT16); + + // Boot description fields + BootOption->Description = (CHAR16*)EfiLoadOptionPtr; + CopyMem (EfiLoadOptionPtr, BootDescription, BootDescriptionSize); + EfiLoadOptionPtr += BootDescriptionSize; + + // File path fields + BootOption->FilePathList = (EFI_DEVICE_PATH_PROTOCOL*)EfiLoadOptionPtr; + DevicePathNode = DevicePath; + while (!IsDevicePathEndType (DevicePathNode)) { + NodeLength = DevicePathNodeLength(DevicePathNode); + CopyMem (EfiLoadOptionPtr, DevicePathNode, NodeLength); + EfiLoadOptionPtr += NodeLength; + DevicePathNode = NextDevicePathNode (DevicePathNode); + } + + // Set the End Device Path Type + SetDevicePathEndNode (EfiLoadOptionPtr); + EfiLoadOptionPtr = (UINT8 *)EfiLoadOptionPtr + sizeof(EFI_DEVICE_PATH); + + // Optional Data fields, Do unaligned writes + WriteUnaligned32 ((UINT32 *)EfiLoadOptionPtr, BootType); + + CopyMem (&((BDS_LOADER_OPTIONAL_DATA*)EfiLoadOptionPtr)->Arguments, BootArguments, AsciiStrSize(BootArguments)); + BootOption->OptionalData = (BDS_LOADER_OPTIONAL_DATA *)EfiLoadOptionPtr; + + // Fill the EFI Load option fields + BootOption->LoadOptionIndex = BootOptionAllocateBootIndex(); + BootOption->LoadOption = EfiLoadOption; + BootOption->LoadOptionSize = EfiLoadOptionSize; + + return EFI_SUCCESS; +} + +EFI_STATUS +BootOptionCreate ( + IN UINT32 Attributes, + IN CHAR16* BootDescription, + IN EFI_DEVICE_PATH_PROTOCOL* DevicePath, + IN BDS_LOADER_TYPE BootType, + IN CHAR8* BootArguments, + OUT BDS_LOAD_OPTION **BdsLoadOption + ) +{ + EFI_STATUS Status; + BDS_LOAD_OPTION *BootOption; + CHAR16 BootVariableName[9]; + UINT16 *BootOrder; + UINTN BootOrderSize; + + // + // Allocate and fill the memory for the BDS Load Option structure + // + BootOption = (BDS_LOAD_OPTION*)AllocateZeroPool(sizeof(BDS_LOAD_OPTION)); + + InitializeListHead (&BootOption->Link); + BootOptionSetFields (BootOption, Attributes, BootDescription, DevicePath, BootType, BootArguments); + + // + // Set the related environment variables + // + + // Create Boot#### environment variable + UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BootOption->LoadOptionIndex); + Status = gRT->SetVariable ( + BootVariableName, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + BootOption->LoadOptionSize, + BootOption->LoadOption + ); + + // Add the new Boot Index to the list + Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder); + if (!EFI_ERROR(Status)) { + BootOrder = ReallocatePool (BootOrderSize, BootOrderSize + sizeof(UINT16), BootOrder); + // Add the new index at the end + BootOrder[BootOrderSize / sizeof(UINT16)] = BootOption->LoadOptionIndex; + BootOrderSize += sizeof(UINT16); + } else { + // BootOrder does not exist. Create it + BootOrderSize = sizeof(UINT16); + BootOrder = &(BootOption->LoadOptionIndex); + } + + // Update (or Create) the BootOrder environment variable + Status = gRT->SetVariable ( + L"BootOrder", + &gEfiGlobalVariableGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + BootOrderSize, + BootOrder + ); + + *BdsLoadOption = BootOption; + return Status; +} + +EFI_STATUS +BootOptionUpdate ( + IN BDS_LOAD_OPTION *BdsLoadOption, + IN UINT32 Attributes, + IN CHAR16* BootDescription, + IN EFI_DEVICE_PATH_PROTOCOL* DevicePath, + IN BDS_LOADER_TYPE BootType, + IN CHAR8* BootArguments + ) +{ + EFI_STATUS Status; + BDS_LOAD_OPTION *BootOption; + CHAR16 BootVariableName[9]; + + // Update the BDS Load Option structure + BootOptionSetFields (BdsLoadOption, Attributes, BootDescription, DevicePath, BootType, BootArguments); + + // Update the related environment variables + UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BootOption->LoadOptionIndex); + Status = gRT->SetVariable ( + BootVariableName, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + BootOption->LoadOptionSize, + BootOption->LoadOption + ); + + return Status; +} + +EFI_STATUS +BootOptionDelete ( + IN BDS_LOAD_OPTION *BootOption + ) +{ + UINTN Index; + UINTN BootOrderSize; + UINT16* BootOrder; + UINTN BootOrderCount; + EFI_STATUS Status; + + // If the Boot Optiono was attached to a list remove it + if (!IsListEmpty (&BootOption->Link)) { + // Remove the entry from the list + RemoveEntryList (&BootOption->Link); + } + + // Remove the entry from the BootOrder environment variable + Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder); + if (!EFI_ERROR(Status)) { + BootOrderCount = BootOrderSize / sizeof(UINT16); + + // Find the index of the removed entry + for (Index = 0; Index < BootOrderCount; Index++) { + if (BootOrder[Index] == BootOption->LoadOptionIndex) { + // If it the last entry we do not need to rearrange the BootOrder list + if (Index + 1 != BootOrderCount) { + CopyMem (&BootOrder[Index],&BootOrder[Index+1], BootOrderCount - (Index + 1)); + } + break; + } + } + + // Update the BootOrder environment variable + Status = gRT->SetVariable ( + L"BootOrder", + &gEfiGlobalVariableGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + BootOrderSize - sizeof(UINT16), + BootOrder + ); + } + + return EFI_SUCCESS; +} diff --git a/ArmPlatformPkg/Bds/BootOptionSupport.c b/ArmPlatformPkg/Bds/BootOptionSupport.c new file mode 100644 index 0000000000..7de2df4825 --- /dev/null +++ b/ArmPlatformPkg/Bds/BootOptionSupport.c @@ -0,0 +1,867 @@ +/** @file +* +* Copyright (c) 2011, 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 + +#include +#include +#include +#include +#include + +#include + +#define IS_DEVICE_PATH_NODE(node,type,subtype) (((node)->Type == (type)) && ((node)->SubType == (subtype))) + +EFI_STATUS +BdsLoadOptionFileSystemList ( + IN OUT LIST_ENTRY* BdsLoadOptionList + ); + +EFI_STATUS +BdsLoadOptionFileSystemCreateDevicePath ( + IN BDS_SUPPORTED_DEVICE* BdsLoadOption, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePathNode, + OUT BDS_LOADER_TYPE *BootType, + OUT UINT32 *Attributes + ); + +EFI_STATUS +BdsLoadOptionFileSystemUpdateDevicePath ( + IN BDS_LOAD_OPTION *BootOption, + OUT EFI_DEVICE_PATH_PROTOCOL** NewDevicePath, + OUT BDS_LOADER_TYPE *BootType, + OUT UINT32 *Attributes + ); + +BOOLEAN +BdsLoadOptionFileSystemIsSupported ( + IN BDS_LOAD_OPTION* BdsLoadOption + ); + +EFI_STATUS +BdsLoadOptionMemMapList ( + IN OUT LIST_ENTRY* BdsLoadOptionList + ); + +EFI_STATUS +BdsLoadOptionMemMapCreateDevicePath ( + IN BDS_SUPPORTED_DEVICE* BdsLoadOption, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePathNode, + OUT BDS_LOADER_TYPE *BootType, + OUT UINT32 *Attributes + ); + +EFI_STATUS +BdsLoadOptionMemMapUpdateDevicePath ( + IN BDS_LOAD_OPTION *BootOption, + OUT EFI_DEVICE_PATH_PROTOCOL** NewDevicePath, + OUT BDS_LOADER_TYPE *BootType, + OUT UINT32 *Attributes + ); + +BOOLEAN +BdsLoadOptionMemMapIsSupported ( + IN BDS_LOAD_OPTION* BdsLoadOption + ); + +EFI_STATUS +BdsLoadOptionPxeList ( + IN OUT LIST_ENTRY* BdsLoadOptionList + ); + +EFI_STATUS +BdsLoadOptionPxeCreateDevicePath ( + IN BDS_SUPPORTED_DEVICE* BdsLoadOption, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePathNode, + OUT BDS_LOADER_TYPE *BootType, + OUT UINT32 *Attributes + ); + +EFI_STATUS +BdsLoadOptionPxeUpdateDevicePath ( + IN BDS_LOAD_OPTION *BootOption, + OUT EFI_DEVICE_PATH_PROTOCOL** NewDevicePath, + OUT BDS_LOADER_TYPE *BootType, + OUT UINT32 *Attributes + ); + +BOOLEAN +BdsLoadOptionPxeIsSupported ( + IN BDS_LOAD_OPTION* BdsLoadOption + ); + +EFI_STATUS +BdsLoadOptionTftpList ( + IN OUT LIST_ENTRY* BdsLoadOptionList + ); + +EFI_STATUS +BdsLoadOptionTftpCreateDevicePath ( + IN BDS_SUPPORTED_DEVICE* BdsLoadOption, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePathNode, + OUT BDS_LOADER_TYPE *BootType, + OUT UINT32 *Attributes + ); + +EFI_STATUS +BdsLoadOptionTftpUpdateDevicePath ( + IN BDS_LOAD_OPTION *BootOption, + OUT EFI_DEVICE_PATH_PROTOCOL** NewDevicePath, + OUT BDS_LOADER_TYPE *BootType, + OUT UINT32 *Attributes + ); + +BOOLEAN +BdsLoadOptionTftpIsSupported ( + IN BDS_LOAD_OPTION* BdsLoadOption + ); + +BDS_LOAD_OPTION_SUPPORT BdsLoadOptionSupportList[] = { + { + BDS_DEVICE_FILESYSTEM, + BdsLoadOptionFileSystemList, + BdsLoadOptionFileSystemIsSupported, + BdsLoadOptionFileSystemCreateDevicePath, + BdsLoadOptionFileSystemUpdateDevicePath + }, + { + BDS_DEVICE_MEMMAP, + BdsLoadOptionMemMapList, + BdsLoadOptionMemMapIsSupported, + BdsLoadOptionMemMapCreateDevicePath, + BdsLoadOptionMemMapUpdateDevicePath + }, + { + BDS_DEVICE_PXE, + BdsLoadOptionPxeList, + BdsLoadOptionPxeIsSupported, + BdsLoadOptionPxeCreateDevicePath, + BdsLoadOptionPxeUpdateDevicePath + }, + { + BDS_DEVICE_TFTP, + BdsLoadOptionTftpList, + BdsLoadOptionTftpIsSupported, + BdsLoadOptionTftpCreateDevicePath, + BdsLoadOptionTftpUpdateDevicePath + } +}; + +EFI_STATUS +BootDeviceListSupportedInit ( + IN OUT LIST_ENTRY *SupportedDeviceList + ) +{ + UINTN Index; + + // Initialize list of supported devices + InitializeListHead (SupportedDeviceList); + + for (Index = 0; Index < BDS_DEVICE_MAX; Index++) { + BdsLoadOptionSupportList[Index].ListDevices(SupportedDeviceList); + } + + return EFI_SUCCESS; +} + +EFI_STATUS +BootDeviceListSupportedFree ( + IN LIST_ENTRY *SupportedDeviceList + ) +{ + LIST_ENTRY *Entry; + BDS_SUPPORTED_DEVICE* SupportedDevice; + + Entry = GetFirstNode (SupportedDeviceList); + while (Entry != SupportedDeviceList) { + SupportedDevice = SUPPORTED_BOOT_DEVICE_FROM_LINK(Entry); + Entry = RemoveEntryList (Entry); + FreePool(SupportedDevice); + } + + return EFI_SUCCESS; +} + +EFI_STATUS +BootDeviceGetDeviceSupport ( + IN BDS_LOAD_OPTION *BootOption, + OUT BDS_LOAD_OPTION_SUPPORT** DeviceSupport + ) +{ + UINTN Index; + + // Find which supported device is the most appropriate + for (Index = 0; Index < BDS_DEVICE_MAX; Index++) { + if (BdsLoadOptionSupportList[Index].IsSupported(BootOption)) { + *DeviceSupport = &BdsLoadOptionSupportList[Index]; + return EFI_SUCCESS; + } + } + + return EFI_UNSUPPORTED; +} + +STATIC +EFI_STATUS +BootDeviceGetType ( + IN CHAR16* FileName, + OUT BDS_LOADER_TYPE *BootType, + OUT UINT32 *Attributes + ) +{ + EFI_STATUS Status; + BOOLEAN IsEfiApp; + BOOLEAN IsBootLoader; + BOOLEAN HasFDTSupport; + + if (FileName == NULL) { + Print(L"Is an EFI Application? "); + Status = GetHIInputBoolean (&IsEfiApp); + if (EFI_ERROR(Status)) { + return EFI_ABORTED; + } + } else if (HasFilePathEfiExtension(FileName)) { + IsEfiApp = TRUE; + } else { + IsEfiApp = FALSE; + } + + if (IsEfiApp) { + Print(L"Is your application is an OS loader? "); + Status = GetHIInputBoolean (&IsBootLoader); + if (EFI_ERROR(Status)) { + return EFI_ABORTED; + } + if (!IsBootLoader) { + *Attributes |= LOAD_OPTION_CATEGORY_APP; + } + *BootType = BDS_LOADER_EFI_APPLICATION; + } else { + Print(L"Has FDT support? "); + Status = GetHIInputBoolean (&HasFDTSupport); + if (EFI_ERROR(Status)) { + return EFI_ABORTED; + } + if (HasFDTSupport) { + *BootType = BDS_LOADER_KERNEL_LINUX_FDT; + } else { + *BootType = BDS_LOADER_KERNEL_LINUX_ATAG; + } + } + + return EFI_SUCCESS; +} + +EFI_STATUS +BdsLoadOptionFileSystemList ( + IN OUT LIST_ENTRY* BdsLoadOptionList + ) +{ + EFI_STATUS Status; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN Index; + BDS_SUPPORTED_DEVICE *SupportedDevice; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL* FileProtocol; + EFI_FILE_HANDLE Fs; + UINTN Size; + EFI_FILE_SYSTEM_INFO* FsInfo; + EFI_DEVICE_PATH_PROTOCOL* DevicePathProtocol; + + // List all the Simple File System Protocols + Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &HandleCount, &HandleBuffer); + if (EFI_ERROR (Status)) { + return Status; + } + + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathProtocol); + if (!EFI_ERROR(Status)) { + // Allocate BDS Supported Device structure + SupportedDevice = (BDS_SUPPORTED_DEVICE*)AllocatePool(sizeof(BDS_SUPPORTED_DEVICE)); + + FileProtocol = NULL; + Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiSimpleFileSystemProtocolGuid, (VOID **)&FileProtocol); + ASSERT_EFI_ERROR(Status); + + FileProtocol->OpenVolume (FileProtocol, &Fs); + + // Generate a Description from the file system + Size = 0; + FsInfo = NULL; + Status = Fs->GetInfo (Fs, &gEfiFileSystemInfoGuid, &Size, FsInfo); + if (Status == EFI_BUFFER_TOO_SMALL) { + FsInfo = AllocatePool (Size); + Status = Fs->GetInfo (Fs, &gEfiFileSystemInfoGuid, &Size, FsInfo); + } + UnicodeSPrint (SupportedDevice->Description,BOOT_DEVICE_DESCRIPTION_MAX,L"%s (%d MB)",FsInfo->VolumeLabel,(UINT32)(FsInfo->VolumeSize / (1024 * 1024))); + FreePool(FsInfo); + Fs->Close (Fs); + + SupportedDevice->DevicePathProtocol = DevicePathProtocol; + SupportedDevice->Support = &BdsLoadOptionSupportList[BDS_DEVICE_FILESYSTEM]; + + InsertTailList (BdsLoadOptionList,&SupportedDevice->Link); + } + } + + return EFI_SUCCESS; +} + +EFI_STATUS +BdsLoadOptionFileSystemCreateDevicePath ( + IN BDS_SUPPORTED_DEVICE* BdsLoadOption, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePathNode, + OUT BDS_LOADER_TYPE *BootType, + OUT UINT32 *Attributes + ) +{ + EFI_STATUS Status; + FILEPATH_DEVICE_PATH* FilePathDevicePath; + CHAR8 AsciiBootFilePath[BOOT_DEVICE_FILEPATH_MAX]; + CHAR16 *BootFilePath; + UINTN BootFilePathSize; + + Print(L"File path of the EFI Application or the kernel: "); + Status = GetHIInputAscii (AsciiBootFilePath,BOOT_DEVICE_FILEPATH_MAX); + if (EFI_ERROR(Status)) { + return EFI_ABORTED; + } + + // Convert Ascii into Unicode + BootFilePath = (CHAR16*)AllocatePool(AsciiStrSize(AsciiBootFilePath) * sizeof(CHAR16)); + AsciiStrToUnicodeStr (AsciiBootFilePath, BootFilePath); + BootFilePathSize = StrSize(BootFilePath); + + // Create the FilePath Device Path node + FilePathDevicePath = (FILEPATH_DEVICE_PATH*)AllocatePool(SIZE_OF_FILEPATH_DEVICE_PATH + BootFilePathSize); + FilePathDevicePath->Header.Type = MEDIA_DEVICE_PATH; + FilePathDevicePath->Header.SubType = MEDIA_FILEPATH_DP; + SetDevicePathNodeLength (FilePathDevicePath, SIZE_OF_FILEPATH_DEVICE_PATH + BootFilePathSize); + CopyMem (FilePathDevicePath->PathName, BootFilePath, BootFilePathSize); + FreePool (BootFilePath); + + Status = BootDeviceGetType (FilePathDevicePath->PathName, BootType, Attributes); + if (EFI_ERROR(Status)) { + FreePool (FilePathDevicePath); + } else { + *DevicePathNode = (EFI_DEVICE_PATH_PROTOCOL*)FilePathDevicePath; + } + + return Status; +} + +EFI_STATUS +BdsLoadOptionFileSystemUpdateDevicePath ( + IN BDS_LOAD_OPTION *BootOption, + OUT EFI_DEVICE_PATH_PROTOCOL** NewDevicePath, + OUT BDS_LOADER_TYPE *BootType, + OUT UINT32 *Attributes + ) +{ + EFI_STATUS Status; + CHAR8 AsciiBootFilePath[BOOT_DEVICE_FILEPATH_MAX]; + CHAR16 *BootFilePath; + UINTN BootFilePathSize; + FILEPATH_DEVICE_PATH* EndingDevicePath; + FILEPATH_DEVICE_PATH* FilePathDevicePath; + EFI_DEVICE_PATH* DevicePath; + + DevicePath = DuplicateDevicePath (BootOption->FilePathList); + EndingDevicePath = (FILEPATH_DEVICE_PATH*)GetLastDevicePathNode (DevicePath); + + Print(L"File path of the EFI Application or the kernel: "); + UnicodeStrToAsciiStr (EndingDevicePath->PathName,AsciiBootFilePath); + Status = EditHIInputAscii(AsciiBootFilePath,BOOT_DEVICE_FILEPATH_MAX); + if (EFI_ERROR(Status)) { + return Status; + } + + // Convert Ascii into Unicode + BootFilePath = (CHAR16*)AllocatePool(AsciiStrSize(AsciiBootFilePath) * sizeof(CHAR16)); + AsciiStrToUnicodeStr (AsciiBootFilePath, BootFilePath); + BootFilePathSize = StrSize(BootFilePath); + + // Create the FilePath Device Path node + FilePathDevicePath = (FILEPATH_DEVICE_PATH*)AllocatePool(SIZE_OF_FILEPATH_DEVICE_PATH + BootFilePathSize); + FilePathDevicePath->Header.Type = MEDIA_DEVICE_PATH; + FilePathDevicePath->Header.SubType = MEDIA_FILEPATH_DP; + SetDevicePathNodeLength (FilePathDevicePath, SIZE_OF_FILEPATH_DEVICE_PATH + BootFilePathSize); + CopyMem (FilePathDevicePath->PathName, BootFilePath, BootFilePathSize); + FreePool (BootFilePath); + + // Generate the new Device Path by replacing the last node by the updated node + SetDevicePathEndNode (EndingDevicePath); + *NewDevicePath = AppendDevicePathNode (DevicePath, (CONST EFI_DEVICE_PATH_PROTOCOL *)FilePathDevicePath); + FreePool(DevicePath); + + return BootDeviceGetType (FilePathDevicePath->PathName, BootType, Attributes); +} + +BOOLEAN +BdsLoadOptionFileSystemIsSupported ( + IN BDS_LOAD_OPTION* BdsLoadOption + ) +{ + EFI_DEVICE_PATH* DevicePathNode; + + DevicePathNode = GetLastDevicePathNode (BdsLoadOption->FilePathList); + + return IS_DEVICE_PATH_NODE(DevicePathNode,MEDIA_DEVICE_PATH,MEDIA_FILEPATH_DP); +} + +STATIC +BOOLEAN +IsParentDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL *ChildDevicePath + ) +{ + UINTN ParentSize; + UINTN ChildSize; + + ParentSize = GetDevicePathSize (ParentDevicePath); + ChildSize = GetDevicePathSize (ChildDevicePath); + + if (ParentSize > ChildSize) { + return FALSE; + } + + if (CompareMem (ParentDevicePath, ChildDevicePath, ParentSize - END_DEVICE_PATH_LENGTH) != 0) { + return FALSE; + } + + return TRUE; +} + +EFI_STATUS +BdsLoadOptionMemMapList ( + IN OUT LIST_ENTRY* BdsLoadOptionList + ) +{ + EFI_STATUS Status; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN DevicePathHandleCount; + EFI_HANDLE *DevicePathHandleBuffer; + BOOLEAN IsParent; + UINTN Index; + UINTN Index2; + BDS_SUPPORTED_DEVICE *SupportedDevice; + EFI_DEVICE_PATH_PROTOCOL* DevicePathProtocol; + EFI_DEVICE_PATH* DevicePath; + + // List all the BlockIo Protocols + Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &HandleCount, &HandleBuffer); + if (EFI_ERROR (Status)) { + return Status; + } + + for (Index = 0; Index < HandleCount; Index++) { + // We only select the handle WITH a Device Path AND not part of Media (to avoid duplication with HardDisk, CDROM, etc) + Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathProtocol); + if (!EFI_ERROR(Status)) { + // BlockIo is not part of Media Device Path + DevicePath = DevicePathProtocol; + while (!IsDevicePathEndType (DevicePath) && (DevicePathType (DevicePath) != MEDIA_DEVICE_PATH)) { + DevicePath = NextDevicePathNode (DevicePath); + } + if (DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) { + continue; + } + + // Open all the handle supporting the DevicePath protocol and verify this handle has not got any child + Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiDevicePathProtocolGuid, NULL, &DevicePathHandleCount, &DevicePathHandleBuffer); + ASSERT_EFI_ERROR (Status); + IsParent = FALSE; + for (Index2 = 0; (Index2 < DevicePathHandleCount) && !IsParent; Index2++) { + if (HandleBuffer[Index] != DevicePathHandleBuffer[Index2]) { + gBS->HandleProtocol (DevicePathHandleBuffer[Index2], &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath); + if (IsParentDevicePath (DevicePathProtocol, DevicePath)) { + IsParent = TRUE; + } + } + } + if (IsParent) { + continue; + } + + // Allocate BDS Supported Device structure + SupportedDevice = (BDS_SUPPORTED_DEVICE*)AllocatePool(sizeof(BDS_SUPPORTED_DEVICE)); + + Status = GenerateDeviceDescriptionName (HandleBuffer[Index], SupportedDevice->Description); + ASSERT_EFI_ERROR (Status); + + SupportedDevice->DevicePathProtocol = DevicePathProtocol; + SupportedDevice->Support = &BdsLoadOptionSupportList[BDS_DEVICE_MEMMAP]; + + InsertTailList (BdsLoadOptionList,&SupportedDevice->Link); + } + } + + return EFI_SUCCESS; +} + +EFI_STATUS +BdsLoadOptionMemMapCreateDevicePath ( + IN BDS_SUPPORTED_DEVICE* BdsLoadOption, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePathNode, + OUT BDS_LOADER_TYPE *BootType, + OUT UINT32 *Attributes + ) +{ + EFI_STATUS Status; + MEMMAP_DEVICE_PATH* MemMapDevicePath; + CHAR8 AsciiStartingAddress[BOOT_DEVICE_ADDRESS_MAX]; + CHAR8 AsciiEndingAddress[BOOT_DEVICE_ADDRESS_MAX]; + + Print(L"Starting Address of the binary: "); + Status = GetHIInputAscii (AsciiStartingAddress,BOOT_DEVICE_ADDRESS_MAX); + if (EFI_ERROR(Status)) { + return EFI_ABORTED; + } + + Print(L"Ending Address of the binary: "); + Status = GetHIInputAscii (AsciiEndingAddress,BOOT_DEVICE_ADDRESS_MAX); + if (EFI_ERROR(Status)) { + return EFI_ABORTED; + } + + // Create the MemMap Device Path Node + MemMapDevicePath = (MEMMAP_DEVICE_PATH*)AllocatePool(sizeof(MEMMAP_DEVICE_PATH)); + MemMapDevicePath->Header.Type = HARDWARE_DEVICE_PATH; + MemMapDevicePath->Header.SubType = HW_MEMMAP_DP; + MemMapDevicePath->MemoryType = EfiBootServicesData; + MemMapDevicePath->StartingAddress = AsciiStrHexToUint64 (AsciiStartingAddress); + MemMapDevicePath->EndingAddress = AsciiStrHexToUint64 (AsciiEndingAddress); + + Status = BootDeviceGetType (NULL, BootType, Attributes); + if (EFI_ERROR(Status)) { + FreePool (MemMapDevicePath); + } else { + *DevicePathNode = (EFI_DEVICE_PATH_PROTOCOL*)MemMapDevicePath; + } + + return Status; +} + +EFI_STATUS +BdsLoadOptionMemMapUpdateDevicePath ( + IN BDS_LOAD_OPTION *BootOption, + OUT EFI_DEVICE_PATH_PROTOCOL** NewDevicePath, + OUT BDS_LOADER_TYPE *BootType, + OUT UINT32 *Attributes + ) +{ + ASSERT(0); + //TODO: Implement me + return EFI_SUCCESS; +} + +BOOLEAN +BdsLoadOptionMemMapIsSupported ( + IN BDS_LOAD_OPTION* BdsLoadOption + ) +{ + EFI_DEVICE_PATH* DevicePathNode; + + DevicePathNode = GetLastDevicePathNode (BdsLoadOption->FilePathList); + + return IS_DEVICE_PATH_NODE(DevicePathNode,HARDWARE_DEVICE_PATH,HW_MEMMAP_DP); +} + +EFI_STATUS +BdsLoadOptionPxeList ( + IN OUT LIST_ENTRY* BdsLoadOptionList + ) +{ + EFI_STATUS Status; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN Index; + BDS_SUPPORTED_DEVICE *SupportedDevice; + EFI_DEVICE_PATH_PROTOCOL* DevicePathProtocol; + EFI_SIMPLE_NETWORK_PROTOCOL* SimpleNet; + CHAR16 DeviceDescription[BOOT_DEVICE_DESCRIPTION_MAX]; + EFI_MAC_ADDRESS *Mac; + + // List all the PXE Protocols + Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiPxeBaseCodeProtocolGuid, NULL, &HandleCount, &HandleBuffer); + if (EFI_ERROR (Status)) { + return Status; + } + + for (Index = 0; Index < HandleCount; Index++) { + // We only select the handle WITH a Device Path AND the PXE Protocol + Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathProtocol); + if (!EFI_ERROR(Status)) { + // Allocate BDS Supported Device structure + SupportedDevice = (BDS_SUPPORTED_DEVICE*)AllocatePool(sizeof(BDS_SUPPORTED_DEVICE)); + + Status = gBS->LocateProtocol (&gEfiSimpleNetworkProtocolGuid, NULL, (VOID **)&SimpleNet); + if (!EFI_ERROR(Status)) { + Mac = &SimpleNet->Mode->CurrentAddress; + UnicodeSPrint (DeviceDescription,BOOT_DEVICE_DESCRIPTION_MAX,L"MAC Address: %02x:%02x:%02x:%02x:%02x:%02x", Mac->Addr[0], Mac->Addr[1], Mac->Addr[2], Mac->Addr[3], Mac->Addr[4], Mac->Addr[5]); + } else { + Status = GenerateDeviceDescriptionName (HandleBuffer[Index], DeviceDescription); + ASSERT_EFI_ERROR (Status); + } + UnicodeSPrint (SupportedDevice->Description,BOOT_DEVICE_DESCRIPTION_MAX,L"PXE on %s",DeviceDescription); + + SupportedDevice->DevicePathProtocol = DevicePathProtocol; + SupportedDevice->Support = &BdsLoadOptionSupportList[BDS_DEVICE_PXE]; + + InsertTailList (BdsLoadOptionList,&SupportedDevice->Link); + } + } + + return EFI_SUCCESS; +} + +EFI_STATUS +BdsLoadOptionPxeCreateDevicePath ( + IN BDS_SUPPORTED_DEVICE* BdsLoadOption, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePathNode, + OUT BDS_LOADER_TYPE *BootType, + OUT UINT32 *Attributes + ) +{ + *DevicePathNode = (EFI_DEVICE_PATH_PROTOCOL *) AllocatePool (END_DEVICE_PATH_LENGTH); + SetDevicePathEndNode (*DevicePathNode); + *BootType = BDS_LOADER_EFI_APPLICATION; + return EFI_SUCCESS; +} + +EFI_STATUS +BdsLoadOptionPxeUpdateDevicePath ( + IN BDS_LOAD_OPTION *BootOption, + OUT EFI_DEVICE_PATH_PROTOCOL** NewDevicePath, + OUT BDS_LOADER_TYPE *BootType, + OUT UINT32 *Attributes + ) +{ + ASSERT (0); + return EFI_SUCCESS; +} + +BOOLEAN +BdsLoadOptionPxeIsSupported ( + IN BDS_LOAD_OPTION* BdsLoadOption + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBcProtocol; + + Status = BdsConnectDevicePath (BdsLoadOption->FilePathList, &Handle, &RemainingDevicePath); + if (EFI_ERROR(Status)) { + return FALSE; + } + + if (!IsDevicePathEnd(RemainingDevicePath)) { + return FALSE; + } + + Status = gBS->HandleProtocol (Handle, &gEfiPxeBaseCodeProtocolGuid, (VOID **)&PxeBcProtocol); + if (EFI_ERROR (Status)) { + return FALSE; + } else { + return TRUE; + } +} + +EFI_STATUS +BdsLoadOptionTftpList ( + IN OUT LIST_ENTRY* BdsLoadOptionList + ) +{ + EFI_STATUS Status; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN Index; + BDS_SUPPORTED_DEVICE *SupportedDevice; + EFI_DEVICE_PATH_PROTOCOL* DevicePathProtocol; + EFI_SIMPLE_NETWORK_PROTOCOL* SimpleNet; + CHAR16 DeviceDescription[BOOT_DEVICE_DESCRIPTION_MAX]; + EFI_MAC_ADDRESS *Mac; + + // List all the PXE Protocols + Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiPxeBaseCodeProtocolGuid, NULL, &HandleCount, &HandleBuffer); + if (EFI_ERROR (Status)) { + return Status; + } + + for (Index = 0; Index < HandleCount; Index++) { + // We only select the handle WITH a Device Path AND the PXE Protocol AND the TFTP Protocol (the TFTP protocol is required to start PXE) + Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathProtocol); + if (!EFI_ERROR(Status)) { + // Allocate BDS Supported Device structure + SupportedDevice = (BDS_SUPPORTED_DEVICE*)AllocatePool(sizeof(BDS_SUPPORTED_DEVICE)); + + Status = gBS->LocateProtocol (&gEfiSimpleNetworkProtocolGuid, NULL, (VOID **)&SimpleNet); + if (!EFI_ERROR(Status)) { + Mac = &SimpleNet->Mode->CurrentAddress; + UnicodeSPrint (DeviceDescription,BOOT_DEVICE_DESCRIPTION_MAX,L"MAC Address: %02x:%02x:%02x:%02x:%02x:%02x", Mac->Addr[0], Mac->Addr[1], Mac->Addr[2], Mac->Addr[3], Mac->Addr[4], Mac->Addr[5]); + } else { + Status = GenerateDeviceDescriptionName (HandleBuffer[Index], DeviceDescription); + ASSERT_EFI_ERROR (Status); + } + UnicodeSPrint (SupportedDevice->Description,BOOT_DEVICE_DESCRIPTION_MAX,L"TFP on %s",DeviceDescription); + + SupportedDevice->DevicePathProtocol = DevicePathProtocol; + SupportedDevice->Support = &BdsLoadOptionSupportList[BDS_DEVICE_TFTP]; + + InsertTailList (BdsLoadOptionList,&SupportedDevice->Link); + } + } + + return EFI_SUCCESS; +} + +EFI_STATUS +BdsLoadOptionTftpCreateDevicePath ( + IN BDS_SUPPORTED_DEVICE* BdsLoadOption, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePathNode, + OUT BDS_LOADER_TYPE *BootType, + OUT UINT32 *Attributes + ) +{ + EFI_STATUS Status; + BOOLEAN IsDHCP; + EFI_IP_ADDRESS LocalIp; + EFI_IP_ADDRESS RemoteIp; + IPv4_DEVICE_PATH* IPv4DevicePathNode; + FILEPATH_DEVICE_PATH* FilePathDevicePath; + CHAR8 AsciiBootFilePath[BOOT_DEVICE_FILEPATH_MAX]; + CHAR16* BootFilePath; + UINTN BootFilePathSize; + + Print(L"Get the IP address from DHCP: "); + Status = GetHIInputBoolean (&IsDHCP); + if (EFI_ERROR(Status)) { + return EFI_ABORTED; + } + + if (!IsDHCP) { + Print(L"Get the static IP address: "); + Status = GetHIInputIP (&LocalIp); + if (EFI_ERROR(Status)) { + return EFI_ABORTED; + } + } + + Print(L"Get the TFTP server IP address: "); + Status = GetHIInputIP (&RemoteIp); + if (EFI_ERROR(Status)) { + return EFI_ABORTED; + } + + Print(L"File path of the EFI Application or the kernel: "); + Status = GetHIInputAscii (AsciiBootFilePath,BOOT_DEVICE_FILEPATH_MAX); + if (EFI_ERROR(Status)) { + return EFI_ABORTED; + } + + // Convert Ascii into Unicode + BootFilePath = (CHAR16*)AllocatePool(AsciiStrSize(AsciiBootFilePath) * sizeof(CHAR16)); + AsciiStrToUnicodeStr (AsciiBootFilePath, BootFilePath); + BootFilePathSize = StrSize(BootFilePath); + + // Allocate the memory for the IPv4 + File Path Device Path Nodes + IPv4DevicePathNode = (IPv4_DEVICE_PATH*)AllocatePool(sizeof(IPv4_DEVICE_PATH) + SIZE_OF_FILEPATH_DEVICE_PATH + BootFilePathSize); + + // Create the IPv4 Device Path + IPv4DevicePathNode->Header.Type = MESSAGING_DEVICE_PATH; + IPv4DevicePathNode->Header.SubType = MSG_IPv4_DP; + SetDevicePathNodeLength (&IPv4DevicePathNode->Header, sizeof(IPv4_DEVICE_PATH)); + CopyMem (&IPv4DevicePathNode->LocalIpAddress, &LocalIp.v4, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&IPv4DevicePathNode->RemoteIpAddress, &RemoteIp.v4, sizeof (EFI_IPv4_ADDRESS)); + IPv4DevicePathNode->LocalPort = 0; + IPv4DevicePathNode->RemotePort = 0; + IPv4DevicePathNode->Protocol = EFI_IP_PROTO_TCP; + IPv4DevicePathNode->StaticIpAddress = (IsDHCP != TRUE); + + // Create the FilePath Device Path node + FilePathDevicePath = (FILEPATH_DEVICE_PATH*)(IPv4DevicePathNode + 1); + FilePathDevicePath->Header.Type = MEDIA_DEVICE_PATH; + FilePathDevicePath->Header.SubType = MEDIA_FILEPATH_DP; + SetDevicePathNodeLength (FilePathDevicePath, SIZE_OF_FILEPATH_DEVICE_PATH + BootFilePathSize); + CopyMem (FilePathDevicePath->PathName, BootFilePath, BootFilePathSize); + FreePool (BootFilePath); + + Status = BootDeviceGetType (NULL, BootType, Attributes); + if (EFI_ERROR(Status)) { + FreePool (IPv4DevicePathNode); + } else { + *DevicePathNode = (EFI_DEVICE_PATH_PROTOCOL*)IPv4DevicePathNode; + } + + return Status; +} + +EFI_STATUS +BdsLoadOptionTftpUpdateDevicePath ( + IN BDS_LOAD_OPTION *BootOption, + OUT EFI_DEVICE_PATH_PROTOCOL** NewDevicePath, + OUT BDS_LOADER_TYPE *BootType, + OUT UINT32 *Attributes + ) +{ + ASSERT (0); + return EFI_SUCCESS; +} + +BOOLEAN +BdsLoadOptionTftpIsSupported ( + IN BDS_LOAD_OPTION* BdsLoadOption + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + EFI_DEVICE_PATH *RemainingDevicePath; + EFI_DEVICE_PATH *NextDevicePath; + EFI_PXE_BASE_CODE_PROTOCOL *PxeBcProtocol; + + Status = BdsConnectDevicePath (BdsLoadOption->FilePathList, &Handle, &RemainingDevicePath); + if (EFI_ERROR(Status)) { + return FALSE; + } + + // Validate the Remaining Device Path + if (IsDevicePathEnd(RemainingDevicePath)) { + return FALSE; + } + if (!IS_DEVICE_PATH_NODE(RemainingDevicePath,MESSAGING_DEVICE_PATH,MSG_IPv4_DP) && + !IS_DEVICE_PATH_NODE(RemainingDevicePath,MESSAGING_DEVICE_PATH,MSG_IPv6_DP)) { + return FALSE; + } + NextDevicePath = NextDevicePathNode (RemainingDevicePath); + if (IsDevicePathEnd(NextDevicePath)) { + return FALSE; + } + if (!IS_DEVICE_PATH_NODE(NextDevicePath,MEDIA_DEVICE_PATH,MEDIA_FILEPATH_DP)) { + return FALSE; + } + + Status = gBS->HandleProtocol (Handle, &gEfiPxeBaseCodeProtocolGuid, (VOID **)&PxeBcProtocol); + if (EFI_ERROR (Status)) { + return FALSE; + } else { + return TRUE; + } +}