]>
Commit | Line | Data |
---|---|---|
59d5af67 | 1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
321d628a FG |
2 | From: James Morse <james.morse@arm.com> |
3 | Date: Mon, 6 Nov 2017 18:44:24 +0000 | |
59d5af67 | 4 | Subject: [PATCH] ACPI / APEI: Replace ioremap_page_range() with fixmap |
321d628a FG |
5 | MIME-Version: 1.0 |
6 | Content-Type: text/plain; charset=UTF-8 | |
7 | Content-Transfer-Encoding: 8bit | |
8 | ||
9 | CVE-2017-5754 | |
10 | ||
11 | Replace ghes_io{re,un}map_pfn_{nmi,irq}()s use of ioremap_page_range() | |
12 | with __set_fixmap() as ioremap_page_range() may sleep to allocate a new | |
13 | level of page-table, even if its passed an existing final-address to | |
14 | use in the mapping. | |
15 | ||
16 | The GHES driver can only be enabled for architectures that select | |
17 | HAVE_ACPI_APEI: Add fixmap entries to both x86 and arm64. | |
18 | ||
19 | clear_fixmap() does the TLB invalidation in __set_fixmap() for arm64 | |
20 | and __set_pte_vaddr() for x86. In each case its the same as the | |
21 | respective arch_apei_flush_tlb_one(). | |
22 | ||
23 | Reported-by: Fengguang Wu <fengguang.wu@intel.com> | |
24 | Suggested-by: Linus Torvalds <torvalds@linux-foundation.org> | |
25 | Signed-off-by: James Morse <james.morse@arm.com> | |
26 | Reviewed-by: Borislav Petkov <bp@suse.de> | |
27 | Tested-by: Tyler Baicar <tbaicar@codeaurora.org> | |
28 | Tested-by: Toshi Kani <toshi.kani@hpe.com> | |
29 | [ For the arm64 bits: ] | |
30 | Acked-by: Will Deacon <will.deacon@arm.com> | |
31 | [ For the x86 bits: ] | |
32 | Acked-by: Ingo Molnar <mingo@kernel.org> | |
33 | Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> | |
34 | Cc: All applicable <stable@vger.kernel.org> | |
35 | (cherry picked from commit 4f89fa286f6729312e227e7c2d764e8e7b9d340e) | |
36 | Signed-off-by: Andy Whitcroft <apw@canonical.com> | |
37 | Signed-off-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com> | |
38 | (cherry picked from commit eda363b23c1601f733cb1d7d66d1a4975c4c5d09) | |
39 | Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com> | |
40 | --- | |
41 | arch/arm64/include/asm/fixmap.h | 7 +++++++ | |
42 | arch/x86/include/asm/fixmap.h | 6 ++++++ | |
43 | drivers/acpi/apei/ghes.c | 44 +++++++++++++---------------------------- | |
44 | 3 files changed, 27 insertions(+), 30 deletions(-) | |
45 | ||
46 | diff --git a/arch/arm64/include/asm/fixmap.h b/arch/arm64/include/asm/fixmap.h | |
47 | index caf86be815ba..4052ec39e8db 100644 | |
48 | --- a/arch/arm64/include/asm/fixmap.h | |
49 | +++ b/arch/arm64/include/asm/fixmap.h | |
50 | @@ -51,6 +51,13 @@ enum fixed_addresses { | |
51 | ||
52 | FIX_EARLYCON_MEM_BASE, | |
53 | FIX_TEXT_POKE0, | |
54 | + | |
55 | +#ifdef CONFIG_ACPI_APEI_GHES | |
56 | + /* Used for GHES mapping from assorted contexts */ | |
57 | + FIX_APEI_GHES_IRQ, | |
58 | + FIX_APEI_GHES_NMI, | |
59 | +#endif /* CONFIG_ACPI_APEI_GHES */ | |
60 | + | |
61 | __end_of_permanent_fixed_addresses, | |
62 | ||
63 | /* | |
64 | diff --git a/arch/x86/include/asm/fixmap.h b/arch/x86/include/asm/fixmap.h | |
65 | index b65155cc3760..81c2b11f50a6 100644 | |
66 | --- a/arch/x86/include/asm/fixmap.h | |
67 | +++ b/arch/x86/include/asm/fixmap.h | |
68 | @@ -104,6 +104,12 @@ enum fixed_addresses { | |
69 | FIX_GDT_REMAP_BEGIN, | |
70 | FIX_GDT_REMAP_END = FIX_GDT_REMAP_BEGIN + NR_CPUS - 1, | |
71 | ||
72 | +#ifdef CONFIG_ACPI_APEI_GHES | |
73 | + /* Used for GHES mapping from assorted contexts */ | |
74 | + FIX_APEI_GHES_IRQ, | |
75 | + FIX_APEI_GHES_NMI, | |
76 | +#endif | |
77 | + | |
78 | __end_of_permanent_fixed_addresses, | |
79 | ||
80 | /* | |
81 | diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c | |
82 | index 4827176f838d..f9f106e62e74 100644 | |
83 | --- a/drivers/acpi/apei/ghes.c | |
84 | +++ b/drivers/acpi/apei/ghes.c | |
85 | @@ -51,6 +51,7 @@ | |
86 | #include <acpi/actbl1.h> | |
87 | #include <acpi/ghes.h> | |
88 | #include <acpi/apei.h> | |
89 | +#include <asm/fixmap.h> | |
90 | #include <asm/tlbflush.h> | |
91 | #include <ras/ras_event.h> | |
92 | ||
93 | @@ -112,7 +113,7 @@ static DEFINE_MUTEX(ghes_list_mutex); | |
94 | * Because the memory area used to transfer hardware error information | |
95 | * from BIOS to Linux can be determined only in NMI, IRQ or timer | |
96 | * handler, but general ioremap can not be used in atomic context, so | |
97 | - * a special version of atomic ioremap is implemented for that. | |
98 | + * the fixmap is used instead. | |
99 | */ | |
100 | ||
101 | /* | |
102 | @@ -126,8 +127,8 @@ static DEFINE_MUTEX(ghes_list_mutex); | |
103 | /* virtual memory area for atomic ioremap */ | |
104 | static struct vm_struct *ghes_ioremap_area; | |
105 | /* | |
106 | - * These 2 spinlock is used to prevent atomic ioremap virtual memory | |
107 | - * area from being mapped simultaneously. | |
108 | + * These 2 spinlocks are used to prevent the fixmap entries from being used | |
109 | + * simultaneously. | |
110 | */ | |
111 | static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); | |
112 | static DEFINE_SPINLOCK(ghes_ioremap_lock_irq); | |
113 | @@ -159,53 +160,36 @@ static void ghes_ioremap_exit(void) | |
114 | ||
115 | static void __iomem *ghes_ioremap_pfn_nmi(u64 pfn) | |
116 | { | |
117 | - unsigned long vaddr; | |
118 | phys_addr_t paddr; | |
119 | pgprot_t prot; | |
120 | ||
121 | - vaddr = (unsigned long)GHES_IOREMAP_NMI_PAGE(ghes_ioremap_area->addr); | |
122 | - | |
123 | paddr = pfn << PAGE_SHIFT; | |
124 | prot = arch_apei_get_mem_attribute(paddr); | |
125 | - ioremap_page_range(vaddr, vaddr + PAGE_SIZE, paddr, prot); | |
126 | + __set_fixmap(FIX_APEI_GHES_NMI, paddr, prot); | |
127 | ||
128 | - return (void __iomem *)vaddr; | |
129 | + return (void __iomem *) fix_to_virt(FIX_APEI_GHES_NMI); | |
130 | } | |
131 | ||
132 | static void __iomem *ghes_ioremap_pfn_irq(u64 pfn) | |
133 | { | |
134 | - unsigned long vaddr; | |
135 | phys_addr_t paddr; | |
136 | pgprot_t prot; | |
137 | ||
138 | - vaddr = (unsigned long)GHES_IOREMAP_IRQ_PAGE(ghes_ioremap_area->addr); | |
139 | - | |
140 | paddr = pfn << PAGE_SHIFT; | |
141 | prot = arch_apei_get_mem_attribute(paddr); | |
142 | + __set_fixmap(FIX_APEI_GHES_IRQ, paddr, prot); | |
143 | ||
144 | - ioremap_page_range(vaddr, vaddr + PAGE_SIZE, paddr, prot); | |
145 | - | |
146 | - return (void __iomem *)vaddr; | |
147 | + return (void __iomem *) fix_to_virt(FIX_APEI_GHES_IRQ); | |
148 | } | |
149 | ||
150 | -static void ghes_iounmap_nmi(void __iomem *vaddr_ptr) | |
151 | +static void ghes_iounmap_nmi(void) | |
152 | { | |
153 | - unsigned long vaddr = (unsigned long __force)vaddr_ptr; | |
154 | - void *base = ghes_ioremap_area->addr; | |
155 | - | |
156 | - BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_NMI_PAGE(base)); | |
157 | - unmap_kernel_range_noflush(vaddr, PAGE_SIZE); | |
158 | - arch_apei_flush_tlb_one(vaddr); | |
159 | + clear_fixmap(FIX_APEI_GHES_NMI); | |
160 | } | |
161 | ||
162 | -static void ghes_iounmap_irq(void __iomem *vaddr_ptr) | |
163 | +static void ghes_iounmap_irq(void) | |
164 | { | |
165 | - unsigned long vaddr = (unsigned long __force)vaddr_ptr; | |
166 | - void *base = ghes_ioremap_area->addr; | |
167 | - | |
168 | - BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_IRQ_PAGE(base)); | |
169 | - unmap_kernel_range_noflush(vaddr, PAGE_SIZE); | |
170 | - arch_apei_flush_tlb_one(vaddr); | |
171 | + clear_fixmap(FIX_APEI_GHES_IRQ); | |
172 | } | |
173 | ||
174 | static int ghes_estatus_pool_init(void) | |
175 | @@ -361,10 +345,10 @@ static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, | |
176 | paddr += trunk; | |
177 | buffer += trunk; | |
178 | if (in_nmi) { | |
179 | - ghes_iounmap_nmi(vaddr); | |
180 | + ghes_iounmap_nmi(); | |
181 | raw_spin_unlock(&ghes_ioremap_lock_nmi); | |
182 | } else { | |
183 | - ghes_iounmap_irq(vaddr); | |
184 | + ghes_iounmap_irq(); | |
185 | spin_unlock_irqrestore(&ghes_ioremap_lock_irq, flags); | |
186 | } | |
187 | } | |
188 | -- | |
189 | 2.14.2 | |
190 |