/** @file Support routines for ASF boot options in the BDS @copyright Copyright (c) 2005-2012 Intel Corporation. All rights reserved This software and associated documentation (if any) is furnished under a license and may only be used or copied in accordance with the terms of the license. Except as permitted by such license, no part of this software or documentation may be reproduced, stored in a retrieval system, or transmitted in any form or by any means without the express written consent of Intel Corporation. This file contains a 'Sample Driver' and is licensed as such under the terms of your license agreement with Intel or your vendor. This file may be modified by the user, subject to the additional terms of the license agreement **/ #include "AsfSupport.h" #pragma pack(push,1) typedef struct { UINT32 Attributes; UINT16 FilePathListLength; } EFI_LOAD_OPTION; #pragma pack(pop) // // Global variables // EFI_ASF_BOOT_OPTIONS *mAsfBootOptions; EFI_GUID gAsfRestoreBootSettingsGuid = RESTORE_SECURE_BOOT_GUID; /** Retrieve the ASF boot options previously recorded by the ASF driver. @param[in] None. @retval EFI_SUCCESS Initialized Boot Options global variable and AMT protocol **/ EFI_STATUS BdsAsfInitialization ( IN VOID ) { EFI_STATUS Status; EFI_ALERT_STANDARD_FORMAT_PROTOCOL *Asf; mAsfBootOptions = NULL; // // Amt Library Init // Status = AmtLibInit (); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Info : Error init AmtLibInit -> %r\n", Status)); return Status; } // // Get Protocol for ASF // Status = gBS->LocateProtocol ( &gEfiAlertStandardFormatProtocolGuid, NULL, &Asf ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Info : Error getting ASF protocol -> %r\n", Status)); return Status; } Status = Asf->GetBootOptions (Asf, &mAsfBootOptions); if (EFI_ERROR(Status)) { DEBUG ((EFI_D_ERROR, "Info : Error getting ASF BootOptions -> %r\n", Status)); return Status; } Status = ManageSecureBootState(); return Status; } /** Get current Secure Boot state (enabled/disabled) @param[in] None. @retval UINT8 Secure Boot State **/ UINT8 GetSecureBootState( IN VOID ) { // // This function is BIOS implementation specific // and should be implemented in platform code // return SECURE_BOOT_DISABLED; } /** Set current Secure Boot state (enabled/disabled) @param[in] SecureBootState Secure Boot State @retval EFI_SUCCESS Secure Boot State successfully changed **/ EFI_STATUS SetSecureBootState( IN UINT8 SecureBootState ) { // // This function is BIOS implementation specific // and should be implemented in platform code // return EFI_SUCCESS; } /** This routine makes necessary Secure Boot & CSM state changes for IDEr boot @param[in] None. @retval EFI_SUCCESS Changes applied succesfully **/ EFI_STATUS ManageSecureBootState( IN VOID ) { EFI_STATUS Status; BOOLEAN EnforceSecureBoot; UINT8 SecureBootState; UINT8 RestoreBootSettings; UINT8 IderBoot; UINTN VarSize; VarSize = sizeof(UINT8); // // Get boot parameters (IDER boot?, EnforceSecureBoot flag set?, secure boot enabled?) // EnforceSecureBoot = ActiveManagementEnforceSecureBoot(); IderBoot = ActiveManagementEnableIdeR(); SecureBootState = GetSecureBootState(); // // Check whether we need to restore SecureBootEnable value changed in previous IDER boot // Status = gRT->GetVariable( L"RestoreBootSettings", &gAsfRestoreBootSettingsGuid, NULL, &VarSize, &RestoreBootSettings ); if (Status == EFI_SUCCESS && RestoreBootSettings != RESTORE_SECURE_BOOT_NONE) { if (RestoreBootSettings == RESTORE_SECURE_BOOT_ENABLED && SecureBootState == SECURE_BOOT_DISABLED && !(IderBoot && !EnforceSecureBoot)) { SecureBootState = SECURE_BOOT_ENABLED; Status = SetSecureBootState(SecureBootState); ASSERT_EFI_ERROR (Status); // // Delete RestoreBootSettings variable // Status = gRT->SetVariable( L"RestoreBootSettings", &gAsfRestoreBootSettingsGuid, 0, 0, NULL ); ASSERT_EFI_ERROR (Status); DEBUG ((EFI_D_INFO, "Secure Boot settings restored after IDER boot - Cold Reset!\n")); gRT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); EFI_DEADLOOP(); } } Status = EFI_SUCCESS; if (IderBoot) { if (SecureBootState == SECURE_BOOT_ENABLED && !EnforceSecureBoot) { // // Secure boot needs to be disabled if we're doing IDER and EnforceSecureBoot not set // SecureBootState = SECURE_BOOT_DISABLED; RestoreBootSettings = RESTORE_SECURE_BOOT_ENABLED; Status = SetSecureBootState(SecureBootState); ASSERT_EFI_ERROR (Status); // // Set variable to restore previous secure boot state // Status = gRT->SetVariable( L"RestoreBootSettings", &gAsfRestoreBootSettingsGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, sizeof(UINT8), &RestoreBootSettings ); ASSERT_EFI_ERROR (Status); DEBUG ((EFI_D_INFO, "Secure Boot disabled for IDER boot - Cold Reset!\n")); gRT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); EFI_DEADLOOP(); } } return Status; } /** This function will create a BootOption from the give device path and description string. @param[in] DevicePath The device path which the option represent @param[in] Description The description of the boot option @retval BDS_COMMON_OPTION - Pointer to created boot option **/ BDS_COMMON_OPTION * BdsCreateBootOption ( IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, IN CHAR16 *Description ) { BDS_COMMON_OPTION *Option; Option = AllocateZeroPool (sizeof (BDS_COMMON_OPTION)); if (Option == NULL) { return NULL; } Option->Signature = BDS_LOAD_OPTION_SIGNATURE; Option->DevicePath = AllocateZeroPool (GetDevicePathSize (DevicePath)); CopyMem (Option->DevicePath, DevicePath, GetDevicePathSize (DevicePath)); Option->Attribute = LOAD_OPTION_ACTIVE; Option->Description = AllocateZeroPool (EfiStrSize (Description)); CopyMem (Option->Description, Description, EfiStrSize (Description)); return Option; } /** This function will create a SHELL BootOption to boot. @param[in] None. @retval EFI_DEVICE_PATH_PROTOCOL Shell Device path for booting. **/ EFI_DEVICE_PATH_PROTOCOL * BdsCreateShellDevicePath ( VOID ) { UINTN FvHandleCount; EFI_HANDLE *FvHandleBuffer; UINTN Index; EFI_STATUS Status; EFI_FIRMWARE_VOLUME_PROTOCOL *Fv; EFI_FV_FILETYPE Type; UINTN Size; EFI_FV_FILE_ATTRIBUTES Attributes; UINT32 AuthenticationStatus; EFI_DEVICE_PATH_PROTOCOL *DevicePath; MEDIA_FW_VOL_FILEPATH_DEVICE_PATH ShellNode; DevicePath = NULL; Status = EFI_SUCCESS; gBS->LocateHandleBuffer ( ByProtocol, &gEfiFirmwareVolumeProtocolGuid, NULL, &FvHandleCount, &FvHandleBuffer ); for (Index = 0; Index < FvHandleCount; Index++) { gBS->HandleProtocol ( FvHandleBuffer[Index], &gEfiFirmwareVolumeProtocolGuid, (VOID **) &Fv ); Status = Fv->ReadFile ( Fv, &gEfiShellFileGuid, NULL, &Size, &Type, &Attributes, &AuthenticationStatus ); if (EFI_ERROR (Status)) { // // Skip if no shell file in the FV // continue; } else { // // Found the shell // break; } } if (EFI_ERROR (Status)) { // // No shell present // if (FvHandleCount) { FreePool (FvHandleBuffer); } return NULL; } // // Build the shell boot option // DevicePath = DevicePathFromHandle (FvHandleBuffer[Index]); // // Build the shell device path // ShellNode.Header.Type = MEDIA_DEVICE_PATH; ShellNode.Header.SubType = MEDIA_FV_FILEPATH_DP; SetDevicePathNodeLength (&ShellNode.Header, sizeof (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH)); CopyMem (&ShellNode.NameGuid, &gEfiShellFileGuid, sizeof (EFI_GUID)); DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &ShellNode); if (FvHandleCount) { FreePool (FvHandleBuffer); } return DevicePath; } /** This function will create a PXE BootOption to boot. @param[in] DeviceIndex PXE handle index @retval EFI_DEVICE_PATH_PROTOCOL PXE Device path for booting. **/ EFI_DEVICE_PATH_PROTOCOL * BdsCreatePxeDevicePath ( IN UINT16 DeviceIndex ) { UINTN Index; EFI_STATUS Status; EFI_DEVICE_PATH_PROTOCOL *DevicePath; UINTN NumberLoadFileHandles; EFI_HANDLE *LoadFileHandles; VOID *ProtocolInstance; DevicePath = NULL; Status = EFI_SUCCESS; // // We want everything connected up for PXE // BdsLibConnectAllDriversToAllControllers (); // // Parse Network Boot Device // gBS->LocateHandleBuffer ( ByProtocol, &gEfiSimpleNetworkProtocolGuid, NULL, &NumberLoadFileHandles, &LoadFileHandles ); for (Index = 0; Index < NumberLoadFileHandles; Index++) { Status = gBS->HandleProtocol ( LoadFileHandles[Index], &gEfiLoadFileProtocolGuid, (VOID **) &ProtocolInstance ); if (EFI_ERROR (Status)) { // // try next handle // continue; } else { if (Index == DeviceIndex) { // // Found a PXE handle // break; } else { Status = EFI_UNSUPPORTED; } } } if (EFI_ERROR (Status)) { // // No PXE present // if (NumberLoadFileHandles) { FreePool (LoadFileHandles); } return NULL; } // // Build the PXE device path // DevicePath = DevicePathFromHandle (LoadFileHandles[Index]); if (NumberLoadFileHandles) { FreePool (LoadFileHandles); } return DevicePath; } BOOLEAN ComparePathNode( IN EFI_DEVICE_PATH_PROTOCOL *PathNode1, IN EFI_DEVICE_PATH_PROTOCOL *PathNode2 ) { BOOLEAN st = FALSE; UINTN Size1, Size2; UINT8 *p1, *p2; if ((PathNode1 == NULL) || (PathNode2 == NULL)) { return FALSE; } if (PathNode1 == PathNode2) { st = TRUE; } else { Size1 = DevicePathNodeLength(PathNode1); Size2 = DevicePathNodeLength(PathNode2); p1 = (UINT8 *)PathNode1; p2 = (UINT8 *)PathNode2; if ((Size1 == Size2) && (DevicePathType(PathNode1) == DevicePathType(PathNode2)) && (CompareMem(p1+1, p2+1, Size1-1) == 0)) { st = TRUE; } } return st; } /** Compare two device paths node by node up to MEDIA_DEVICE_PATH node @param[in] BootOptionDP Device path acquired from BootXXXX EFI variable @param[in] FileSysDP Device path acquired through EFI_SIMPLE_FILE_SYSTEM_PROTOCOL Handles buffer @retval TRUE Both device paths point to the same device @retval FALSE Device paths point to different devices **/ BOOLEAN CompareDevicePaths( IN EFI_DEVICE_PATH_PROTOCOL *BootOptionDP, IN EFI_DEVICE_PATH_PROTOCOL *FileSysDP ) { EFI_DEVICE_PATH_PROTOCOL *DevPathNodeA; EFI_DEVICE_PATH_PROTOCOL *DevPathNodeB; if (BootOptionDP == NULL || FileSysDP == NULL) { return FALSE; } DevPathNodeA = BdsLibUnpackDevicePath(BootOptionDP); if (DevPathNodeA == NULL) { return FALSE; } DevPathNodeB = BdsLibUnpackDevicePath(FileSysDP); if (DevPathNodeB == NULL) { return FALSE; } while (!IsDevicePathEnd(DevPathNodeB)) { if (DevicePathType(DevPathNodeB) == MEDIA_DEVICE_PATH) { // // If we have reached MEDIA_DEVICE_PATH node and all previous // nodes matched - we can be sure path points to the same device // return TRUE; } if (!ComparePathNode(DevPathNodeA, DevPathNodeB)) { break; } DevPathNodeA = NextDevicePathNode(DevPathNodeA); DevPathNodeB = NextDevicePathNode(DevPathNodeB); } return FALSE; } /** Get EFI device path through EFI_SIMPLE_FILE_SYSTEM_PROTOCOL Handles buffer. Acquired path must point to the same device as argument DevicePath passed to the function. @param[in] DevicePath Device path acquired from BootXXXX EFI variable @retval EFI_DEVICE_PATH_PROTOCOL Device path for booting **/ EFI_DEVICE_PATH_PROTOCOL * GetFullBootDevicePath( IN EFI_DEVICE_PATH_PROTOCOL *DevicePath ) { EFI_STATUS Status; EFI_DEVICE_PATH_PROTOCOL *DPath; EFI_DEVICE_PATH_PROTOCOL *DevPath; UINTN HandleNum; EFI_HANDLE *HandleBuf; UINTN Index; DevPath = NULL; Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &HandleNum, &HandleBuf ); if ((EFI_ERROR (Status)) || (HandleBuf == NULL)) { return NULL; } for (Index = 0; Index < HandleNum; Index++) { Status = gBS->HandleProtocol ( HandleBuf[Index], &gEfiDevicePathProtocolGuid, &DPath ); if (CompareDevicePaths(DevicePath, DPath)) { DevPath = DuplicateDevicePath(DPath); break; } } return DevPath; } /*++ Translate ASF request type to BBS or EFI device path type @param[in] DeviceType - ASF request type @param[in] Efi - Set to TRUE if DeviceType is to be translated to EFI device path type; FALSE if BBS type @retval UINTN Translated device type --*/ UINTN GetBootDeviceType ( IN UINTN DeviceType, IN BOOLEAN Efi ) { UINTN Type = 0; switch (DeviceType) { case FORCE_PXE: if (Efi) { Type = MEDIA_FILEPATH_DP; } else { Type = BBS_EMBED_NETWORK; } break; case FORCE_HARDDRIVE: case FORCE_SAFEMODE: if (Efi) { Type = MEDIA_HARDDRIVE_DP; } else { Type = BBS_TYPE_HARDDRIVE; } break; case FORCE_DIAGNOSTICS: if (Efi) { Type = MEDIA_FILEPATH_DP; } break; case FORCE_CDDVD: if (Efi) { Type = MEDIA_CDROM_DP; } else { Type = BBS_TYPE_CDROM; } break; default: break; } return Type; } /** Update the BBS table with our required boot device @param[in] DeviceIndex Boot device whose device index @param[in] DevType Boot device whose device type @param[in] BbsCount Number of BBS_TABLE structures @param[in] BbsTable BBS entry @param[in] IderBoot set to TRUE if this is IDER boot @retval EFI_SUCCESS BBS table successfully updated **/ EFI_STATUS RefreshBbsTableForBoot ( IN UINT16 DeviceIndex, IN UINT16 DevType, IN BOOLEAN IderBoot ) { EFI_STATUS Status; UINTN Index; UINT16 TempIndex; BOOLEAN IderBootDevice; BOOLEAN RegularBootDevice; HDD_INFO *LocalHddInfo; EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; BBS_TABLE *BbsTable; UINT16 HddCount; UINT16 BbsCount; TempIndex = (IderBoot) ? 0 : ((DeviceIndex <= 1) ? DeviceIndex : 1); // // Make sure the Legacy Boot Protocol is available // Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, &LegacyBios); if (LegacyBios == NULL) { return EFI_ABORTED; } // // Get BBS table instance // Status = LegacyBios->GetBbsInfo ( LegacyBios, &HddCount, &LocalHddInfo, &BbsCount, &BbsTable ); if (EFI_ERROR (Status)) { return EFI_ABORTED; } Status = EFI_NOT_FOUND; // // For debug // PrintBbsTable (BbsTable); // // Find the first present boot device whose device type // matches the DevType, we use it to boot first. This is different // from the other Bbs table refresh since we are looking for the device type // index instead of the first device to match the device type. // // And set other present boot devices' priority to BBS_UNPRIORITIZED_ENTRY // their priority will be set by LegacyBiosPlatform protocol by default // for (Index = 0; Index < BbsCount; Index++) { if (BbsTable[Index].BootPriority == BBS_IGNORE_ENTRY) { continue; } BbsTable[Index].BootPriority = BBS_DO_NOT_BOOT_FROM; IderBootDevice = IderBoot && IS_IDER(BbsTable[Index].Bus, BbsTable[Index].Device, BbsTable[Index].Function) && BbsTable[Index].DeviceType == DevType; RegularBootDevice = !IderBoot && (BbsTable[Index].DeviceType == DevType || (DevType == BBS_EMBED_NETWORK && IS_PXE(BbsTable[Index].DeviceType, BbsTable[Index].Class)) || (DevType == BBS_TYPE_CDROM && IS_CDROM(BbsTable[Index].DeviceType, BbsTable[Index].Class))); if ((IderBootDevice || RegularBootDevice) && Status != EFI_SUCCESS) { if (IderBoot || (TempIndex++ == DeviceIndex)) { BbsTable[Index].BootPriority = 0; Status = EFI_SUCCESS; continue; } } } // // For debug // PrintBbsTable (BbsTable); return Status; } EFI_DEVICE_PATH_PROTOCOL * BdsCreateBootDevicePath ( IN UINT16 DeviceType, IN UINT16 DeviceIndex, IN BOOLEAN IdeRBoot, IN BOOLEAN EfiBoot ) { EFI_DEVICE_PATH_PROTOCOL *DevicePath; EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; EFI_DEVICE_PATH_PROTOCOL *FullDevicePath; UINTN OptionOrderSize; UINT16 *OptionOrder; EFI_LOAD_OPTION *Option; CHAR16 OptionName[sizeof ("Driver####")]; UINT16 OptionNumber; UINTN OptionIndex; UINTN OptionCount; UINTN Index; UINTN OptionSize; UINTN TempIndex; EFI_DEVICE_PATH_PROTOCOL *DevPathNode; EFI_DEVICE_PATH_PROTOCOL *DevPathNodeBackup; ATAPI_DEVICE_PATH *AtaPath; BOOLEAN AtaDeviceMatch; PCI_DEVICE_PATH *PciPath; BOOLEAN PciDeviceMatch; UINT8 PrimarySecondary; UINT8 SlaveMaster; UINTN EfiDeviceType; UINTN LegacyDeviceType; BOOLEAN TypeMatched; PrimarySecondary = ((mAsfBootOptions->SpecialCommandParam >> IDER_BOOT_DEVICE_SHIFT) & IDER_PRIMARY_SECONDARY_MASK) >> IDER_PRIMARY_SECONDARY_SHIFT; SlaveMaster = (mAsfBootOptions->SpecialCommandParam >> IDER_BOOT_DEVICE_SHIFT) & IDER_MASTER_SLAVE_MASK; DevicePath = NULL; FullDevicePath = NULL; TempIndex = 1; AtaDeviceMatch = FALSE; PciDeviceMatch = FALSE; EfiDeviceType = GetBootDeviceType(DeviceType, TRUE); LegacyDeviceType = GetBootDeviceType(DeviceType, FALSE); TypeMatched = FALSE; if (IdeRBoot && !EfiBoot) { LegacyDeviceType = (SlaveMaster == 1) ? BBS_CDROM : BBS_HARDDISK; } // // Read the BootOrder variable. // OptionOrder = BdsLibGetVariableAndSize (L"BootOrder", &gEfiGlobalVariableGuid, &OptionOrderSize); if (OptionOrder == NULL) { return NULL; } OptionCount = OptionOrderSize/sizeof(UINT16); OptionIndex = 0; for (Index = 0; Index < OptionCount; Index++) { OptionNumber = OptionOrder[Index]; UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", OptionNumber); Option = BdsLibGetVariableAndSize (OptionName, &gEfiGlobalVariableGuid, &OptionSize); if (Option == NULL) { continue; } // // Extract device path from the boot order entry // TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL*) ( //skip the header (UINT8*)(Option+1) //skip the string +(EfiStrLen((CHAR16*)(Option+1))+1)*sizeof(CHAR16) ); if (DevicePathType(TempDevicePath) == BBS_DEVICE_PATH && DevicePathSubType(TempDevicePath) == BBS_BBS_DP) { FullDevicePath = DuplicateDevicePath(TempDevicePath); } else { // // If this is EFI boot option, we need to get full device path from EFI_SIMPLE_FILE_SYSTEM_PROTOCOL // to determine type of device and provide LoadImage with proper path to bootloader image later on // FullDevicePath = GetFullBootDevicePath(TempDevicePath); if (FullDevicePath == NULL) { continue; } } TempDevicePath = FullDevicePath; DevPathNode = BdsLibUnpackDevicePath(TempDevicePath); if (DevPathNode == NULL) { continue; } DevPathNodeBackup = DevPathNode; // // Check if this is our requested boot device // while (!IsDevicePathEnd(DevPathNode)) { if (IdeRBoot && EfiBoot) { // // IDER EFI boot, check for PCI/ATA device match // if ((DevicePathType(DevPathNode) == HARDWARE_DEVICE_PATH) && (DevicePathSubType(DevPathNode) == HW_PCI_DP)) { PciPath = (PCI_DEVICE_PATH*) DevPathNode; if ((PciPath->Device == IDER_DEVICE_NUMBER) && (PciPath->Function == IDER_FUNCTION_NUMBER)) { PciDeviceMatch = TRUE; } } else if ((DevicePathType(DevPathNode) == MESSAGING_DEVICE_PATH) && (DevicePathSubType(DevPathNode) == MSG_ATAPI_DP)) { AtaPath = (ATAPI_DEVICE_PATH*) DevPathNode; if ((AtaPath->PrimarySecondary == PrimarySecondary) && (AtaPath->SlaveMaster == SlaveMaster)) { AtaDeviceMatch = TRUE; } } if (PciDeviceMatch && AtaDeviceMatch) { TypeMatched = TRUE; } } else { if (DevicePathType(DevPathNode) == BBS_DEVICE_PATH && DevicePathSubType(DevPathNode) == BBS_BBS_DP) { // // Legacy boot option // if (((BBS_BBS_DEVICE_PATH *)DevPathNode)->DeviceType == LegacyDeviceType) { TypeMatched = TRUE; } } else { // // EFI boot option // if (DevicePathType(DevPathNode) == MEDIA_DEVICE_PATH && DevicePathSubType(DevPathNode) == EfiDeviceType) { TypeMatched = TRUE; } } } if (TypeMatched) { // // Type matched, check for device index // if (!IdeRBoot && TempIndex < DeviceIndex) { TempIndex++; TypeMatched = FALSE; break; } DevicePath = DuplicateDevicePath(TempDevicePath); // // Refresh BBS table if legacy option // if (DevicePathType(DevicePath) == BBS_DEVICE_PATH && DevicePathSubType(DevicePath) == BBS_BBS_DP) { RefreshBbsTableForBoot(DeviceIndex, (UINT16)LegacyDeviceType, IdeRBoot); } break; } DevPathNode = NextDevicePathNode(DevPathNode); } if (FullDevicePath != NULL) { FreePool(FullDevicePath); FullDevicePath = NULL; } FreePool(DevPathNodeBackup); FreePool(Option); if (DevicePath != NULL) { // // Set Boot Current and leave // gRT->SetVariable ( L"BootCurrent", &gEfiGlobalVariableGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, sizeof (UINT16), &OptionNumber ); break; } } FreePool(OptionOrder); return DevicePath; } /** Boot the legacy system with the boot option @param[in] Option The legacy boot option which have BBS device path @retval EFI_UNSUPPORTED - There is no legacybios protocol, do not support legacy boot. @retval EFI_STATUS - Return the status of LegacyBios->LegacyBoot (). **/ EFI_STATUS AsfDoLegacyBoot ( IN BDS_COMMON_OPTION *Option ) { EFI_STATUS Status; EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, &LegacyBios); if (EFI_ERROR (Status)) { // // If no LegacyBios protocol we do not support legacy boot // return EFI_UNSUPPORTED; } // // Write boot to OS performance data to a file // WRITE_BOOT_TO_OS_PERFORMANCE_DATA; DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Legacy Boot: %S\n", Option->Description)); return LegacyBios->LegacyBoot ( LegacyBios, (BBS_BBS_DEVICE_PATH *) Option->DevicePath, Option->LoadOptionsSize, Option->LoadOptions ); } /** Process the boot option follow the EFI 1.1 specification and special treat the legacy boot option with BBS_DEVICE_PATH. @param[in] Option The boot option need to be processed @param[in] DevicePath The device path which describe where to load the boot image or the legcy BBS device path to boot the legacy OS @param[in] ExitDataSize Returned directly from gBS->StartImage () @param[in] ExitData Returned directly from gBS->StartImage () @retval EFI_SUCCESS - Status from gBS->StartImage (), or BdsBootByDiskSignatureAndPartition () @retval EFI_NOT_FOUND - If the Device Path is not found in the system **/ EFI_STATUS AsfBootViaBootOption ( IN BDS_COMMON_OPTION * Option, IN EFI_DEVICE_PATH_PROTOCOL * DevicePath, OUT UINTN *ExitDataSize, OUT CHAR16 **ExitData OPTIONAL ) { EFI_STATUS Status; EFI_HANDLE Handle; EFI_HANDLE ImageHandle; EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; EFI_DEVICE_PATH_PROTOCOL *FilePath; EFI_LOADED_IMAGE_PROTOCOL *ImageInfo; EFI_EVENT ReadyToBootEvent; EFI_ACPI_S3_SAVE_PROTOCOL *AcpiS3Save; UINTN DataSize; EFI_INPUT_KEY Key; UINTN EventIndex; #ifdef EFI_DEBUG UINT8 SecureBootState; #endif *ExitDataSize = 0; *ExitData = NULL; DataSize = sizeof(UINT16); // // Notes: put EFI64 ROM Shadow Solution // EFI64_SHADOW_ALL_LEGACY_ROM (); // // Notes: this code can be remove after the s3 script table // hook on the event EFI_EVENT_SIGNAL_READY_TO_BOOT or // EFI_EVENT_SIGNAL_LEGACY_BOOT // Status = gBS->LocateProtocol (&gEfiAcpiS3SaveGuid, NULL, &AcpiS3Save); if (!EFI_ERROR (Status)) { AcpiS3Save->S3Save (AcpiS3Save, NULL); } // // If it's Device Path that starts with a hard drive path, // this routine will do the booting. // Status = BdsBootByDiskSignatureAndPartition ( Option, (HARDDRIVE_DEVICE_PATH *) DevicePath, Option->LoadOptionsSize, Option->LoadOptions, ExitDataSize, ExitData ); if (!EFI_ERROR (Status)) { // // If we found a disk signature and partition device path return success // return EFI_SUCCESS; } // // Set Option's BootCurrent field // gRT->GetVariable ( L"BootCurrent", &gEfiGlobalVariableGuid, 0, &DataSize, &Option->BootCurrent ); DEBUG ((EFI_D_INFO, "AsfBootViaBootOption: BootCurrent = %d, DevicePath = %s\n", Option->BootCurrent, DevicePathToStr(DevicePath))); // // Signal the EFI_EVENT_SIGNAL_READY_TO_BOOT event // Status = EfiCreateEventReadyToBoot (&ReadyToBootEvent); if (!EFI_ERROR (Status)) { gBS->SignalEvent (ReadyToBootEvent); gBS->CloseEvent (ReadyToBootEvent); } if ((DevicePathType (Option->DevicePath) == BBS_DEVICE_PATH) && (DevicePathSubType (Option->DevicePath) == BBS_BBS_DP) ) { // // Check to see if we should legacy BOOT. If yes then do the legacy boot // return AsfDoLegacyBoot (Option); } DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Booting EFI 1.1 way %S\n", Option->Description)); // // If this is RCO/IDER EFI Boot, don't allow returning to regular boot // and booting other devices // while (1) { Status = gBS->LoadImage ( TRUE, mBdsImageHandle, DevicePath, NULL, 0, &ImageHandle ); // // If we didn't find an image, we may need to load the default // boot behavior for the device. // if (EFI_ERROR (Status)) { // // Find a Simple File System protocol on the device path. If the remaining // device path is set to end then no Files are being specified, so try // the removable media file name. // TempDevicePath = DevicePath; Status = gBS->LocateDevicePath ( &gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle ); if (!EFI_ERROR (Status) && IsDevicePathEnd (TempDevicePath)) { FilePath = FileDevicePath (Handle, DEFAULT_REMOVABLE_FILE_NAME); if (FilePath) { Status = gBS->LoadImage ( TRUE, mBdsImageHandle, FilePath, NULL, 0, &ImageHandle ); } else { Status = EFI_NOT_FOUND; } } else { Status = EFI_NOT_FOUND; } } if (!EFI_ERROR (Status)) { // // Provide the image with it's load options // Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, &ImageInfo); ASSERT_EFI_ERROR (Status); if (Option->LoadOptionsSize != 0) { ImageInfo->LoadOptionsSize = Option->LoadOptionsSize; ImageInfo->LoadOptions = Option->LoadOptions; } #ifdef EFI_DEBUG // // Get SecureBoot state // SecureBootState = GetSecureBootState(); DEBUG ((EFI_D_INFO | EFI_D_LOAD, "SecureBootEnable value prior to image execution %d\n", SecureBootState)); #endif // // Before calling the image, enable the Watchdog Timer for // the 5 Minute period // gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL); Status = gBS->StartImage (ImageHandle, ExitDataSize, ExitData); DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Image Return Status = %r\n", Status)); // // Clear the Watchdog Timer after the image returns // gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL); } // // Display message to user before attempting another RCO/IDER boot // gST->ConOut->ClearScreen (gST->ConOut); gST->ConOut->OutputString ( gST->ConOut, L"EFI RCO/IDER boot failed. Press ENTER to try again\r\n" ); Key.ScanCode = 0; Key.UnicodeChar = 0; while (!(Key.ScanCode == 0 && Key.UnicodeChar == L'\r')) { gBS->WaitForEvent (1, &(gST->ConIn->WaitForKey), &EventIndex); gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); } } // // Clear Boot Current // gRT->SetVariable ( L"BootCurrent", &gEfiGlobalVariableGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 0, &Option->BootCurrent ); return Status; } /** Found out ASF boot options. @param[in] EfiBoot Set to TRUE if this is EFI boot @retval EFI_DEVICE_PATH_PROTOCOL Device path for booting. **/ EFI_DEVICE_PATH_PROTOCOL * BdsAsfBoot ( IN BOOLEAN EfiBoot ) { EFI_DEVICE_PATH_PROTOCOL *DevicePath; DevicePath = NULL; // // First we check ASF boot options Special Command // switch (mAsfBootOptions->SpecialCommand) { // // No additional special command is included; the Special Command Parameter has no // meaning. // case NOP: break; // // The Special Command Parameter can be used to specify a PXE // parameter. When the parameter value is 0, the system default PXE device is booted. All // other values for the PXE parameter are reserved for future definition by this specification. // case FORCE_PXE: if (mAsfBootOptions->SpecialCommandParam != 0) { // // ASF spec says 0 currently only option // break; } if (EfiBoot == TRUE) { DevicePath = BdsCreatePxeDevicePath (mAsfBootOptions->SpecialCommandParam); } else { DevicePath = BdsCreateBootDevicePath (FORCE_PXE, mAsfBootOptions->SpecialCommandParam, FALSE, EfiBoot); } break; // // The Special Command Parameter identifies the boot-media index for // the managed client. When the parameter value is 0, the default hard-drive is booted, when the // parameter value is 1, the primary hard-drive is booted; when the value is 2, the secondary // hard-drive is booted and so on. // case FORCE_HARDDRIVE: // // The Special Command Parameter identifies the boot-media // index for the managed client. When the parameter value is 0, the default hard-drive is // booted, when the parameter value is 1, the primary hard-drive is booted; when the value is 2, // the secondary hard-drive is booted and so on. // case FORCE_SAFEMODE: DevicePath = BdsCreateBootDevicePath(FORCE_HARDDRIVE, mAsfBootOptions->SpecialCommandParam, FALSE, EfiBoot); break; // // The Special Command Parameter can be used to specify a // diagnostic parameter. When the parameter value is 0, the default diagnostic media is booted. // All other values for the diagnostic parameter are reserved for future definition by this // specification. // case FORCE_DIAGNOSTICS: if (mAsfBootOptions->SpecialCommandParam != 0) { // // ASF spec says 0 currently only option // break; } DevicePath = BdsCreateShellDevicePath (); // // We want everything connected up for shell // BdsLibConnectAllDriversToAllControllers (); break; // // The Special Command Parameter identifies the boot-media index for // the managed client. When the parameter value is 0, the default CD/DVD is booted, when the // parameter value is 1, the primary CD/DVD is booted; when the value is 2, the secondary // CD/DVD is booted and so on. // case FORCE_CDDVD: DevicePath = BdsCreateBootDevicePath (FORCE_CDDVD, mAsfBootOptions->SpecialCommandParam, FALSE, EfiBoot); break; default: break;; } return DevicePath; } /** Check IdeR boot device and Asf boot device @param[in] EfiBoot Set to TRUE if this is EFI boot @retval EFI_DEVICE_PATH_PROTOCOL Device path for booting. **/ EFI_DEVICE_PATH_PROTOCOL * BdsForcedBoot ( IN BOOLEAN EfiBoot ) { EFI_DEVICE_PATH_PROTOCOL *DevicePath; DevicePath = NULL; // // OEM command values; the interpretation of the Special Command and associated Special // Command Parameters is defined by the entity associated with the Enterprise ID. // if (ActiveManagementEnableIdeR ()) { // // Check if any media exist in Ider device // if (BdsCheckIderMedia ()) { DevicePath = BdsCreateBootDevicePath ( FORCE_CDDVD, 0, TRUE, EfiBoot ); } } else if (mAsfBootOptions->IanaId != ASF_INDUSTRY_CONVERTED_IANA) { DevicePath = BdsAsfBoot (EfiBoot); } return DevicePath; } /** Process ASF boot options and if available, attempt the boot @param[in] None. @retval EFI_SUCCESS The command completed successfully **/ EFI_STATUS BdsBootViaAsf ( IN VOID ) { EFI_STATUS Status; EFI_DEVICE_PATH_PROTOCOL *DevicePath; BDS_COMMON_OPTION *BootOption; UINTN ExitDataSize; CHAR16 *ExitData; BOOLEAN EfiBoot; EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; Status = EFI_SUCCESS; DevicePath = NULL; EfiBoot = FALSE; // // Check if this is legacy or efi boot // Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, &LegacyBios); if (LegacyBios == NULL) { EfiBoot = TRUE; } // // Check if ASF Boot Options is present. // if (mAsfBootOptions->SubCommand != ASF_BOOT_OPTIONS_PRESENT) { return EFI_NOT_FOUND; } DevicePath = BdsForcedBoot (EfiBoot); // // If device path was set, the we have a boot option to use // if (DevicePath == NULL) { return EFI_UNSUPPORTED; } BootOption = BdsCreateBootOption (DevicePath, L"ASF Boot"); if (BootOption == NULL) { return EFI_OUT_OF_RESOURCES; } Status = AsfBootViaBootOption (BootOption, BootOption->DevicePath, &ExitDataSize, &ExitData); FreePool (BootOption); FreePool (DevicePath); return Status; } /** This will return if Media in IDE-R is present. @param[in] None. @retval TRUE Media is present. @retval FALSE Media is not present. **/ BOOLEAN BdsCheckIderMedia ( IN VOID ) { UINTN HandleNum; EFI_HANDLE *HandleBuf; EFI_HANDLE Handle; EFI_STATUS Status; EFI_DEVICE_PATH_PROTOCOL *DPath; UINTN Index; UINTN EventIndex; EFI_INPUT_KEY Key; EFI_BLOCK_IO_PROTOCOL *BlkIo; EFI_DISK_INFO_PROTOCOL *DiskInfo; EFI_BLOCK_IO_MEDIA *BlkMedia; VOID *Buffer; UINT8 IdeBootDevice; UINT32 IdeChannel; UINT32 IdeDevice; IdeBootDevice = ActiveManagementIderBootDeviceGet (); DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Ide Channel Device Index = %d\n", IdeBootDevice)); // // Make sure the Legacy Boot Protocol is available // Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &HandleNum, &HandleBuf ); if ((EFI_ERROR (Status)) || (HandleBuf == NULL)) { goto Exit; } for (Index = 0; Index < HandleNum; Index++) { Status = gBS->HandleProtocol ( HandleBuf[Index], &gEfiDevicePathProtocolGuid, &DPath ); if (EFI_ERROR (Status)) { continue; } Status = gBS->LocateDevicePath ( &gEfiIderControllerDriverProtocolGuid, &DPath, &Handle ); if (EFI_ERROR (Status)) { continue; } Status = gBS->HandleProtocol ( HandleBuf[Index], &gEfiBlockIoProtocolGuid, &BlkIo ); if (EFI_ERROR(Status)) { continue; } Status = gBS->HandleProtocol ( HandleBuf[Index], &gEfiDiskInfoProtocolGuid, &DiskInfo ); if (EFI_ERROR(Status)) { continue; } DiskInfo->WhichIde (DiskInfo, &IdeChannel, &IdeDevice); if (IdeBootDevice != (UINT8) (IdeChannel * 2 + IdeDevice)) { continue; } if (BlkIo->Media->MediaPresent) { if (HandleBuf != NULL) { FreePool (HandleBuf); } return TRUE; } while (TRUE) { BlkMedia = BlkIo->Media; Buffer = AllocatePool (BlkMedia->BlockSize); if (Buffer) { BlkIo->ReadBlocks ( BlkIo, BlkMedia->MediaId, 0, BlkMedia->BlockSize, Buffer ); FreePool (Buffer); } if (BlkMedia->MediaPresent) { if (HandleBuf != NULL) { FreePool (HandleBuf); } return TRUE; } gST->ConOut->OutputString ( gST->ConOut, L"Boot disk missing, please insert boot disk and press ENTER\r\n" ); Key.ScanCode = 0; Key.UnicodeChar = 0; gBS->RestoreTPL (EFI_TPL_APPLICATION); while (!(Key.ScanCode == 0 && Key.UnicodeChar == L'\r')) { Status = gBS->WaitForEvent (1, &(gST->ConIn->WaitForKey), &EventIndex); gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); } gBS->RaiseTPL (EFI_TPL_DRIVER); } break; } Exit: if (HandleBuf != NULL) { FreePool (HandleBuf); } return FALSE; }