diff options
author | H. Peter Anvin <hpa@linux.intel.com> | 2013-09-20 10:55:39 -0400 |
---|---|---|
committer | Matt Fleming <matt.fleming@intel.com> | 2014-04-17 07:29:25 -0400 |
commit | c625d1c203941fad755eb4eb729db1f65d6e9836 (patch) | |
tree | 2b1cf021ceb69963092ec5db8fbb83c356b81f75 | |
parent | 9bb40191e88d23563fd0467ac195debf5f6daaf9 (diff) |
efi: x86: Handle arbitrary Unicode characters
Instead of truncating UTF-16 assuming all characters is ASCII,
properly convert it to UTF-8.
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
[ Bug and style fixes. ]
Signed-off-by: Roy Franz <roy.franz@linaro.org>
Signed-off-by: Leif Lindholm <leif.lindholm@linaro.org>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
-rw-r--r-- | arch/x86/boot/compressed/eboot.c | 3 | ||||
-rw-r--r-- | drivers/firmware/efi/efi-stub-helper.c | 87 |
2 files changed, 68 insertions, 22 deletions
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 4703a6c4b8e3..0331d765c2bb 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c | |||
@@ -1087,8 +1087,7 @@ struct boot_params *make_boot_params(struct efi_config *c) | |||
1087 | hdr->type_of_loader = 0x21; | 1087 | hdr->type_of_loader = 0x21; |
1088 | 1088 | ||
1089 | /* Convert unicode cmdline to ascii */ | 1089 | /* Convert unicode cmdline to ascii */ |
1090 | cmdline_ptr = efi_convert_cmdline_to_ascii(sys_table, image, | 1090 | cmdline_ptr = efi_convert_cmdline(sys_table, image, &options_size); |
1091 | &options_size); | ||
1092 | if (!cmdline_ptr) | 1091 | if (!cmdline_ptr) |
1093 | goto fail; | 1092 | goto fail; |
1094 | hdr->cmd_line_ptr = (unsigned long)cmdline_ptr; | 1093 | hdr->cmd_line_ptr = (unsigned long)cmdline_ptr; |
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index a168dd20511f..eb6d4be9e722 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c | |||
@@ -536,52 +536,99 @@ static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg, | |||
536 | } | 536 | } |
537 | 537 | ||
538 | /* | 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 | /* | ||
539 | * 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. |
540 | * Size of memory allocated return in *cmd_line_len. | 589 | * Size of memory allocated return in *cmd_line_len. |
541 | * Returns NULL on error. | 590 | * Returns NULL on error. |
542 | */ | 591 | */ |
543 | 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, |
544 | efi_loaded_image_t *image, | 593 | efi_loaded_image_t *image, |
545 | int *cmd_line_len) | 594 | int *cmd_line_len) |
546 | { | 595 | { |
547 | u16 *s2; | 596 | const u16 *s2; |
548 | u8 *s1 = NULL; | 597 | u8 *s1 = NULL; |
549 | unsigned long cmdline_addr = 0; | 598 | unsigned long cmdline_addr = 0; |
550 | int load_options_size = image->load_options_size / 2; /* ASCII */ | 599 | int load_options_chars = image->load_options_size / 2; /* UTF-16 */ |
551 | void *options = image->load_options; | 600 | const u16 *options = image->load_options; |
552 | int options_size = 0; | 601 | int options_bytes = 0; /* UTF-8 bytes */ |
602 | int options_chars = 0; /* UTF-16 chars */ | ||
553 | efi_status_t status; | 603 | efi_status_t status; |
554 | int i; | ||
555 | u16 zero = 0; | 604 | u16 zero = 0; |
556 | 605 | ||
557 | if (options) { | 606 | if (options) { |
558 | s2 = options; | 607 | s2 = options; |
559 | while (*s2 && *s2 != '\n' && options_size < load_options_size) { | 608 | while (*s2 && *s2 != '\n' |
560 | s2++; | 609 | && options_chars < load_options_chars) { |
561 | options_size++; | 610 | options_bytes += efi_utf8_bytes(*s2++); |
611 | options_chars++; | ||
562 | } | 612 | } |
563 | } | 613 | } |
564 | 614 | ||
565 | if (options_size == 0) { | 615 | if (!options_chars) { |
566 | /* No command line options, so return empty string*/ | 616 | /* No command line options, so return empty string*/ |
567 | options_size = 1; | ||
568 | options = &zero; | 617 | options = &zero; |
569 | } | 618 | } |
570 | 619 | ||
571 | options_size++; /* NUL termination */ | 620 | options_bytes++; /* NUL termination */ |
572 | 621 | ||
573 | status = efi_low_alloc(sys_table_arg, options_size, 0, &cmdline_addr); | 622 | status = efi_low_alloc(sys_table_arg, options_bytes, 0, &cmdline_addr); |
574 | if (status != EFI_SUCCESS) | 623 | if (status != EFI_SUCCESS) |
575 | return NULL; | 624 | return NULL; |
576 | 625 | ||
577 | s1 = (u8 *)cmdline_addr; | 626 | s1 = (u8 *)cmdline_addr; |
578 | s2 = (u16 *)options; | 627 | s2 = (const u16 *)options; |
579 | |||
580 | for (i = 0; i < options_size - 1; i++) | ||
581 | *s1++ = *s2++; | ||
582 | 628 | ||
629 | s1 = efi_utf16_to_utf8(s1, s2, options_chars); | ||
583 | *s1 = '\0'; | 630 | *s1 = '\0'; |
584 | 631 | ||
585 | *cmd_line_len = options_size; | 632 | *cmd_line_len = options_bytes; |
586 | return (char *)cmdline_addr; | 633 | return (char *)cmdline_addr; |
587 | } | 634 | } |