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