1f412fd2aSLoGin #include "efidef.h" 2f412fd2aSLoGin #include <efi.h> 3f412fd2aSLoGin #include <efilib.h> 4f412fd2aSLoGin #include <lib.h> 5f412fd2aSLoGin #include <dragonstub/dragonstub.h> 6f412fd2aSLoGin 7*78b790faSLoGin bool efi_nochunk; 8*78b790faSLoGin bool efi_nokaslr = true; 9*78b790faSLoGin // bool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE); 10*78b790faSLoGin bool efi_novamap; 11*78b790faSLoGin 12*78b790faSLoGin static bool efi_noinitrd; 13*78b790faSLoGin static bool efi_nosoftreserve; 14*78b790faSLoGin static bool efi_disable_pci_dma = false; 15*78b790faSLoGin // static bool efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA); 16*78b790faSLoGin 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 } 213*78b790faSLoGin 214*78b790faSLoGin /** 215*78b790faSLoGin * parse_option_str - Parse a string and check an option is set or not 216*78b790faSLoGin * @str: String to be parsed 217*78b790faSLoGin * @option: option name 218*78b790faSLoGin * 219*78b790faSLoGin * This function parses a string containing a comma-separated list of 220*78b790faSLoGin * strings like a=b,c. 221*78b790faSLoGin * 222*78b790faSLoGin * Return true if there's such option in the string, or return false. 223*78b790faSLoGin */ 224*78b790faSLoGin bool parse_option_str(const char *str, const char *option) 225*78b790faSLoGin { 226*78b790faSLoGin while (*str) { 227*78b790faSLoGin if (!strncmp(str, option, strlen(option))) { 228*78b790faSLoGin str += strlen(option); 229*78b790faSLoGin if (!*str || *str == ',') 230*78b790faSLoGin return true; 231*78b790faSLoGin } 232*78b790faSLoGin 233*78b790faSLoGin while (*str && *str != ',') 234*78b790faSLoGin str++; 235*78b790faSLoGin 236*78b790faSLoGin if (*str == ',') 237*78b790faSLoGin str++; 238*78b790faSLoGin } 239*78b790faSLoGin 240*78b790faSLoGin return false; 241*78b790faSLoGin } 242*78b790faSLoGin 243*78b790faSLoGin /** 244*78b790faSLoGin * efi_parse_options() - Parse EFI command line options 245*78b790faSLoGin * @cmdline: kernel command line 246*78b790faSLoGin * 247*78b790faSLoGin * Parse the ASCII string @cmdline for EFI options, denoted by the efi= 248*78b790faSLoGin * option, e.g. efi=nochunk. 249*78b790faSLoGin * 250*78b790faSLoGin * It should be noted that efi= is parsed in two very different 251*78b790faSLoGin * environments, first in the early boot environment of the EFI boot 252*78b790faSLoGin * stub, and subsequently during the kernel boot. 253*78b790faSLoGin * 254*78b790faSLoGin * Return: status code 255*78b790faSLoGin */ 256*78b790faSLoGin efi_status_t efi_parse_options(char const *cmdline) 257*78b790faSLoGin { 258*78b790faSLoGin size_t len; 259*78b790faSLoGin efi_status_t status; 260*78b790faSLoGin char *str, *buf; 261*78b790faSLoGin 262*78b790faSLoGin if (!cmdline) 263*78b790faSLoGin return EFI_SUCCESS; 264*78b790faSLoGin 265*78b790faSLoGin len = strnlen(cmdline, COMMAND_LINE_SIZE - 1) + 1; 266*78b790faSLoGin status = efi_bs_call(AllocatePool, EfiLoaderData, len, (void **)&buf); 267*78b790faSLoGin if (status != EFI_SUCCESS) 268*78b790faSLoGin return status; 269*78b790faSLoGin 270*78b790faSLoGin memcpy(buf, cmdline, len - 1); 271*78b790faSLoGin buf[len - 1] = '\0'; 272*78b790faSLoGin str = skip_spaces(buf); 273*78b790faSLoGin 274*78b790faSLoGin while (*str) { 275*78b790faSLoGin char *param, *val; 276*78b790faSLoGin 277*78b790faSLoGin str = next_arg(str, ¶m, &val); 278*78b790faSLoGin if (!val && !strcmp(param, "--")) 279*78b790faSLoGin break; 280*78b790faSLoGin 281*78b790faSLoGin if (!strcmp(param, "nokaslr")) { 282*78b790faSLoGin efi_nokaslr = true; 283*78b790faSLoGin } else if (!strcmp(param, "quiet")) { 284*78b790faSLoGin // efi_loglevel = CONSOLE_LOGLEVEL_QUIET; 285*78b790faSLoGin } else if (!strcmp(param, "noinitrd")) { 286*78b790faSLoGin efi_noinitrd = true; 287*78b790faSLoGin } 288*78b790faSLoGin #ifdef CONFIG_X86_64 289*78b790faSLoGin else if (IS_ENABLED(CONFIG_X86_64) && 290*78b790faSLoGin !strcmp(param, "no5lvl")) { 291*78b790faSLoGin efi_no5lvl = true; 292*78b790faSLoGin } 293*78b790faSLoGin #endif 294*78b790faSLoGin else if (!strcmp(param, "efi") && val) { 295*78b790faSLoGin efi_nochunk = parse_option_str(val, "nochunk"); 296*78b790faSLoGin efi_novamap |= parse_option_str(val, "novamap"); 297*78b790faSLoGin 298*78b790faSLoGin // efi_nosoftreserve = 299*78b790faSLoGin // IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) && 300*78b790faSLoGin // parse_option_str(val, "nosoftreserve"); 301*78b790faSLoGin efi_nosoftreserve = false; 302*78b790faSLoGin 303*78b790faSLoGin if (parse_option_str(val, "disable_early_pci_dma")) 304*78b790faSLoGin efi_disable_pci_dma = true; 305*78b790faSLoGin if (parse_option_str(val, "no_disable_early_pci_dma")) 306*78b790faSLoGin efi_disable_pci_dma = false; 307*78b790faSLoGin if (parse_option_str(val, "debug")) { 308*78b790faSLoGin // efi_loglevel = CONSOLE_LOGLEVEL_DEBUG; 309*78b790faSLoGin } 310*78b790faSLoGin } else if (!strcmp(param, "video") && val && 311*78b790faSLoGin strstarts(val, "efifb:")) { 312*78b790faSLoGin // efi_parse_option_graphics(val + strlen("efifb:")); 313*78b790faSLoGin } 314*78b790faSLoGin } 315*78b790faSLoGin efi_bs_call(FreePool, buf); 316*78b790faSLoGin return EFI_SUCCESS; 317*78b790faSLoGin } 318