1*2604d783SLoGin #include "dragonstub/printk.h" 2f412fd2aSLoGin #include "efidef.h" 3f412fd2aSLoGin #include <efi.h> 4f412fd2aSLoGin #include <efilib.h> 5f412fd2aSLoGin #include <lib.h> 6f412fd2aSLoGin #include <dragonstub/dragonstub.h> 7f412fd2aSLoGin 878b790faSLoGin bool efi_nochunk; 978b790faSLoGin bool efi_nokaslr = true; 1078b790faSLoGin // bool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE); 11823f0493SLoGin bool efi_novamap = false; 1278b790faSLoGin 1378b790faSLoGin static bool efi_noinitrd; 1478b790faSLoGin static bool efi_nosoftreserve; 1578b790faSLoGin static bool efi_disable_pci_dma = false; 1678b790faSLoGin // static bool efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA); 1778b790faSLoGin 18f412fd2aSLoGin enum efistub_event { 19f412fd2aSLoGin EFISTUB_EVT_INITRD, 20f412fd2aSLoGin EFISTUB_EVT_LOAD_OPTIONS, 21f412fd2aSLoGin EFISTUB_EVT_COUNT, 22f412fd2aSLoGin }; 23f412fd2aSLoGin 24f412fd2aSLoGin #define STR_WITH_SIZE(s) sizeof(s), s 25f412fd2aSLoGin 26f412fd2aSLoGin static const struct { 27f412fd2aSLoGin u32 pcr_index; 28f412fd2aSLoGin u32 event_id; 29f412fd2aSLoGin u32 event_data_len; 30f412fd2aSLoGin u8 event_data[52]; 31f412fd2aSLoGin } events[] = { 32f412fd2aSLoGin [EFISTUB_EVT_INITRD] = { 9, INITRD_EVENT_TAG_ID, 33f412fd2aSLoGin STR_WITH_SIZE("Linux initrd") }, 34f412fd2aSLoGin [EFISTUB_EVT_LOAD_OPTIONS] = { 9, LOAD_OPTIONS_EVENT_TAG_ID, 35f412fd2aSLoGin STR_WITH_SIZE( 36f412fd2aSLoGin "LOADED_IMAGE::LoadOptions") }, 37f412fd2aSLoGin }; 38f412fd2aSLoGin 39f412fd2aSLoGin static efi_status_t efi_measure_tagged_event(unsigned long load_addr, 40f412fd2aSLoGin unsigned long load_size, 41f412fd2aSLoGin enum efistub_event event) 42f412fd2aSLoGin { 43f412fd2aSLoGin efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID; 44f412fd2aSLoGin efi_tcg2_protocol_t *tcg2 = NULL; 45f412fd2aSLoGin efi_status_t status; 46f412fd2aSLoGin 47f412fd2aSLoGin efi_bs_call(LocateProtocol, &tcg2_guid, NULL, (void **)&tcg2); 48f412fd2aSLoGin if (tcg2) { 49f412fd2aSLoGin struct efi_measured_event { 50f412fd2aSLoGin efi_tcg2_event_t event_data; 51f412fd2aSLoGin efi_tcg2_tagged_event_t tagged_event; 52f412fd2aSLoGin u8 tagged_event_data[]; 53f412fd2aSLoGin } * evt; 54f412fd2aSLoGin int size = sizeof(*evt) + events[event].event_data_len; 55f412fd2aSLoGin 56f412fd2aSLoGin status = efi_bs_call(AllocatePool, EfiLoaderData, size, 57f412fd2aSLoGin (void **)&evt); 58f412fd2aSLoGin if (status != EFI_SUCCESS) 59f412fd2aSLoGin goto fail; 60f412fd2aSLoGin 61f412fd2aSLoGin evt->event_data = (struct efi_tcg2_event){ 62f412fd2aSLoGin .event_size = size, 63f412fd2aSLoGin .event_header.header_size = 64f412fd2aSLoGin sizeof(evt->event_data.event_header), 65f412fd2aSLoGin .event_header.header_version = 66f412fd2aSLoGin EFI_TCG2_EVENT_HEADER_VERSION, 67f412fd2aSLoGin .event_header.pcr_index = events[event].pcr_index, 68f412fd2aSLoGin .event_header.event_type = EV_EVENT_TAG, 69f412fd2aSLoGin }; 70f412fd2aSLoGin 71f412fd2aSLoGin evt->tagged_event = (struct efi_tcg2_tagged_event){ 72f412fd2aSLoGin .tagged_event_id = events[event].event_id, 73f412fd2aSLoGin .tagged_event_data_size = events[event].event_data_len, 74f412fd2aSLoGin }; 75f412fd2aSLoGin 76f412fd2aSLoGin memcpy(evt->tagged_event_data, events[event].event_data, 77f412fd2aSLoGin events[event].event_data_len); 78f412fd2aSLoGin 79f412fd2aSLoGin status = efi_call_proto(tcg2, hash_log_extend_event, 0, 80f412fd2aSLoGin load_addr, load_size, &evt->event_data); 81f412fd2aSLoGin efi_bs_call(FreePool, evt); 82f412fd2aSLoGin 83f412fd2aSLoGin if (status != EFI_SUCCESS) 84f412fd2aSLoGin goto fail; 85f412fd2aSLoGin return EFI_SUCCESS; 86f412fd2aSLoGin } 87f412fd2aSLoGin 88f412fd2aSLoGin return EFI_UNSUPPORTED; 89f412fd2aSLoGin fail: 90f412fd2aSLoGin efi_warn("Failed to measure data for event %d: 0x%lx\n", event, status); 91f412fd2aSLoGin return status; 92f412fd2aSLoGin } 93f412fd2aSLoGin 94f412fd2aSLoGin /* 95f412fd2aSLoGin * At least some versions of Dell firmware pass the entire contents of the 96f412fd2aSLoGin * Boot#### variable, i.e. the EFI_LOAD_OPTION descriptor, rather than just the 97f412fd2aSLoGin * OptionalData field. 98f412fd2aSLoGin * 99f412fd2aSLoGin * Detect this case and extract OptionalData. 100f412fd2aSLoGin */ 101f412fd2aSLoGin void efi_apply_loadoptions_quirk(const void **load_options, 102f412fd2aSLoGin u32 *load_options_size) 103f412fd2aSLoGin { 104f412fd2aSLoGin #ifndef CONFIG_X86 105f412fd2aSLoGin return; 106f412fd2aSLoGin #else 107f412fd2aSLoGin const efi_load_option_t *load_option = *load_options; 108f412fd2aSLoGin efi_load_option_unpacked_t load_option_unpacked; 109f412fd2aSLoGin if (!load_option) 110f412fd2aSLoGin return; 111f412fd2aSLoGin if (*load_options_size < sizeof(*load_option)) 112f412fd2aSLoGin return; 113f412fd2aSLoGin if ((load_option->attributes & ~EFI_LOAD_OPTION_BOOT_MASK) != 0) 114f412fd2aSLoGin return; 115f412fd2aSLoGin 116f412fd2aSLoGin if (!efi_load_option_unpack(&load_option_unpacked, load_option, 117f412fd2aSLoGin *load_options_size)) 118f412fd2aSLoGin return; 119f412fd2aSLoGin 120f412fd2aSLoGin efi_warn_once(FW_BUG "LoadOptions is an EFI_LOAD_OPTION descriptor\n"); 121f412fd2aSLoGin efi_warn_once(FW_BUG "Using OptionalData as a workaround\n"); 122f412fd2aSLoGin 123f412fd2aSLoGin *load_options = load_option_unpacked.optional_data; 124f412fd2aSLoGin *load_options_size = load_option_unpacked.optional_data_size; 125f412fd2aSLoGin #endif 126f412fd2aSLoGin } 127f412fd2aSLoGin 128f412fd2aSLoGin /* 129f412fd2aSLoGin * Convert the unicode UEFI command line to ASCII to pass to kernel. 130f412fd2aSLoGin * Size of memory allocated return in *cmd_line_len. 131f412fd2aSLoGin * Returns NULL on error. 132f412fd2aSLoGin */ 133f412fd2aSLoGin char *efi_convert_cmdline(EFI_LOADED_IMAGE *image, int *cmd_line_len) 134f412fd2aSLoGin { 135f412fd2aSLoGin const efi_char16_t *options = efi_table_attr(image, LoadOptions); 136f412fd2aSLoGin u32 options_size = efi_table_attr(image, LoadOptionsSize); 137f412fd2aSLoGin int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */ 138f412fd2aSLoGin unsigned long cmdline_addr = 0; 139f412fd2aSLoGin const efi_char16_t *s2; 140f412fd2aSLoGin bool in_quote = false; 141f412fd2aSLoGin efi_status_t status; 142f412fd2aSLoGin u32 options_chars; 143f412fd2aSLoGin 144f412fd2aSLoGin if (options_size > 0) 145f412fd2aSLoGin efi_measure_tagged_event((unsigned long)options, options_size, 146f412fd2aSLoGin EFISTUB_EVT_LOAD_OPTIONS); 147f412fd2aSLoGin 148f412fd2aSLoGin efi_apply_loadoptions_quirk((const void **)&options, &options_size); 149f412fd2aSLoGin options_chars = options_size / sizeof(efi_char16_t); 150f412fd2aSLoGin 151f412fd2aSLoGin if (options) { 152f412fd2aSLoGin s2 = options; 153f412fd2aSLoGin while (options_bytes < COMMAND_LINE_SIZE && options_chars--) { 154f412fd2aSLoGin efi_char16_t c = *s2++; 155f412fd2aSLoGin 156f412fd2aSLoGin if (c < 0x80) { 157f412fd2aSLoGin if (c == L'\0' || c == L'\n') 158f412fd2aSLoGin break; 159f412fd2aSLoGin if (c == L'"') 160f412fd2aSLoGin in_quote = !in_quote; 161f412fd2aSLoGin else if (!in_quote && isspace((char)c)) 162f412fd2aSLoGin safe_options_bytes = options_bytes; 163f412fd2aSLoGin 164f412fd2aSLoGin options_bytes++; 165f412fd2aSLoGin continue; 166f412fd2aSLoGin } 167f412fd2aSLoGin 168f412fd2aSLoGin /* 169f412fd2aSLoGin * Get the number of UTF-8 bytes corresponding to a 170f412fd2aSLoGin * UTF-16 character. 171f412fd2aSLoGin * The first part handles everything in the BMP. 172f412fd2aSLoGin */ 173f412fd2aSLoGin options_bytes += 2 + (c >= 0x800); 174f412fd2aSLoGin /* 175f412fd2aSLoGin * Add one more byte for valid surrogate pairs. Invalid 176f412fd2aSLoGin * surrogates will be replaced with 0xfffd and take up 177f412fd2aSLoGin * only 3 bytes. 178f412fd2aSLoGin */ 179f412fd2aSLoGin if ((c & 0xfc00) == 0xd800) { 180f412fd2aSLoGin /* 181f412fd2aSLoGin * If the very last word is a high surrogate, 182f412fd2aSLoGin * we must ignore it since we can't access the 183f412fd2aSLoGin * low surrogate. 184f412fd2aSLoGin */ 185f412fd2aSLoGin if (!options_chars) { 186f412fd2aSLoGin options_bytes -= 3; 187f412fd2aSLoGin } else if ((*s2 & 0xfc00) == 0xdc00) { 188f412fd2aSLoGin options_bytes++; 189f412fd2aSLoGin options_chars--; 190f412fd2aSLoGin s2++; 191f412fd2aSLoGin } 192f412fd2aSLoGin } 193f412fd2aSLoGin } 194f412fd2aSLoGin if (options_bytes >= COMMAND_LINE_SIZE) { 195f412fd2aSLoGin options_bytes = safe_options_bytes; 196f412fd2aSLoGin efi_err("Command line is too long: truncated to %d bytes\n", 197f412fd2aSLoGin options_bytes); 198f412fd2aSLoGin } 199f412fd2aSLoGin } 200f412fd2aSLoGin 201f412fd2aSLoGin options_bytes++; /* NUL termination */ 202f412fd2aSLoGin 203f412fd2aSLoGin status = efi_bs_call(AllocatePool, EfiLoaderData, options_bytes, 204f412fd2aSLoGin (void **)&cmdline_addr); 205f412fd2aSLoGin if (status != EFI_SUCCESS) 206f412fd2aSLoGin return NULL; 207f412fd2aSLoGin 208f412fd2aSLoGin snprintf((char *)cmdline_addr, options_bytes, "%.*ls", 209f412fd2aSLoGin options_bytes - 1, options); 210f412fd2aSLoGin 211f412fd2aSLoGin *cmd_line_len = options_bytes; 212f412fd2aSLoGin return (char *)cmdline_addr; 213f412fd2aSLoGin } 21478b790faSLoGin 21578b790faSLoGin /** 21678b790faSLoGin * parse_option_str - Parse a string and check an option is set or not 21778b790faSLoGin * @str: String to be parsed 21878b790faSLoGin * @option: option name 21978b790faSLoGin * 22078b790faSLoGin * This function parses a string containing a comma-separated list of 22178b790faSLoGin * strings like a=b,c. 22278b790faSLoGin * 22378b790faSLoGin * Return true if there's such option in the string, or return false. 22478b790faSLoGin */ 22578b790faSLoGin bool parse_option_str(const char *str, const char *option) 22678b790faSLoGin { 22778b790faSLoGin while (*str) { 22878b790faSLoGin if (!strncmp(str, option, strlen(option))) { 22978b790faSLoGin str += strlen(option); 23078b790faSLoGin if (!*str || *str == ',') 23178b790faSLoGin return true; 23278b790faSLoGin } 23378b790faSLoGin 23478b790faSLoGin while (*str && *str != ',') 23578b790faSLoGin str++; 23678b790faSLoGin 23778b790faSLoGin if (*str == ',') 23878b790faSLoGin str++; 23978b790faSLoGin } 24078b790faSLoGin 24178b790faSLoGin return false; 24278b790faSLoGin } 24378b790faSLoGin 24478b790faSLoGin /** 24578b790faSLoGin * efi_parse_options() - Parse EFI command line options 24678b790faSLoGin * @cmdline: kernel command line 24778b790faSLoGin * 24878b790faSLoGin * Parse the ASCII string @cmdline for EFI options, denoted by the efi= 24978b790faSLoGin * option, e.g. efi=nochunk. 25078b790faSLoGin * 25178b790faSLoGin * It should be noted that efi= is parsed in two very different 25278b790faSLoGin * environments, first in the early boot environment of the EFI boot 25378b790faSLoGin * stub, and subsequently during the kernel boot. 25478b790faSLoGin * 25578b790faSLoGin * Return: status code 25678b790faSLoGin */ 25778b790faSLoGin efi_status_t efi_parse_options(char const *cmdline) 25878b790faSLoGin { 25978b790faSLoGin size_t len; 26078b790faSLoGin efi_status_t status; 26178b790faSLoGin char *str, *buf; 26278b790faSLoGin 26378b790faSLoGin if (!cmdline) 26478b790faSLoGin return EFI_SUCCESS; 26578b790faSLoGin 26678b790faSLoGin len = strnlen(cmdline, COMMAND_LINE_SIZE - 1) + 1; 26778b790faSLoGin status = efi_bs_call(AllocatePool, EfiLoaderData, len, (void **)&buf); 26878b790faSLoGin if (status != EFI_SUCCESS) 26978b790faSLoGin return status; 27078b790faSLoGin 27178b790faSLoGin memcpy(buf, cmdline, len - 1); 27278b790faSLoGin buf[len - 1] = '\0'; 27378b790faSLoGin str = skip_spaces(buf); 27478b790faSLoGin 27578b790faSLoGin while (*str) { 27678b790faSLoGin char *param, *val; 27778b790faSLoGin 27878b790faSLoGin str = next_arg(str, ¶m, &val); 27978b790faSLoGin if (!val && !strcmp(param, "--")) 28078b790faSLoGin break; 28178b790faSLoGin 28278b790faSLoGin if (!strcmp(param, "nokaslr")) { 28378b790faSLoGin efi_nokaslr = true; 28478b790faSLoGin } else if (!strcmp(param, "quiet")) { 28578b790faSLoGin // efi_loglevel = CONSOLE_LOGLEVEL_QUIET; 28678b790faSLoGin } else if (!strcmp(param, "noinitrd")) { 28778b790faSLoGin efi_noinitrd = true; 28878b790faSLoGin } 28978b790faSLoGin #ifdef CONFIG_X86_64 29078b790faSLoGin else if (IS_ENABLED(CONFIG_X86_64) && 29178b790faSLoGin !strcmp(param, "no5lvl")) { 29278b790faSLoGin efi_no5lvl = true; 29378b790faSLoGin } 29478b790faSLoGin #endif 29578b790faSLoGin else if (!strcmp(param, "efi") && val) { 29678b790faSLoGin efi_nochunk = parse_option_str(val, "nochunk"); 29778b790faSLoGin efi_novamap |= parse_option_str(val, "novamap"); 29878b790faSLoGin 29978b790faSLoGin // efi_nosoftreserve = 30078b790faSLoGin // IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) && 30178b790faSLoGin // parse_option_str(val, "nosoftreserve"); 30278b790faSLoGin efi_nosoftreserve = false; 30378b790faSLoGin 30478b790faSLoGin if (parse_option_str(val, "disable_early_pci_dma")) 30578b790faSLoGin efi_disable_pci_dma = true; 30678b790faSLoGin if (parse_option_str(val, "no_disable_early_pci_dma")) 30778b790faSLoGin efi_disable_pci_dma = false; 30878b790faSLoGin if (parse_option_str(val, "debug")) { 30978b790faSLoGin // efi_loglevel = CONSOLE_LOGLEVEL_DEBUG; 31078b790faSLoGin } 31178b790faSLoGin } else if (!strcmp(param, "video") && val && 31278b790faSLoGin strstarts(val, "efifb:")) { 31378b790faSLoGin // efi_parse_option_graphics(val + strlen("efifb:")); 31478b790faSLoGin } 31578b790faSLoGin } 31678b790faSLoGin efi_bs_call(FreePool, buf); 31778b790faSLoGin return EFI_SUCCESS; 31878b790faSLoGin } 3193e6106c4SLoGin 3203e6106c4SLoGin /** 3213e6106c4SLoGin * get_efi_config_table() - retrieve UEFI configuration table 3223e6106c4SLoGin * @guid: GUID of the configuration table to be retrieved 3233e6106c4SLoGin * Return: pointer to the configuration table or NULL 3243e6106c4SLoGin */ 3253e6106c4SLoGin void *get_efi_config_table(efi_guid_t guid) 3263e6106c4SLoGin { 3273e6106c4SLoGin efi_config_table_t *tables = efi_table_attr(ST, ConfigurationTable); 3283e6106c4SLoGin int nr_tables = efi_table_attr(ST, NumberOfTableEntries); 3293e6106c4SLoGin int i; 3303e6106c4SLoGin 3313e6106c4SLoGin for (i = 0; i < nr_tables; i++) { 3323e6106c4SLoGin efi_config_table_t *t = (void *)tables; 3333e6106c4SLoGin // print_efi_guid(&t->VendorGuid); 3343e6106c4SLoGin if (efi_guidcmp(t->VendorGuid, guid) == 0) 3353e6106c4SLoGin return efi_table_attr(t, VendorTable); 3363e6106c4SLoGin 3373e6106c4SLoGin tables++; 3383e6106c4SLoGin } 3393e6106c4SLoGin return NULL; 3403e6106c4SLoGin } 341823f0493SLoGin 342823f0493SLoGin /** 343823f0493SLoGin * efi_exit_boot_services() - Exit boot services 344823f0493SLoGin * @handle: handle of the exiting image 345823f0493SLoGin * @priv: argument to be passed to @priv_func 346823f0493SLoGin * @priv_func: function to process the memory map before exiting boot services 347823f0493SLoGin * 348823f0493SLoGin * Handle calling ExitBootServices according to the requirements set out by the 349823f0493SLoGin * spec. Obtains the current memory map, and returns that info after calling 350823f0493SLoGin * ExitBootServices. The client must specify a function to perform any 351823f0493SLoGin * processing of the memory map data prior to ExitBootServices. A client 352823f0493SLoGin * specific structure may be passed to the function via priv. The client 353823f0493SLoGin * function may be called multiple times. 354823f0493SLoGin * 355823f0493SLoGin * Return: status code 356823f0493SLoGin */ 357823f0493SLoGin efi_status_t efi_exit_boot_services(void *handle, void *priv, 358823f0493SLoGin efi_exit_boot_map_processing priv_func) 359823f0493SLoGin { 360823f0493SLoGin struct efi_boot_memmap *map; 361823f0493SLoGin efi_status_t status; 362823f0493SLoGin 363823f0493SLoGin if (efi_disable_pci_dma) { 364823f0493SLoGin efi_todo("efi_exit_boot_services:: efi_disable_pci_dma: efi_pci_disable_bridge_busmaster"); 365823f0493SLoGin // efi_pci_disable_bridge_busmaster(); 366823f0493SLoGin } 367823f0493SLoGin 368823f0493SLoGin status = efi_get_memory_map(&map, true); 369823f0493SLoGin if (status != EFI_SUCCESS) 370823f0493SLoGin return status; 371823f0493SLoGin efi_debug("before priv_func\n"); 372823f0493SLoGin status = priv_func(map, priv); 373823f0493SLoGin if (status != EFI_SUCCESS) { 374823f0493SLoGin 375823f0493SLoGin efi_bs_call(FreePool, map); 376823f0493SLoGin return status; 377823f0493SLoGin } 378823f0493SLoGin 379823f0493SLoGin efi_debug("before ExitBootServices, handle=%p, map_key=%p\n", handle, map->map_key); 380*2604d783SLoGin efi_debug("BS->ExitBootServices=%p\n", BS->ExitBootServices); 381*2604d783SLoGin efi_debug("ST->BS->ExitBootServices=%p\n", ST->BootServices->ExitBootServices); 382823f0493SLoGin status = efi_bs_call(ExitBootServices, handle, map->map_key); 383*2604d783SLoGin // exit之后不能再使用打印函数,否则会出现错误 384823f0493SLoGin 385823f0493SLoGin if (status == EFI_INVALID_PARAMETER) { 386823f0493SLoGin /* 387823f0493SLoGin * The memory map changed between efi_get_memory_map() and 388823f0493SLoGin * exit_boot_services(). Per the UEFI Spec v2.6, Section 6.4: 389823f0493SLoGin * EFI_BOOT_SERVICES.ExitBootServices we need to get the 390823f0493SLoGin * updated map, and try again. The spec implies one retry 391823f0493SLoGin * should be sufficent, which is confirmed against the EDK2 392823f0493SLoGin * implementation. Per the spec, we can only invoke 393823f0493SLoGin * get_memory_map() and exit_boot_services() - we cannot alloc 394823f0493SLoGin * so efi_get_memory_map() cannot be used, and we must reuse 395823f0493SLoGin * the buffer. For all practical purposes, the headroom in the 396823f0493SLoGin * buffer should account for any changes in the map so the call 397823f0493SLoGin * to get_memory_map() is expected to succeed here. 398823f0493SLoGin */ 399823f0493SLoGin map->map_size = map->buff_size; 400823f0493SLoGin status = efi_bs_call(GetMemoryMap, &map->map_size, &map->map, 401823f0493SLoGin &map->map_key, &map->desc_size, 402823f0493SLoGin &map->desc_ver); 403823f0493SLoGin 404823f0493SLoGin /* exit_boot_services() was called, thus cannot free */ 405823f0493SLoGin if (status != EFI_SUCCESS) 406823f0493SLoGin return status; 407823f0493SLoGin 408823f0493SLoGin status = priv_func(map, priv); 409823f0493SLoGin /* exit_boot_services() was called, thus cannot free */ 410823f0493SLoGin if (status != EFI_SUCCESS) 411823f0493SLoGin return status; 412823f0493SLoGin 413823f0493SLoGin status = efi_bs_call(ExitBootServices, handle, map->map_key); 414823f0493SLoGin } 415823f0493SLoGin 416823f0493SLoGin return status; 417823f0493SLoGin }