diff options
-rw-r--r-- | arch/x86/include/asm/efi.h | 12 | ||||
-rw-r--r-- | arch/x86/include/uapi/asm/bootparam.h | 1 | ||||
-rw-r--r-- | arch/x86/kernel/setup.c | 3 | ||||
-rw-r--r-- | arch/x86/platform/efi/efi.c | 158 | ||||
-rw-r--r-- | arch/x86/platform/efi/efi_32.c | 1 | ||||
-rw-r--r-- | arch/x86/platform/efi/efi_64.c | 6 |
6 files changed, 156 insertions, 25 deletions
diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 9fbaeb239bde..4d1ba80b6ff1 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h | |||
@@ -133,6 +133,18 @@ extern void efi_sync_low_kernel_mappings(void); | |||
133 | extern void efi_setup_page_tables(void); | 133 | extern void efi_setup_page_tables(void); |
134 | extern void __init old_map_region(efi_memory_desc_t *md); | 134 | extern void __init old_map_region(efi_memory_desc_t *md); |
135 | 135 | ||
136 | struct efi_setup_data { | ||
137 | u64 fw_vendor; | ||
138 | u64 runtime; | ||
139 | u64 tables; | ||
140 | u64 smbios; | ||
141 | u64 reserved[8]; | ||
142 | }; | ||
143 | |||
144 | extern u64 efi_setup; | ||
145 | extern u32 efi_data_len; | ||
146 | extern void parse_efi_setup(u64 phys_addr, u32 data_len); | ||
147 | |||
136 | #ifdef CONFIG_EFI | 148 | #ifdef CONFIG_EFI |
137 | 149 | ||
138 | static inline bool efi_is_native(void) | 150 | static inline bool efi_is_native(void) |
diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h index 9c3733c5f8f7..64fe421aab65 100644 --- a/arch/x86/include/uapi/asm/bootparam.h +++ b/arch/x86/include/uapi/asm/bootparam.h | |||
@@ -6,6 +6,7 @@ | |||
6 | #define SETUP_E820_EXT 1 | 6 | #define SETUP_E820_EXT 1 |
7 | #define SETUP_DTB 2 | 7 | #define SETUP_DTB 2 |
8 | #define SETUP_PCI 3 | 8 | #define SETUP_PCI 3 |
9 | #define SETUP_EFI 4 | ||
9 | 10 | ||
10 | /* ram_size flags */ | 11 | /* ram_size flags */ |
11 | #define RAMDISK_IMAGE_START_MASK 0x07FF | 12 | #define RAMDISK_IMAGE_START_MASK 0x07FF |
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index cb233bc9dee3..24536f7a0ae6 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c | |||
@@ -447,6 +447,9 @@ static void __init parse_setup_data(void) | |||
447 | case SETUP_DTB: | 447 | case SETUP_DTB: |
448 | add_dtb(pa_data); | 448 | add_dtb(pa_data); |
449 | break; | 449 | break; |
450 | case SETUP_EFI: | ||
451 | parse_efi_setup(pa_data, data_len); | ||
452 | break; | ||
450 | default: | 453 | default: |
451 | break; | 454 | break; |
452 | } | 455 | } |
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 74fe7a719508..9965ff403c6e 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c | |||
@@ -78,6 +78,8 @@ static __initdata efi_config_table_type_t arch_tables[] = { | |||
78 | 78 | ||
79 | static void *efi_runtime_map; | 79 | static void *efi_runtime_map; |
80 | static int nr_efi_runtime_map; | 80 | static int nr_efi_runtime_map; |
81 | u64 efi_setup; /* efi setup_data physical address */ | ||
82 | u32 efi_data_len; /* efi setup_data payload length */ | ||
81 | 83 | ||
82 | /* | 84 | /* |
83 | * Returns 1 if 'facility' is enabled, 0 otherwise. | 85 | * Returns 1 if 'facility' is enabled, 0 otherwise. |
@@ -115,7 +117,6 @@ static int __init setup_storage_paranoia(char *arg) | |||
115 | } | 117 | } |
116 | early_param("efi_no_storage_paranoia", setup_storage_paranoia); | 118 | early_param("efi_no_storage_paranoia", setup_storage_paranoia); |
117 | 119 | ||
118 | |||
119 | static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) | 120 | static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) |
120 | { | 121 | { |
121 | unsigned long flags; | 122 | unsigned long flags; |
@@ -494,18 +495,27 @@ static int __init efi_systab_init(void *phys) | |||
494 | { | 495 | { |
495 | if (efi_enabled(EFI_64BIT)) { | 496 | if (efi_enabled(EFI_64BIT)) { |
496 | efi_system_table_64_t *systab64; | 497 | efi_system_table_64_t *systab64; |
498 | struct efi_setup_data *data = NULL; | ||
497 | u64 tmp = 0; | 499 | u64 tmp = 0; |
498 | 500 | ||
501 | if (efi_setup) { | ||
502 | data = early_memremap(efi_setup, sizeof(*data)); | ||
503 | if (!data) | ||
504 | return -ENOMEM; | ||
505 | } | ||
499 | systab64 = early_ioremap((unsigned long)phys, | 506 | systab64 = early_ioremap((unsigned long)phys, |
500 | sizeof(*systab64)); | 507 | sizeof(*systab64)); |
501 | if (systab64 == NULL) { | 508 | if (systab64 == NULL) { |
502 | pr_err("Couldn't map the system table!\n"); | 509 | pr_err("Couldn't map the system table!\n"); |
510 | if (data) | ||
511 | early_iounmap(data, sizeof(*data)); | ||
503 | return -ENOMEM; | 512 | return -ENOMEM; |
504 | } | 513 | } |
505 | 514 | ||
506 | efi_systab.hdr = systab64->hdr; | 515 | efi_systab.hdr = systab64->hdr; |
507 | efi_systab.fw_vendor = systab64->fw_vendor; | 516 | efi_systab.fw_vendor = data ? (unsigned long)data->fw_vendor : |
508 | tmp |= systab64->fw_vendor; | 517 | systab64->fw_vendor; |
518 | tmp |= data ? data->fw_vendor : systab64->fw_vendor; | ||
509 | efi_systab.fw_revision = systab64->fw_revision; | 519 | efi_systab.fw_revision = systab64->fw_revision; |
510 | efi_systab.con_in_handle = systab64->con_in_handle; | 520 | efi_systab.con_in_handle = systab64->con_in_handle; |
511 | tmp |= systab64->con_in_handle; | 521 | tmp |= systab64->con_in_handle; |
@@ -519,15 +529,20 @@ static int __init efi_systab_init(void *phys) | |||
519 | tmp |= systab64->stderr_handle; | 529 | tmp |= systab64->stderr_handle; |
520 | efi_systab.stderr = systab64->stderr; | 530 | efi_systab.stderr = systab64->stderr; |
521 | tmp |= systab64->stderr; | 531 | tmp |= systab64->stderr; |
522 | efi_systab.runtime = (void *)(unsigned long)systab64->runtime; | 532 | efi_systab.runtime = data ? |
523 | tmp |= systab64->runtime; | 533 | (void *)(unsigned long)data->runtime : |
534 | (void *)(unsigned long)systab64->runtime; | ||
535 | tmp |= data ? data->runtime : systab64->runtime; | ||
524 | efi_systab.boottime = (void *)(unsigned long)systab64->boottime; | 536 | efi_systab.boottime = (void *)(unsigned long)systab64->boottime; |
525 | tmp |= systab64->boottime; | 537 | tmp |= systab64->boottime; |
526 | efi_systab.nr_tables = systab64->nr_tables; | 538 | efi_systab.nr_tables = systab64->nr_tables; |
527 | efi_systab.tables = systab64->tables; | 539 | efi_systab.tables = data ? (unsigned long)data->tables : |
528 | tmp |= systab64->tables; | 540 | systab64->tables; |
541 | tmp |= data ? data->tables : systab64->tables; | ||
529 | 542 | ||
530 | early_iounmap(systab64, sizeof(*systab64)); | 543 | early_iounmap(systab64, sizeof(*systab64)); |
544 | if (data) | ||
545 | early_iounmap(data, sizeof(*data)); | ||
531 | #ifdef CONFIG_X86_32 | 546 | #ifdef CONFIG_X86_32 |
532 | if (tmp >> 32) { | 547 | if (tmp >> 32) { |
533 | pr_err("EFI data located above 4GB, disabling EFI.\n"); | 548 | pr_err("EFI data located above 4GB, disabling EFI.\n"); |
@@ -631,6 +646,71 @@ static int __init efi_memmap_init(void) | |||
631 | return 0; | 646 | return 0; |
632 | } | 647 | } |
633 | 648 | ||
649 | /* | ||
650 | * A number of config table entries get remapped to virtual addresses | ||
651 | * after entering EFI virtual mode. However, the kexec kernel requires | ||
652 | * their physical addresses therefore we pass them via setup_data and | ||
653 | * correct those entries to their respective physical addresses here. | ||
654 | * | ||
655 | * Currently only handles smbios which is necessary for some firmware | ||
656 | * implementation. | ||
657 | */ | ||
658 | static int __init efi_reuse_config(u64 tables, int nr_tables) | ||
659 | { | ||
660 | int i, sz, ret = 0; | ||
661 | void *p, *tablep; | ||
662 | struct efi_setup_data *data; | ||
663 | |||
664 | if (!efi_setup) | ||
665 | return 0; | ||
666 | |||
667 | if (!efi_enabled(EFI_64BIT)) | ||
668 | return 0; | ||
669 | |||
670 | data = early_memremap(efi_setup, sizeof(*data)); | ||
671 | if (!data) { | ||
672 | ret = -ENOMEM; | ||
673 | goto out; | ||
674 | } | ||
675 | |||
676 | if (!data->smbios) | ||
677 | goto out_memremap; | ||
678 | |||
679 | sz = sizeof(efi_config_table_64_t); | ||
680 | |||
681 | p = tablep = early_memremap(tables, nr_tables * sz); | ||
682 | if (!p) { | ||
683 | pr_err("Could not map Configuration table!\n"); | ||
684 | ret = -ENOMEM; | ||
685 | goto out_memremap; | ||
686 | } | ||
687 | |||
688 | for (i = 0; i < efi.systab->nr_tables; i++) { | ||
689 | efi_guid_t guid; | ||
690 | |||
691 | guid = ((efi_config_table_64_t *)p)->guid; | ||
692 | |||
693 | if (!efi_guidcmp(guid, SMBIOS_TABLE_GUID)) | ||
694 | ((efi_config_table_64_t *)p)->table = data->smbios; | ||
695 | p += sz; | ||
696 | } | ||
697 | early_iounmap(tablep, nr_tables * sz); | ||
698 | |||
699 | out_memremap: | ||
700 | early_iounmap(data, sizeof(*data)); | ||
701 | out: | ||
702 | return ret; | ||
703 | } | ||
704 | |||
705 | static void get_nr_runtime_map(void) | ||
706 | { | ||
707 | if (!efi_setup) | ||
708 | return; | ||
709 | |||
710 | nr_efi_runtime_map = (efi_data_len - sizeof(struct efi_setup_data)) / | ||
711 | sizeof(efi_memory_desc_t); | ||
712 | } | ||
713 | |||
634 | void __init efi_init(void) | 714 | void __init efi_init(void) |
635 | { | 715 | { |
636 | efi_char16_t *c16; | 716 | efi_char16_t *c16; |
@@ -638,6 +718,7 @@ void __init efi_init(void) | |||
638 | int i = 0; | 718 | int i = 0; |
639 | void *tmp; | 719 | void *tmp; |
640 | 720 | ||
721 | get_nr_runtime_map(); | ||
641 | #ifdef CONFIG_X86_32 | 722 | #ifdef CONFIG_X86_32 |
642 | if (boot_params.efi_info.efi_systab_hi || | 723 | if (boot_params.efi_info.efi_systab_hi || |
643 | boot_params.efi_info.efi_memmap_hi) { | 724 | boot_params.efi_info.efi_memmap_hi) { |
@@ -676,6 +757,9 @@ void __init efi_init(void) | |||
676 | efi.systab->hdr.revision >> 16, | 757 | efi.systab->hdr.revision >> 16, |
677 | efi.systab->hdr.revision & 0xffff, vendor); | 758 | efi.systab->hdr.revision & 0xffff, vendor); |
678 | 759 | ||
760 | if (efi_reuse_config(efi.systab->tables, efi.systab->nr_tables)) | ||
761 | return; | ||
762 | |||
679 | if (efi_config_init(arch_tables)) | 763 | if (efi_config_init(arch_tables)) |
680 | return; | 764 | return; |
681 | 765 | ||
@@ -861,6 +945,23 @@ out: | |||
861 | } | 945 | } |
862 | 946 | ||
863 | /* | 947 | /* |
948 | * Map efi regions which were passed via setup_data. The virt_addr is a fixed | ||
949 | * addr which was used in first kernel of a kexec boot. | ||
950 | */ | ||
951 | static void __init efi_map_regions_fixed(void) | ||
952 | { | ||
953 | void *p; | ||
954 | efi_memory_desc_t *md; | ||
955 | |||
956 | for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { | ||
957 | md = p; | ||
958 | efi_map_region_fixed(md); /* FIXME: add error handling */ | ||
959 | get_systab_virt_addr(md); | ||
960 | } | ||
961 | |||
962 | } | ||
963 | |||
964 | /* | ||
864 | * Map efi memory ranges for runtime serivce and update new_memmap with virtual | 965 | * Map efi memory ranges for runtime serivce and update new_memmap with virtual |
865 | * addresses. | 966 | * addresses. |
866 | */ | 967 | */ |
@@ -914,6 +1015,10 @@ out: | |||
914 | * so that we're in a different address space when calling a runtime | 1015 | * so that we're in a different address space when calling a runtime |
915 | * function. For function arguments passing we do copy the PGDs of the | 1016 | * function. For function arguments passing we do copy the PGDs of the |
916 | * kernel page table into ->trampoline_pgd prior to each call. | 1017 | * kernel page table into ->trampoline_pgd prior to each call. |
1018 | * | ||
1019 | * Specially for kexec boot, efi runtime maps in previous kernel should | ||
1020 | * be passed in via setup_data. In that case runtime ranges will be mapped | ||
1021 | * to the same virtual addresses as the first kernel. | ||
917 | */ | 1022 | */ |
918 | void __init efi_enter_virtual_mode(void) | 1023 | void __init efi_enter_virtual_mode(void) |
919 | { | 1024 | { |
@@ -932,12 +1037,15 @@ void __init efi_enter_virtual_mode(void) | |||
932 | return; | 1037 | return; |
933 | } | 1038 | } |
934 | 1039 | ||
935 | efi_merge_regions(); | 1040 | if (efi_setup) { |
936 | 1041 | efi_map_regions_fixed(); | |
937 | new_memmap = efi_map_regions(&count); | 1042 | } else { |
938 | if (!new_memmap) { | 1043 | efi_merge_regions(); |
939 | pr_err("Error reallocating memory, EFI runtime non-functional!\n"); | 1044 | new_memmap = efi_map_regions(&count); |
940 | return; | 1045 | if (!new_memmap) { |
1046 | pr_err("Error reallocating memory, EFI runtime non-functional!\n"); | ||
1047 | return; | ||
1048 | } | ||
941 | } | 1049 | } |
942 | 1050 | ||
943 | err = save_runtime_map(); | 1051 | err = save_runtime_map(); |
@@ -949,16 +1057,18 @@ void __init efi_enter_virtual_mode(void) | |||
949 | efi_setup_page_tables(); | 1057 | efi_setup_page_tables(); |
950 | efi_sync_low_kernel_mappings(); | 1058 | efi_sync_low_kernel_mappings(); |
951 | 1059 | ||
952 | status = phys_efi_set_virtual_address_map( | 1060 | if (!efi_setup) { |
953 | memmap.desc_size * count, | 1061 | status = phys_efi_set_virtual_address_map( |
954 | memmap.desc_size, | 1062 | memmap.desc_size * count, |
955 | memmap.desc_version, | 1063 | memmap.desc_size, |
956 | (efi_memory_desc_t *)__pa(new_memmap)); | 1064 | memmap.desc_version, |
957 | 1065 | (efi_memory_desc_t *)__pa(new_memmap)); | |
958 | if (status != EFI_SUCCESS) { | 1066 | |
959 | pr_alert("Unable to switch EFI into virtual mode " | 1067 | if (status != EFI_SUCCESS) { |
960 | "(status=%lx)!\n", status); | 1068 | pr_alert("Unable to switch EFI into virtual mode (status=%lx)!\n", |
961 | panic("EFI call to SetVirtualAddressMap() failed!"); | 1069 | status); |
1070 | panic("EFI call to SetVirtualAddressMap() failed!"); | ||
1071 | } | ||
962 | } | 1072 | } |
963 | 1073 | ||
964 | /* | 1074 | /* |
@@ -993,8 +1103,6 @@ void __init efi_enter_virtual_mode(void) | |||
993 | EFI_VARIABLE_BOOTSERVICE_ACCESS | | 1103 | EFI_VARIABLE_BOOTSERVICE_ACCESS | |
994 | EFI_VARIABLE_RUNTIME_ACCESS, | 1104 | EFI_VARIABLE_RUNTIME_ACCESS, |
995 | 0, NULL); | 1105 | 0, NULL); |
996 | |||
997 | return; | ||
998 | } | 1106 | } |
999 | 1107 | ||
1000 | /* | 1108 | /* |
diff --git a/arch/x86/platform/efi/efi_32.c b/arch/x86/platform/efi/efi_32.c index 7b3ec6ed99af..249b183cf417 100644 --- a/arch/x86/platform/efi/efi_32.c +++ b/arch/x86/platform/efi/efi_32.c | |||
@@ -48,6 +48,7 @@ void __init efi_map_region(efi_memory_desc_t *md) | |||
48 | } | 48 | } |
49 | 49 | ||
50 | void __init efi_map_region_fixed(efi_memory_desc_t *md) {} | 50 | void __init efi_map_region_fixed(efi_memory_desc_t *md) {} |
51 | void __init parse_efi_setup(u64 phys_addr, u32 data_len) {} | ||
51 | 52 | ||
52 | void efi_call_phys_prelog(void) | 53 | void efi_call_phys_prelog(void) |
53 | { | 54 | { |
diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c index ff08cb19630b..324b65103851 100644 --- a/arch/x86/platform/efi/efi_64.c +++ b/arch/x86/platform/efi/efi_64.c | |||
@@ -228,3 +228,9 @@ void __iomem *__init efi_ioremap(unsigned long phys_addr, unsigned long size, | |||
228 | 228 | ||
229 | return (void __iomem *)__va(phys_addr); | 229 | return (void __iomem *)__va(phys_addr); |
230 | } | 230 | } |
231 | |||
232 | void __init parse_efi_setup(u64 phys_addr, u32 data_len) | ||
233 | { | ||
234 | efi_setup = phys_addr + sizeof(struct setup_data); | ||
235 | efi_data_len = data_len - sizeof(struct setup_data); | ||
236 | } | ||