efiboot.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. /*++
  2. Copyright (c) 2014 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. efiboot.c
  5. Abstract:
  6. This module implements the efiboot utility, which is a usermode program
  7. that allows for manipulation of EFI boot entries.
  8. Author:
  9. Evan Green 9-Dec-2014
  10. Environment:
  11. User Mode
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. #include <assert.h>
  17. #include <errno.h>
  18. #include <getopt.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <minoca/lib/minocaos.h>
  22. #include <minoca/lib/mlibc.h>
  23. #include <minoca/uefi/uefi.h>
  24. #include <minoca/uefi/guid/globlvar.h>
  25. //
  26. // ---------------------------------------------------------------- Definitions
  27. //
  28. #define EFIBOOT_VERSION_MAJOR 1
  29. #define EFIBOOT_VERSION_MINOR 0
  30. #define EFIBOOT_USAGE \
  31. "usage: efiboot [Options] \n" \
  32. "The efiboot utility can be used to manipulate EFI boot options via \n" \
  33. "kernel UEFI environment variable access. With no options, displays the \n"\
  34. "current information. Options are:\n" \
  35. " -o, --bootorder=xxxx,yyyy,zzzz -- Sets the boot order. Values \n" \
  36. " should be in hexadecimal.\n" \
  37. " -V, --version -- Prints application version information and exits.\n" \
  38. #define EFIBOOT_OPTIONS "ohV"
  39. #define EFIBOOT_DEFAULT_VARIABLE_SIZE 4096
  40. //
  41. // ------------------------------------------------------ Data Type Definitions
  42. //
  43. //
  44. // ----------------------------------------------- Internal Function Prototypes
  45. //
  46. BOOL
  47. EfibootIsEfiSystem (
  48. VOID
  49. );
  50. INT
  51. EfibootPrintConfiguration (
  52. VOID
  53. );
  54. INT
  55. EfibootConvertBootOrderStringToBinary (
  56. PSTR BootOrderString,
  57. PVOID *BootOrder,
  58. PUINTN BootOrderSize
  59. );
  60. VOID
  61. EfibootPrintBootOrderVariable (
  62. PVOID VariableData,
  63. INT VariableDataSize
  64. );
  65. KSTATUS
  66. EfibootGetVariable (
  67. CHAR16 *VariableName,
  68. EFI_GUID *VendorGuid,
  69. UINT32 *Attributes,
  70. UINTN *DataSize,
  71. VOID **Data
  72. );
  73. KSTATUS
  74. EfibootGetSetVariable (
  75. BOOL Set,
  76. CHAR16 *VariableName,
  77. EFI_GUID *VendorGuid,
  78. UINT32 *Attributes,
  79. UINTN *DataSize,
  80. VOID *Data
  81. );
  82. //
  83. // -------------------------------------------------------------------- Globals
  84. //
  85. struct option EfibootLongOptions[] = {
  86. {"bootorder", required_argument, 0, 'o'},
  87. {"help", no_argument, 0, 'h'},
  88. {"version", no_argument, 0, 'V'},
  89. {NULL, 0, 0, 0},
  90. };
  91. EFI_GUID EfibootGlobalVariableGuid = EFI_GLOBAL_VARIABLE_GUID;
  92. //
  93. // ------------------------------------------------------------------ Functions
  94. //
  95. INT
  96. main (
  97. INT ArgumentCount,
  98. CHAR **Arguments
  99. )
  100. /*++
  101. Routine Description:
  102. This routine implements the main entry point for the EFIboot application.
  103. Arguments:
  104. ArgumentCount - Supplies the number of arguments on the command line.
  105. Arguments - Supplies an array of pointers to strings representing the
  106. arguments.
  107. Return Value:
  108. 0 on success.
  109. Non-zero on failure.
  110. --*/
  111. {
  112. UINT32 Attributes;
  113. PVOID BootOrder;
  114. UINTN BootOrderSize;
  115. INT Option;
  116. BOOL OptionSpecified;
  117. INT Result;
  118. KSTATUS Status;
  119. BootOrder = NULL;
  120. BootOrderSize = 0;
  121. //
  122. // Process the control arguments.
  123. //
  124. OptionSpecified = FALSE;
  125. while (TRUE) {
  126. Option = getopt_long(ArgumentCount,
  127. Arguments,
  128. EFIBOOT_OPTIONS,
  129. EfibootLongOptions,
  130. NULL);
  131. if (Option == -1) {
  132. break;
  133. }
  134. if ((Option == '?') || (Option == ':')) {
  135. Status = 1;
  136. goto mainEnd;
  137. }
  138. switch (Option) {
  139. case 'o':
  140. Result = EfibootConvertBootOrderStringToBinary(optarg,
  141. &BootOrder,
  142. &BootOrderSize);
  143. if (Result != 0) {
  144. goto mainEnd;
  145. }
  146. OptionSpecified = TRUE;
  147. break;
  148. case 'V':
  149. printf("efiboot version %d.%d.\n",
  150. EFIBOOT_VERSION_MAJOR,
  151. EFIBOOT_VERSION_MINOR);
  152. return 1;
  153. case 'h':
  154. printf(EFIBOOT_USAGE);
  155. return 1;
  156. default:
  157. assert(FALSE);
  158. Status = 1;
  159. goto mainEnd;
  160. }
  161. }
  162. //
  163. // Skip everything if this isn't even a UEFI system.
  164. //
  165. if (EfibootIsEfiSystem() == FALSE) {
  166. fprintf(stderr, "efiboot: Error: This is not a UEFI system.\n");
  167. Result = EINVAL;
  168. goto mainEnd;
  169. }
  170. if (OptionSpecified == FALSE) {
  171. Result = EfibootPrintConfiguration();
  172. goto mainEnd;
  173. }
  174. //
  175. // Set the boot order variable if requested.
  176. //
  177. Attributes = EFI_VARIABLE_NON_VOLATILE |
  178. EFI_VARIABLE_RUNTIME_ACCESS |
  179. EFI_VARIABLE_BOOTSERVICE_ACCESS;
  180. Status = EfibootGetSetVariable(TRUE,
  181. L"BootOrder",
  182. &EfibootGlobalVariableGuid,
  183. &Attributes,
  184. &BootOrderSize,
  185. BootOrder);
  186. if (!KSUCCESS(Status)) {
  187. Result = ClConvertKstatusToErrorNumber(Status);
  188. fprintf(stderr,
  189. "efiboot: Error: Failed to set BootOrder: %d: %s.\n",
  190. Status,
  191. strerror(Result));
  192. goto mainEnd;
  193. }
  194. mainEnd:
  195. if (BootOrder != NULL) {
  196. free(BootOrder);
  197. }
  198. if (Result != 0) {
  199. fprintf(stderr, "efiboot: Exiting with status: %s\n", strerror(Result));
  200. }
  201. return Result;
  202. }
  203. //
  204. // --------------------------------------------------------- Internal Functions
  205. //
  206. BOOL
  207. EfibootIsEfiSystem (
  208. VOID
  209. )
  210. /*++
  211. Routine Description:
  212. This routine determines if the current system is UEFI-based.
  213. Arguments:
  214. None.
  215. Return Value:
  216. TRUE if the system is UEFI based.
  217. FALSE if the system is not UEFI based or on failure.
  218. --*/
  219. {
  220. ULONG FirmwareType;
  221. UINTN FirmwareTypeSize;
  222. KSTATUS Status;
  223. FirmwareTypeSize = sizeof(ULONG);
  224. Status = OsGetSetSystemInformation(SystemInformationKe,
  225. KeInformationFirmwareType,
  226. &FirmwareType,
  227. &FirmwareTypeSize,
  228. FALSE);
  229. if (!KSUCCESS(Status)) {
  230. fprintf(stderr,
  231. "efiboot: Failed to determine if firmware type is EFI: %d.\n",
  232. Status);
  233. return FALSE;
  234. }
  235. if (FirmwareType == SystemFirmwareEfi) {
  236. return TRUE;
  237. }
  238. return FALSE;
  239. }
  240. INT
  241. EfibootPrintConfiguration (
  242. VOID
  243. )
  244. /*++
  245. Routine Description:
  246. This routine prints the current EFI configuration.
  247. Arguments:
  248. None.
  249. Return Value:
  250. 0 on success.
  251. Non-zero on failure.
  252. --*/
  253. {
  254. PVOID BootOrder;
  255. UINTN BootOrderSize;
  256. KSTATUS Status;
  257. BootOrder = NULL;
  258. Status = EfibootGetVariable(L"BootOrder",
  259. &EfibootGlobalVariableGuid,
  260. NULL,
  261. &BootOrderSize,
  262. &BootOrder);
  263. if (Status != STATUS_NOT_FOUND) {
  264. if (!KSUCCESS(Status)) {
  265. fprintf(stderr,
  266. "efiboot: Error: Failed to get BootOrder: %d\n",
  267. Status);
  268. } else {
  269. printf("BootOrder: ");
  270. EfibootPrintBootOrderVariable(BootOrder, BootOrderSize);
  271. printf("\n");
  272. }
  273. }
  274. if (BootOrder != NULL) {
  275. free(BootOrder);
  276. }
  277. return ClConvertKstatusToErrorNumber(Status);
  278. }
  279. INT
  280. EfibootConvertBootOrderStringToBinary (
  281. PSTR BootOrderString,
  282. PVOID *BootOrder,
  283. PUINTN BootOrderSize
  284. )
  285. /*++
  286. Routine Description:
  287. This routine converts a boot order string specified by the user to a boot
  288. order EFI variable.
  289. Arguments:
  290. BootOrderString - Supplies a pointer to the human readable boot order
  291. string, in the form XXXX,YYYY,ZZZZ,... where XXXX are hexadecimal
  292. numbers.
  293. BootOrder - Supplies a pointer where the boot order binary data will be
  294. returned on success. The caller is responsible for freeing this memory.
  295. BootOrderSize - Supplies a pointer where the size of the boot order data
  296. will be returned on success.
  297. Return Value:
  298. 0 on success.
  299. Non-zero on failure.
  300. --*/
  301. {
  302. INT Count;
  303. PSTR CurrentString;
  304. INT Index;
  305. INT Integer;
  306. INT ItemsScanned;
  307. INT Result;
  308. UINT16 *Variable;
  309. if ((BootOrderString == NULL) || (*BootOrderString == '\0')) {
  310. *BootOrder = NULL;
  311. *BootOrderSize = 0;
  312. return 0;
  313. }
  314. //
  315. // Count the commas to determine how many boot entries there are.
  316. //
  317. Count = 1;
  318. CurrentString = BootOrderString;
  319. while (TRUE) {
  320. CurrentString = strchr(CurrentString, ',');
  321. if (CurrentString == NULL) {
  322. break;
  323. }
  324. Count += 1;
  325. CurrentString += 1;
  326. }
  327. Variable = malloc(Count * sizeof(UINT16));
  328. if (Variable == NULL) {
  329. Result = ENOMEM;
  330. goto ConvertBootOrderStringToBinaryEnd;
  331. }
  332. //
  333. // Scan a string in the form NNNN,NNNN,..., where NNNN is a 4 digit
  334. // hexadecimal value.
  335. //
  336. Index = 0;
  337. CurrentString = BootOrderString;
  338. while (TRUE) {
  339. assert(Index < Count);
  340. ItemsScanned = sscanf(CurrentString, "%x", &Integer);
  341. if (ItemsScanned != 1) {
  342. fprintf(stderr,
  343. "efiboot: Invalid boot entry number starting at '%s'.\n"
  344. "boot entries should be 4 digit hex numbers, like "
  345. "0001,001E,0000\n",
  346. CurrentString);
  347. Result = EINVAL;
  348. goto ConvertBootOrderStringToBinaryEnd;
  349. }
  350. Variable[Index] = Integer;
  351. Index += 1;
  352. while (isxdigit(*CurrentString)) {
  353. CurrentString += 1;
  354. }
  355. if (*CurrentString == '\0') {
  356. break;
  357. }
  358. if (*CurrentString != ',') {
  359. fprintf(stderr,
  360. "efiboot: Expected comma, got '%s'.\n", CurrentString);
  361. Result = EINVAL;
  362. goto ConvertBootOrderStringToBinaryEnd;
  363. }
  364. CurrentString += 1;
  365. }
  366. Count = Index;
  367. Result = 0;
  368. ConvertBootOrderStringToBinaryEnd:
  369. if (Result != 0) {
  370. if (Variable != NULL) {
  371. free(Variable);
  372. Variable = NULL;
  373. Count = 0;
  374. }
  375. }
  376. *BootOrder = Variable;
  377. *BootOrderSize = Count * sizeof(UINT16);
  378. return Result;
  379. }
  380. VOID
  381. EfibootPrintBootOrderVariable (
  382. PVOID VariableData,
  383. INT VariableDataSize
  384. )
  385. /*++
  386. Routine Description:
  387. This routine prints the contents of the given boot order variable to
  388. standard out.
  389. Arguments:
  390. VariableData - Supplies a pointer to the binary boot variable data.
  391. VariableDataSize - Supplies the size of the variable data in bytes.
  392. Return Value:
  393. None.
  394. --*/
  395. {
  396. INT Count;
  397. UINT16 *Entry;
  398. INT Index;
  399. if ((VariableDataSize % sizeof(UINT16)) != 0) {
  400. fprintf(stderr,
  401. "efiboot: Warning: BootOrder variable size was %d, not a "
  402. "multiple of 2!\n");
  403. }
  404. Count = VariableDataSize / sizeof(UINT16);
  405. Entry = VariableData;
  406. for (Index = 0; Index < Count; Index += 1) {
  407. printf("%04X", Entry[Index]);
  408. if (Index != Count - 1) {
  409. printf(",");
  410. }
  411. }
  412. return;
  413. }
  414. KSTATUS
  415. EfibootGetVariable (
  416. CHAR16 *VariableName,
  417. EFI_GUID *VendorGuid,
  418. UINT32 *Attributes,
  419. UINTN *DataSize,
  420. VOID **Data
  421. )
  422. /*++
  423. Routine Description:
  424. This routine gets an EFI firmware variable. The caller must be a system
  425. administrator.
  426. Arguments:
  427. VariableName - Supplies a pointer to a null-terminated string containing
  428. the name of the vendor's variable.
  429. VendorGuid - Supplies a pointer to the unique GUID for the vendor.
  430. Attributes - Supplies an optional pointer to the attributes.
  431. DataSize - Supplies a pointer where the size of the data will be returned.
  432. Data - Supplies a pointer where the allocated data will be returned on
  433. success. The caller is responsible for freeing this memory.
  434. Return Value:
  435. None.
  436. --*/
  437. {
  438. UINT32 LocalAttributes;
  439. VOID *LocalData;
  440. UINTN LocalDataSize;
  441. KSTATUS Status;
  442. LocalDataSize = EFIBOOT_DEFAULT_VARIABLE_SIZE;
  443. LocalData = malloc(LocalDataSize);
  444. if (LocalData == NULL) {
  445. return STATUS_INSUFFICIENT_RESOURCES;
  446. }
  447. memset(LocalData, 0, LocalDataSize);
  448. Status = EfibootGetSetVariable(FALSE,
  449. VariableName,
  450. VendorGuid,
  451. &LocalAttributes,
  452. &LocalDataSize,
  453. LocalData);
  454. if (!KSUCCESS(Status)) {
  455. free(LocalData);
  456. LocalData = NULL;
  457. LocalDataSize = 0;
  458. LocalAttributes = 0;
  459. }
  460. if (Attributes != NULL) {
  461. *Attributes = LocalAttributes;
  462. }
  463. *DataSize = LocalDataSize;
  464. *Data = LocalData;
  465. return Status;
  466. }
  467. KSTATUS
  468. EfibootGetSetVariable (
  469. BOOL Set,
  470. CHAR16 *VariableName,
  471. EFI_GUID *VendorGuid,
  472. UINT32 *Attributes,
  473. UINTN *DataSize,
  474. VOID *Data
  475. )
  476. /*++
  477. Routine Description:
  478. This routine gets or sets an EFI firmware variable. The caller must be a
  479. system administrator.
  480. Arguments:
  481. Set - Supplies a boolean indicating whether to get the variable (FALSE) or
  482. set the variable (TRUE).
  483. VariableName - Supplies a pointer to a null-terminated string containing
  484. the name of the vendor's variable.
  485. VendorGuid - Supplies a pointer to the unique GUID for the vendor.
  486. Attributes - Supplies a pointer to the attributes.
  487. DataSize - Supplies a pointer that on input contains the size of the data
  488. buffer. On output, the actual size of the data will be returned.
  489. Data - Supplies a pointer to the variable data buffer.
  490. Return Value:
  491. None.
  492. --*/
  493. {
  494. UINTN AllocationSize;
  495. PHL_EFI_VARIABLE_INFORMATION Information;
  496. VOID *InformationData;
  497. CHAR16 *InformationVariable;
  498. KSTATUS Status;
  499. UINTN VariableNameSize;
  500. VariableNameSize = 0;
  501. while (VariableName[VariableNameSize] != L'\0') {
  502. VariableNameSize += 1;
  503. }
  504. VariableNameSize += 1;
  505. VariableNameSize *= sizeof(CHAR16);
  506. AllocationSize = sizeof(HL_EFI_VARIABLE_INFORMATION) +
  507. VariableNameSize +
  508. *DataSize;
  509. Information = malloc(AllocationSize);
  510. if (Information == NULL) {
  511. return STATUS_INSUFFICIENT_RESOURCES;
  512. }
  513. memset(Information, 0, AllocationSize);
  514. Information->VariableNameSize = VariableNameSize;
  515. memcpy(&(Information->VendorGuid), VendorGuid, sizeof(EFI_GUID));
  516. Information->Attributes = *Attributes;
  517. Information->DataSize = *DataSize;
  518. InformationVariable = (CHAR16 *)(Information + 1);
  519. InformationData = ((PUCHAR)InformationVariable) + VariableNameSize;
  520. memcpy(InformationVariable, VariableName, VariableNameSize);
  521. if (Set != FALSE) {
  522. memcpy(InformationData, Data, *DataSize);
  523. }
  524. Status = OsGetSetSystemInformation(SystemInformationHl,
  525. HlInformationEfiVariable,
  526. Information,
  527. &AllocationSize,
  528. Set);
  529. if (!KSUCCESS(Status)) {
  530. goto GetSetVariableEnd;
  531. }
  532. *Attributes = Information->Attributes;
  533. *DataSize = Information->DataSize;
  534. if (Set == FALSE) {
  535. memcpy(Data, InformationData, Information->DataSize);
  536. }
  537. GetSetVariableEnd:
  538. if (Information != NULL) {
  539. free(Information);
  540. }
  541. return Status;
  542. }