1f412fd2aSLoGin #include "efidef.h" 2f412fd2aSLoGin #include <efi.h> 3f412fd2aSLoGin #include <efilib.h> 4f412fd2aSLoGin #include <lib.h> 5f412fd2aSLoGin #include <dragonstub/dragonstub.h> 6f412fd2aSLoGin 778b790faSLoGin bool efi_nochunk; 878b790faSLoGin bool efi_nokaslr = true; 978b790faSLoGin // bool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE); 1078b790faSLoGin bool efi_novamap; 1178b790faSLoGin 1278b790faSLoGin static bool efi_noinitrd; 1378b790faSLoGin static bool efi_nosoftreserve; 1478b790faSLoGin static bool efi_disable_pci_dma = false; 1578b790faSLoGin // static bool efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA); 1678b790faSLoGin 17f412fd2aSLoGin enum efistub_event { 18f412fd2aSLoGin EFISTUB_EVT_INITRD, 19f412fd2aSLoGin EFISTUB_EVT_LOAD_OPTIONS, 20f412fd2aSLoGin EFISTUB_EVT_COUNT, 21f412fd2aSLoGin }; 22f412fd2aSLoGin 23f412fd2aSLoGin #define STR_WITH_SIZE(s) sizeof(s), s 24f412fd2aSLoGin 25f412fd2aSLoGin static const struct { 26f412fd2aSLoGin u32 pcr_index; 27f412fd2aSLoGin u32 event_id; 28f412fd2aSLoGin u32 event_data_len; 29f412fd2aSLoGin u8 event_data[52]; 30f412fd2aSLoGin } events[] = { 31f412fd2aSLoGin [EFISTUB_EVT_INITRD] = { 9, INITRD_EVENT_TAG_ID, 32f412fd2aSLoGin STR_WITH_SIZE("Linux initrd") }, 33f412fd2aSLoGin [EFISTUB_EVT_LOAD_OPTIONS] = { 9, LOAD_OPTIONS_EVENT_TAG_ID, 34f412fd2aSLoGin STR_WITH_SIZE( 35f412fd2aSLoGin "LOADED_IMAGE::LoadOptions") }, 36f412fd2aSLoGin }; 37f412fd2aSLoGin 38f412fd2aSLoGin static efi_status_t efi_measure_tagged_event(unsigned long load_addr, 39f412fd2aSLoGin unsigned long load_size, 40f412fd2aSLoGin enum efistub_event event) 41f412fd2aSLoGin { 42f412fd2aSLoGin efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID; 43f412fd2aSLoGin efi_tcg2_protocol_t *tcg2 = NULL; 44f412fd2aSLoGin efi_status_t status; 45f412fd2aSLoGin 46f412fd2aSLoGin efi_bs_call(LocateProtocol, &tcg2_guid, NULL, (void **)&tcg2); 47f412fd2aSLoGin if (tcg2) { 48f412fd2aSLoGin struct efi_measured_event { 49f412fd2aSLoGin efi_tcg2_event_t event_data; 50f412fd2aSLoGin efi_tcg2_tagged_event_t tagged_event; 51f412fd2aSLoGin u8 tagged_event_data[]; 52f412fd2aSLoGin } * evt; 53f412fd2aSLoGin int size = sizeof(*evt) + events[event].event_data_len; 54f412fd2aSLoGin 55f412fd2aSLoGin status = efi_bs_call(AllocatePool, EfiLoaderData, size, 56f412fd2aSLoGin (void **)&evt); 57f412fd2aSLoGin if (status != EFI_SUCCESS) 58f412fd2aSLoGin goto fail; 59f412fd2aSLoGin 60f412fd2aSLoGin evt->event_data = (struct efi_tcg2_event){ 61f412fd2aSLoGin .event_size = size, 62f412fd2aSLoGin .event_header.header_size = 63f412fd2aSLoGin sizeof(evt->event_data.event_header), 64f412fd2aSLoGin .event_header.header_version = 65f412fd2aSLoGin EFI_TCG2_EVENT_HEADER_VERSION, 66f412fd2aSLoGin .event_header.pcr_index = events[event].pcr_index, 67f412fd2aSLoGin .event_header.event_type = EV_EVENT_TAG, 68f412fd2aSLoGin }; 69f412fd2aSLoGin 70f412fd2aSLoGin evt->tagged_event = (struct efi_tcg2_tagged_event){ 71f412fd2aSLoGin .tagged_event_id = events[event].event_id, 72f412fd2aSLoGin .tagged_event_data_size = events[event].event_data_len, 73f412fd2aSLoGin }; 74f412fd2aSLoGin 75f412fd2aSLoGin memcpy(evt->tagged_event_data, events[event].event_data, 76f412fd2aSLoGin events[event].event_data_len); 77f412fd2aSLoGin 78f412fd2aSLoGin status = efi_call_proto(tcg2, hash_log_extend_event, 0, 79f412fd2aSLoGin load_addr, load_size, &evt->event_data); 80f412fd2aSLoGin efi_bs_call(FreePool, evt); 81f412fd2aSLoGin 82f412fd2aSLoGin if (status != EFI_SUCCESS) 83f412fd2aSLoGin goto fail; 84f412fd2aSLoGin return EFI_SUCCESS; 85f412fd2aSLoGin } 86f412fd2aSLoGin 87f412fd2aSLoGin return EFI_UNSUPPORTED; 88f412fd2aSLoGin fail: 89f412fd2aSLoGin efi_warn("Failed to measure data for event %d: 0x%lx\n", event, status); 90f412fd2aSLoGin return status; 91f412fd2aSLoGin } 92f412fd2aSLoGin 93f412fd2aSLoGin /* 94f412fd2aSLoGin * At least some versions of Dell firmware pass the entire contents of the 95f412fd2aSLoGin * Boot#### variable, i.e. the EFI_LOAD_OPTION descriptor, rather than just the 96f412fd2aSLoGin * OptionalData field. 97f412fd2aSLoGin * 98f412fd2aSLoGin * Detect this case and extract OptionalData. 99f412fd2aSLoGin */ 100f412fd2aSLoGin void efi_apply_loadoptions_quirk(const void **load_options, 101f412fd2aSLoGin u32 *load_options_size) 102f412fd2aSLoGin { 103f412fd2aSLoGin #ifndef CONFIG_X86 104f412fd2aSLoGin return; 105f412fd2aSLoGin #else 106f412fd2aSLoGin const efi_load_option_t *load_option = *load_options; 107f412fd2aSLoGin efi_load_option_unpacked_t load_option_unpacked; 108f412fd2aSLoGin if (!load_option) 109f412fd2aSLoGin return; 110f412fd2aSLoGin if (*load_options_size < sizeof(*load_option)) 111f412fd2aSLoGin return; 112f412fd2aSLoGin if ((load_option->attributes & ~EFI_LOAD_OPTION_BOOT_MASK) != 0) 113f412fd2aSLoGin return; 114f412fd2aSLoGin 115f412fd2aSLoGin if (!efi_load_option_unpack(&load_option_unpacked, load_option, 116f412fd2aSLoGin *load_options_size)) 117f412fd2aSLoGin return; 118f412fd2aSLoGin 119f412fd2aSLoGin efi_warn_once(FW_BUG "LoadOptions is an EFI_LOAD_OPTION descriptor\n"); 120f412fd2aSLoGin efi_warn_once(FW_BUG "Using OptionalData as a workaround\n"); 121f412fd2aSLoGin 122f412fd2aSLoGin *load_options = load_option_unpacked.optional_data; 123f412fd2aSLoGin *load_options_size = load_option_unpacked.optional_data_size; 124f412fd2aSLoGin #endif 125f412fd2aSLoGin } 126f412fd2aSLoGin 127f412fd2aSLoGin /* 128f412fd2aSLoGin * Convert the unicode UEFI command line to ASCII to pass to kernel. 129f412fd2aSLoGin * Size of memory allocated return in *cmd_line_len. 130f412fd2aSLoGin * Returns NULL on error. 131f412fd2aSLoGin */ 132f412fd2aSLoGin char *efi_convert_cmdline(EFI_LOADED_IMAGE *image, int *cmd_line_len) 133f412fd2aSLoGin { 134f412fd2aSLoGin const efi_char16_t *options = efi_table_attr(image, LoadOptions); 135f412fd2aSLoGin u32 options_size = efi_table_attr(image, LoadOptionsSize); 136f412fd2aSLoGin int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */ 137f412fd2aSLoGin unsigned long cmdline_addr = 0; 138f412fd2aSLoGin const efi_char16_t *s2; 139f412fd2aSLoGin bool in_quote = false; 140f412fd2aSLoGin efi_status_t status; 141f412fd2aSLoGin u32 options_chars; 142f412fd2aSLoGin 143f412fd2aSLoGin if (options_size > 0) 144f412fd2aSLoGin efi_measure_tagged_event((unsigned long)options, options_size, 145f412fd2aSLoGin EFISTUB_EVT_LOAD_OPTIONS); 146f412fd2aSLoGin 147f412fd2aSLoGin efi_apply_loadoptions_quirk((const void **)&options, &options_size); 148f412fd2aSLoGin options_chars = options_size / sizeof(efi_char16_t); 149f412fd2aSLoGin 150f412fd2aSLoGin if (options) { 151f412fd2aSLoGin s2 = options; 152f412fd2aSLoGin while (options_bytes < COMMAND_LINE_SIZE && options_chars--) { 153f412fd2aSLoGin efi_char16_t c = *s2++; 154f412fd2aSLoGin 155f412fd2aSLoGin if (c < 0x80) { 156f412fd2aSLoGin if (c == L'\0' || c == L'\n') 157f412fd2aSLoGin break; 158f412fd2aSLoGin if (c == L'"') 159f412fd2aSLoGin in_quote = !in_quote; 160f412fd2aSLoGin else if (!in_quote && isspace((char)c)) 161f412fd2aSLoGin safe_options_bytes = options_bytes; 162f412fd2aSLoGin 163f412fd2aSLoGin options_bytes++; 164f412fd2aSLoGin continue; 165f412fd2aSLoGin } 166f412fd2aSLoGin 167f412fd2aSLoGin /* 168f412fd2aSLoGin * Get the number of UTF-8 bytes corresponding to a 169f412fd2aSLoGin * UTF-16 character. 170f412fd2aSLoGin * The first part handles everything in the BMP. 171f412fd2aSLoGin */ 172f412fd2aSLoGin options_bytes += 2 + (c >= 0x800); 173f412fd2aSLoGin /* 174f412fd2aSLoGin * Add one more byte for valid surrogate pairs. Invalid 175f412fd2aSLoGin * surrogates will be replaced with 0xfffd and take up 176f412fd2aSLoGin * only 3 bytes. 177f412fd2aSLoGin */ 178f412fd2aSLoGin if ((c & 0xfc00) == 0xd800) { 179f412fd2aSLoGin /* 180f412fd2aSLoGin * If the very last word is a high surrogate, 181f412fd2aSLoGin * we must ignore it since we can't access the 182f412fd2aSLoGin * low surrogate. 183f412fd2aSLoGin */ 184f412fd2aSLoGin if (!options_chars) { 185f412fd2aSLoGin options_bytes -= 3; 186f412fd2aSLoGin } else if ((*s2 & 0xfc00) == 0xdc00) { 187f412fd2aSLoGin options_bytes++; 188f412fd2aSLoGin options_chars--; 189f412fd2aSLoGin s2++; 190f412fd2aSLoGin } 191f412fd2aSLoGin } 192f412fd2aSLoGin } 193f412fd2aSLoGin if (options_bytes >= COMMAND_LINE_SIZE) { 194f412fd2aSLoGin options_bytes = safe_options_bytes; 195f412fd2aSLoGin efi_err("Command line is too long: truncated to %d bytes\n", 196f412fd2aSLoGin options_bytes); 197f412fd2aSLoGin } 198f412fd2aSLoGin } 199f412fd2aSLoGin 200f412fd2aSLoGin options_bytes++; /* NUL termination */ 201f412fd2aSLoGin 202f412fd2aSLoGin status = efi_bs_call(AllocatePool, EfiLoaderData, options_bytes, 203f412fd2aSLoGin (void **)&cmdline_addr); 204f412fd2aSLoGin if (status != EFI_SUCCESS) 205f412fd2aSLoGin return NULL; 206f412fd2aSLoGin 207f412fd2aSLoGin snprintf((char *)cmdline_addr, options_bytes, "%.*ls", 208f412fd2aSLoGin options_bytes - 1, options); 209f412fd2aSLoGin 210f412fd2aSLoGin *cmd_line_len = options_bytes; 211f412fd2aSLoGin return (char *)cmdline_addr; 212f412fd2aSLoGin } 21378b790faSLoGin 21478b790faSLoGin /** 21578b790faSLoGin * parse_option_str - Parse a string and check an option is set or not 21678b790faSLoGin * @str: String to be parsed 21778b790faSLoGin * @option: option name 21878b790faSLoGin * 21978b790faSLoGin * This function parses a string containing a comma-separated list of 22078b790faSLoGin * strings like a=b,c. 22178b790faSLoGin * 22278b790faSLoGin * Return true if there's such option in the string, or return false. 22378b790faSLoGin */ 22478b790faSLoGin bool parse_option_str(const char *str, const char *option) 22578b790faSLoGin { 22678b790faSLoGin while (*str) { 22778b790faSLoGin if (!strncmp(str, option, strlen(option))) { 22878b790faSLoGin str += strlen(option); 22978b790faSLoGin if (!*str || *str == ',') 23078b790faSLoGin return true; 23178b790faSLoGin } 23278b790faSLoGin 23378b790faSLoGin while (*str && *str != ',') 23478b790faSLoGin str++; 23578b790faSLoGin 23678b790faSLoGin if (*str == ',') 23778b790faSLoGin str++; 23878b790faSLoGin } 23978b790faSLoGin 24078b790faSLoGin return false; 24178b790faSLoGin } 24278b790faSLoGin 24378b790faSLoGin /** 24478b790faSLoGin * efi_parse_options() - Parse EFI command line options 24578b790faSLoGin * @cmdline: kernel command line 24678b790faSLoGin * 24778b790faSLoGin * Parse the ASCII string @cmdline for EFI options, denoted by the efi= 24878b790faSLoGin * option, e.g. efi=nochunk. 24978b790faSLoGin * 25078b790faSLoGin * It should be noted that efi= is parsed in two very different 25178b790faSLoGin * environments, first in the early boot environment of the EFI boot 25278b790faSLoGin * stub, and subsequently during the kernel boot. 25378b790faSLoGin * 25478b790faSLoGin * Return: status code 25578b790faSLoGin */ 25678b790faSLoGin efi_status_t efi_parse_options(char const *cmdline) 25778b790faSLoGin { 25878b790faSLoGin size_t len; 25978b790faSLoGin efi_status_t status; 26078b790faSLoGin char *str, *buf; 26178b790faSLoGin 26278b790faSLoGin if (!cmdline) 26378b790faSLoGin return EFI_SUCCESS; 26478b790faSLoGin 26578b790faSLoGin len = strnlen(cmdline, COMMAND_LINE_SIZE - 1) + 1; 26678b790faSLoGin status = efi_bs_call(AllocatePool, EfiLoaderData, len, (void **)&buf); 26778b790faSLoGin if (status != EFI_SUCCESS) 26878b790faSLoGin return status; 26978b790faSLoGin 27078b790faSLoGin memcpy(buf, cmdline, len - 1); 27178b790faSLoGin buf[len - 1] = '\0'; 27278b790faSLoGin str = skip_spaces(buf); 27378b790faSLoGin 27478b790faSLoGin while (*str) { 27578b790faSLoGin char *param, *val; 27678b790faSLoGin 27778b790faSLoGin str = next_arg(str, ¶m, &val); 27878b790faSLoGin if (!val && !strcmp(param, "--")) 27978b790faSLoGin break; 28078b790faSLoGin 28178b790faSLoGin if (!strcmp(param, "nokaslr")) { 28278b790faSLoGin efi_nokaslr = true; 28378b790faSLoGin } else if (!strcmp(param, "quiet")) { 28478b790faSLoGin // efi_loglevel = CONSOLE_LOGLEVEL_QUIET; 28578b790faSLoGin } else if (!strcmp(param, "noinitrd")) { 28678b790faSLoGin efi_noinitrd = true; 28778b790faSLoGin } 28878b790faSLoGin #ifdef CONFIG_X86_64 28978b790faSLoGin else if (IS_ENABLED(CONFIG_X86_64) && 29078b790faSLoGin !strcmp(param, "no5lvl")) { 29178b790faSLoGin efi_no5lvl = true; 29278b790faSLoGin } 29378b790faSLoGin #endif 29478b790faSLoGin else if (!strcmp(param, "efi") && val) { 29578b790faSLoGin efi_nochunk = parse_option_str(val, "nochunk"); 29678b790faSLoGin efi_novamap |= parse_option_str(val, "novamap"); 29778b790faSLoGin 29878b790faSLoGin // efi_nosoftreserve = 29978b790faSLoGin // IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) && 30078b790faSLoGin // parse_option_str(val, "nosoftreserve"); 30178b790faSLoGin efi_nosoftreserve = false; 30278b790faSLoGin 30378b790faSLoGin if (parse_option_str(val, "disable_early_pci_dma")) 30478b790faSLoGin efi_disable_pci_dma = true; 30578b790faSLoGin if (parse_option_str(val, "no_disable_early_pci_dma")) 30678b790faSLoGin efi_disable_pci_dma = false; 30778b790faSLoGin if (parse_option_str(val, "debug")) { 30878b790faSLoGin // efi_loglevel = CONSOLE_LOGLEVEL_DEBUG; 30978b790faSLoGin } 31078b790faSLoGin } else if (!strcmp(param, "video") && val && 31178b790faSLoGin strstarts(val, "efifb:")) { 31278b790faSLoGin // efi_parse_option_graphics(val + strlen("efifb:")); 31378b790faSLoGin } 31478b790faSLoGin } 31578b790faSLoGin efi_bs_call(FreePool, buf); 31678b790faSLoGin return EFI_SUCCESS; 31778b790faSLoGin } 318*3e6106c4SLoGin 319*3e6106c4SLoGin /** 320*3e6106c4SLoGin * get_efi_config_table() - retrieve UEFI configuration table 321*3e6106c4SLoGin * @guid: GUID of the configuration table to be retrieved 322*3e6106c4SLoGin * Return: pointer to the configuration table or NULL 323*3e6106c4SLoGin */ 324*3e6106c4SLoGin void *get_efi_config_table(efi_guid_t guid) 325*3e6106c4SLoGin { 326*3e6106c4SLoGin efi_config_table_t *tables = efi_table_attr(ST, ConfigurationTable); 327*3e6106c4SLoGin int nr_tables = efi_table_attr(ST, NumberOfTableEntries); 328*3e6106c4SLoGin int i; 329*3e6106c4SLoGin 330*3e6106c4SLoGin for (i = 0; i < nr_tables; i++) { 331*3e6106c4SLoGin efi_config_table_t *t = (void *)tables; 332*3e6106c4SLoGin // print_efi_guid(&t->VendorGuid); 333*3e6106c4SLoGin if (efi_guidcmp(t->VendorGuid, guid) == 0) 334*3e6106c4SLoGin return efi_table_attr(t, VendorTable); 335*3e6106c4SLoGin 336*3e6106c4SLoGin tables++; 337*3e6106c4SLoGin } 338*3e6106c4SLoGin return NULL; 339*3e6106c4SLoGin }