helper.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. #include "dragonstub/printk.h"
  2. #include "efidef.h"
  3. #include <efi.h>
  4. #include <efilib.h>
  5. #include <lib.h>
  6. #include <dragonstub/dragonstub.h>
  7. bool efi_nochunk;
  8. bool efi_nokaslr = true;
  9. // bool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE);
  10. bool efi_novamap = false;
  11. static bool efi_noinitrd;
  12. static bool efi_nosoftreserve;
  13. static bool efi_disable_pci_dma = false;
  14. // static bool efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA);
  15. enum efistub_event {
  16. EFISTUB_EVT_INITRD,
  17. EFISTUB_EVT_LOAD_OPTIONS,
  18. EFISTUB_EVT_COUNT,
  19. };
  20. #define STR_WITH_SIZE(s) sizeof(s), s
  21. static const struct {
  22. u32 pcr_index;
  23. u32 event_id;
  24. u32 event_data_len;
  25. u8 event_data[52];
  26. } events[] = {
  27. [EFISTUB_EVT_INITRD] = { 9, INITRD_EVENT_TAG_ID,
  28. STR_WITH_SIZE("Linux initrd") },
  29. [EFISTUB_EVT_LOAD_OPTIONS] = { 9, LOAD_OPTIONS_EVENT_TAG_ID,
  30. STR_WITH_SIZE(
  31. "LOADED_IMAGE::LoadOptions") },
  32. };
  33. static efi_status_t efi_measure_tagged_event(unsigned long load_addr,
  34. unsigned long load_size,
  35. enum efistub_event event)
  36. {
  37. efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID;
  38. efi_tcg2_protocol_t *tcg2 = NULL;
  39. efi_status_t status;
  40. efi_bs_call(LocateProtocol, &tcg2_guid, NULL, (void **)&tcg2);
  41. if (tcg2) {
  42. struct efi_measured_event {
  43. efi_tcg2_event_t event_data;
  44. efi_tcg2_tagged_event_t tagged_event;
  45. u8 tagged_event_data[];
  46. } * evt;
  47. int size = sizeof(*evt) + events[event].event_data_len;
  48. status = efi_bs_call(AllocatePool, EfiLoaderData, size,
  49. (void **)&evt);
  50. if (status != EFI_SUCCESS)
  51. goto fail;
  52. evt->event_data = (struct efi_tcg2_event){
  53. .event_size = size,
  54. .event_header.header_size =
  55. sizeof(evt->event_data.event_header),
  56. .event_header.header_version =
  57. EFI_TCG2_EVENT_HEADER_VERSION,
  58. .event_header.pcr_index = events[event].pcr_index,
  59. .event_header.event_type = EV_EVENT_TAG,
  60. };
  61. evt->tagged_event = (struct efi_tcg2_tagged_event){
  62. .tagged_event_id = events[event].event_id,
  63. .tagged_event_data_size = events[event].event_data_len,
  64. };
  65. memcpy(evt->tagged_event_data, events[event].event_data,
  66. events[event].event_data_len);
  67. status = efi_call_proto(tcg2, hash_log_extend_event, 0,
  68. load_addr, load_size, &evt->event_data);
  69. efi_bs_call(FreePool, evt);
  70. if (status != EFI_SUCCESS)
  71. goto fail;
  72. return EFI_SUCCESS;
  73. }
  74. return EFI_UNSUPPORTED;
  75. fail:
  76. efi_warn("Failed to measure data for event %d: 0x%lx\n", event, status);
  77. return status;
  78. }
  79. /*
  80. * At least some versions of Dell firmware pass the entire contents of the
  81. * Boot#### variable, i.e. the EFI_LOAD_OPTION descriptor, rather than just the
  82. * OptionalData field.
  83. *
  84. * Detect this case and extract OptionalData.
  85. */
  86. void efi_apply_loadoptions_quirk(const void **load_options,
  87. u32 *load_options_size)
  88. {
  89. #ifndef CONFIG_X86
  90. return;
  91. #else
  92. const efi_load_option_t *load_option = *load_options;
  93. efi_load_option_unpacked_t load_option_unpacked;
  94. if (!load_option)
  95. return;
  96. if (*load_options_size < sizeof(*load_option))
  97. return;
  98. if ((load_option->attributes & ~EFI_LOAD_OPTION_BOOT_MASK) != 0)
  99. return;
  100. if (!efi_load_option_unpack(&load_option_unpacked, load_option,
  101. *load_options_size))
  102. return;
  103. efi_warn_once(FW_BUG "LoadOptions is an EFI_LOAD_OPTION descriptor\n");
  104. efi_warn_once(FW_BUG "Using OptionalData as a workaround\n");
  105. *load_options = load_option_unpacked.optional_data;
  106. *load_options_size = load_option_unpacked.optional_data_size;
  107. #endif
  108. }
  109. /*
  110. * Convert the unicode UEFI command line to ASCII to pass to kernel.
  111. * Size of memory allocated return in *cmd_line_len.
  112. * Returns NULL on error.
  113. */
  114. char *efi_convert_cmdline(EFI_LOADED_IMAGE *image, int *cmd_line_len)
  115. {
  116. const efi_char16_t *options = efi_table_attr(image, LoadOptions);
  117. u32 options_size = efi_table_attr(image, LoadOptionsSize);
  118. int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */
  119. unsigned long cmdline_addr = 0;
  120. const efi_char16_t *s2;
  121. bool in_quote = false;
  122. efi_status_t status;
  123. u32 options_chars;
  124. if (options_size > 0)
  125. efi_measure_tagged_event((unsigned long)options, options_size,
  126. EFISTUB_EVT_LOAD_OPTIONS);
  127. efi_apply_loadoptions_quirk((const void **)&options, &options_size);
  128. options_chars = options_size / sizeof(efi_char16_t);
  129. if (options) {
  130. s2 = options;
  131. while (options_bytes < COMMAND_LINE_SIZE && options_chars--) {
  132. efi_char16_t c = *s2++;
  133. if (c < 0x80) {
  134. if (c == L'\0' || c == L'\n')
  135. break;
  136. if (c == L'"')
  137. in_quote = !in_quote;
  138. else if (!in_quote && isspace((char)c))
  139. safe_options_bytes = options_bytes;
  140. options_bytes++;
  141. continue;
  142. }
  143. /*
  144. * Get the number of UTF-8 bytes corresponding to a
  145. * UTF-16 character.
  146. * The first part handles everything in the BMP.
  147. */
  148. options_bytes += 2 + (c >= 0x800);
  149. /*
  150. * Add one more byte for valid surrogate pairs. Invalid
  151. * surrogates will be replaced with 0xfffd and take up
  152. * only 3 bytes.
  153. */
  154. if ((c & 0xfc00) == 0xd800) {
  155. /*
  156. * If the very last word is a high surrogate,
  157. * we must ignore it since we can't access the
  158. * low surrogate.
  159. */
  160. if (!options_chars) {
  161. options_bytes -= 3;
  162. } else if ((*s2 & 0xfc00) == 0xdc00) {
  163. options_bytes++;
  164. options_chars--;
  165. s2++;
  166. }
  167. }
  168. }
  169. if (options_bytes >= COMMAND_LINE_SIZE) {
  170. options_bytes = safe_options_bytes;
  171. efi_err("Command line is too long: truncated to %d bytes\n",
  172. options_bytes);
  173. }
  174. }
  175. options_bytes++; /* NUL termination */
  176. status = efi_bs_call(AllocatePool, EfiLoaderData, options_bytes,
  177. (void **)&cmdline_addr);
  178. if (status != EFI_SUCCESS)
  179. return NULL;
  180. snprintf((char *)cmdline_addr, options_bytes, "%.*ls",
  181. options_bytes - 1, options);
  182. *cmd_line_len = options_bytes;
  183. return (char *)cmdline_addr;
  184. }
  185. /**
  186. * parse_option_str - Parse a string and check an option is set or not
  187. * @str: String to be parsed
  188. * @option: option name
  189. *
  190. * This function parses a string containing a comma-separated list of
  191. * strings like a=b,c.
  192. *
  193. * Return true if there's such option in the string, or return false.
  194. */
  195. bool parse_option_str(const char *str, const char *option)
  196. {
  197. while (*str) {
  198. if (!strncmp(str, option, strlen(option))) {
  199. str += strlen(option);
  200. if (!*str || *str == ',')
  201. return true;
  202. }
  203. while (*str && *str != ',')
  204. str++;
  205. if (*str == ',')
  206. str++;
  207. }
  208. return false;
  209. }
  210. /**
  211. * efi_parse_options() - Parse EFI command line options
  212. * @cmdline: kernel command line
  213. *
  214. * Parse the ASCII string @cmdline for EFI options, denoted by the efi=
  215. * option, e.g. efi=nochunk.
  216. *
  217. * It should be noted that efi= is parsed in two very different
  218. * environments, first in the early boot environment of the EFI boot
  219. * stub, and subsequently during the kernel boot.
  220. *
  221. * Return: status code
  222. */
  223. efi_status_t efi_parse_options(char const *cmdline)
  224. {
  225. size_t len;
  226. efi_status_t status;
  227. char *str, *buf;
  228. if (!cmdline)
  229. return EFI_SUCCESS;
  230. len = strnlen(cmdline, COMMAND_LINE_SIZE - 1) + 1;
  231. status = efi_bs_call(AllocatePool, EfiLoaderData, len, (void **)&buf);
  232. if (status != EFI_SUCCESS)
  233. return status;
  234. memcpy(buf, cmdline, len - 1);
  235. buf[len - 1] = '\0';
  236. str = skip_spaces(buf);
  237. while (*str) {
  238. char *param, *val;
  239. str = next_arg(str, &param, &val);
  240. if (!val && !strcmp(param, "--"))
  241. break;
  242. if (!strcmp(param, "nokaslr")) {
  243. efi_nokaslr = true;
  244. } else if (!strcmp(param, "quiet")) {
  245. // efi_loglevel = CONSOLE_LOGLEVEL_QUIET;
  246. } else if (!strcmp(param, "noinitrd")) {
  247. efi_noinitrd = true;
  248. }
  249. #ifdef CONFIG_X86_64
  250. else if (IS_ENABLED(CONFIG_X86_64) &&
  251. !strcmp(param, "no5lvl")) {
  252. efi_no5lvl = true;
  253. }
  254. #endif
  255. else if (!strcmp(param, "efi") && val) {
  256. efi_nochunk = parse_option_str(val, "nochunk");
  257. efi_novamap |= parse_option_str(val, "novamap");
  258. // efi_nosoftreserve =
  259. // IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) &&
  260. // parse_option_str(val, "nosoftreserve");
  261. efi_nosoftreserve = false;
  262. if (parse_option_str(val, "disable_early_pci_dma"))
  263. efi_disable_pci_dma = true;
  264. if (parse_option_str(val, "no_disable_early_pci_dma"))
  265. efi_disable_pci_dma = false;
  266. if (parse_option_str(val, "debug")) {
  267. // efi_loglevel = CONSOLE_LOGLEVEL_DEBUG;
  268. }
  269. } else if (!strcmp(param, "video") && val &&
  270. strstarts(val, "efifb:")) {
  271. // efi_parse_option_graphics(val + strlen("efifb:"));
  272. }
  273. }
  274. efi_bs_call(FreePool, buf);
  275. return EFI_SUCCESS;
  276. }
  277. /**
  278. * get_efi_config_table() - retrieve UEFI configuration table
  279. * @guid: GUID of the configuration table to be retrieved
  280. * Return: pointer to the configuration table or NULL
  281. */
  282. void *get_efi_config_table(efi_guid_t guid)
  283. {
  284. efi_config_table_t *tables = efi_table_attr(ST, ConfigurationTable);
  285. int nr_tables = efi_table_attr(ST, NumberOfTableEntries);
  286. int i;
  287. for (i = 0; i < nr_tables; i++) {
  288. efi_config_table_t *t = (void *)tables;
  289. // print_efi_guid(&t->VendorGuid);
  290. if (efi_guidcmp(t->VendorGuid, guid) == 0)
  291. return efi_table_attr(t, VendorTable);
  292. tables++;
  293. }
  294. return NULL;
  295. }
  296. /**
  297. * efi_exit_boot_services() - Exit boot services
  298. * @handle: handle of the exiting image
  299. * @priv: argument to be passed to @priv_func
  300. * @priv_func: function to process the memory map before exiting boot services
  301. *
  302. * Handle calling ExitBootServices according to the requirements set out by the
  303. * spec. Obtains the current memory map, and returns that info after calling
  304. * ExitBootServices. The client must specify a function to perform any
  305. * processing of the memory map data prior to ExitBootServices. A client
  306. * specific structure may be passed to the function via priv. The client
  307. * function may be called multiple times.
  308. *
  309. * Return: status code
  310. */
  311. efi_status_t efi_exit_boot_services(void *handle, void *priv,
  312. efi_exit_boot_map_processing priv_func)
  313. {
  314. struct efi_boot_memmap *map;
  315. efi_status_t status;
  316. if (efi_disable_pci_dma) {
  317. efi_todo("efi_exit_boot_services:: efi_disable_pci_dma: efi_pci_disable_bridge_busmaster");
  318. // efi_pci_disable_bridge_busmaster();
  319. }
  320. status = efi_get_memory_map(&map, true);
  321. if (status != EFI_SUCCESS)
  322. return status;
  323. efi_debug("before priv_func\n");
  324. status = priv_func(map, priv);
  325. if (status != EFI_SUCCESS) {
  326. efi_bs_call(FreePool, map);
  327. return status;
  328. }
  329. efi_debug("before ExitBootServices, handle=%p, map_key=%p\n", handle, map->map_key);
  330. efi_debug("BS->ExitBootServices=%p\n", BS->ExitBootServices);
  331. efi_debug("ST->BS->ExitBootServices=%p\n", ST->BootServices->ExitBootServices);
  332. status = efi_bs_call(ExitBootServices, handle, map->map_key);
  333. // exit之后不能再使用打印函数,否则会出现错误
  334. if (status == EFI_INVALID_PARAMETER) {
  335. /*
  336. * The memory map changed between efi_get_memory_map() and
  337. * exit_boot_services(). Per the UEFI Spec v2.6, Section 6.4:
  338. * EFI_BOOT_SERVICES.ExitBootServices we need to get the
  339. * updated map, and try again. The spec implies one retry
  340. * should be sufficent, which is confirmed against the EDK2
  341. * implementation. Per the spec, we can only invoke
  342. * get_memory_map() and exit_boot_services() - we cannot alloc
  343. * so efi_get_memory_map() cannot be used, and we must reuse
  344. * the buffer. For all practical purposes, the headroom in the
  345. * buffer should account for any changes in the map so the call
  346. * to get_memory_map() is expected to succeed here.
  347. */
  348. map->map_size = map->buff_size;
  349. status = efi_bs_call(GetMemoryMap, &map->map_size, &map->map,
  350. &map->map_key, &map->desc_size,
  351. &map->desc_ver);
  352. /* exit_boot_services() was called, thus cannot free */
  353. if (status != EFI_SUCCESS)
  354. return status;
  355. status = priv_func(map, priv);
  356. /* exit_boot_services() was called, thus cannot free */
  357. if (status != EFI_SUCCESS)
  358. return status;
  359. status = efi_bs_call(ExitBootServices, handle, map->map_key);
  360. }
  361. return status;
  362. }