xref: /freebsd/sys/x86/x86/ucode.c (revision 83804499b72405475027e670690d4cdbada46090)
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/stdarg.h>
39 #include <sys/systm.h>
40 
41 #include <machine/atomic.h>
42 #include <machine/cpufunc.h>
43 #include <x86/specialreg.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,
281 	    len));
282 }
283 
284 /*
285  * Release any memory backing unused microcode blobs back to the system.
286  * We copy the selected update and free the entire microcode file.
287  */
288 static void
ucode_release(void * arg __unused)289 ucode_release(void *arg __unused)
290 {
291 	char *name, *type;
292 	caddr_t file;
293 	int release;
294 
295 	if (early_ucode_data == NULL)
296 		return;
297 	release = 1;
298 	TUNABLE_INT_FETCH("debug.ucode.release", &release);
299 	if (!release)
300 		return;
301 
302 restart:
303 	file = 0;
304 	for (;;) {
305 		file = preload_search_next_name(file);
306 		if (file == 0)
307 			break;
308 		type = (char *)preload_search_info(file, MODINFO_TYPE);
309 		if (type == NULL || strcmp(type, "cpu_microcode") != 0)
310 			continue;
311 
312 		name = preload_search_info(file, MODINFO_NAME);
313 		preload_delete_name(name);
314 		goto restart;
315 	}
316 }
317 SYSINIT(ucode_release, SI_SUB_SMP + 1, SI_ORDER_ANY, ucode_release, NULL);
318 
319 void
ucode_load_ap(int cpu)320 ucode_load_ap(int cpu)
321 {
322 #ifdef SMP
323 	KASSERT(cpu_info[cpu_apic_ids[cpu]].cpu_present,
324 	    ("cpu %d not present", cpu));
325 
326 	if (cpu_info[cpu_apic_ids[cpu]].cpu_hyperthread)
327 		return;
328 #endif
329 
330 	if (ucode_data != NULL)
331 		(void)ucode_loader->load(ucode_data, false, NULL, NULL);
332 }
333 
334 static void *
map_ucode(uintptr_t free,size_t len)335 map_ucode(uintptr_t free, size_t len)
336 {
337 #ifdef __i386__
338 	uintptr_t va;
339 
340 	for (va = free; va < free + len; va += PAGE_SIZE)
341 		pmap_kenter(va, (vm_paddr_t)va);
342 #else
343 	(void)len;
344 #endif
345 	return ((void *)free);
346 }
347 
348 static void
unmap_ucode(uintptr_t free,size_t len)349 unmap_ucode(uintptr_t free, size_t len)
350 {
351 #ifdef __i386__
352 	uintptr_t va;
353 
354 	for (va = free; va < free + len; va += PAGE_SIZE)
355 		pmap_kremove(va);
356 #else
357 	(void)free;
358 	(void)len;
359 #endif
360 }
361 
362 /*
363  * Search for an applicable microcode update, and load it.  APs will load the
364  * selected update once they come online.
365  *
366  * "free" is the address of the next free physical page.  If a microcode update
367  * is selected, it will be copied to this region prior to loading in order to
368  * satisfy alignment requirements.
369  */
370 size_t
ucode_load_bsp(uintptr_t free)371 ucode_load_bsp(uintptr_t free)
372 {
373 	union {
374 		uint32_t regs[4];
375 		char vendor[13];
376 	} cpuid;
377 	const uint8_t *fileaddr, *match;
378 	uint8_t *addr;
379 	char *type;
380 	uint64_t nrev, orev;
381 	caddr_t file;
382 	size_t i, len;
383 	int error;
384 
385 	KASSERT(free % PAGE_SIZE == 0, ("unaligned boundary %p", (void *)free));
386 
387 	do_cpuid(0, cpuid.regs);
388 	cpuid.regs[0] = cpuid.regs[1];
389 	cpuid.regs[1] = cpuid.regs[3];
390 	cpuid.vendor[12] = '\0';
391 	for (i = 0; i < nitems(loaders); i++)
392 		if (strcmp(cpuid.vendor, loaders[i].vendor) == 0) {
393 			ucode_loader = &loaders[i];
394 			break;
395 		}
396 	if (ucode_loader == NULL)
397 		return (0);
398 
399 	file = 0;
400 	fileaddr = match = NULL;
401 	for (;;) {
402 		file = preload_search_next_name(file);
403 		if (file == 0)
404 			break;
405 		type = (char *)preload_search_info(file, MODINFO_TYPE);
406 		if (type == NULL || strcmp(type, "cpu_microcode") != 0)
407 			continue;
408 
409 		fileaddr = preload_fetch_addr(file);
410 		len = preload_fetch_size(file);
411 		match = ucode_loader->match(fileaddr, &len);
412 		if (match != NULL) {
413 			addr = map_ucode(free, len);
414 			/* We can't use memcpy() before ifunc resolution. */
415 			memcpy_early(addr, match, len);
416 			match = addr;
417 
418 			error = ucode_loader->load(match, false, &nrev, &orev);
419 			if (error == 0) {
420 				ucode_data = early_ucode_data = match;
421 				ucode_nrev = nrev;
422 				ucode_orev = orev;
423 				return (len);
424 			}
425 			unmap_ucode(free, len);
426 		}
427 	}
428 	if (fileaddr != NULL && ucode_error == NO_ERROR)
429 		ucode_error = NO_MATCH;
430 	return (0);
431 }
432 
433 /*
434  * Reload microcode following an ACPI resume.
435  */
436 void
ucode_reload(void)437 ucode_reload(void)
438 {
439 
440 	ucode_load_ap(PCPU_GET(cpuid));
441 }
442 
443 /*
444  * Replace an existing microcode update.
445  */
446 void *
ucode_update(void * newdata)447 ucode_update(void *newdata)
448 {
449 
450 	newdata = (void *)atomic_swap_ptr((void *)&ucode_data,
451 	    (uintptr_t)newdata);
452 	if (newdata == early_ucode_data)
453 		newdata = NULL;
454 	return (newdata);
455 }
456