aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorBorislav Petkov <borislav.petkov@amd.com>2011-12-02 12:02:17 -0500
committerBorislav Petkov <bp@amd64.org>2011-12-14 06:46:51 -0500
commitbe62adb492943ce2525ff19401b389a85006ad15 (patch)
tree470fa32a41f95430d1c918f411415ae3343334ad /arch
parent96b0ee4588036b6fa7cf38c17a9e40531241e895 (diff)
x86, microcode, AMD: Simplify ucode verification
Basically, what we did until now is take out a chunk of the firmware image, vmalloc space for it and inspect it before application. And repeat. This patch changes all that so that we look at each ucode patch from the firmware image, check it for sanity and copy it to local buffer for application only once and if it passes all checks. Thus, vmalloc-ing for each piece is gone, we can do proper size checking only of the patch which is destined for the CPU of the current machine instead of each single patch, which is clearly wrong. Oh yeah, simplify and cleanup the code while at it, along with adding comments as to what actually happens. Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/kernel/microcode_amd.c179
1 files changed, 93 insertions, 86 deletions
diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c
index 9129c6981c5..384990d2c54 100644
--- a/arch/x86/kernel/microcode_amd.c
+++ b/arch/x86/kernel/microcode_amd.c
@@ -89,27 +89,76 @@ static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig)
89 return 0; 89 return 0;
90} 90}
91 91
92static int get_matching_microcode(int cpu, struct microcode_header_amd *mc_hdr, 92static unsigned int verify_ucode_size(int cpu, u32 patch_size,
93 int rev) 93 unsigned int size)
94{ 94{
95 unsigned int current_cpu_id; 95 struct cpuinfo_x86 *c = &cpu_data(cpu);
96 u16 equiv_cpu_id = 0; 96 u32 max_size;
97 unsigned int i = 0; 97
98#define F1XH_MPB_MAX_SIZE 2048
99#define F14H_MPB_MAX_SIZE 1824
100#define F15H_MPB_MAX_SIZE 4096
101
102 switch (c->x86) {
103 case 0x14:
104 max_size = F14H_MPB_MAX_SIZE;
105 break;
106 case 0x15:
107 max_size = F15H_MPB_MAX_SIZE;
108 break;
109 default:
110 max_size = F1XH_MPB_MAX_SIZE;
111 break;
112 }
113
114 if (patch_size > min_t(u32, size, max_size)) {
115 pr_err("patch size mismatch\n");
116 return 0;
117 }
118
119 return patch_size;
120}
121
122static u16 find_equiv_id(void)
123{
124 unsigned int current_cpu_id, i = 0;
98 125
99 BUG_ON(equiv_cpu_table == NULL); 126 BUG_ON(equiv_cpu_table == NULL);
127
100 current_cpu_id = cpuid_eax(0x00000001); 128 current_cpu_id = cpuid_eax(0x00000001);
101 129
102 while (equiv_cpu_table[i].installed_cpu != 0) { 130 while (equiv_cpu_table[i].installed_cpu != 0) {
103 if (current_cpu_id == equiv_cpu_table[i].installed_cpu) { 131 if (current_cpu_id == equiv_cpu_table[i].installed_cpu)
104 equiv_cpu_id = equiv_cpu_table[i].equiv_cpu; 132 return equiv_cpu_table[i].equiv_cpu;
105 break; 133
106 }
107 i++; 134 i++;
108 } 135 }
136 return 0;
137}
109 138
139/*
140 * we signal a good patch is found by returning its size > 0
141 */
142static int get_matching_microcode(int cpu, const u8 *ucode_ptr,
143 unsigned int leftover_size, int rev,
144 unsigned int *current_size)
145{
146 struct microcode_header_amd *mc_hdr;
147 unsigned int actual_size;
148 u16 equiv_cpu_id;
149
150 /* size of the current patch we're staring at */
151 *current_size = *(u32 *)(ucode_ptr + 4) + SECTION_HDR_SIZE;
152
153 equiv_cpu_id = find_equiv_id();
110 if (!equiv_cpu_id) 154 if (!equiv_cpu_id)
111 return 0; 155 return 0;
112 156
157 /*
158 * let's look at the patch header itself now
159 */
160 mc_hdr = (struct microcode_header_amd *)(ucode_ptr + SECTION_HDR_SIZE);
161
113 if (mc_hdr->processor_rev_id != equiv_cpu_id) 162 if (mc_hdr->processor_rev_id != equiv_cpu_id)
114 return 0; 163 return 0;
115 164
@@ -123,7 +172,20 @@ static int get_matching_microcode(int cpu, struct microcode_header_amd *mc_hdr,
123 if (mc_hdr->patch_id <= rev) 172 if (mc_hdr->patch_id <= rev)
124 return 0; 173 return 0;
125 174
126 return 1; 175 /*
176 * now that the header looks sane, verify its size
177 */
178 actual_size = verify_ucode_size(cpu, *current_size, leftover_size);
179 if (!actual_size)
180 return 0;
181
182 /* clear the patch buffer */
183 memset(patch, 0, PAGE_SIZE);
184
185 /* all looks ok, get the binary patch */
186 get_ucode_data(patch, ucode_ptr + SECTION_HDR_SIZE, actual_size);
187
188 return actual_size;
127} 189}
128 190
129static int apply_microcode_amd(int cpu) 191static int apply_microcode_amd(int cpu)
@@ -158,63 +220,6 @@ static int apply_microcode_amd(int cpu)
158 return 0; 220 return 0;
159} 221}
160 222
161static unsigned int verify_ucode_size(int cpu, const u8 *buf, unsigned int size)
162{
163 struct cpuinfo_x86 *c = &cpu_data(cpu);
164 u32 max_size, actual_size;
165
166#define F1XH_MPB_MAX_SIZE 2048
167#define F14H_MPB_MAX_SIZE 1824
168#define F15H_MPB_MAX_SIZE 4096
169
170 switch (c->x86) {
171 case 0x14:
172 max_size = F14H_MPB_MAX_SIZE;
173 break;
174 case 0x15:
175 max_size = F15H_MPB_MAX_SIZE;
176 break;
177 default:
178 max_size = F1XH_MPB_MAX_SIZE;
179 break;
180 }
181
182 actual_size = *(u32 *)(buf + 4);
183
184 if (actual_size + SECTION_HDR_SIZE > size || actual_size > max_size) {
185 pr_err("section size mismatch\n");
186 return 0;
187 }
188
189 return actual_size;
190}
191
192static struct microcode_header_amd *
193get_next_ucode(int cpu, const u8 *buf, unsigned int size, unsigned int *mc_size)
194{
195 struct microcode_header_amd *mc = NULL;
196 unsigned int actual_size = 0;
197
198 if (*(u32 *)buf != UCODE_UCODE_TYPE) {
199 pr_err("invalid type field in container file section header\n");
200 goto out;
201 }
202
203 actual_size = verify_ucode_size(cpu, buf, size);
204 if (!actual_size)
205 goto out;
206
207 mc = vzalloc(actual_size);
208 if (!mc)
209 goto out;
210
211 get_ucode_data(mc, buf + SECTION_HDR_SIZE, actual_size);
212 *mc_size = actual_size + SECTION_HDR_SIZE;
213
214out:
215 return mc;
216}
217
218static int install_equiv_cpu_table(const u8 *buf) 223static int install_equiv_cpu_table(const u8 *buf)
219{ 224{
220 unsigned int *ibuf = (unsigned int *)buf; 225 unsigned int *ibuf = (unsigned int *)buf;
@@ -250,36 +255,38 @@ generic_load_microcode(int cpu, const u8 *data, size_t size)
250{ 255{
251 struct ucode_cpu_info *uci = ucode_cpu_info + cpu; 256 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
252 struct microcode_header_amd *mc_hdr = NULL; 257 struct microcode_header_amd *mc_hdr = NULL;
253 unsigned int mc_size, leftover; 258 unsigned int mc_size, leftover, current_size = 0;
254 int offset; 259 int offset;
255 const u8 *ucode_ptr = data; 260 const u8 *ucode_ptr = data;
256 void *new_mc = NULL; 261 void *new_mc = NULL;
257 unsigned int new_rev = uci->cpu_sig.rev; 262 unsigned int new_rev = uci->cpu_sig.rev;
258 enum ucode_state state = UCODE_OK; 263 enum ucode_state state = UCODE_ERROR;
259 264
260 offset = install_equiv_cpu_table(ucode_ptr); 265 offset = install_equiv_cpu_table(ucode_ptr);
261 if (offset < 0) { 266 if (offset < 0) {
262 pr_err("failed to create equivalent cpu table\n"); 267 pr_err("failed to create equivalent cpu table\n");
263 return UCODE_ERROR; 268 goto out;
264 } 269 }
265
266 ucode_ptr += offset; 270 ucode_ptr += offset;
267 leftover = size - offset; 271 leftover = size - offset;
268 272
269 while (leftover) { 273 if (*(u32 *)ucode_ptr != UCODE_UCODE_TYPE) {
270 mc_hdr = get_next_ucode(cpu, ucode_ptr, leftover, &mc_size); 274 pr_err("invalid type field in container file section header\n");
271 if (!mc_hdr) 275 goto free_table;
272 break; 276 }
273 277
274 if (get_matching_microcode(cpu, mc_hdr, new_rev)) { 278 while (leftover) {
275 vfree(new_mc); 279 mc_size = get_matching_microcode(cpu, ucode_ptr, leftover,
280 new_rev, &current_size);
281 if (mc_size) {
282 mc_hdr = patch;
283 new_mc = patch;
276 new_rev = mc_hdr->patch_id; 284 new_rev = mc_hdr->patch_id;
277 new_mc = mc_hdr; 285 leftover -= mc_size;
278 } else 286 } else {
279 vfree(mc_hdr); 287 ucode_ptr += current_size;
280 288 leftover -= current_size;
281 ucode_ptr += mc_size; 289 }
282 leftover -= mc_size;
283 } 290 }
284 291
285 if (!new_mc) { 292 if (!new_mc) {
@@ -288,18 +295,19 @@ generic_load_microcode(int cpu, const u8 *data, size_t size)
288 } 295 }
289 296
290 if (!leftover) { 297 if (!leftover) {
291 vfree(uci->mc);
292 uci->mc = new_mc; 298 uci->mc = new_mc;
299 state = UCODE_OK;
293 pr_debug("CPU%d update ucode (0x%08x -> 0x%08x)\n", 300 pr_debug("CPU%d update ucode (0x%08x -> 0x%08x)\n",
294 cpu, uci->cpu_sig.rev, new_rev); 301 cpu, uci->cpu_sig.rev, new_rev);
295 } else { 302 } else {
296 vfree(new_mc); 303 new_mc = NULL;
297 state = UCODE_ERROR; 304 state = UCODE_ERROR;
298 } 305 }
299 306
300free_table: 307free_table:
301 free_equiv_cpu_table(); 308 free_equiv_cpu_table();
302 309
310out:
303 return state; 311 return state;
304} 312}
305 313
@@ -340,7 +348,6 @@ static void microcode_fini_cpu_amd(int cpu)
340{ 348{
341 struct ucode_cpu_info *uci = ucode_cpu_info + cpu; 349 struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
342 350
343 vfree(uci->mc);
344 uci->mc = NULL; 351 uci->mc = NULL;
345} 352}
346 353