bdsentry.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. /*++
  2. Copyright (c) 2014 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. bdsentry.c
  5. Abstract:
  6. This module implements the high level Boot Device Selection code.
  7. Author:
  8. Evan Green 17-Mar-2014
  9. Environment:
  10. Firmware
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include "ueficore.h"
  16. #include "bds.h"
  17. #include <minoca/uefi/guid/globlvar.h>
  18. #include <minoca/uefi/guid/coninct.h>
  19. //
  20. // ---------------------------------------------------------------- Definitions
  21. //
  22. #define EFI_FIRMWARE_REVISION 0x00010000
  23. //
  24. // ------------------------------------------------------ Data Type Definitions
  25. //
  26. //
  27. // ----------------------------------------------- Internal Function Prototypes
  28. //
  29. VOID
  30. EfipBdsBootDeviceSelect (
  31. VOID
  32. );
  33. VOID
  34. EfipBdsFormalizeEfiGlobalVariables (
  35. VOID
  36. );
  37. VOID
  38. EfipBdsFormalizeConsoleVariable (
  39. CHAR16 *VariableName
  40. );
  41. //
  42. // -------------------------------------------------------------------- Globals
  43. //
  44. //
  45. // Define the firmware vendor, whose contents are not assumed to be in
  46. // runtime data. The platform code can override this variable.
  47. //
  48. CHAR16 *EfiFirmwareVendor = L"Minoca Corp";
  49. UINT32 EfiFirmwareRevision = EFI_FIRMWARE_REVISION;
  50. //
  51. // Define the default timeout value.
  52. //
  53. UINT16 EfiBootTimeout = 0xFFFF;
  54. UINT16 *EfiBootNext;
  55. EFI_GUID EfiGlobalVariableGuid = EFI_GLOBAL_VARIABLE_GUID;
  56. EFI_GUID EfiConnectConInEventGuid = CONNECT_CONIN_EVENT_GUID;
  57. //
  58. // ------------------------------------------------------------------ Functions
  59. //
  60. EFIAPI
  61. VOID
  62. EfiBdsEntry (
  63. VOID
  64. )
  65. /*++
  66. Routine Description:
  67. This routine is the entry point into the boot device selection phase of
  68. the firmware. It attempts to find an OS loader and launch it.
  69. Arguments:
  70. None.
  71. Return Value:
  72. None. This routine does not return.
  73. --*/
  74. {
  75. UINT32 Attributes;
  76. UINTN BootNextSize;
  77. LIST_ENTRY BootOptionList;
  78. UINTN BootTimeout;
  79. LIST_ENTRY DriverOptionList;
  80. CHAR16 *FirmwareVendor;
  81. UINTN FirmwareVendorSize;
  82. EFI_HANDLE *HandleBuffer;
  83. UINTN HandleCount;
  84. UINTN Index;
  85. UINTN OldHandleCount;
  86. EFI_STATUS Status;
  87. INITIALIZE_LIST_HEAD(&DriverOptionList);
  88. INITIALIZE_LIST_HEAD(&BootOptionList);
  89. //
  90. // Copy the firmware vendor into runtime pool and set the firmware revision.
  91. // This then requires recomputing the CRC of the system table.
  92. //
  93. if (EfiFirmwareVendor != NULL) {
  94. FirmwareVendorSize = EfiCoreStringLength(EfiFirmwareVendor);
  95. FirmwareVendorSize = (FirmwareVendorSize + 1) * sizeof(CHAR16);
  96. FirmwareVendor = EfiCoreAllocateRuntimePool(FirmwareVendorSize);
  97. if (FirmwareVendor != NULL) {
  98. EfiCoreCopyMemory(FirmwareVendor,
  99. EfiFirmwareVendor,
  100. FirmwareVendorSize);
  101. EfiSystemTable->FirmwareVendor = FirmwareVendor;
  102. }
  103. }
  104. EfiSystemTable->FirmwareRevision = EfiFirmwareRevision;
  105. EfiSystemTable->Hdr.CRC32 = 0;
  106. EfiCalculateCrc32(EfiSystemTable,
  107. sizeof(EFI_SYSTEM_TABLE),
  108. &(EfiSystemTable->Hdr.CRC32));
  109. //
  110. // Connect all controllers.
  111. //
  112. HandleCount = 0;
  113. while (TRUE) {
  114. OldHandleCount = HandleCount;
  115. Status = EfiLocateHandleBuffer(AllHandles,
  116. NULL,
  117. NULL,
  118. &HandleCount,
  119. &HandleBuffer);
  120. if (EFI_ERROR(Status)) {
  121. break;
  122. }
  123. if (HandleCount == OldHandleCount) {
  124. break;
  125. }
  126. for (Index = 0; Index < HandleCount; Index += 1) {
  127. EfiConnectController(HandleBuffer[Index], NULL, NULL, TRUE);
  128. }
  129. EfiFreePool(HandleBuffer);
  130. }
  131. EfiCoreLoadVariablesFromFileSystem();
  132. EfipBdsFormalizeEfiGlobalVariables();
  133. EfipBdsConnectAllDefaultConsoles();
  134. BootTimeout = EfiBootTimeout;
  135. if (BootTimeout != 0xFFFF) {
  136. Attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
  137. EFI_VARIABLE_RUNTIME_ACCESS |
  138. EFI_VARIABLE_NON_VOLATILE;
  139. Status = EfiSetVariable(L"Timeout",
  140. &EfiGlobalVariableGuid,
  141. Attributes,
  142. sizeof(UINT16),
  143. &BootTimeout);
  144. ASSERT(!EFI_ERROR(Status));
  145. }
  146. //
  147. // Set up the device list based on EFI 1.1 variables. Process Driver####
  148. // and load the drivers in the option list.
  149. //
  150. EfipBdsBuildOptionFromVariable(&DriverOptionList, L"DriverOrder");
  151. if (!LIST_EMPTY(&DriverOptionList)) {
  152. EfipBdsLoadDrivers(&DriverOptionList);
  153. }
  154. //
  155. // Look for a boot next option.
  156. //
  157. EfiBootNext = EfipBdsGetVariable(L"BootNext",
  158. &EfiGlobalVariableGuid,
  159. &BootNextSize);
  160. EfipBdsBootDeviceSelect();
  161. //
  162. // Execution should never reach here.
  163. //
  164. ASSERT(FALSE);
  165. return;
  166. }
  167. //
  168. // --------------------------------------------------------- Internal Functions
  169. //
  170. VOID
  171. EfipBdsBootDeviceSelect (
  172. VOID
  173. )
  174. /*++
  175. Routine Description:
  176. This routine validates the global variables set in EFI for the BDS phase.
  177. Arguments:
  178. None.
  179. Return Value:
  180. None.
  181. --*/
  182. {
  183. UINT32 Attributes;
  184. LIST_ENTRY BootList;
  185. PLIST_ENTRY BootNextEntry;
  186. BOOLEAN BootNextExists;
  187. PEFI_BDS_COMMON_OPTION BootOption;
  188. CHAR16 Buffer[20];
  189. EFI_EVENT ConnectInputEvent;
  190. PLIST_ENTRY CurrentEntry;
  191. CHAR16 *ExitData;
  192. UINTN ExitDataSize;
  193. EFI_STATUS Status;
  194. BOOLEAN TriedEverything;
  195. BootNextEntry = NULL;
  196. BootNextExists = FALSE;
  197. CurrentEntry = NULL;
  198. ConnectInputEvent = NULL;
  199. INITIALIZE_LIST_HEAD(&BootList);
  200. TriedEverything = FALSE;
  201. EfiCoreSetMemory(Buffer, sizeof(Buffer), 0);
  202. //
  203. // Create an event to fire when console input is connected.
  204. //
  205. Status = EfiCreateEventEx(EVT_NOTIFY_SIGNAL,
  206. TPL_CALLBACK,
  207. EfiCoreEmptyCallbackFunction,
  208. NULL,
  209. &EfiConnectConInEventGuid,
  210. &ConnectInputEvent);
  211. if (EFI_ERROR(Status)) {
  212. ConnectInputEvent = NULL;
  213. }
  214. if (EfiBootNext != NULL) {
  215. BootNextExists = TRUE;
  216. //
  217. // Clear the variable so that it only tries to boot once.
  218. //
  219. Attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
  220. EFI_VARIABLE_RUNTIME_ACCESS |
  221. EFI_VARIABLE_NON_VOLATILE;
  222. EfiSetVariable(L"BootNext",
  223. &EfiGlobalVariableGuid,
  224. Attributes,
  225. 0,
  226. NULL);
  227. //
  228. // Add the boot next option.
  229. //
  230. EfipBdsCreateHexCodeString(L"Boot",
  231. *EfiBootNext,
  232. Buffer,
  233. sizeof(Buffer));
  234. BootOption = EfipBdsConvertVariableToOption(&BootList, Buffer);
  235. if (BootOption == NULL) {
  236. return;
  237. }
  238. BootOption->BootCurrent = *EfiBootNext;
  239. }
  240. //
  241. // Parse the boot order to get boot options.
  242. //
  243. EfipBdsBuildOptionFromVariable(&BootList, L"BootOrder");
  244. //
  245. // If nothing was enumerated, get desperate.
  246. //
  247. if (LIST_EMPTY(&BootList)) {
  248. EfipBdsEnumerateAllBootOptions(&BootList);
  249. TriedEverything = TRUE;
  250. }
  251. CurrentEntry = BootList.Next;
  252. if (CurrentEntry == NULL) {
  253. ASSERT(FALSE);
  254. return;
  255. }
  256. //
  257. // Loop forever.
  258. //
  259. while (TRUE) {
  260. //
  261. // Handle reaching the end of the list.
  262. //
  263. if (CurrentEntry == &BootList) {
  264. if (TriedEverything == FALSE) {
  265. EfipBdsEnumerateAllBootOptions(&BootList);
  266. TriedEverything = TRUE;
  267. CurrentEntry = BootList.Next;
  268. continue;
  269. }
  270. if (ConnectInputEvent != NULL) {
  271. EfiSignalEvent(ConnectInputEvent);
  272. }
  273. if (EfiSystemTable->StdErr != NULL) {
  274. EfiSystemTable->StdErr->OutputString(EfiSystemTable->StdErr,
  275. L"Found nothing to boot.");
  276. }
  277. //
  278. // Hmm... eventually do something more intelligent here.
  279. //
  280. RtlDebugPrint("Nothing to boot, hanging...\n");
  281. while (TRUE) {
  282. NOTHING;
  283. }
  284. INITIALIZE_LIST_HEAD(&BootList);
  285. EfipBdsBuildOptionFromVariable(&BootList, L"BootOrder");
  286. CurrentEntry = BootList.Next;
  287. continue;
  288. }
  289. //
  290. // Grab the boot option.
  291. //
  292. BootOption = LIST_VALUE(CurrentEntry, EFI_BDS_COMMON_OPTION, ListEntry);
  293. CurrentEntry = CurrentEntry->Next;
  294. ASSERT(BootOption->Magic == EFI_BDS_COMMON_OPTION_MAGIC);
  295. //
  296. // Skip anything not marked active.
  297. //
  298. if ((BootOption->Attribute & LOAD_OPTION_ACTIVE) == 0) {
  299. continue;
  300. }
  301. //
  302. // Make sure the device path is connected, except for BBS paths.
  303. //
  304. if (EfiCoreGetDevicePathType(BootOption->DevicePath) !=
  305. BBS_DEVICE_PATH) {
  306. EfipBdsConnectDevicePath(BootOption->DevicePath);
  307. }
  308. Status = EfipBdsBootViaBootOption(BootOption,
  309. BootOption->DevicePath,
  310. &ExitDataSize,
  311. &ExitData);
  312. if (Status != EFI_SUCCESS) {
  313. //
  314. // Potentially do something if the boot entry failed. For now,
  315. // nothing.
  316. //
  317. } else {
  318. if (ConnectInputEvent != NULL) {
  319. EfiSignalEvent(ConnectInputEvent);
  320. }
  321. //
  322. // This is where the boot menu would be presented, which might
  323. // change the boot list. Re-enumerate that now even though there
  324. // is no boot menu.
  325. //
  326. if (BootNextExists != FALSE) {
  327. BootNextEntry = BootList.Next;
  328. }
  329. INITIALIZE_LIST_HEAD(&BootList);
  330. if (BootNextEntry != NULL) {
  331. INSERT_BEFORE(BootNextEntry, &BootList);
  332. }
  333. EfipBdsBuildOptionFromVariable(&BootList, L"BootOrder");
  334. CurrentEntry = BootList.Next;
  335. }
  336. }
  337. //
  338. // Execution should never get here.
  339. //
  340. ASSERT(FALSE);
  341. return;
  342. }
  343. VOID
  344. EfipBdsFormalizeEfiGlobalVariables (
  345. VOID
  346. )
  347. /*++
  348. Routine Description:
  349. This routine validates the global variables set in EFI for the BDS phase.
  350. Arguments:
  351. None.
  352. Return Value:
  353. None.
  354. --*/
  355. {
  356. EfipBdsFormalizeConsoleVariable(L"ConIn");
  357. EfipBdsFormalizeConsoleVariable(L"ConOut");
  358. EfipBdsFormalizeConsoleVariable(L"ErrOut");
  359. return;
  360. }
  361. VOID
  362. EfipBdsFormalizeConsoleVariable (
  363. CHAR16 *VariableName
  364. )
  365. /*++
  366. Routine Description:
  367. This routine validates that one of the console variables is a valid
  368. device path.
  369. Arguments:
  370. VariableName - Supplies a pointer to the variable name. This is expected to
  371. be ConIn, ConOut, or ErrOut.
  372. Return Value:
  373. None.
  374. --*/
  375. {
  376. UINT32 Attributes;
  377. EFI_DEVICE_PATH_PROTOCOL *DevicePath;
  378. UINTN DevicePathSize;
  379. EFI_GUID *Guid;
  380. UINTN HandleCount;
  381. EFI_HANDLE *Handles;
  382. EFI_STATUS Status;
  383. UINTN VariableSize;
  384. Attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
  385. EFI_VARIABLE_RUNTIME_ACCESS |
  386. EFI_VARIABLE_NON_VOLATILE;
  387. DevicePath = EfipBdsGetVariable(VariableName,
  388. &EfiGlobalVariableGuid,
  389. &VariableSize);
  390. //
  391. // If the device path is not set, try to find one.
  392. //
  393. if (DevicePath == NULL) {
  394. //
  395. // For ConIn, get the input protocol, otherwise get the simple text
  396. // output protocol.
  397. //
  398. if ((VariableName[0] != L'\0') && (VariableName[1] != L'\0') &&
  399. (VariableName[2] != L'\0') && (VariableName[3] == L'I')) {
  400. Guid = &EfiSimpleTextInputProtocolGuid;
  401. } else {
  402. Guid = &EfiSimpleTextOutputProtocolGuid;
  403. }
  404. HandleCount = 0;
  405. Handles = NULL;
  406. Status = EfiLocateHandleBuffer(ByProtocol,
  407. Guid,
  408. NULL,
  409. &HandleCount,
  410. &Handles);
  411. if ((!EFI_ERROR(Status)) && (HandleCount != 0)) {
  412. DevicePath = EfiCoreGetDevicePathFromHandle(Handles[0]);
  413. if (DevicePath != NULL) {
  414. DevicePathSize = EfiCoreGetDevicePathSize(DevicePath);
  415. EfiSetVariable(VariableName,
  416. &EfiGlobalVariableGuid,
  417. Attributes,
  418. DevicePathSize,
  419. DevicePath);
  420. }
  421. EfiFreePool(Handles);
  422. }
  423. }
  424. //
  425. // If the device path is invalid, delete it.
  426. //
  427. if ((DevicePath != NULL) &&
  428. (EfiCoreIsDevicePathValid(DevicePath, VariableSize) == FALSE)) {
  429. RtlDebugPrint("Deleting invalid console variable.\n");
  430. Status = EfiSetVariable(VariableName,
  431. &EfiGlobalVariableGuid,
  432. Attributes,
  433. 0,
  434. NULL);
  435. ASSERT(!EFI_ERROR(Status));
  436. DevicePath = NULL;
  437. }
  438. return;
  439. }