aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86
diff options
context:
space:
mode:
authorMatt Fleming <matt.fleming@intel.com>2012-09-07 13:28:04 -0400
committerMatt Fleming <matt.fleming@intel.com>2012-10-30 06:39:19 -0400
commit185034e72d591f9465e5e18f937ed642e7ea0070 (patch)
treeecb81fad559d790db1a0d390a8fa6e209662c315 /arch/x86
parent53b87cf088e2ea68d7c59619d0214cc15bb76133 (diff)
x86, efi: 1:1 pagetable mapping for virtual EFI calls
Some firmware still needs a 1:1 (virt->phys) mapping even after we've called SetVirtualAddressMap(). So install the mapping alongside our existing kernel mapping whenever we make EFI calls in virtual mode. This bug was discovered on ASUS machines where the firmware implementation of GetTime() accesses the RTC device via physical addresses, even though that's bogus per the UEFI spec since we've informed the firmware via SetVirtualAddressMap() that the boottime memory map is no longer valid. This bug seems to be present in a lot of consumer devices, so there's not a lot we can do about this spec violation apart from workaround it. Cc: JérômeCarretero <cJ-ko@zougloub.eu> Cc: Vasco Dias <rafa.vasco@gmail.com> Acked-by: Jan Beulich <jbeulich@suse.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Diffstat (limited to 'arch/x86')
-rw-r--r--arch/x86/include/asm/efi.h28
-rw-r--r--arch/x86/platform/efi/efi_64.c15
2 files changed, 36 insertions, 7 deletions
diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
index c9dcc181d4d1..ae3bf3b8da71 100644
--- a/arch/x86/include/asm/efi.h
+++ b/arch/x86/include/asm/efi.h
@@ -69,23 +69,37 @@ extern u64 efi_call6(void *fp, u64 arg1, u64 arg2, u64 arg3,
69 efi_call6((void *)(f), (u64)(a1), (u64)(a2), (u64)(a3), \ 69 efi_call6((void *)(f), (u64)(a1), (u64)(a2), (u64)(a3), \
70 (u64)(a4), (u64)(a5), (u64)(a6)) 70 (u64)(a4), (u64)(a5), (u64)(a6))
71 71
72extern unsigned long efi_call_virt_prelog(void);
73extern void efi_call_virt_epilog(unsigned long);
74
75#define efi_callx(x, func, ...) \
76 ({ \
77 efi_status_t __status; \
78 unsigned long __pgd; \
79 \
80 __pgd = efi_call_virt_prelog(); \
81 __status = efi_call##x(func, __VA_ARGS__); \
82 efi_call_virt_epilog(__pgd); \
83 __status; \
84 })
85
72#define efi_call_virt0(f) \ 86#define efi_call_virt0(f) \
73 efi_call0((void *)(efi.systab->runtime->f)) 87 efi_callx(0, (void *)(efi.systab->runtime->f))
74#define efi_call_virt1(f, a1) \ 88#define efi_call_virt1(f, a1) \
75 efi_call1((void *)(efi.systab->runtime->f), (u64)(a1)) 89 efi_callx(1, (void *)(efi.systab->runtime->f), (u64)(a1))
76#define efi_call_virt2(f, a1, a2) \ 90#define efi_call_virt2(f, a1, a2) \
77 efi_call2((void *)(efi.systab->runtime->f), (u64)(a1), (u64)(a2)) 91 efi_callx(2, (void *)(efi.systab->runtime->f), (u64)(a1), (u64)(a2))
78#define efi_call_virt3(f, a1, a2, a3) \ 92#define efi_call_virt3(f, a1, a2, a3) \
79 efi_call3((void *)(efi.systab->runtime->f), (u64)(a1), (u64)(a2), \ 93 efi_callx(3, (void *)(efi.systab->runtime->f), (u64)(a1), (u64)(a2), \
80 (u64)(a3)) 94 (u64)(a3))
81#define efi_call_virt4(f, a1, a2, a3, a4) \ 95#define efi_call_virt4(f, a1, a2, a3, a4) \
82 efi_call4((void *)(efi.systab->runtime->f), (u64)(a1), (u64)(a2), \ 96 efi_callx(4, (void *)(efi.systab->runtime->f), (u64)(a1), (u64)(a2), \
83 (u64)(a3), (u64)(a4)) 97 (u64)(a3), (u64)(a4))
84#define efi_call_virt5(f, a1, a2, a3, a4, a5) \ 98#define efi_call_virt5(f, a1, a2, a3, a4, a5) \
85 efi_call5((void *)(efi.systab->runtime->f), (u64)(a1), (u64)(a2), \ 99 efi_callx(5, (void *)(efi.systab->runtime->f), (u64)(a1), (u64)(a2), \
86 (u64)(a3), (u64)(a4), (u64)(a5)) 100 (u64)(a3), (u64)(a4), (u64)(a5))
87#define efi_call_virt6(f, a1, a2, a3, a4, a5, a6) \ 101#define efi_call_virt6(f, a1, a2, a3, a4, a5, a6) \
88 efi_call6((void *)(efi.systab->runtime->f), (u64)(a1), (u64)(a2), \ 102 efi_callx(6, (void *)(efi.systab->runtime->f), (u64)(a1), (u64)(a2), \
89 (u64)(a3), (u64)(a4), (u64)(a5), (u64)(a6)) 103 (u64)(a3), (u64)(a4), (u64)(a5), (u64)(a6))
90 104
91extern void __iomem *efi_ioremap(unsigned long addr, unsigned long size, 105extern void __iomem *efi_ioremap(unsigned long addr, unsigned long size,
diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c
index ac3aa54e2654..ddb0174cf093 100644
--- a/arch/x86/platform/efi/efi_64.c
+++ b/arch/x86/platform/efi/efi_64.c
@@ -58,6 +58,21 @@ static void __init early_code_mapping_set_exec(int executable)
58 } 58 }
59} 59}
60 60
61unsigned long efi_call_virt_prelog(void)
62{
63 unsigned long saved;
64
65 saved = read_cr3();
66 write_cr3(real_mode_header->trampoline_pgd);
67
68 return saved;
69}
70
71void efi_call_virt_epilog(unsigned long saved)
72{
73 write_cr3(saved);
74}
75
61void __init efi_call_phys_prelog(void) 76void __init efi_call_phys_prelog(void)
62{ 77{
63 unsigned long vaddress; 78 unsigned long vaddress;