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
53static const void	*ucode_intel_match(const uint8_t *data, size_t *len);
54static int	ucode_intel_verify(const struct ucode_intel_header *hdr,
55		    size_t resid);
56
57static const void	*ucode_amd_match(const uint8_t *data, size_t *len);
58
59static 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. */
77static const void *early_ucode_data;
78static const void *ucode_data;
79static struct ucode_ops *ucode_loader;
80
81/* Variables used for reporting success or failure. */
82enum {
83	NO_ERROR,
84	NO_MATCH,
85	VERIFICATION_FAILED,
86} ucode_error = NO_ERROR;
87static uint64_t ucode_nrev, ucode_orev;
88
89static void
90log_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}
110SYSINIT(ucode_log, SI_SUB_CPU, SI_ORDER_FIRST, log_msg, NULL);
111
112int
113ucode_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
149static int
150ucode_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
179static const void *
180ucode_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
235int
236ucode_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
270static const void *
271ucode_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 */
287static void
288ucode_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
301restart:
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}
316SYSINIT(ucode_release, SI_SUB_SMP + 1, SI_ORDER_ANY, ucode_release, NULL);
317
318void
319ucode_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
333static void *
334map_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
347static void
348unmap_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 */
369size_t
370ucode_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 */
435void
436ucode_reload(void)
437{
438
439	ucode_load_ap(PCPU_GET(cpuid));
440}
441
442/*
443 * Replace an existing microcode update.
444 */
445void *
446ucode_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