ucode.c revision 337715
1337715Smarkj/*-
2337715Smarkj * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3337715Smarkj *
4337715Smarkj * Copyright (c) 2018 The FreeBSD Foundation
5337715Smarkj *
6337715Smarkj * This software was developed by Mark Johnston under sponsorship from
7337715Smarkj * the FreeBSD Foundation.
8337715Smarkj *
9337715Smarkj * Redistribution and use in source and binary forms, with or without
10337715Smarkj * modification, are permitted provided that the following conditions
11337715Smarkj * are met:
12337715Smarkj * 1. Redistributions of source code must retain the above copyright
13337715Smarkj *    notice, this list of conditions and the following disclaimer.
14337715Smarkj * 2. Redistributions in binary form must reproduce the above copyright
15337715Smarkj *    notice, this list of conditions and the following disclaimer in the
16337715Smarkj *    documentation and/or other materials provided with the distribution.
17337715Smarkj *
18337715Smarkj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19337715Smarkj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20337715Smarkj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21337715Smarkj * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22337715Smarkj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23337715Smarkj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24337715Smarkj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25337715Smarkj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26337715Smarkj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27337715Smarkj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28337715Smarkj * SUCH DAMAGE.
29337715Smarkj */
30337715Smarkj
31337715Smarkj#include <sys/cdefs.h>
32337715Smarkj__FBSDID("$FreeBSD: head/sys/x86/x86/ucode.c 337715 2018-08-13 17:13:09Z markj $");
33337715Smarkj
34337715Smarkj#include <sys/param.h>
35337715Smarkj#include <sys/cpuset.h>
36337715Smarkj#include <sys/kernel.h>
37337715Smarkj#include <sys/linker.h>
38337715Smarkj#include <sys/malloc.h>
39337715Smarkj#include <sys/pcpu.h>
40337715Smarkj#include <sys/smp.h>
41337715Smarkj#include <sys/systm.h>
42337715Smarkj
43337715Smarkj#include <machine/atomic.h>
44337715Smarkj#include <machine/cpufunc.h>
45337715Smarkj#include <x86/specialreg.h>
46337715Smarkj#include <machine/stdarg.h>
47337715Smarkj#include <x86/ucode.h>
48337715Smarkj#include <x86/x86_smp.h>
49337715Smarkj
50337715Smarkj#include <vm/vm.h>
51337715Smarkj#include <vm/pmap.h>
52337715Smarkj#include <vm/vm_extern.h>
53337715Smarkj#include <vm/vm_kern.h>
54337715Smarkj#include <vm/vm_param.h>
55337715Smarkj
56337715Smarkjstatic void	*ucode_intel_match(uint8_t *data, size_t *len);
57337715Smarkjstatic int	ucode_intel_verify(struct ucode_intel_header *hdr,
58337715Smarkj		    size_t resid);
59337715Smarkj
60337715Smarkjstatic struct ucode_ops {
61337715Smarkj	const char *vendor;
62337715Smarkj	int (*load)(void *, bool);
63337715Smarkj	void *(*match)(uint8_t *, size_t *);
64337715Smarkj} loaders[] = {
65337715Smarkj	{
66337715Smarkj		.vendor = INTEL_VENDOR_ID,
67337715Smarkj		.load = ucode_intel_load,
68337715Smarkj		.match = ucode_intel_match,
69337715Smarkj	},
70337715Smarkj};
71337715Smarkj
72337715Smarkj/* Selected microcode update data. */
73337715Smarkjstatic void *early_ucode_data;
74337715Smarkjstatic void *ucode_data;
75337715Smarkj
76337715Smarkjstatic char errbuf[128];
77337715Smarkj
78337715Smarkjstatic void __printflike(1, 2)
79337715Smarkjlog_err(const char *fmt, ...)
80337715Smarkj{
81337715Smarkj	va_list ap;
82337715Smarkj
83337715Smarkj	va_start(ap, fmt);
84337715Smarkj	vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
85337715Smarkj	va_end(ap);
86337715Smarkj}
87337715Smarkj
88337715Smarkjstatic void
89337715Smarkjprint_err(void *arg __unused)
90337715Smarkj{
91337715Smarkj
92337715Smarkj	if (errbuf[0] != '\0')
93337715Smarkj		printf("microcode load error: %s\n", errbuf);
94337715Smarkj}
95337715SmarkjSYSINIT(ucode_print_err, SI_SUB_CPU, SI_ORDER_FIRST, print_err, NULL);
96337715Smarkj
97337715Smarkjint
98337715Smarkjucode_intel_load(void *data, bool unsafe)
99337715Smarkj{
100337715Smarkj	uint64_t rev0, rev1;
101337715Smarkj	uint32_t cpuid[4];
102337715Smarkj
103337715Smarkj	rev0 = rdmsr(MSR_BIOS_SIGN);
104337715Smarkj
105337715Smarkj	/*
106337715Smarkj	 * Perform update.  Flush caches first to work around seemingly
107337715Smarkj	 * undocumented errata applying to some Broadwell CPUs.
108337715Smarkj	 */
109337715Smarkj	wbinvd();
110337715Smarkj	if (unsafe)
111337715Smarkj		wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data);
112337715Smarkj	else
113337715Smarkj		wrmsr(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data);
114337715Smarkj	wrmsr(MSR_BIOS_SIGN, 0);
115337715Smarkj
116337715Smarkj	/*
117337715Smarkj	 * Serialize instruction flow.
118337715Smarkj	 */
119337715Smarkj	do_cpuid(0, cpuid);
120337715Smarkj
121337715Smarkj	rev1 = rdmsr(MSR_BIOS_SIGN);
122337715Smarkj	if (rev1 <= rev0)
123337715Smarkj		return (EEXIST);
124337715Smarkj	return (0);
125337715Smarkj}
126337715Smarkj
127337715Smarkjstatic int
128337715Smarkjucode_intel_verify(struct ucode_intel_header *hdr, size_t resid)
129337715Smarkj{
130337715Smarkj	uint32_t cksum, *data, size;
131337715Smarkj	int i;
132337715Smarkj
133337715Smarkj	if (resid < sizeof(struct ucode_intel_header)) {
134337715Smarkj		log_err("truncated update header");
135337715Smarkj		return (1);
136337715Smarkj	}
137337715Smarkj	size = hdr->total_size;
138337715Smarkj	if (size == 0)
139337715Smarkj		size = UCODE_INTEL_DEFAULT_DATA_SIZE +
140337715Smarkj		    sizeof(struct ucode_intel_header);
141337715Smarkj
142337715Smarkj	if (hdr->header_version != 1) {
143337715Smarkj		log_err("unexpected header version %u", hdr->header_version);
144337715Smarkj		return (1);
145337715Smarkj	}
146337715Smarkj	if (size % 16 != 0) {
147337715Smarkj		log_err("unexpected update size %u", hdr->total_size);
148337715Smarkj		return (1);
149337715Smarkj	}
150337715Smarkj	if (resid < size) {
151337715Smarkj		log_err("truncated update");
152337715Smarkj		return (1);
153337715Smarkj	}
154337715Smarkj
155337715Smarkj	cksum = 0;
156337715Smarkj	data = (uint32_t *)hdr;
157337715Smarkj	for (i = 0; i < size / sizeof(uint32_t); i++)
158337715Smarkj		cksum += data[i];
159337715Smarkj	if (cksum != 0) {
160337715Smarkj		log_err("checksum failed");
161337715Smarkj		return (1);
162337715Smarkj	}
163337715Smarkj	return (0);
164337715Smarkj}
165337715Smarkj
166337715Smarkjstatic void *
167337715Smarkjucode_intel_match(uint8_t *data, size_t *len)
168337715Smarkj{
169337715Smarkj	struct ucode_intel_header *hdr;
170337715Smarkj	struct ucode_intel_extsig_table *table;
171337715Smarkj	struct ucode_intel_extsig *entry;
172337715Smarkj	uint64_t platformid;
173337715Smarkj	size_t resid;
174337715Smarkj	uint32_t data_size, flags, regs[4], sig, total_size;
175337715Smarkj	int i;
176337715Smarkj
177337715Smarkj	do_cpuid(1, regs);
178337715Smarkj	sig = regs[0];
179337715Smarkj
180337715Smarkj	platformid = rdmsr(MSR_IA32_PLATFORM_ID);
181337715Smarkj	flags = 1 << ((platformid >> 50) & 0x7);
182337715Smarkj
183337715Smarkj	for (resid = *len; resid > 0; data += total_size, resid -= total_size) {
184337715Smarkj		hdr = (struct ucode_intel_header *)data;
185337715Smarkj		if (ucode_intel_verify(hdr, resid) != 0)
186337715Smarkj			break;
187337715Smarkj
188337715Smarkj		data_size = hdr->data_size;
189337715Smarkj		total_size = hdr->total_size;
190337715Smarkj		if (data_size == 0)
191337715Smarkj			data_size = UCODE_INTEL_DEFAULT_DATA_SIZE;
192337715Smarkj		if (total_size == 0)
193337715Smarkj			total_size = UCODE_INTEL_DEFAULT_DATA_SIZE +
194337715Smarkj			    sizeof(struct ucode_intel_header);
195337715Smarkj		if (data_size > total_size + sizeof(struct ucode_intel_header))
196337715Smarkj			table = (struct ucode_intel_extsig_table *)
197337715Smarkj			    ((uint8_t *)(hdr + 1) + data_size);
198337715Smarkj		else
199337715Smarkj			table = NULL;
200337715Smarkj
201337715Smarkj		if (hdr->processor_signature == sig) {
202337715Smarkj			if ((hdr->processor_flags & flags) != 0) {
203337715Smarkj				*len = data_size;
204337715Smarkj				return (hdr + 1);
205337715Smarkj			}
206337715Smarkj		} else if (table != NULL) {
207337715Smarkj			for (i = 0; i < table->signature_count; i++) {
208337715Smarkj				entry = &table->entries[i];
209337715Smarkj				if (entry->processor_signature == sig &&
210337715Smarkj				    (entry->processor_flags & flags) != 0) {
211337715Smarkj					*len = data_size;
212337715Smarkj					return (hdr + 1);
213337715Smarkj				}
214337715Smarkj			}
215337715Smarkj		}
216337715Smarkj	}
217337715Smarkj	return (NULL);
218337715Smarkj}
219337715Smarkj
220337715Smarkj/*
221337715Smarkj * Release any memory backing unused microcode blobs back to the system.
222337715Smarkj * We copy the selected update and free the entire microcode file.
223337715Smarkj */
224337715Smarkjstatic void
225337715Smarkjucode_release(void *arg __unused)
226337715Smarkj{
227337715Smarkj	char *name, *type;
228337715Smarkj	caddr_t file;
229337715Smarkj	int release;
230337715Smarkj
231337715Smarkj	if (early_ucode_data == NULL)
232337715Smarkj		return;
233337715Smarkj	release = 1;
234337715Smarkj	TUNABLE_INT_FETCH("debug.ucode.release", &release);
235337715Smarkj	if (!release)
236337715Smarkj		return;
237337715Smarkj
238337715Smarkjrestart:
239337715Smarkj	file = 0;
240337715Smarkj	for (;;) {
241337715Smarkj		file = preload_search_next_name(file);
242337715Smarkj		if (file == 0)
243337715Smarkj			break;
244337715Smarkj		type = (char *)preload_search_info(file, MODINFO_TYPE);
245337715Smarkj		if (type == NULL || strcmp(type, "cpu_microcode") != 0)
246337715Smarkj			continue;
247337715Smarkj
248337715Smarkj		name = preload_search_info(file, MODINFO_NAME);
249337715Smarkj		preload_delete_name(name);
250337715Smarkj		goto restart;
251337715Smarkj	}
252337715Smarkj}
253337715SmarkjSYSINIT(ucode_release, SI_SUB_KMEM + 1, SI_ORDER_ANY, ucode_release, NULL);
254337715Smarkj
255337715Smarkjvoid
256337715Smarkjucode_load_ap(int cpu)
257337715Smarkj{
258337715Smarkj
259337715Smarkj	KASSERT(cpu_info[cpu_apic_ids[cpu]].cpu_present,
260337715Smarkj	    ("cpu %d not present", cpu));
261337715Smarkj
262337715Smarkj	if (ucode_data != NULL && !cpu_info[cpu_apic_ids[cpu]].cpu_hyperthread)
263337715Smarkj		(void)ucode_intel_load(ucode_data, false);
264337715Smarkj}
265337715Smarkj
266337715Smarkjstatic void *
267337715Smarkjmap_ucode(vm_paddr_t free, size_t len)
268337715Smarkj{
269337715Smarkj
270337715Smarkj#ifdef __i386__
271337715Smarkj	for (vm_paddr_t pa = free; pa < free + len; pa += PAGE_SIZE)
272337715Smarkj		pmap_kenter(pa, pa);
273337715Smarkj#else
274337715Smarkj	(void)len;
275337715Smarkj#endif
276337715Smarkj	return ((void *)free);
277337715Smarkj}
278337715Smarkj
279337715Smarkjstatic void
280337715Smarkjunmap_ucode(vm_paddr_t free, size_t len)
281337715Smarkj{
282337715Smarkj
283337715Smarkj#ifdef __i386__
284337715Smarkj	for (vm_paddr_t pa = free; pa < free + len; pa += PAGE_SIZE)
285337715Smarkj		pmap_kremove((vm_offset_t)pa);
286337715Smarkj#else
287337715Smarkj	(void)free;
288337715Smarkj	(void)len;
289337715Smarkj#endif
290337715Smarkj}
291337715Smarkj
292337715Smarkj/*
293337715Smarkj * Search for an applicable microcode update, and load it.  APs will load the
294337715Smarkj * selected update once they come online.
295337715Smarkj *
296337715Smarkj * "free" is the address of the next free physical page.  If a microcode update
297337715Smarkj * is selected, it will be copied to this region prior to loading in order to
298337715Smarkj * satisfy alignment requirements.
299337715Smarkj */
300337715Smarkjsize_t
301337715Smarkjucode_load_bsp(uintptr_t free)
302337715Smarkj{
303337715Smarkj	union {
304337715Smarkj		uint32_t regs[4];
305337715Smarkj		char vendor[13];
306337715Smarkj	} cpuid;
307337715Smarkj	struct ucode_ops *loader;
308337715Smarkj	uint8_t *addr, *fileaddr, *match;
309337715Smarkj	char *type;
310337715Smarkj	caddr_t file;
311337715Smarkj	size_t len, ucode_len;
312337715Smarkj	int i;
313337715Smarkj
314337715Smarkj	KASSERT(free % PAGE_SIZE == 0, ("unaligned boundary %p", (void *)free));
315337715Smarkj
316337715Smarkj	do_cpuid(0, cpuid.regs);
317337715Smarkj	cpuid.regs[0] = cpuid.regs[1];
318337715Smarkj	cpuid.regs[1] = cpuid.regs[3];
319337715Smarkj	cpuid.vendor[12] = '\0';
320337715Smarkj	for (i = 0, loader = NULL; i < nitems(loaders); i++)
321337715Smarkj		if (strcmp(cpuid.vendor, loaders[i].vendor) == 0) {
322337715Smarkj			loader = &loaders[i];
323337715Smarkj			break;
324337715Smarkj		}
325337715Smarkj	if (loader == NULL)
326337715Smarkj		return (0);
327337715Smarkj
328337715Smarkj	file = 0;
329337715Smarkj	fileaddr = match = NULL;
330337715Smarkj	ucode_len = 0;
331337715Smarkj	for (;;) {
332337715Smarkj		file = preload_search_next_name(file);
333337715Smarkj		if (file == 0)
334337715Smarkj			break;
335337715Smarkj		type = (char *)preload_search_info(file, MODINFO_TYPE);
336337715Smarkj		if (type == NULL || strcmp(type, "cpu_microcode") != 0)
337337715Smarkj			continue;
338337715Smarkj
339337715Smarkj		fileaddr = preload_fetch_addr(file);
340337715Smarkj		len = preload_fetch_size(file);
341337715Smarkj		match = loader->match(fileaddr, &len);
342337715Smarkj		if (match != NULL) {
343337715Smarkj			addr = map_ucode(free, len);
344337715Smarkj			memcpy(addr, match, len);
345337715Smarkj			match = addr;
346337715Smarkj
347337715Smarkj			if (loader->load(match, false) == 0) {
348337715Smarkj				ucode_data = match;
349337715Smarkj				ucode_len = len;
350337715Smarkj				early_ucode_data = ucode_data;
351337715Smarkj				break;
352337715Smarkj			}
353337715Smarkj			unmap_ucode(free, len);
354337715Smarkj		}
355337715Smarkj	}
356337715Smarkj	if (fileaddr != NULL && ucode_data == NULL)
357337715Smarkj		log_err("no matching update found");
358337715Smarkj	return (ucode_len);
359337715Smarkj}
360337715Smarkj
361337715Smarkj/*
362337715Smarkj * Reload microcode following an ACPI resume.
363337715Smarkj */
364337715Smarkjvoid
365337715Smarkjucode_reload(void)
366337715Smarkj{
367337715Smarkj
368337715Smarkj	ucode_load_ap(PCPU_GET(cpuid));
369337715Smarkj}
370337715Smarkj
371337715Smarkj/*
372337715Smarkj * Replace an existing microcode update.
373337715Smarkj */
374337715Smarkjvoid *
375337715Smarkjucode_update(void *newdata)
376337715Smarkj{
377337715Smarkj
378337715Smarkj	newdata = (void *)atomic_swap_ptr((void *)&ucode_data,
379337715Smarkj	    (uintptr_t)newdata);
380337715Smarkj	if (newdata == early_ucode_data)
381337715Smarkj		newdata = NULL;
382337715Smarkj	return (newdata);
383337715Smarkj}
384