ucode.c revision 337715
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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/cdefs.h>
32__FBSDID("$FreeBSD: head/sys/x86/x86/ucode.c 337715 2018-08-13 17:13:09Z markj $");
33
34#include <sys/param.h>
35#include <sys/cpuset.h>
36#include <sys/kernel.h>
37#include <sys/linker.h>
38#include <sys/malloc.h>
39#include <sys/pcpu.h>
40#include <sys/smp.h>
41#include <sys/systm.h>
42
43#include <machine/atomic.h>
44#include <machine/cpufunc.h>
45#include <x86/specialreg.h>
46#include <machine/stdarg.h>
47#include <x86/ucode.h>
48#include <x86/x86_smp.h>
49
50#include <vm/vm.h>
51#include <vm/pmap.h>
52#include <vm/vm_extern.h>
53#include <vm/vm_kern.h>
54#include <vm/vm_param.h>
55
56static void	*ucode_intel_match(uint8_t *data, size_t *len);
57static int	ucode_intel_verify(struct ucode_intel_header *hdr,
58		    size_t resid);
59
60static struct ucode_ops {
61	const char *vendor;
62	int (*load)(void *, bool);
63	void *(*match)(uint8_t *, size_t *);
64} loaders[] = {
65	{
66		.vendor = INTEL_VENDOR_ID,
67		.load = ucode_intel_load,
68		.match = ucode_intel_match,
69	},
70};
71
72/* Selected microcode update data. */
73static void *early_ucode_data;
74static void *ucode_data;
75
76static char errbuf[128];
77
78static void __printflike(1, 2)
79log_err(const char *fmt, ...)
80{
81	va_list ap;
82
83	va_start(ap, fmt);
84	vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
85	va_end(ap);
86}
87
88static void
89print_err(void *arg __unused)
90{
91
92	if (errbuf[0] != '\0')
93		printf("microcode load error: %s\n", errbuf);
94}
95SYSINIT(ucode_print_err, SI_SUB_CPU, SI_ORDER_FIRST, print_err, NULL);
96
97int
98ucode_intel_load(void *data, bool unsafe)
99{
100	uint64_t rev0, rev1;
101	uint32_t cpuid[4];
102
103	rev0 = rdmsr(MSR_BIOS_SIGN);
104
105	/*
106	 * Perform update.  Flush caches first to work around seemingly
107	 * undocumented errata applying to some Broadwell CPUs.
108	 */
109	wbinvd();
110	if (unsafe)
111		wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data);
112	else
113		wrmsr(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data);
114	wrmsr(MSR_BIOS_SIGN, 0);
115
116	/*
117	 * Serialize instruction flow.
118	 */
119	do_cpuid(0, cpuid);
120
121	rev1 = rdmsr(MSR_BIOS_SIGN);
122	if (rev1 <= rev0)
123		return (EEXIST);
124	return (0);
125}
126
127static int
128ucode_intel_verify(struct ucode_intel_header *hdr, size_t resid)
129{
130	uint32_t cksum, *data, size;
131	int i;
132
133	if (resid < sizeof(struct ucode_intel_header)) {
134		log_err("truncated update header");
135		return (1);
136	}
137	size = hdr->total_size;
138	if (size == 0)
139		size = UCODE_INTEL_DEFAULT_DATA_SIZE +
140		    sizeof(struct ucode_intel_header);
141
142	if (hdr->header_version != 1) {
143		log_err("unexpected header version %u", hdr->header_version);
144		return (1);
145	}
146	if (size % 16 != 0) {
147		log_err("unexpected update size %u", hdr->total_size);
148		return (1);
149	}
150	if (resid < size) {
151		log_err("truncated update");
152		return (1);
153	}
154
155	cksum = 0;
156	data = (uint32_t *)hdr;
157	for (i = 0; i < size / sizeof(uint32_t); i++)
158		cksum += data[i];
159	if (cksum != 0) {
160		log_err("checksum failed");
161		return (1);
162	}
163	return (0);
164}
165
166static void *
167ucode_intel_match(uint8_t *data, size_t *len)
168{
169	struct ucode_intel_header *hdr;
170	struct ucode_intel_extsig_table *table;
171	struct ucode_intel_extsig *entry;
172	uint64_t platformid;
173	size_t resid;
174	uint32_t data_size, flags, regs[4], sig, total_size;
175	int i;
176
177	do_cpuid(1, regs);
178	sig = regs[0];
179
180	platformid = rdmsr(MSR_IA32_PLATFORM_ID);
181	flags = 1 << ((platformid >> 50) & 0x7);
182
183	for (resid = *len; resid > 0; data += total_size, resid -= total_size) {
184		hdr = (struct ucode_intel_header *)data;
185		if (ucode_intel_verify(hdr, resid) != 0)
186			break;
187
188		data_size = hdr->data_size;
189		total_size = hdr->total_size;
190		if (data_size == 0)
191			data_size = UCODE_INTEL_DEFAULT_DATA_SIZE;
192		if (total_size == 0)
193			total_size = UCODE_INTEL_DEFAULT_DATA_SIZE +
194			    sizeof(struct ucode_intel_header);
195		if (data_size > total_size + sizeof(struct ucode_intel_header))
196			table = (struct ucode_intel_extsig_table *)
197			    ((uint8_t *)(hdr + 1) + data_size);
198		else
199			table = NULL;
200
201		if (hdr->processor_signature == sig) {
202			if ((hdr->processor_flags & flags) != 0) {
203				*len = data_size;
204				return (hdr + 1);
205			}
206		} else if (table != NULL) {
207			for (i = 0; i < table->signature_count; i++) {
208				entry = &table->entries[i];
209				if (entry->processor_signature == sig &&
210				    (entry->processor_flags & flags) != 0) {
211					*len = data_size;
212					return (hdr + 1);
213				}
214			}
215		}
216	}
217	return (NULL);
218}
219
220/*
221 * Release any memory backing unused microcode blobs back to the system.
222 * We copy the selected update and free the entire microcode file.
223 */
224static void
225ucode_release(void *arg __unused)
226{
227	char *name, *type;
228	caddr_t file;
229	int release;
230
231	if (early_ucode_data == NULL)
232		return;
233	release = 1;
234	TUNABLE_INT_FETCH("debug.ucode.release", &release);
235	if (!release)
236		return;
237
238restart:
239	file = 0;
240	for (;;) {
241		file = preload_search_next_name(file);
242		if (file == 0)
243			break;
244		type = (char *)preload_search_info(file, MODINFO_TYPE);
245		if (type == NULL || strcmp(type, "cpu_microcode") != 0)
246			continue;
247
248		name = preload_search_info(file, MODINFO_NAME);
249		preload_delete_name(name);
250		goto restart;
251	}
252}
253SYSINIT(ucode_release, SI_SUB_KMEM + 1, SI_ORDER_ANY, ucode_release, NULL);
254
255void
256ucode_load_ap(int cpu)
257{
258
259	KASSERT(cpu_info[cpu_apic_ids[cpu]].cpu_present,
260	    ("cpu %d not present", cpu));
261
262	if (ucode_data != NULL && !cpu_info[cpu_apic_ids[cpu]].cpu_hyperthread)
263		(void)ucode_intel_load(ucode_data, false);
264}
265
266static void *
267map_ucode(vm_paddr_t free, size_t len)
268{
269
270#ifdef __i386__
271	for (vm_paddr_t pa = free; pa < free + len; pa += PAGE_SIZE)
272		pmap_kenter(pa, pa);
273#else
274	(void)len;
275#endif
276	return ((void *)free);
277}
278
279static void
280unmap_ucode(vm_paddr_t free, size_t len)
281{
282
283#ifdef __i386__
284	for (vm_paddr_t pa = free; pa < free + len; pa += PAGE_SIZE)
285		pmap_kremove((vm_offset_t)pa);
286#else
287	(void)free;
288	(void)len;
289#endif
290}
291
292/*
293 * Search for an applicable microcode update, and load it.  APs will load the
294 * selected update once they come online.
295 *
296 * "free" is the address of the next free physical page.  If a microcode update
297 * is selected, it will be copied to this region prior to loading in order to
298 * satisfy alignment requirements.
299 */
300size_t
301ucode_load_bsp(uintptr_t free)
302{
303	union {
304		uint32_t regs[4];
305		char vendor[13];
306	} cpuid;
307	struct ucode_ops *loader;
308	uint8_t *addr, *fileaddr, *match;
309	char *type;
310	caddr_t file;
311	size_t len, ucode_len;
312	int i;
313
314	KASSERT(free % PAGE_SIZE == 0, ("unaligned boundary %p", (void *)free));
315
316	do_cpuid(0, cpuid.regs);
317	cpuid.regs[0] = cpuid.regs[1];
318	cpuid.regs[1] = cpuid.regs[3];
319	cpuid.vendor[12] = '\0';
320	for (i = 0, loader = NULL; i < nitems(loaders); i++)
321		if (strcmp(cpuid.vendor, loaders[i].vendor) == 0) {
322			loader = &loaders[i];
323			break;
324		}
325	if (loader == NULL)
326		return (0);
327
328	file = 0;
329	fileaddr = match = NULL;
330	ucode_len = 0;
331	for (;;) {
332		file = preload_search_next_name(file);
333		if (file == 0)
334			break;
335		type = (char *)preload_search_info(file, MODINFO_TYPE);
336		if (type == NULL || strcmp(type, "cpu_microcode") != 0)
337			continue;
338
339		fileaddr = preload_fetch_addr(file);
340		len = preload_fetch_size(file);
341		match = loader->match(fileaddr, &len);
342		if (match != NULL) {
343			addr = map_ucode(free, len);
344			memcpy(addr, match, len);
345			match = addr;
346
347			if (loader->load(match, false) == 0) {
348				ucode_data = match;
349				ucode_len = len;
350				early_ucode_data = ucode_data;
351				break;
352			}
353			unmap_ucode(free, len);
354		}
355	}
356	if (fileaddr != NULL && ucode_data == NULL)
357		log_err("no matching update found");
358	return (ucode_len);
359}
360
361/*
362 * Reload microcode following an ACPI resume.
363 */
364void
365ucode_reload(void)
366{
367
368	ucode_load_ap(PCPU_GET(cpuid));
369}
370
371/*
372 * Replace an existing microcode update.
373 */
374void *
375ucode_update(void *newdata)
376{
377
378	newdata = (void *)atomic_swap_ptr((void *)&ucode_data,
379	    (uintptr_t)newdata);
380	if (newdata == early_ucode_data)
381		newdata = NULL;
382	return (newdata);
383}
384