1 |
From f24c4d478013d82bd1b943df566fff3561d52864 Mon Sep 17 00:00:00 2001 |
2 |
From: Ard Biesheuvel <ard.biesheuvel@linaro.org> |
3 |
Date: Tue, 2 Jan 2018 17:21:10 +0000 |
4 |
Subject: efi/capsule-loader: Reinstate virtual capsule mapping |
5 |
|
6 |
From: Ard Biesheuvel <ard.biesheuvel@linaro.org> |
7 |
|
8 |
commit f24c4d478013d82bd1b943df566fff3561d52864 upstream. |
9 |
|
10 |
Commit: |
11 |
|
12 |
82c3768b8d68 ("efi/capsule-loader: Use a cached copy of the capsule header") |
13 |
|
14 |
... refactored the capsule loading code that maps the capsule header, |
15 |
to avoid having to map it several times. |
16 |
|
17 |
However, as it turns out, the vmap() call we ended up removing did not |
18 |
just map the header, but the entire capsule image, and dropping this |
19 |
virtual mapping breaks capsules that are processed by the firmware |
20 |
immediately (i.e., without a reboot). |
21 |
|
22 |
Unfortunately, that change was part of a larger refactor that allowed |
23 |
a quirk to be implemented for Quark, which has a non-standard memory |
24 |
layout for capsules, and we have slightly painted ourselves into a |
25 |
corner by allowing quirk code to mangle the capsule header and memory |
26 |
layout. |
27 |
|
28 |
So we need to fix this without breaking Quark. Fortunately, Quark does |
29 |
not appear to care about the virtual mapping, and so we can simply |
30 |
do a partial revert of commit: |
31 |
|
32 |
2a457fb31df6 ("efi/capsule-loader: Use page addresses rather than struct page pointers") |
33 |
|
34 |
... and create a vmap() mapping of the entire capsule (including header) |
35 |
based on the reinstated struct page array, unless running on Quark, in |
36 |
which case we pass the capsule header copy as before. |
37 |
|
38 |
Reported-by: Ge Song <ge.song@hxt-semitech.com> |
39 |
Tested-by: Bryan O'Donoghue <pure.logic@nexus-software.ie> |
40 |
Tested-by: Ge Song <ge.song@hxt-semitech.com> |
41 |
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> |
42 |
Cc: Dave Young <dyoung@redhat.com> |
43 |
Cc: Linus Torvalds <torvalds@linux-foundation.org> |
44 |
Cc: Matt Fleming <matt@codeblueprint.co.uk> |
45 |
Cc: Peter Zijlstra <peterz@infradead.org> |
46 |
Cc: Thomas Gleixner <tglx@linutronix.de> |
47 |
Cc: linux-efi@vger.kernel.org |
48 |
Fixes: 82c3768b8d68 ("efi/capsule-loader: Use a cached copy of the capsule header") |
49 |
Link: http://lkml.kernel.org/r/20180102172110.17018-3-ard.biesheuvel@linaro.org |
50 |
Signed-off-by: Ingo Molnar <mingo@kernel.org> |
51 |
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
52 |
|
53 |
--- |
54 |
arch/x86/platform/efi/quirks.c | 13 +++++++++ |
55 |
drivers/firmware/efi/capsule-loader.c | 45 +++++++++++++++++++++++++++------- |
56 |
include/linux/efi.h | 4 ++- |
57 |
3 files changed, 52 insertions(+), 10 deletions(-) |
58 |
|
59 |
--- a/arch/x86/platform/efi/quirks.c |
60 |
+++ b/arch/x86/platform/efi/quirks.c |
61 |
@@ -592,7 +592,18 @@ static int qrk_capsule_setup_info(struct |
62 |
/* |
63 |
* Update the first page pointer to skip over the CSH header. |
64 |
*/ |
65 |
- cap_info->pages[0] += csh->headersize; |
66 |
+ cap_info->phys[0] += csh->headersize; |
67 |
+ |
68 |
+ /* |
69 |
+ * cap_info->capsule should point at a virtual mapping of the entire |
70 |
+ * capsule, starting at the capsule header. Our image has the Quark |
71 |
+ * security header prepended, so we cannot rely on the default vmap() |
72 |
+ * mapping created by the generic capsule code. |
73 |
+ * Given that the Quark firmware does not appear to care about the |
74 |
+ * virtual mapping, let's just point cap_info->capsule at our copy |
75 |
+ * of the capsule header. |
76 |
+ */ |
77 |
+ cap_info->capsule = &cap_info->header; |
78 |
|
79 |
return 1; |
80 |
} |
81 |
--- a/drivers/firmware/efi/capsule-loader.c |
82 |
+++ b/drivers/firmware/efi/capsule-loader.c |
83 |
@@ -20,10 +20,6 @@ |
84 |
|
85 |
#define NO_FURTHER_WRITE_ACTION -1 |
86 |
|
87 |
-#ifndef phys_to_page |
88 |
-#define phys_to_page(x) pfn_to_page((x) >> PAGE_SHIFT) |
89 |
-#endif |
90 |
- |
91 |
/** |
92 |
* efi_free_all_buff_pages - free all previous allocated buffer pages |
93 |
* @cap_info: pointer to current instance of capsule_info structure |
94 |
@@ -35,7 +31,7 @@ |
95 |
static void efi_free_all_buff_pages(struct capsule_info *cap_info) |
96 |
{ |
97 |
while (cap_info->index > 0) |
98 |
- __free_page(phys_to_page(cap_info->pages[--cap_info->index])); |
99 |
+ __free_page(cap_info->pages[--cap_info->index]); |
100 |
|
101 |
cap_info->index = NO_FURTHER_WRITE_ACTION; |
102 |
} |
103 |
@@ -71,6 +67,14 @@ int __efi_capsule_setup_info(struct caps |
104 |
|
105 |
cap_info->pages = temp_page; |
106 |
|
107 |
+ temp_page = krealloc(cap_info->phys, |
108 |
+ pages_needed * sizeof(phys_addr_t *), |
109 |
+ GFP_KERNEL | __GFP_ZERO); |
110 |
+ if (!temp_page) |
111 |
+ return -ENOMEM; |
112 |
+ |
113 |
+ cap_info->phys = temp_page; |
114 |
+ |
115 |
return 0; |
116 |
} |
117 |
|
118 |
@@ -105,9 +109,24 @@ int __weak efi_capsule_setup_info(struct |
119 |
**/ |
120 |
static ssize_t efi_capsule_submit_update(struct capsule_info *cap_info) |
121 |
{ |
122 |
+ bool do_vunmap = false; |
123 |
int ret; |
124 |
|
125 |
- ret = efi_capsule_update(&cap_info->header, cap_info->pages); |
126 |
+ /* |
127 |
+ * cap_info->capsule may have been assigned already by a quirk |
128 |
+ * handler, so only overwrite it if it is NULL |
129 |
+ */ |
130 |
+ if (!cap_info->capsule) { |
131 |
+ cap_info->capsule = vmap(cap_info->pages, cap_info->index, |
132 |
+ VM_MAP, PAGE_KERNEL); |
133 |
+ if (!cap_info->capsule) |
134 |
+ return -ENOMEM; |
135 |
+ do_vunmap = true; |
136 |
+ } |
137 |
+ |
138 |
+ ret = efi_capsule_update(cap_info->capsule, cap_info->phys); |
139 |
+ if (do_vunmap) |
140 |
+ vunmap(cap_info->capsule); |
141 |
if (ret) { |
142 |
pr_err("capsule update failed\n"); |
143 |
return ret; |
144 |
@@ -165,10 +184,12 @@ static ssize_t efi_capsule_write(struct |
145 |
goto failed; |
146 |
} |
147 |
|
148 |
- cap_info->pages[cap_info->index++] = page_to_phys(page); |
149 |
+ cap_info->pages[cap_info->index] = page; |
150 |
+ cap_info->phys[cap_info->index] = page_to_phys(page); |
151 |
cap_info->page_bytes_remain = PAGE_SIZE; |
152 |
+ cap_info->index++; |
153 |
} else { |
154 |
- page = phys_to_page(cap_info->pages[cap_info->index - 1]); |
155 |
+ page = cap_info->pages[cap_info->index - 1]; |
156 |
} |
157 |
|
158 |
kbuff = kmap(page); |
159 |
@@ -252,6 +273,7 @@ static int efi_capsule_release(struct in |
160 |
struct capsule_info *cap_info = file->private_data; |
161 |
|
162 |
kfree(cap_info->pages); |
163 |
+ kfree(cap_info->phys); |
164 |
kfree(file->private_data); |
165 |
file->private_data = NULL; |
166 |
return 0; |
167 |
@@ -280,6 +302,13 @@ static int efi_capsule_open(struct inode |
168 |
kfree(cap_info); |
169 |
return -ENOMEM; |
170 |
} |
171 |
+ |
172 |
+ cap_info->phys = kzalloc(sizeof(void *), GFP_KERNEL); |
173 |
+ if (!cap_info->phys) { |
174 |
+ kfree(cap_info->pages); |
175 |
+ kfree(cap_info); |
176 |
+ return -ENOMEM; |
177 |
+ } |
178 |
|
179 |
file->private_data = cap_info; |
180 |
|
181 |
--- a/include/linux/efi.h |
182 |
+++ b/include/linux/efi.h |
183 |
@@ -140,11 +140,13 @@ struct efi_boot_memmap { |
184 |
|
185 |
struct capsule_info { |
186 |
efi_capsule_header_t header; |
187 |
+ efi_capsule_header_t *capsule; |
188 |
int reset_type; |
189 |
long index; |
190 |
size_t count; |
191 |
size_t total_size; |
192 |
- phys_addr_t *pages; |
193 |
+ struct page **pages; |
194 |
+ phys_addr_t *phys; |
195 |
size_t page_bytes_remain; |
196 |
}; |
197 |
|