diff options
Diffstat (limited to 'arch/x86/platform')
-rw-r--r-- | arch/x86/platform/efi/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/platform/efi/efi-bgrt.c | 76 | ||||
-rw-r--r-- | arch/x86/platform/efi/efi.c | 66 |
3 files changed, 130 insertions, 13 deletions
diff --git a/arch/x86/platform/efi/Makefile b/arch/x86/platform/efi/Makefile index 73b8be0f3675..6db1cc4c7534 100644 --- a/arch/x86/platform/efi/Makefile +++ b/arch/x86/platform/efi/Makefile | |||
@@ -1 +1,2 @@ | |||
1 | obj-$(CONFIG_EFI) += efi.o efi_$(BITS).o efi_stub_$(BITS).o | 1 | obj-$(CONFIG_EFI) += efi.o efi_$(BITS).o efi_stub_$(BITS).o |
2 | obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o | ||
diff --git a/arch/x86/platform/efi/efi-bgrt.c b/arch/x86/platform/efi/efi-bgrt.c new file mode 100644 index 000000000000..f6a0c1b8e518 --- /dev/null +++ b/arch/x86/platform/efi/efi-bgrt.c | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * Copyright 2012 Intel Corporation | ||
3 | * Author: Josh Triplett <josh@joshtriplett.org> | ||
4 | * | ||
5 | * Based on the bgrt driver: | ||
6 | * Copyright 2012 Red Hat, Inc <mjg@redhat.com> | ||
7 | * Author: Matthew Garrett | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/acpi.h> | ||
15 | #include <linux/efi.h> | ||
16 | #include <linux/efi-bgrt.h> | ||
17 | |||
18 | struct acpi_table_bgrt *bgrt_tab; | ||
19 | void *bgrt_image; | ||
20 | size_t bgrt_image_size; | ||
21 | |||
22 | struct bmp_header { | ||
23 | u16 id; | ||
24 | u32 size; | ||
25 | } __packed; | ||
26 | |||
27 | void efi_bgrt_init(void) | ||
28 | { | ||
29 | acpi_status status; | ||
30 | void __iomem *image; | ||
31 | bool ioremapped = false; | ||
32 | struct bmp_header bmp_header; | ||
33 | |||
34 | if (acpi_disabled) | ||
35 | return; | ||
36 | |||
37 | status = acpi_get_table("BGRT", 0, | ||
38 | (struct acpi_table_header **)&bgrt_tab); | ||
39 | if (ACPI_FAILURE(status)) | ||
40 | return; | ||
41 | |||
42 | if (bgrt_tab->version != 1) | ||
43 | return; | ||
44 | if (bgrt_tab->image_type != 0 || !bgrt_tab->image_address) | ||
45 | return; | ||
46 | |||
47 | image = efi_lookup_mapped_addr(bgrt_tab->image_address); | ||
48 | if (!image) { | ||
49 | image = ioremap(bgrt_tab->image_address, sizeof(bmp_header)); | ||
50 | ioremapped = true; | ||
51 | if (!image) | ||
52 | return; | ||
53 | } | ||
54 | |||
55 | memcpy_fromio(&bmp_header, image, sizeof(bmp_header)); | ||
56 | if (ioremapped) | ||
57 | iounmap(image); | ||
58 | bgrt_image_size = bmp_header.size; | ||
59 | |||
60 | bgrt_image = kmalloc(bgrt_image_size, GFP_KERNEL); | ||
61 | if (!bgrt_image) | ||
62 | return; | ||
63 | |||
64 | if (ioremapped) { | ||
65 | image = ioremap(bgrt_tab->image_address, bmp_header.size); | ||
66 | if (!image) { | ||
67 | kfree(bgrt_image); | ||
68 | bgrt_image = NULL; | ||
69 | return; | ||
70 | } | ||
71 | } | ||
72 | |||
73 | memcpy_fromio(bgrt_image, image, bgrt_image_size); | ||
74 | if (ioremapped) | ||
75 | iounmap(image); | ||
76 | } | ||
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 92660edaa1e7..aded2a91162a 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/kernel.h> | 31 | #include <linux/kernel.h> |
32 | #include <linux/init.h> | 32 | #include <linux/init.h> |
33 | #include <linux/efi.h> | 33 | #include <linux/efi.h> |
34 | #include <linux/efi-bgrt.h> | ||
34 | #include <linux/export.h> | 35 | #include <linux/export.h> |
35 | #include <linux/bootmem.h> | 36 | #include <linux/bootmem.h> |
36 | #include <linux/memblock.h> | 37 | #include <linux/memblock.h> |
@@ -419,10 +420,21 @@ void __init efi_reserve_boot_services(void) | |||
419 | } | 420 | } |
420 | } | 421 | } |
421 | 422 | ||
422 | static void __init efi_free_boot_services(void) | 423 | static void __init efi_unmap_memmap(void) |
424 | { | ||
425 | if (memmap.map) { | ||
426 | early_iounmap(memmap.map, memmap.nr_map * memmap.desc_size); | ||
427 | memmap.map = NULL; | ||
428 | } | ||
429 | } | ||
430 | |||
431 | void __init efi_free_boot_services(void) | ||
423 | { | 432 | { |
424 | void *p; | 433 | void *p; |
425 | 434 | ||
435 | if (!efi_native) | ||
436 | return; | ||
437 | |||
426 | for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { | 438 | for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { |
427 | efi_memory_desc_t *md = p; | 439 | efi_memory_desc_t *md = p; |
428 | unsigned long long start = md->phys_addr; | 440 | unsigned long long start = md->phys_addr; |
@@ -438,6 +450,8 @@ static void __init efi_free_boot_services(void) | |||
438 | 450 | ||
439 | free_bootmem_late(start, size); | 451 | free_bootmem_late(start, size); |
440 | } | 452 | } |
453 | |||
454 | efi_unmap_memmap(); | ||
441 | } | 455 | } |
442 | 456 | ||
443 | static int __init efi_systab_init(void *phys) | 457 | static int __init efi_systab_init(void *phys) |
@@ -732,6 +746,11 @@ void __init efi_init(void) | |||
732 | #endif | 746 | #endif |
733 | } | 747 | } |
734 | 748 | ||
749 | void __init efi_late_init(void) | ||
750 | { | ||
751 | efi_bgrt_init(); | ||
752 | } | ||
753 | |||
735 | void __init efi_set_executable(efi_memory_desc_t *md, bool executable) | 754 | void __init efi_set_executable(efi_memory_desc_t *md, bool executable) |
736 | { | 755 | { |
737 | u64 addr, npages; | 756 | u64 addr, npages; |
@@ -764,6 +783,34 @@ static void __init runtime_code_page_mkexec(void) | |||
764 | } | 783 | } |
765 | 784 | ||
766 | /* | 785 | /* |
786 | * We can't ioremap data in EFI boot services RAM, because we've already mapped | ||
787 | * it as RAM. So, look it up in the existing EFI memory map instead. Only | ||
788 | * callable after efi_enter_virtual_mode and before efi_free_boot_services. | ||
789 | */ | ||
790 | void __iomem *efi_lookup_mapped_addr(u64 phys_addr) | ||
791 | { | ||
792 | void *p; | ||
793 | if (WARN_ON(!memmap.map)) | ||
794 | return NULL; | ||
795 | for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { | ||
796 | efi_memory_desc_t *md = p; | ||
797 | u64 size = md->num_pages << EFI_PAGE_SHIFT; | ||
798 | u64 end = md->phys_addr + size; | ||
799 | if (!(md->attribute & EFI_MEMORY_RUNTIME) && | ||
800 | md->type != EFI_BOOT_SERVICES_CODE && | ||
801 | md->type != EFI_BOOT_SERVICES_DATA) | ||
802 | continue; | ||
803 | if (!md->virt_addr) | ||
804 | continue; | ||
805 | if (phys_addr >= md->phys_addr && phys_addr < end) { | ||
806 | phys_addr += md->virt_addr - md->phys_addr; | ||
807 | return (__force void __iomem *)(unsigned long)phys_addr; | ||
808 | } | ||
809 | } | ||
810 | return NULL; | ||
811 | } | ||
812 | |||
813 | /* | ||
767 | * This function will switch the EFI runtime services to virtual mode. | 814 | * This function will switch the EFI runtime services to virtual mode. |
768 | * Essentially, look through the EFI memmap and map every region that | 815 | * Essentially, look through the EFI memmap and map every region that |
769 | * has the runtime attribute bit set in its memory descriptor and update | 816 | * has the runtime attribute bit set in its memory descriptor and update |
@@ -787,8 +834,10 @@ void __init efi_enter_virtual_mode(void) | |||
787 | * non-native EFI | 834 | * non-native EFI |
788 | */ | 835 | */ |
789 | 836 | ||
790 | if (!efi_native) | 837 | if (!efi_native) { |
791 | goto out; | 838 | efi_unmap_memmap(); |
839 | return; | ||
840 | } | ||
792 | 841 | ||
793 | /* Merge contiguous regions of the same type and attribute */ | 842 | /* Merge contiguous regions of the same type and attribute */ |
794 | for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { | 843 | for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { |
@@ -878,18 +927,12 @@ void __init efi_enter_virtual_mode(void) | |||
878 | } | 927 | } |
879 | 928 | ||
880 | /* | 929 | /* |
881 | * Thankfully, it does seem that no runtime services other than | ||
882 | * SetVirtualAddressMap() will touch boot services code, so we can | ||
883 | * get rid of it all at this point | ||
884 | */ | ||
885 | efi_free_boot_services(); | ||
886 | |||
887 | /* | ||
888 | * Now that EFI is in virtual mode, update the function | 930 | * Now that EFI is in virtual mode, update the function |
889 | * pointers in the runtime service table to the new virtual addresses. | 931 | * pointers in the runtime service table to the new virtual addresses. |
890 | * | 932 | * |
891 | * Call EFI services through wrapper functions. | 933 | * Call EFI services through wrapper functions. |
892 | */ | 934 | */ |
935 | efi.runtime_version = efi_systab.fw_revision; | ||
893 | efi.get_time = virt_efi_get_time; | 936 | efi.get_time = virt_efi_get_time; |
894 | efi.set_time = virt_efi_set_time; | 937 | efi.set_time = virt_efi_set_time; |
895 | efi.get_wakeup_time = virt_efi_get_wakeup_time; | 938 | efi.get_wakeup_time = virt_efi_get_wakeup_time; |
@@ -906,9 +949,6 @@ void __init efi_enter_virtual_mode(void) | |||
906 | if (__supported_pte_mask & _PAGE_NX) | 949 | if (__supported_pte_mask & _PAGE_NX) |
907 | runtime_code_page_mkexec(); | 950 | runtime_code_page_mkexec(); |
908 | 951 | ||
909 | out: | ||
910 | early_iounmap(memmap.map, memmap.nr_map * memmap.desc_size); | ||
911 | memmap.map = NULL; | ||
912 | kfree(new_memmap); | 952 | kfree(new_memmap); |
913 | } | 953 | } |
914 | 954 | ||