diff options
Diffstat (limited to 'drivers/firmware/efi/efi-stub-helper.c')
-rw-r--r-- | drivers/firmware/efi/efi-stub-helper.c | 144 |
1 files changed, 107 insertions, 37 deletions
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 2c41eaece2c1..eb6d4be9e722 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c | |||
@@ -11,6 +11,10 @@ | |||
11 | */ | 11 | */ |
12 | #define EFI_READ_CHUNK_SIZE (1024 * 1024) | 12 | #define EFI_READ_CHUNK_SIZE (1024 * 1024) |
13 | 13 | ||
14 | /* error code which can't be mistaken for valid address */ | ||
15 | #define EFI_ERROR (~0UL) | ||
16 | |||
17 | |||
14 | struct file_info { | 18 | struct file_info { |
15 | efi_file_handle_t *handle; | 19 | efi_file_handle_t *handle; |
16 | u64 size; | 20 | u64 size; |
@@ -33,6 +37,9 @@ static void efi_printk(efi_system_table_t *sys_table_arg, char *str) | |||
33 | } | 37 | } |
34 | } | 38 | } |
35 | 39 | ||
40 | #define pr_efi(sys_table, msg) efi_printk(sys_table, "EFI stub: "msg) | ||
41 | #define pr_efi_err(sys_table, msg) efi_printk(sys_table, "EFI stub: ERROR: "msg) | ||
42 | |||
36 | 43 | ||
37 | static efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg, | 44 | static efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg, |
38 | efi_memory_desc_t **map, | 45 | efi_memory_desc_t **map, |
@@ -80,6 +87,32 @@ fail: | |||
80 | return status; | 87 | return status; |
81 | } | 88 | } |
82 | 89 | ||
90 | |||
91 | static unsigned long __init get_dram_base(efi_system_table_t *sys_table_arg) | ||
92 | { | ||
93 | efi_status_t status; | ||
94 | unsigned long map_size; | ||
95 | unsigned long membase = EFI_ERROR; | ||
96 | struct efi_memory_map map; | ||
97 | efi_memory_desc_t *md; | ||
98 | |||
99 | status = efi_get_memory_map(sys_table_arg, (efi_memory_desc_t **)&map.map, | ||
100 | &map_size, &map.desc_size, NULL, NULL); | ||
101 | if (status != EFI_SUCCESS) | ||
102 | return membase; | ||
103 | |||
104 | map.map_end = map.map + map_size; | ||
105 | |||
106 | for_each_efi_memory_desc(&map, md) | ||
107 | if (md->attribute & EFI_MEMORY_WB) | ||
108 | if (membase > md->phys_addr) | ||
109 | membase = md->phys_addr; | ||
110 | |||
111 | efi_call_early(free_pool, map.map); | ||
112 | |||
113 | return membase; | ||
114 | } | ||
115 | |||
83 | /* | 116 | /* |
84 | * Allocate at the highest possible address that is not above 'max'. | 117 | * Allocate at the highest possible address that is not above 'max'. |
85 | */ | 118 | */ |
@@ -267,7 +300,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, | |||
267 | struct file_info *files; | 300 | struct file_info *files; |
268 | unsigned long file_addr; | 301 | unsigned long file_addr; |
269 | u64 file_size_total; | 302 | u64 file_size_total; |
270 | efi_file_handle_t *fh; | 303 | efi_file_handle_t *fh = NULL; |
271 | efi_status_t status; | 304 | efi_status_t status; |
272 | int nr_files; | 305 | int nr_files; |
273 | char *str; | 306 | char *str; |
@@ -310,7 +343,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, | |||
310 | status = efi_call_early(allocate_pool, EFI_LOADER_DATA, | 343 | status = efi_call_early(allocate_pool, EFI_LOADER_DATA, |
311 | nr_files * sizeof(*files), (void **)&files); | 344 | nr_files * sizeof(*files), (void **)&files); |
312 | if (status != EFI_SUCCESS) { | 345 | if (status != EFI_SUCCESS) { |
313 | efi_printk(sys_table_arg, "Failed to alloc mem for file handle list\n"); | 346 | pr_efi_err(sys_table_arg, "Failed to alloc mem for file handle list\n"); |
314 | goto fail; | 347 | goto fail; |
315 | } | 348 | } |
316 | 349 | ||
@@ -374,13 +407,13 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, | |||
374 | status = efi_high_alloc(sys_table_arg, file_size_total, 0x1000, | 407 | status = efi_high_alloc(sys_table_arg, file_size_total, 0x1000, |
375 | &file_addr, max_addr); | 408 | &file_addr, max_addr); |
376 | if (status != EFI_SUCCESS) { | 409 | if (status != EFI_SUCCESS) { |
377 | efi_printk(sys_table_arg, "Failed to alloc highmem for files\n"); | 410 | pr_efi_err(sys_table_arg, "Failed to alloc highmem for files\n"); |
378 | goto close_handles; | 411 | goto close_handles; |
379 | } | 412 | } |
380 | 413 | ||
381 | /* We've run out of free low memory. */ | 414 | /* We've run out of free low memory. */ |
382 | if (file_addr > max_addr) { | 415 | if (file_addr > max_addr) { |
383 | efi_printk(sys_table_arg, "We've run out of free low memory\n"); | 416 | pr_efi_err(sys_table_arg, "We've run out of free low memory\n"); |
384 | status = EFI_INVALID_PARAMETER; | 417 | status = EFI_INVALID_PARAMETER; |
385 | goto free_file_total; | 418 | goto free_file_total; |
386 | } | 419 | } |
@@ -401,7 +434,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, | |||
401 | &chunksize, | 434 | &chunksize, |
402 | (void *)addr); | 435 | (void *)addr); |
403 | if (status != EFI_SUCCESS) { | 436 | if (status != EFI_SUCCESS) { |
404 | efi_printk(sys_table_arg, "Failed to read file\n"); | 437 | pr_efi_err(sys_table_arg, "Failed to read file\n"); |
405 | goto free_file_total; | 438 | goto free_file_total; |
406 | } | 439 | } |
407 | addr += chunksize; | 440 | addr += chunksize; |
@@ -486,7 +519,7 @@ static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg, | |||
486 | &new_addr); | 519 | &new_addr); |
487 | } | 520 | } |
488 | if (status != EFI_SUCCESS) { | 521 | if (status != EFI_SUCCESS) { |
489 | efi_printk(sys_table_arg, "ERROR: Failed to allocate usable memory for kernel.\n"); | 522 | pr_efi_err(sys_table_arg, "Failed to allocate usable memory for kernel.\n"); |
490 | return status; | 523 | return status; |
491 | } | 524 | } |
492 | 525 | ||
@@ -503,62 +536,99 @@ static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg, | |||
503 | } | 536 | } |
504 | 537 | ||
505 | /* | 538 | /* |
539 | * Get the number of UTF-8 bytes corresponding to an UTF-16 character. | ||
540 | * This overestimates for surrogates, but that is okay. | ||
541 | */ | ||
542 | static int efi_utf8_bytes(u16 c) | ||
543 | { | ||
544 | return 1 + (c >= 0x80) + (c >= 0x800); | ||
545 | } | ||
546 | |||
547 | /* | ||
548 | * Convert an UTF-16 string, not necessarily null terminated, to UTF-8. | ||
549 | */ | ||
550 | static u8 *efi_utf16_to_utf8(u8 *dst, const u16 *src, int n) | ||
551 | { | ||
552 | unsigned int c; | ||
553 | |||
554 | while (n--) { | ||
555 | c = *src++; | ||
556 | if (n && c >= 0xd800 && c <= 0xdbff && | ||
557 | *src >= 0xdc00 && *src <= 0xdfff) { | ||
558 | c = 0x10000 + ((c & 0x3ff) << 10) + (*src & 0x3ff); | ||
559 | src++; | ||
560 | n--; | ||
561 | } | ||
562 | if (c >= 0xd800 && c <= 0xdfff) | ||
563 | c = 0xfffd; /* Unmatched surrogate */ | ||
564 | if (c < 0x80) { | ||
565 | *dst++ = c; | ||
566 | continue; | ||
567 | } | ||
568 | if (c < 0x800) { | ||
569 | *dst++ = 0xc0 + (c >> 6); | ||
570 | goto t1; | ||
571 | } | ||
572 | if (c < 0x10000) { | ||
573 | *dst++ = 0xe0 + (c >> 12); | ||
574 | goto t2; | ||
575 | } | ||
576 | *dst++ = 0xf0 + (c >> 18); | ||
577 | *dst++ = 0x80 + ((c >> 12) & 0x3f); | ||
578 | t2: | ||
579 | *dst++ = 0x80 + ((c >> 6) & 0x3f); | ||
580 | t1: | ||
581 | *dst++ = 0x80 + (c & 0x3f); | ||
582 | } | ||
583 | |||
584 | return dst; | ||
585 | } | ||
586 | |||
587 | /* | ||
506 | * Convert the unicode UEFI command line to ASCII to pass to kernel. | 588 | * Convert the unicode UEFI command line to ASCII to pass to kernel. |
507 | * Size of memory allocated return in *cmd_line_len. | 589 | * Size of memory allocated return in *cmd_line_len. |
508 | * Returns NULL on error. | 590 | * Returns NULL on error. |
509 | */ | 591 | */ |
510 | static char *efi_convert_cmdline_to_ascii(efi_system_table_t *sys_table_arg, | 592 | static char *efi_convert_cmdline(efi_system_table_t *sys_table_arg, |
511 | efi_loaded_image_t *image, | 593 | efi_loaded_image_t *image, |
512 | int *cmd_line_len) | 594 | int *cmd_line_len) |
513 | { | 595 | { |
514 | u16 *s2; | 596 | const u16 *s2; |
515 | u8 *s1 = NULL; | 597 | u8 *s1 = NULL; |
516 | unsigned long cmdline_addr = 0; | 598 | unsigned long cmdline_addr = 0; |
517 | int load_options_size = image->load_options_size / 2; /* ASCII */ | 599 | int load_options_chars = image->load_options_size / 2; /* UTF-16 */ |
518 | void *options = image->load_options; | 600 | const u16 *options = image->load_options; |
519 | int options_size = 0; | 601 | int options_bytes = 0; /* UTF-8 bytes */ |
602 | int options_chars = 0; /* UTF-16 chars */ | ||
520 | efi_status_t status; | 603 | efi_status_t status; |
521 | int i; | ||
522 | u16 zero = 0; | 604 | u16 zero = 0; |
523 | 605 | ||
524 | if (options) { | 606 | if (options) { |
525 | s2 = options; | 607 | s2 = options; |
526 | while (*s2 && *s2 != '\n' && options_size < load_options_size) { | 608 | while (*s2 && *s2 != '\n' |
527 | s2++; | 609 | && options_chars < load_options_chars) { |
528 | options_size++; | 610 | options_bytes += efi_utf8_bytes(*s2++); |
611 | options_chars++; | ||
529 | } | 612 | } |
530 | } | 613 | } |
531 | 614 | ||
532 | if (options_size == 0) { | 615 | if (!options_chars) { |
533 | /* No command line options, so return empty string*/ | 616 | /* No command line options, so return empty string*/ |
534 | options_size = 1; | ||
535 | options = &zero; | 617 | options = &zero; |
536 | } | 618 | } |
537 | 619 | ||
538 | options_size++; /* NUL termination */ | 620 | options_bytes++; /* NUL termination */ |
539 | #ifdef CONFIG_ARM | 621 | |
540 | /* | 622 | status = efi_low_alloc(sys_table_arg, options_bytes, 0, &cmdline_addr); |
541 | * For ARM, allocate at a high address to avoid reserved | ||
542 | * regions at low addresses that we don't know the specfics of | ||
543 | * at the time we are processing the command line. | ||
544 | */ | ||
545 | status = efi_high_alloc(sys_table_arg, options_size, 0, | ||
546 | &cmdline_addr, 0xfffff000); | ||
547 | #else | ||
548 | status = efi_low_alloc(sys_table_arg, options_size, 0, | ||
549 | &cmdline_addr); | ||
550 | #endif | ||
551 | if (status != EFI_SUCCESS) | 623 | if (status != EFI_SUCCESS) |
552 | return NULL; | 624 | return NULL; |
553 | 625 | ||
554 | s1 = (u8 *)cmdline_addr; | 626 | s1 = (u8 *)cmdline_addr; |
555 | s2 = (u16 *)options; | 627 | s2 = (const u16 *)options; |
556 | |||
557 | for (i = 0; i < options_size - 1; i++) | ||
558 | *s1++ = *s2++; | ||
559 | 628 | ||
629 | s1 = efi_utf16_to_utf8(s1, s2, options_chars); | ||
560 | *s1 = '\0'; | 630 | *s1 = '\0'; |
561 | 631 | ||
562 | *cmd_line_len = options_size; | 632 | *cmd_line_len = options_bytes; |
563 | return (char *)cmdline_addr; | 633 | return (char *)cmdline_addr; |
564 | } | 634 | } |