1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2018 The FreeBSD Foundation
5 *
6 * This software was developed by Mark Johnston under sponsorship from
7 * the FreeBSD Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/param.h>
32 #include <sys/cpuset.h>
33 #include <sys/kernel.h>
34 #include <sys/linker.h>
35 #include <sys/malloc.h>
36 #include <sys/pcpu.h>
37 #include <sys/smp.h>
38 #include <sys/systm.h>
39
40 #include <machine/atomic.h>
41 #include <machine/cpufunc.h>
42 #include <x86/specialreg.h>
43 #include <machine/stdarg.h>
44 #include <x86/ucode.h>
45 #include <x86/x86_smp.h>
46
47 #include <vm/vm.h>
48 #include <vm/pmap.h>
49 #include <vm/vm_extern.h>
50 #include <vm/vm_kern.h>
51 #include <vm/vm_param.h>
52
53 static const void *ucode_intel_match(const uint8_t *data, size_t *len);
54 static int ucode_intel_verify(const struct ucode_intel_header *hdr,
55 size_t resid);
56
57 static const void *ucode_amd_match(const uint8_t *data, size_t *len);
58
59 static struct ucode_ops {
60 const char *vendor;
61 int (*load)(const void *, bool, uint64_t *, uint64_t *);
62 const void *(*match)(const uint8_t *, size_t *);
63 } loaders[] = {
64 {
65 .vendor = INTEL_VENDOR_ID,
66 .load = ucode_intel_load,
67 .match = ucode_intel_match,
68 },
69 {
70 .vendor = AMD_VENDOR_ID,
71 .load = ucode_amd_load,
72 .match = ucode_amd_match,
73 },
74 };
75
76 /* Selected microcode update data. */
77 static const void *early_ucode_data;
78 static const void *ucode_data;
79 static struct ucode_ops *ucode_loader;
80
81 /* Variables used for reporting success or failure. */
82 enum {
83 NO_ERROR,
84 NO_MATCH,
85 VERIFICATION_FAILED,
86 } ucode_error = NO_ERROR;
87 static uint64_t ucode_nrev, ucode_orev;
88
89 static void
log_msg(void * arg __unused)90 log_msg(void *arg __unused)
91 {
92
93 if (ucode_nrev != 0) {
94 printf("CPU microcode: updated from %#jx to %#jx\n",
95 (uintmax_t)ucode_orev, (uintmax_t)ucode_nrev);
96 return;
97 }
98
99 switch (ucode_error) {
100 case NO_MATCH:
101 printf("CPU microcode: no matching update found\n");
102 break;
103 case VERIFICATION_FAILED:
104 printf("CPU microcode: microcode verification failed\n");
105 break;
106 default:
107 break;
108 }
109 }
110 SYSINIT(ucode_log, SI_SUB_CPU, SI_ORDER_FIRST, log_msg, NULL);
111
112 int
ucode_intel_load(const void * data,bool unsafe,uint64_t * nrevp,uint64_t * orevp)113 ucode_intel_load(const void *data, bool unsafe, uint64_t *nrevp, uint64_t *orevp)
114 {
115 uint64_t nrev, orev;
116 uint32_t cpuid[4];
117
118 orev = rdmsr(MSR_BIOS_SIGN) >> 32;
119
120 /*
121 * Perform update. Flush caches first to work around seemingly
122 * undocumented errata applying to some Broadwell CPUs.
123 */
124 wbinvd();
125 if (unsafe)
126 wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data);
127 else
128 wrmsr(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data);
129 wrmsr(MSR_BIOS_SIGN, 0);
130
131 /*
132 * Serialize instruction flow.
133 */
134 do_cpuid(0, cpuid);
135
136 /*
137 * Verify that the microcode revision changed.
138 */
139 nrev = rdmsr(MSR_BIOS_SIGN) >> 32;
140 if (nrevp != NULL)
141 *nrevp = nrev;
142 if (orevp != NULL)
143 *orevp = orev;
144 if (nrev <= orev)
145 return (EEXIST);
146 return (0);
147 }
148
149 static int
ucode_intel_verify(const struct ucode_intel_header * hdr,size_t resid)150 ucode_intel_verify(const struct ucode_intel_header *hdr, size_t resid)
151 {
152 const uint32_t *data;
153 uint32_t cksum, size;
154 int i;
155
156 if (resid < sizeof(struct ucode_intel_header))
157 return (1);
158 size = hdr->total_size;
159 if (size == 0)
160 size = UCODE_INTEL_DEFAULT_DATA_SIZE +
161 sizeof(struct ucode_intel_header);
162
163 if (hdr->header_version != 1)
164 return (1);
165 if (size % 16 != 0)
166 return (1);
167 if (resid < size)
168 return (1);
169
170 cksum = 0;
171 data = (const uint32_t *)hdr;
172 for (i = 0; i < size / sizeof(uint32_t); i++)
173 cksum += data[i];
174 if (cksum != 0)
175 return (1);
176 return (0);
177 }
178
179 static const void *
ucode_intel_match(const uint8_t * data,size_t * len)180 ucode_intel_match(const uint8_t *data, size_t *len)
181 {
182 const struct ucode_intel_header *hdr;
183 const struct ucode_intel_extsig_table *table;
184 const struct ucode_intel_extsig *entry;
185 uint64_t platformid;
186 size_t resid;
187 uint32_t data_size, flags, regs[4], sig, total_size;
188 int i;
189
190 do_cpuid(1, regs);
191 sig = regs[0];
192
193 platformid = rdmsr(MSR_IA32_PLATFORM_ID);
194 flags = 1 << ((platformid >> 50) & 0x7);
195
196 for (resid = *len; resid > 0; data += total_size, resid -= total_size) {
197 hdr = (const struct ucode_intel_header *)data;
198 if (ucode_intel_verify(hdr, resid) != 0) {
199 ucode_error = VERIFICATION_FAILED;
200 break;
201 }
202
203 data_size = hdr->data_size;
204 total_size = hdr->total_size;
205 if (data_size == 0)
206 data_size = UCODE_INTEL_DEFAULT_DATA_SIZE;
207 if (total_size == 0)
208 total_size = UCODE_INTEL_DEFAULT_DATA_SIZE +
209 sizeof(struct ucode_intel_header);
210 if (data_size > total_size + sizeof(struct ucode_intel_header))
211 table = (const struct ucode_intel_extsig_table *)
212 ((const uint8_t *)(hdr + 1) + data_size);
213 else
214 table = NULL;
215
216 if (hdr->processor_signature == sig) {
217 if ((hdr->processor_flags & flags) != 0) {
218 *len = data_size;
219 return (hdr + 1);
220 }
221 } else if (table != NULL) {
222 for (i = 0; i < table->signature_count; i++) {
223 entry = &table->entries[i];
224 if (entry->processor_signature == sig &&
225 (entry->processor_flags & flags) != 0) {
226 *len = data_size;
227 return (hdr + 1);
228 }
229 }
230 }
231 }
232 return (NULL);
233 }
234
235 int
ucode_amd_load(const void * data,bool unsafe,uint64_t * nrevp,uint64_t * orevp)236 ucode_amd_load(const void *data, bool unsafe, uint64_t *nrevp, uint64_t *orevp)
237 {
238 uint64_t nrev, orev;
239 uint32_t cpuid[4];
240
241 orev = rdmsr(MSR_BIOS_SIGN);
242
243 /*
244 * Perform update.
245 */
246 if (unsafe)
247 wrmsr_safe(MSR_K8_UCODE_UPDATE, (uint64_t)(uintptr_t)data);
248 else
249 wrmsr(MSR_K8_UCODE_UPDATE, (uint64_t)(uintptr_t)data);
250
251 /*
252 * Serialize instruction flow.
253 */
254 do_cpuid(0, cpuid);
255
256 /*
257 * Verify that the microcode revision changed.
258 */
259 nrev = rdmsr(MSR_BIOS_SIGN);
260 if (nrevp != NULL)
261 *nrevp = nrev;
262 if (orevp != NULL)
263 *orevp = orev;
264 if (nrev <= orev)
265 return (EEXIST);
266 return (0);
267
268 }
269
270 static const void *
ucode_amd_match(const uint8_t * data,size_t * len)271 ucode_amd_match(const uint8_t *data, size_t *len)
272 {
273 uint32_t signature, revision;
274 uint32_t regs[4];
275
276 do_cpuid(1, regs);
277 signature = regs[0];
278 revision = rdmsr(MSR_BIOS_SIGN);
279
280 return (ucode_amd_find("loader blob", signature, revision, data, *len, len));
281 }
282
283 /*
284 * Release any memory backing unused microcode blobs back to the system.
285 * We copy the selected update and free the entire microcode file.
286 */
287 static void
ucode_release(void * arg __unused)288 ucode_release(void *arg __unused)
289 {
290 char *name, *type;
291 caddr_t file;
292 int release;
293
294 if (early_ucode_data == NULL)
295 return;
296 release = 1;
297 TUNABLE_INT_FETCH("debug.ucode.release", &release);
298 if (!release)
299 return;
300
301 restart:
302 file = 0;
303 for (;;) {
304 file = preload_search_next_name(file);
305 if (file == 0)
306 break;
307 type = (char *)preload_search_info(file, MODINFO_TYPE);
308 if (type == NULL || strcmp(type, "cpu_microcode") != 0)
309 continue;
310
311 name = preload_search_info(file, MODINFO_NAME);
312 preload_delete_name(name);
313 goto restart;
314 }
315 }
316 SYSINIT(ucode_release, SI_SUB_SMP + 1, SI_ORDER_ANY, ucode_release, NULL);
317
318 void
ucode_load_ap(int cpu)319 ucode_load_ap(int cpu)
320 {
321 #ifdef SMP
322 KASSERT(cpu_info[cpu_apic_ids[cpu]].cpu_present,
323 ("cpu %d not present", cpu));
324
325 if (cpu_info[cpu_apic_ids[cpu]].cpu_hyperthread)
326 return;
327 #endif
328
329 if (ucode_data != NULL)
330 (void)ucode_loader->load(ucode_data, false, NULL, NULL);
331 }
332
333 static void *
map_ucode(uintptr_t free,size_t len)334 map_ucode(uintptr_t free, size_t len)
335 {
336 #ifdef __i386__
337 uintptr_t va;
338
339 for (va = free; va < free + len; va += PAGE_SIZE)
340 pmap_kenter(va, (vm_paddr_t)va);
341 #else
342 (void)len;
343 #endif
344 return ((void *)free);
345 }
346
347 static void
unmap_ucode(uintptr_t free,size_t len)348 unmap_ucode(uintptr_t free, size_t len)
349 {
350 #ifdef __i386__
351 uintptr_t va;
352
353 for (va = free; va < free + len; va += PAGE_SIZE)
354 pmap_kremove(va);
355 #else
356 (void)free;
357 (void)len;
358 #endif
359 }
360
361 /*
362 * Search for an applicable microcode update, and load it. APs will load the
363 * selected update once they come online.
364 *
365 * "free" is the address of the next free physical page. If a microcode update
366 * is selected, it will be copied to this region prior to loading in order to
367 * satisfy alignment requirements.
368 */
369 size_t
ucode_load_bsp(uintptr_t free)370 ucode_load_bsp(uintptr_t free)
371 {
372 union {
373 uint32_t regs[4];
374 char vendor[13];
375 } cpuid;
376 const uint8_t *fileaddr, *match;
377 uint8_t *addr;
378 char *type;
379 uint64_t nrev, orev;
380 caddr_t file;
381 size_t i, len;
382 int error;
383
384 KASSERT(free % PAGE_SIZE == 0, ("unaligned boundary %p", (void *)free));
385
386 do_cpuid(0, cpuid.regs);
387 cpuid.regs[0] = cpuid.regs[1];
388 cpuid.regs[1] = cpuid.regs[3];
389 cpuid.vendor[12] = '\0';
390 for (i = 0; i < nitems(loaders); i++)
391 if (strcmp(cpuid.vendor, loaders[i].vendor) == 0) {
392 ucode_loader = &loaders[i];
393 break;
394 }
395 if (ucode_loader == NULL)
396 return (0);
397
398 file = 0;
399 fileaddr = match = NULL;
400 for (;;) {
401 file = preload_search_next_name(file);
402 if (file == 0)
403 break;
404 type = (char *)preload_search_info(file, MODINFO_TYPE);
405 if (type == NULL || strcmp(type, "cpu_microcode") != 0)
406 continue;
407
408 fileaddr = preload_fetch_addr(file);
409 len = preload_fetch_size(file);
410 match = ucode_loader->match(fileaddr, &len);
411 if (match != NULL) {
412 addr = map_ucode(free, len);
413 /* We can't use memcpy() before ifunc resolution. */
414 memcpy_early(addr, match, len);
415 match = addr;
416
417 error = ucode_loader->load(match, false, &nrev, &orev);
418 if (error == 0) {
419 ucode_data = early_ucode_data = match;
420 ucode_nrev = nrev;
421 ucode_orev = orev;
422 return (len);
423 }
424 unmap_ucode(free, len);
425 }
426 }
427 if (fileaddr != NULL && ucode_error == NO_ERROR)
428 ucode_error = NO_MATCH;
429 return (0);
430 }
431
432 /*
433 * Reload microcode following an ACPI resume.
434 */
435 void
ucode_reload(void)436 ucode_reload(void)
437 {
438
439 ucode_load_ap(PCPU_GET(cpuid));
440 }
441
442 /*
443 * Replace an existing microcode update.
444 */
445 void *
ucode_update(void * newdata)446 ucode_update(void *newdata)
447 {
448
449 newdata = (void *)atomic_swap_ptr((void *)&ucode_data,
450 (uintptr_t)newdata);
451 if (newdata == early_ucode_data)
452 newdata = NULL;
453 return (newdata);
454 }
455