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