/** Start an EFI Application from a Device Path @param ParentImageHandle Handle of the calling image @param DevicePath Location of the EFI Application @retval EFI_SUCCESS All drivers have been connected @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results. **/ EFI_STATUS BdsStartEfiApplication ( IN EFI_HANDLE ParentImageHandle, IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, IN UINTN LoadOptionsSize, IN VOID* LoadOptions ) { EFI_STATUS Status; EFI_HANDLE ImageHandle; EFI_PHYSICAL_ADDRESS BinaryBuffer; UINTN BinarySize; EFI_LOADED_IMAGE_PROTOCOL* LoadedImage; // Find the nearest supported file loader Status = BdsLoadImage (DevicePath, AllocateAnyPages, &BinaryBuffer, &BinarySize); if (EFI_ERROR(Status)) { return Status; } // Load the image from the Buffer with Boot Services function Status = gBS->LoadImage (TRUE, ParentImageHandle, DevicePath, (VOID*)(UINTN)BinaryBuffer, BinarySize, &ImageHandle); if (EFI_ERROR(Status)) { return Status; } // Passed LoadOptions to the EFI Application if (LoadOptionsSize != 0) { Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &LoadedImage); if (EFI_ERROR(Status)) { return Status; } LoadedImage->LoadOptionsSize = LoadOptionsSize; LoadedImage->LoadOptions = LoadOptions; } // Before calling the image, enable the Watchdog Timer for the 5 Minute period gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL); // Start the image Status = gBS->StartImage (ImageHandle, NULL, NULL); // Clear the Watchdog Timer after the image returns gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL); return Status; }
EFI_STATUS BootDeviceGetType ( IN EFI_DEVICE_PATH* DevicePath, OUT ARM_BDS_LOADER_TYPE *BootType, OUT UINT32 *Attributes ) { EFI_STATUS Status; BOOLEAN IsEfiApp; BOOLEAN IsBootLoader; BOOLEAN HasFDTSupport; CHAR16* FileName; EFI_DEVICE_PATH* PrevDevicePathNode; EFI_DEVICE_PATH* DevicePathNode; EFI_PHYSICAL_ADDRESS Image; UINTN FileSize; EFI_IMAGE_DOS_HEADER* DosHeader; UINTN PeCoffHeaderOffset; EFI_IMAGE_NT_HEADERS32* NtHeader; // // Check if the last node of the device path is a FilePath node // PrevDevicePathNode = NULL; DevicePathNode = DevicePath; while ((DevicePathNode != NULL) && !IsDevicePathEnd (DevicePathNode)) { PrevDevicePathNode = DevicePathNode; DevicePathNode = NextDevicePathNode (DevicePathNode); } if ((PrevDevicePathNode != NULL) && (PrevDevicePathNode->Type == MEDIA_DEVICE_PATH) && (PrevDevicePathNode->SubType == MEDIA_FILEPATH_DP)) { FileName = ((FILEPATH_DEVICE_PATH*)PrevDevicePathNode)->PathName; } else { FileName = NULL; } 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 { // Check if the file exist Status = BdsLoadImage (DevicePath, AllocateAnyPages, &Image, &FileSize); if (!EFI_ERROR (Status)) { DosHeader = (EFI_IMAGE_DOS_HEADER *)(UINTN) Image; if (DosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE) { // // DOS image header is present, // so read the PE header after the DOS image header. // PeCoffHeaderOffset = DosHeader->e_lfanew; } else { PeCoffHeaderOffset = 0; } // // Check PE/COFF image. // NtHeader = (EFI_IMAGE_NT_HEADERS32 *)(UINTN) (Image + PeCoffHeaderOffset); if (NtHeader->Signature != EFI_IMAGE_NT_SIGNATURE) { IsEfiApp = FALSE; } else { IsEfiApp = TRUE; } // Free memory gBS->FreePages (Image, EFI_SIZE_TO_PAGES(FileSize)); } else { // If we did not manage to open it then ask for the type Print(L"Is an EFI Application? "); Status = GetHIInputBoolean (&IsEfiApp); if (EFI_ERROR(Status)) { return EFI_ABORTED; } } } if (IsEfiApp) { Print(L"Is your application 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; }
/** Start a Linux kernel from a Device Path @param LinuxKernelDevicePath Device Path to the Linux Kernel @param InitrdDevicePath Device Path to the Initrd @param CommandLineArguments Linux command line @retval EFI_SUCCESS All drivers have been connected @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results. **/ EFI_STATUS BdsBootLinuxFdt ( IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath, IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath, IN CONST CHAR8* CommandLineArguments ) { EFI_STATUS Status; UINT32 LinuxImageSize; UINT32 InitrdImageBaseSize = 0; UINT32 InitrdImageSize = 0; VOID *InstalledFdtBase; UINT32 FdtBlobSize; EFI_PHYSICAL_ADDRESS FdtBlobBase; EFI_PHYSICAL_ADDRESS LinuxImage; EFI_PHYSICAL_ADDRESS InitrdImageBase = 0; EFI_PHYSICAL_ADDRESS InitrdImage = 0; PERF_START (NULL, "BDS", NULL, 0); // Load the Linux kernel from a device path LinuxImage = LINUX_KERNEL_MAX_OFFSET; Status = BdsLoadImage (LinuxKernelDevicePath, AllocateMaxAddress, &LinuxImage, &LinuxImageSize); if (EFI_ERROR(Status)) { Print (L"ERROR: Did not find Linux kernel.\n"); return Status; } if (InitrdDevicePath) { InitrdImageBase = LINUX_KERNEL_MAX_OFFSET; Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, &InitrdImageBase, &InitrdImageBaseSize); if (Status == EFI_OUT_OF_RESOURCES) { Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImageBase, &InitrdImageBaseSize); } if (EFI_ERROR(Status)) { Print (L"ERROR: Did not find initrd image.\n"); goto EXIT_FREE_LINUX; } // Check if the initrd is a uInitrd if (*(UINT32*)((UINTN)InitrdImageBase) == LINUX_UIMAGE_SIGNATURE) { // Skip the 64-byte image header InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImageBase + 64); InitrdImageSize = InitrdImageBaseSize - 64; } else { InitrdImage = InitrdImageBase; InitrdImageSize = InitrdImageBaseSize; } } // // Get the FDT from the Configuration Table. // The FDT will be reloaded in PrepareFdt() to a more appropriate // location for the Linux Kernel. // Status = EfiGetSystemConfigurationTable (&gFdtTableGuid, &InstalledFdtBase); if (EFI_ERROR (Status)) { Print (L"ERROR: Did not get the Device Tree blob (%r).\n", Status); goto EXIT_FREE_INITRD; } FdtBlobBase = (EFI_PHYSICAL_ADDRESS)(UINTN)InstalledFdtBase; FdtBlobSize = fdt_totalsize (InstalledFdtBase); // Update the Fdt with the Initrd information. The FDT will increase in size. // By setting address=0 we leave the memory allocation to the function Status = PrepareFdt (CommandLineArguments, InitrdImage, InitrdImageSize, &FdtBlobBase, &FdtBlobSize); if (EFI_ERROR(Status)) { Print(L"ERROR: Can not load kernel with FDT. Status=%r\n", Status); goto EXIT_FREE_FDT; } return StartLinux (LinuxImage, LinuxImageSize, FdtBlobBase, FdtBlobSize, ARM_FDT_MACHINE_TYPE); EXIT_FREE_FDT: gBS->FreePages (FdtBlobBase, EFI_SIZE_TO_PAGES (FdtBlobSize)); EXIT_FREE_INITRD: if (InitrdDevicePath) { gBS->FreePages (InitrdImageBase, EFI_SIZE_TO_PAGES (InitrdImageBaseSize)); } EXIT_FREE_LINUX: gBS->FreePages (LinuxImage, EFI_SIZE_TO_PAGES (LinuxImageSize)); return Status; }
/** * This function check if the DevicePath defines an EFI binary * * This function is used when the BDS support Linux loader to * detect if the binary is an EFI application or potentially a * Linux kernel. */ EFI_STATUS IsEfiBinary ( IN EFI_DEVICE_PATH* DevicePath, OUT BOOLEAN *EfiBinary ) { EFI_STATUS Status; CHAR16* FileName; EFI_DEVICE_PATH* PrevDevicePathNode; EFI_DEVICE_PATH* DevicePathNode; EFI_PHYSICAL_ADDRESS Image; UINTN FileSize; EFI_IMAGE_DOS_HEADER* DosHeader; UINTN PeCoffHeaderOffset; EFI_IMAGE_NT_HEADERS32* NtHeader; ASSERT (EfiBinary != NULL); // // Check if the last node of the device path is a FilePath node // PrevDevicePathNode = NULL; DevicePathNode = DevicePath; while ((DevicePathNode != NULL) && !IsDevicePathEnd (DevicePathNode)) { PrevDevicePathNode = DevicePathNode; DevicePathNode = NextDevicePathNode (DevicePathNode); } if ((PrevDevicePathNode != NULL) && (PrevDevicePathNode->Type == MEDIA_DEVICE_PATH) && (PrevDevicePathNode->SubType == MEDIA_FILEPATH_DP)) { FileName = ((FILEPATH_DEVICE_PATH*)PrevDevicePathNode)->PathName; } else { FileName = NULL; } if (FileName == NULL) { Print (L"Is an EFI Application? "); Status = GetHIInputBoolean (EfiBinary); if (EFI_ERROR (Status)) { return EFI_ABORTED; } } else if (HasFilePathEfiExtension (FileName)) { *EfiBinary = TRUE; } else { // Check if the file exist Status = BdsLoadImage (DevicePath, AllocateAnyPages, &Image, &FileSize); if (!EFI_ERROR (Status)) { DosHeader = (EFI_IMAGE_DOS_HEADER *)(UINTN) Image; if (DosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE) { // // DOS image header is present, // so read the PE header after the DOS image header. // PeCoffHeaderOffset = DosHeader->e_lfanew; } else { PeCoffHeaderOffset = 0; } // // Check PE/COFF image. // NtHeader = (EFI_IMAGE_NT_HEADERS32 *)(UINTN) (Image + PeCoffHeaderOffset); if (NtHeader->Signature != EFI_IMAGE_NT_SIGNATURE) { *EfiBinary = FALSE; } else { *EfiBinary = TRUE; } // Free memory gBS->FreePages (Image, EFI_SIZE_TO_PAGES (FileSize)); } else { // If we did not manage to open it then ask for the type Print (L"Is an EFI Application? "); Status = GetHIInputBoolean (EfiBinary); if (EFI_ERROR (Status)) { return EFI_ABORTED; } } } return EFI_SUCCESS; }
/** Start a Linux kernel from a Device Path @param LinuxKernel Device Path to the Linux Kernel @param Parameters Linux kernel arguments @param Fdt Device Path to the Flat Device Tree @retval EFI_SUCCESS All drivers have been connected @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results. **/ EFI_STATUS BdsBootLinuxAtag ( IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath, IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath, IN CONST CHAR8* CommandLineArguments ) { EFI_STATUS Status; UINT32 LinuxImageSize; UINT32 InitrdImageBaseSize = 0; UINT32 InitrdImageSize = 0; UINT32 AtagSize; EFI_PHYSICAL_ADDRESS AtagBase; EFI_PHYSICAL_ADDRESS LinuxImage; EFI_PHYSICAL_ADDRESS InitrdImageBase = 0; EFI_PHYSICAL_ADDRESS InitrdImage = 0; PERF_START (NULL, "BDS", NULL, 0); // Load the Linux kernel from a device path LinuxImage = LINUX_KERNEL_MAX_OFFSET; Status = BdsLoadImage (LinuxKernelDevicePath, AllocateMaxAddress, &LinuxImage, &LinuxImageSize); if (EFI_ERROR(Status)) { Print (L"ERROR: Did not find Linux kernel.\n"); return Status; } if (InitrdDevicePath) { // Load the initrd near to the Linux kernel InitrdImageBase = LINUX_KERNEL_MAX_OFFSET; Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, &InitrdImageBase, &InitrdImageBaseSize); if (Status == EFI_OUT_OF_RESOURCES) { Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImageBase, &InitrdImageBaseSize); } if (EFI_ERROR(Status)) { Print (L"ERROR: Did not find initrd image.\n"); goto EXIT_FREE_LINUX; } // Check if the initrd is a uInitrd if (*(UINT32*)((UINTN)InitrdImageBase) == LINUX_UIMAGE_SIGNATURE) { // Skip the 64-byte image header InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImageBase + 64); InitrdImageSize = InitrdImageBaseSize - 64; } else { InitrdImage = InitrdImageBase; InitrdImageSize = InitrdImageBaseSize; } } // // Setup the Linux Kernel Parameters // // By setting address=0 we leave the memory allocation to the function Status = PrepareAtagList (CommandLineArguments, InitrdImage, InitrdImageSize, &AtagBase, &AtagSize); if (EFI_ERROR(Status)) { Print(L"ERROR: Can not prepare ATAG list. Status=0x%X\n", Status); goto EXIT_FREE_INITRD; } return StartLinux (LinuxImage, LinuxImageSize, AtagBase, AtagSize, PcdGet32(PcdArmMachineType)); EXIT_FREE_INITRD: if (InitrdDevicePath) { gBS->FreePages (InitrdImageBase, EFI_SIZE_TO_PAGES (InitrdImageBaseSize)); } EXIT_FREE_LINUX: gBS->FreePages (LinuxImage, EFI_SIZE_TO_PAGES (LinuxImageSize)); return Status; }