PlatformBootOption.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. /** @file
  2. Driver for Platform Boot Options support.
  3. Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. **/
  6. #include "BdsPlatform.h"
  7. #include <Library/PcdLib.h>
  8. BOOLEAN mContinueBoot = FALSE;
  9. BOOLEAN mBootMenuBoot = FALSE;
  10. BOOLEAN mPxeBoot = FALSE;
  11. BOOLEAN mHotKeypressed = FALSE;
  12. EFI_EVENT HotKeyEvent = NULL;
  13. UINTN mBootMenuOptionNumber;
  14. EFI_DEVICE_PATH_PROTOCOL *
  15. BdsCreateShellDevicePath (
  16. VOID
  17. )
  18. /*++
  19. Routine Description:
  20. This function will create a SHELL BootOption to boot.
  21. Arguments:
  22. None.
  23. Returns:
  24. Shell Device path for booting.
  25. --*/
  26. {
  27. UINTN FvHandleCount;
  28. EFI_HANDLE *FvHandleBuffer;
  29. UINTN Index;
  30. EFI_STATUS Status;
  31. EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
  32. UINTN Size;
  33. UINT32 AuthenticationStatus;
  34. EFI_DEVICE_PATH_PROTOCOL *DevicePath;
  35. VOID *Buffer;
  36. DevicePath = NULL;
  37. Status = EFI_SUCCESS;
  38. DEBUG ((DEBUG_INFO, "BdsCreateShellDevicePath\n"));
  39. gBS->LocateHandleBuffer (
  40. ByProtocol,
  41. &gEfiFirmwareVolume2ProtocolGuid,
  42. NULL,
  43. &FvHandleCount,
  44. &FvHandleBuffer
  45. );
  46. for (Index = 0; Index < FvHandleCount; Index++) {
  47. gBS->HandleProtocol (
  48. FvHandleBuffer[Index],
  49. &gEfiFirmwareVolume2ProtocolGuid,
  50. (VOID **) &Fv
  51. );
  52. Buffer = NULL;
  53. Size = 0;
  54. Status = Fv->ReadSection (
  55. Fv,
  56. &gUefiShellFileGuid,
  57. EFI_SECTION_PE32,
  58. 0,
  59. &Buffer,
  60. &Size,
  61. &AuthenticationStatus
  62. );
  63. if (EFI_ERROR (Status)) {
  64. //
  65. // Skip if no shell file in the FV
  66. //
  67. continue;
  68. } else {
  69. //
  70. // Found the shell
  71. //
  72. break;
  73. }
  74. }
  75. if (EFI_ERROR (Status)) {
  76. //
  77. // No shell present
  78. //
  79. if (FvHandleCount) {
  80. FreePool (FvHandleBuffer);
  81. }
  82. return NULL;
  83. }
  84. //
  85. // Build the shell boot option
  86. //
  87. DevicePath = DevicePathFromHandle (FvHandleBuffer[Index]);
  88. if (FvHandleCount) {
  89. FreePool (FvHandleBuffer);
  90. }
  91. return DevicePath;
  92. }
  93. EFI_STATUS
  94. CreateFvBootOption (
  95. EFI_GUID *FileGuid,
  96. CHAR16 *Description,
  97. EFI_BOOT_MANAGER_LOAD_OPTION *BootOption,
  98. UINT32 Attributes,
  99. UINT8 *OptionalData, OPTIONAL
  100. UINT32 OptionalDataSize
  101. )
  102. {
  103. EFI_STATUS Status;
  104. EFI_DEVICE_PATH_PROTOCOL *DevicePath;
  105. EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
  106. MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
  107. EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
  108. UINT32 AuthenticationStatus;
  109. VOID *Buffer;
  110. UINTN Size;
  111. if ((BootOption == NULL) || (FileGuid == NULL) || (Description == NULL)) {
  112. return EFI_INVALID_PARAMETER;
  113. }
  114. EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid);
  115. if (!CompareGuid (&gUefiShellFileGuid, FileGuid)) {
  116. Status = gBS->HandleProtocol (
  117. gImageHandle,
  118. &gEfiLoadedImageProtocolGuid,
  119. (VOID **) &LoadedImage
  120. );
  121. if (!EFI_ERROR (Status)) {
  122. Status = gBS->HandleProtocol (
  123. LoadedImage->DeviceHandle,
  124. &gEfiFirmwareVolume2ProtocolGuid,
  125. (VOID **) &Fv
  126. );
  127. if (!EFI_ERROR (Status)) {
  128. Buffer = NULL;
  129. Size = 0;
  130. Status = Fv->ReadSection (
  131. Fv,
  132. FileGuid,
  133. EFI_SECTION_PE32,
  134. 0,
  135. &Buffer,
  136. &Size,
  137. &AuthenticationStatus
  138. );
  139. if (Buffer != NULL) {
  140. FreePool (Buffer);
  141. }
  142. }
  143. }
  144. if (EFI_ERROR (Status)) {
  145. return EFI_NOT_FOUND;
  146. }
  147. DevicePath = AppendDevicePathNode (
  148. DevicePathFromHandle (LoadedImage->DeviceHandle),
  149. (EFI_DEVICE_PATH_PROTOCOL *) &FileNode
  150. );
  151. } else {
  152. DevicePath = AppendDevicePathNode (
  153. BdsCreateShellDevicePath (),
  154. (EFI_DEVICE_PATH_PROTOCOL *) &FileNode
  155. );
  156. }
  157. Status = EfiBootManagerInitializeLoadOption (
  158. BootOption,
  159. LoadOptionNumberUnassigned,
  160. LoadOptionTypeBoot,
  161. Attributes,
  162. Description,
  163. DevicePath,
  164. OptionalData,
  165. OptionalDataSize
  166. );
  167. FreePool (DevicePath);
  168. return Status;
  169. }
  170. EFI_GUID mUiFile = {
  171. 0x462CAA21, 0x7614, 0x4503, { 0x83, 0x6E, 0x8A, 0xB6, 0xF4, 0x66, 0x23, 0x31 }
  172. };
  173. EFI_GUID mBootMenuFile = {
  174. 0xEEC25BDC, 0x67F2, 0x4D95, { 0xB1, 0xD5, 0xF8, 0x1B, 0x20, 0x39, 0xD1, 0x1D }
  175. };
  176. /**
  177. Return the index of the load option in the load option array.
  178. The function consider two load options are equal when the
  179. OptionType, Attributes, Description, FilePath and OptionalData are equal.
  180. @param Key Pointer to the load option to be found.
  181. @param Array Pointer to the array of load options to be found.
  182. @param Count Number of entries in the Array.
  183. @retval -1 Key wasn't found in the Array.
  184. @retval 0 ~ Count-1 The index of the Key in the Array.
  185. **/
  186. INTN
  187. PlatformFindLoadOption (
  188. IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Key,
  189. IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Array,
  190. IN UINTN Count
  191. )
  192. {
  193. UINTN Index;
  194. for (Index = 0; Index < Count; Index++) {
  195. if ((Key->OptionType == Array[Index].OptionType) &&
  196. (Key->Attributes == Array[Index].Attributes) &&
  197. (StrCmp (Key->Description, Array[Index].Description) == 0) &&
  198. (CompareMem (Key->FilePath, Array[Index].FilePath, GetDevicePathSize (Key->FilePath)) == 0) &&
  199. (Key->OptionalDataSize == Array[Index].OptionalDataSize) &&
  200. (CompareMem (Key->OptionalData, Array[Index].OptionalData, Key->OptionalDataSize) == 0)) {
  201. return (INTN) Index;
  202. }
  203. }
  204. return -1;
  205. }
  206. UINTN
  207. RegisterFvBootOption (
  208. EFI_GUID *FileGuid,
  209. CHAR16 *Description,
  210. UINTN Position,
  211. UINT32 Attributes,
  212. UINT8 *OptionalData, OPTIONAL
  213. UINT32 OptionalDataSize
  214. )
  215. {
  216. EFI_STATUS Status;
  217. UINTN OptionIndex;
  218. EFI_BOOT_MANAGER_LOAD_OPTION NewOption;
  219. EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
  220. UINTN BootOptionCount;
  221. NewOption.OptionNumber = LoadOptionNumberUnassigned;
  222. Status = CreateFvBootOption (FileGuid, Description, &NewOption, Attributes, OptionalData, OptionalDataSize);
  223. if (!EFI_ERROR (Status)) {
  224. BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
  225. OptionIndex = PlatformFindLoadOption (&NewOption, BootOptions, BootOptionCount);
  226. if (OptionIndex == -1) {
  227. Status = EfiBootManagerAddLoadOptionVariable (&NewOption, Position);
  228. ASSERT_EFI_ERROR (Status);
  229. } else {
  230. NewOption.OptionNumber = BootOptions[OptionIndex].OptionNumber;
  231. }
  232. EfiBootManagerFreeLoadOption (&NewOption);
  233. EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
  234. }
  235. return NewOption.OptionNumber;
  236. }
  237. VOID
  238. EFIAPI
  239. PlatformBootManagerWaitCallback (
  240. UINT16 TimeoutRemain
  241. )
  242. {
  243. EFI_STATUS Status;
  244. EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx;
  245. EFI_KEY_DATA KeyData;
  246. BOOLEAN PausePressed;
  247. //
  248. // Pause on PAUSE key
  249. //
  250. Status = gBS->HandleProtocol (gST->ConsoleInHandle, &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx);
  251. ASSERT_EFI_ERROR (Status);
  252. PausePressed = FALSE;
  253. while (TRUE) {
  254. Status = TxtInEx->ReadKeyStrokeEx (TxtInEx, &KeyData);
  255. if (EFI_ERROR (Status)) {
  256. break;
  257. }
  258. if (KeyData.Key.ScanCode == SCAN_PAUSE) {
  259. PausePressed = TRUE;
  260. break;
  261. }
  262. }
  263. //
  264. // Loop until non-PAUSE key pressed
  265. //
  266. while (PausePressed) {
  267. Status = TxtInEx->ReadKeyStrokeEx (TxtInEx, &KeyData);
  268. if (!EFI_ERROR (Status)) {
  269. DEBUG ((
  270. DEBUG_INFO, "[PauseCallback] %x/%x %x/%x\n",
  271. KeyData.Key.ScanCode, KeyData.Key.UnicodeChar,
  272. KeyData.KeyState.KeyShiftState, KeyData.KeyState.KeyToggleState
  273. ));
  274. PausePressed = (BOOLEAN) (KeyData.Key.ScanCode == SCAN_PAUSE);
  275. }
  276. }
  277. }
  278. EFI_GUID gUefiShellFileGuid = { 0x7C04A583, 0x9E3E, 0x4f1c, { 0xAD, 0x65, 0xE0, 0x52, 0x68, 0xD0, 0xB4, 0xD1 } };
  279. #define INTERNAL_UEFI_SHELL_NAME L"Internal UEFI Shell 2.0"
  280. #define UEFI_HARD_DRIVE_NAME L"UEFI Hard Drive"
  281. VOID
  282. RegisterDefaultBootOption (
  283. VOID
  284. )
  285. {
  286. #if 0
  287. EFI_DEVICE_PATH_PROTOCOL *DevicePath;
  288. EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
  289. MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
  290. #endif
  291. UINT16 *ShellData;
  292. UINT32 ShellDataSize;
  293. ShellData = NULL;
  294. ShellDataSize = 0;
  295. RegisterFvBootOption (&gUefiShellFileGuid, INTERNAL_UEFI_SHELL_NAME, (UINTN) -1, LOAD_OPTION_ACTIVE, (UINT8 *)ShellData, ShellDataSize);
  296. //
  297. // Boot Menu
  298. //
  299. mBootMenuOptionNumber = RegisterFvBootOption (&mBootMenuFile, L"Boot Device List", (UINTN) -1, LOAD_OPTION_CATEGORY_APP | LOAD_OPTION_ACTIVE | LOAD_OPTION_HIDDEN, NULL, 0);
  300. if (mBootMenuOptionNumber == LoadOptionNumberUnassigned) {
  301. DEBUG ((DEBUG_INFO, "BootMenuOptionNumber (%d) should not be same to LoadOptionNumberUnassigned(%d).\n", mBootMenuOptionNumber, LoadOptionNumberUnassigned));
  302. }
  303. #if 0
  304. //
  305. // Boot Manager Menu
  306. //
  307. EfiInitializeFwVolDevicepathNode (&FileNode, &mUiFile);
  308. gBS->HandleProtocol (
  309. gImageHandle,
  310. &gEfiLoadedImageProtocolGuid,
  311. (VOID **) &LoadedImage
  312. );
  313. DevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), (EFI_DEVICE_PATH_PROTOCOL *) &FileNode);
  314. #endif
  315. }
  316. VOID
  317. RegisterBootOptionHotkey (
  318. UINT16 OptionNumber,
  319. EFI_INPUT_KEY *Key,
  320. BOOLEAN Add
  321. )
  322. {
  323. EFI_STATUS Status;
  324. if (!Add) {
  325. //
  326. // No enter hotkey when force to setup or there is no boot option
  327. //
  328. Status = EfiBootManagerDeleteKeyOptionVariable (NULL, 0, Key, NULL);
  329. ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);
  330. } else {
  331. //
  332. // Register enter hotkey for the first boot option
  333. //
  334. Status = EfiBootManagerAddKeyOptionVariable (NULL, OptionNumber, 0, Key,NULL);
  335. ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
  336. }
  337. }
  338. EFI_STATUS
  339. EFIAPI
  340. DetectKeypressCallback (
  341. IN EFI_KEY_DATA *KeyData
  342. )
  343. {
  344. mHotKeypressed = TRUE;
  345. if (HotKeyEvent != NULL) {
  346. gBS->SignalEvent(HotKeyEvent);
  347. }
  348. return EFI_SUCCESS;
  349. }
  350. /**
  351. This function is called after all the boot options are enumerated and ordered properly.
  352. **/
  353. VOID
  354. RegisterStaticHotkey (
  355. VOID
  356. )
  357. {
  358. EFI_INPUT_KEY Enter;
  359. EFI_KEY_DATA F2;
  360. EFI_KEY_DATA F7;
  361. BOOLEAN EnterSetup;
  362. EFI_STATUS Status;
  363. EFI_BOOT_MANAGER_LOAD_OPTION BootOption;
  364. EnterSetup = FALSE;
  365. //
  366. // [Enter]
  367. //
  368. mContinueBoot = !EnterSetup;
  369. if (mContinueBoot) {
  370. Enter.ScanCode = SCAN_NULL;
  371. Enter.UnicodeChar = CHAR_CARRIAGE_RETURN;
  372. EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL);
  373. }
  374. //
  375. // [F2]/[F7]
  376. //
  377. F2.Key.ScanCode = SCAN_F2;
  378. F2.Key.UnicodeChar = CHAR_NULL;
  379. F2.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID;
  380. F2.KeyState.KeyToggleState = 0;
  381. Status = EfiBootManagerGetBootManagerMenu (&BootOption);
  382. ASSERT_EFI_ERROR (Status);
  383. RegisterBootOptionHotkey ((UINT16) BootOption.OptionNumber, &F2.Key, TRUE);
  384. EfiBootManagerFreeLoadOption (&BootOption);
  385. F7.Key.ScanCode = SCAN_F7;
  386. F7.Key.UnicodeChar = CHAR_NULL;
  387. F7.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID;
  388. F7.KeyState.KeyToggleState = 0;
  389. mBootMenuBoot = !EnterSetup;
  390. RegisterBootOptionHotkey ((UINT16) mBootMenuOptionNumber, &F7.Key, mBootMenuBoot);
  391. }
  392. UINT8
  393. BootOptionType (
  394. IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
  395. )
  396. {
  397. EFI_DEVICE_PATH_PROTOCOL *Node;
  398. EFI_DEVICE_PATH_PROTOCOL *NextNode;
  399. for (Node = DevicePath; !IsDevicePathEndType (Node); Node = NextDevicePathNode (Node)) {
  400. if (DevicePathType (Node) == MESSAGING_DEVICE_PATH) {
  401. //
  402. // Make sure the device path points to the driver device.
  403. //
  404. NextNode = NextDevicePathNode (Node);
  405. if (DevicePathSubType(NextNode) == MSG_DEVICE_LOGICAL_UNIT_DP) {
  406. //
  407. // if the next node type is Device Logical Unit, which specify the Logical Unit Number (LUN),
  408. // skip it
  409. //
  410. NextNode = NextDevicePathNode (NextNode);
  411. }
  412. if (IsDevicePathEndType (NextNode)) {
  413. if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH)) {
  414. return DevicePathSubType (Node);
  415. } else {
  416. return MSG_SATA_DP;
  417. }
  418. }
  419. }
  420. }
  421. return (UINT8) -1;
  422. }
  423. /**
  424. Returns the priority number.
  425. OptionType EFI
  426. ------------------------------------
  427. PXE 2
  428. DVD 4
  429. USB 6
  430. NVME 7
  431. HDD 8
  432. EFI Shell 9
  433. Others 100
  434. @param BootOption
  435. **/
  436. UINTN
  437. BootOptionPriority (
  438. CONST EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
  439. )
  440. {
  441. //
  442. // EFI boot options
  443. //
  444. switch (BootOptionType (BootOption->FilePath)) {
  445. case MSG_MAC_ADDR_DP:
  446. case MSG_VLAN_DP:
  447. case MSG_IPv4_DP:
  448. case MSG_IPv6_DP:
  449. return 2;
  450. case MSG_SATA_DP:
  451. case MSG_ATAPI_DP:
  452. case MSG_UFS_DP:
  453. case MSG_NVME_NAMESPACE_DP:
  454. return 4;
  455. case MSG_USB_DP:
  456. return 6;
  457. }
  458. if (StrCmp (BootOption->Description, INTERNAL_UEFI_SHELL_NAME) == 0) {
  459. if (PcdGetBool (PcdBootToShellOnly)) {
  460. return 0;
  461. }
  462. return 9;
  463. }
  464. if (StrCmp (BootOption->Description, UEFI_HARD_DRIVE_NAME) == 0) {
  465. return 8;
  466. }
  467. return 100;
  468. }
  469. INTN
  470. EFIAPI
  471. CompareBootOption (
  472. CONST VOID *Left,
  473. CONST VOID *Right
  474. )
  475. {
  476. return BootOptionPriority ((EFI_BOOT_MANAGER_LOAD_OPTION *) Left) -
  477. BootOptionPriority ((EFI_BOOT_MANAGER_LOAD_OPTION *) Right);
  478. }