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: stable/11/sys/x86/x86/ucode.c 347700 2019-05-16 14:42:16Z 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, uint64_t *, uint64_t *);
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;
75static struct ucode_ops *ucode_loader;
76
77/* Variables used for reporting success or failure. */
78enum {
79	NO_ERROR,
80	NO_MATCH,
81	VERIFICATION_FAILED,
82} ucode_error = NO_ERROR;
83static uint64_t ucode_nrev, ucode_orev;
84
85static void
86log_msg(void *arg __unused)
87{
88
89	if (ucode_nrev != 0) {
90		printf("CPU microcode: updated from %#jx to %#jx\n",
91		    (uintmax_t)ucode_orev, (uintmax_t)ucode_nrev);
92		return;
93	}
94
95	switch (ucode_error) {
96	case NO_MATCH:
97		printf("CPU microcode: no matching update found\n");
98		break;
99	case VERIFICATION_FAILED:
100		printf("CPU microcode: microcode verification failed\n");
101		break;
102	default:
103		break;
104	}
105}
106SYSINIT(ucode_log, SI_SUB_CPU, SI_ORDER_FIRST, log_msg, NULL);
107
108int
109ucode_intel_load(void *data, bool unsafe, uint64_t *nrevp, uint64_t *orevp)
110{
111	uint64_t nrev, orev;
112	uint32_t cpuid[4];
113
114	orev = rdmsr(MSR_BIOS_SIGN) >> 32;
115
116	/*
117	 * Perform update.  Flush caches first to work around seemingly
118	 * undocumented errata applying to some Broadwell CPUs.
119	 */
120	wbinvd();
121	if (unsafe)
122		wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data);
123	else
124		wrmsr(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data);
125	wrmsr(MSR_BIOS_SIGN, 0);
126
127	/*
128	 * Serialize instruction flow.
129	 */
130	do_cpuid(0, cpuid);
131
132	/*
133	 * Verify that the microcode revision changed.
134	 */
135	nrev = rdmsr(MSR_BIOS_SIGN) >> 32;
136	if (nrevp != NULL)
137		*nrevp = nrev;
138	if (orevp != NULL)
139		*orevp = orev;
140	if (nrev <= orev)
141		return (EEXIST);
142	return (0);
143}
144
145static int
146ucode_intel_verify(struct ucode_intel_header *hdr, size_t resid)
147{
148	uint32_t cksum, *data, size;
149	int i;
150
151	if (resid < sizeof(struct ucode_intel_header))
152		return (1);
153	size = hdr->total_size;
154	if (size == 0)
155		size = UCODE_INTEL_DEFAULT_DATA_SIZE +
156		    sizeof(struct ucode_intel_header);
157
158	if (hdr->header_version != 1)
159		return (1);
160	if (size % 16 != 0)
161		return (1);
162	if (resid < size)
163		return (1);
164
165	cksum = 0;
166	data = (uint32_t *)hdr;
167	for (i = 0; i < size / sizeof(uint32_t); i++)
168		cksum += data[i];
169	if (cksum != 0)
170		return (1);
171	return (0);
172}
173
174static void *
175ucode_intel_match(uint8_t *data, size_t *len)
176{
177	struct ucode_intel_header *hdr;
178	struct ucode_intel_extsig_table *table;
179	struct ucode_intel_extsig *entry;
180	uint64_t platformid;
181	size_t resid;
182	uint32_t data_size, flags, regs[4], sig, total_size;
183	int i;
184
185	do_cpuid(1, regs);
186	sig = regs[0];
187
188	platformid = rdmsr(MSR_IA32_PLATFORM_ID);
189	flags = 1 << ((platformid >> 50) & 0x7);
190
191	for (resid = *len; resid > 0; data += total_size, resid -= total_size) {
192		hdr = (struct ucode_intel_header *)data;
193		if (ucode_intel_verify(hdr, resid) != 0) {
194			ucode_error = VERIFICATION_FAILED;
195			break;
196		}
197
198		data_size = hdr->data_size;
199		total_size = hdr->total_size;
200		if (data_size == 0)
201			data_size = UCODE_INTEL_DEFAULT_DATA_SIZE;
202		if (total_size == 0)
203			total_size = UCODE_INTEL_DEFAULT_DATA_SIZE +
204			    sizeof(struct ucode_intel_header);
205		if (data_size > total_size + sizeof(struct ucode_intel_header))
206			table = (struct ucode_intel_extsig_table *)
207			    ((uint8_t *)(hdr + 1) + data_size);
208		else
209			table = NULL;
210
211		if (hdr->processor_signature == sig) {
212			if ((hdr->processor_flags & flags) != 0) {
213				*len = data_size;
214				return (hdr + 1);
215			}
216		} else if (table != NULL) {
217			for (i = 0; i < table->signature_count; i++) {
218				entry = &table->entries[i];
219				if (entry->processor_signature == sig &&
220				    (entry->processor_flags & flags) != 0) {
221					*len = data_size;
222					return (hdr + 1);
223				}
224			}
225		}
226	}
227	return (NULL);
228}
229
230/*
231 * Release any memory backing unused microcode blobs back to the system.
232 * We copy the selected update and free the entire microcode file.
233 */
234static void
235ucode_release(void *arg __unused)
236{
237	char *name, *type;
238	caddr_t file;
239	int release;
240
241	if (early_ucode_data == NULL)
242		return;
243	release = 1;
244	TUNABLE_INT_FETCH("debug.ucode.release", &release);
245	if (!release)
246		return;
247
248restart:
249	file = 0;
250	for (;;) {
251		file = preload_search_next_name(file);
252		if (file == 0)
253			break;
254		type = (char *)preload_search_info(file, MODINFO_TYPE);
255		if (type == NULL || strcmp(type, "cpu_microcode") != 0)
256			continue;
257
258		name = preload_search_info(file, MODINFO_NAME);
259		preload_delete_name(name);
260		goto restart;
261	}
262}
263SYSINIT(ucode_release, SI_SUB_KMEM + 1, SI_ORDER_ANY, ucode_release, NULL);
264
265void
266ucode_load_ap(int cpu)
267{
268#ifdef SMP
269	KASSERT(cpu_info[cpu_apic_ids[cpu]].cpu_present,
270	    ("cpu %d not present", cpu));
271
272	if (cpu_info[cpu_apic_ids[cpu]].cpu_hyperthread)
273		return;
274#endif
275
276	if (ucode_data != NULL)
277		(void)ucode_loader->load(ucode_data, false, NULL, NULL);
278}
279
280static void *
281map_ucode(uintptr_t free, size_t len)
282{
283#ifdef __i386__
284	uintptr_t va;
285
286	for (va = free; va < free + len; va += PAGE_SIZE)
287		pmap_kenter(va, (vm_paddr_t)va);
288#else
289	(void)len;
290#endif
291	return ((void *)free);
292}
293
294static void
295unmap_ucode(uintptr_t free, size_t len)
296{
297#ifdef __i386__
298	uintptr_t va;
299
300	for (va = free; va < free + len; va += PAGE_SIZE)
301		pmap_kremove(va);
302#else
303	(void)free;
304	(void)len;
305#endif
306}
307
308/*
309 * Search for an applicable microcode update, and load it.  APs will load the
310 * selected update once they come online.
311 *
312 * "free" is the address of the next free physical page.  If a microcode update
313 * is selected, it will be copied to this region prior to loading in order to
314 * satisfy alignment requirements.
315 */
316size_t
317ucode_load_bsp(uintptr_t free)
318{
319	union {
320		uint32_t regs[4];
321		char vendor[13];
322	} cpuid;
323	uint8_t *addr, *fileaddr, *match;
324	char *type;
325	uint64_t nrev, orev;
326	caddr_t file;
327	size_t i, len;
328	int error;
329
330	KASSERT(free % PAGE_SIZE == 0, ("unaligned boundary %p", (void *)free));
331
332	do_cpuid(0, cpuid.regs);
333	cpuid.regs[0] = cpuid.regs[1];
334	cpuid.regs[1] = cpuid.regs[3];
335	cpuid.vendor[12] = '\0';
336	for (i = 0; i < nitems(loaders); i++)
337		if (strcmp(cpuid.vendor, loaders[i].vendor) == 0) {
338			ucode_loader = &loaders[i];
339			break;
340		}
341	if (ucode_loader == NULL)
342		return (0);
343
344	file = 0;
345	fileaddr = match = NULL;
346	for (;;) {
347		file = preload_search_next_name(file);
348		if (file == 0)
349			break;
350		type = (char *)preload_search_info(file, MODINFO_TYPE);
351		if (type == NULL || strcmp(type, "cpu_microcode") != 0)
352			continue;
353
354		fileaddr = preload_fetch_addr(file);
355		len = preload_fetch_size(file);
356		match = ucode_loader->match(fileaddr, &len);
357		if (match != NULL) {
358			addr = map_ucode(free, len);
359			/* We can't use memcpy() before ifunc resolution. */
360			for (i = 0; i < len; i++)
361				addr[i] = ((volatile uint8_t *)match)[i];
362			match = addr;
363
364			error = ucode_loader->load(match, false, &nrev, &orev);
365			if (error == 0) {
366				ucode_data = early_ucode_data = match;
367				ucode_nrev = nrev;
368				ucode_orev = orev;
369				return (len);
370			}
371			unmap_ucode(free, len);
372		}
373	}
374	if (fileaddr != NULL && ucode_error == NO_ERROR)
375		ucode_error = NO_MATCH;
376	return (0);
377}
378
379/*
380 * Reload microcode following an ACPI resume.
381 */
382void
383ucode_reload(void)
384{
385
386	ucode_load_ap(PCPU_GET(cpuid));
387}
388
389/*
390 * Replace an existing microcode update.
391 */
392void *
393ucode_update(void *newdata)
394{
395
396	newdata = (void *)atomic_swap_ptr((void *)&ucode_data,
397	    (uintptr_t)newdata);
398	if (newdata == early_ucode_data)
399		newdata = NULL;
400	return (newdata);
401}
402