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: stable/11/sys/x86/x86/ucode.c 347700 2019-05-16 14:42:16Z 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;
62347700Smarkj	int (*load)(void *, bool, uint64_t *, uint64_t *);
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;
75347700Smarkjstatic struct ucode_ops *ucode_loader;
76337715Smarkj
77347700Smarkj/* Variables used for reporting success or failure. */
78347700Smarkjenum {
79347700Smarkj	NO_ERROR,
80347700Smarkj	NO_MATCH,
81347700Smarkj	VERIFICATION_FAILED,
82347700Smarkj} ucode_error = NO_ERROR;
83347700Smarkjstatic uint64_t ucode_nrev, ucode_orev;
84337715Smarkj
85347700Smarkjstatic void
86347700Smarkjlog_msg(void *arg __unused)
87337715Smarkj{
88337715Smarkj
89347700Smarkj	if (ucode_nrev != 0) {
90347700Smarkj		printf("CPU microcode: updated from %#jx to %#jx\n",
91347700Smarkj		    (uintmax_t)ucode_orev, (uintmax_t)ucode_nrev);
92347700Smarkj		return;
93347700Smarkj	}
94337715Smarkj
95347700Smarkj	switch (ucode_error) {
96347700Smarkj	case NO_MATCH:
97347700Smarkj		printf("CPU microcode: no matching update found\n");
98347700Smarkj		break;
99347700Smarkj	case VERIFICATION_FAILED:
100347700Smarkj		printf("CPU microcode: microcode verification failed\n");
101347700Smarkj		break;
102347700Smarkj	default:
103347700Smarkj		break;
104347700Smarkj	}
105337715Smarkj}
106347700SmarkjSYSINIT(ucode_log, SI_SUB_CPU, SI_ORDER_FIRST, log_msg, NULL);
107337715Smarkj
108337715Smarkjint
109347700Smarkjucode_intel_load(void *data, bool unsafe, uint64_t *nrevp, uint64_t *orevp)
110337715Smarkj{
111347700Smarkj	uint64_t nrev, orev;
112337715Smarkj	uint32_t cpuid[4];
113337715Smarkj
114347700Smarkj	orev = rdmsr(MSR_BIOS_SIGN) >> 32;
115337715Smarkj
116337715Smarkj	/*
117337715Smarkj	 * Perform update.  Flush caches first to work around seemingly
118337715Smarkj	 * undocumented errata applying to some Broadwell CPUs.
119337715Smarkj	 */
120337715Smarkj	wbinvd();
121337715Smarkj	if (unsafe)
122337715Smarkj		wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data);
123337715Smarkj	else
124337715Smarkj		wrmsr(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data);
125337715Smarkj	wrmsr(MSR_BIOS_SIGN, 0);
126337715Smarkj
127337715Smarkj	/*
128337715Smarkj	 * Serialize instruction flow.
129337715Smarkj	 */
130337715Smarkj	do_cpuid(0, cpuid);
131337715Smarkj
132347700Smarkj	/*
133347700Smarkj	 * Verify that the microcode revision changed.
134347700Smarkj	 */
135347700Smarkj	nrev = rdmsr(MSR_BIOS_SIGN) >> 32;
136347700Smarkj	if (nrevp != NULL)
137347700Smarkj		*nrevp = nrev;
138347700Smarkj	if (orevp != NULL)
139347700Smarkj		*orevp = orev;
140347700Smarkj	if (nrev <= orev)
141337715Smarkj		return (EEXIST);
142337715Smarkj	return (0);
143337715Smarkj}
144337715Smarkj
145337715Smarkjstatic int
146337715Smarkjucode_intel_verify(struct ucode_intel_header *hdr, size_t resid)
147337715Smarkj{
148337715Smarkj	uint32_t cksum, *data, size;
149337715Smarkj	int i;
150337715Smarkj
151347700Smarkj	if (resid < sizeof(struct ucode_intel_header))
152337715Smarkj		return (1);
153337715Smarkj	size = hdr->total_size;
154337715Smarkj	if (size == 0)
155337715Smarkj		size = UCODE_INTEL_DEFAULT_DATA_SIZE +
156337715Smarkj		    sizeof(struct ucode_intel_header);
157337715Smarkj
158347700Smarkj	if (hdr->header_version != 1)
159337715Smarkj		return (1);
160347700Smarkj	if (size % 16 != 0)
161337715Smarkj		return (1);
162347700Smarkj	if (resid < size)
163337715Smarkj		return (1);
164337715Smarkj
165337715Smarkj	cksum = 0;
166337715Smarkj	data = (uint32_t *)hdr;
167337715Smarkj	for (i = 0; i < size / sizeof(uint32_t); i++)
168337715Smarkj		cksum += data[i];
169347700Smarkj	if (cksum != 0)
170337715Smarkj		return (1);
171337715Smarkj	return (0);
172337715Smarkj}
173337715Smarkj
174337715Smarkjstatic void *
175337715Smarkjucode_intel_match(uint8_t *data, size_t *len)
176337715Smarkj{
177337715Smarkj	struct ucode_intel_header *hdr;
178337715Smarkj	struct ucode_intel_extsig_table *table;
179337715Smarkj	struct ucode_intel_extsig *entry;
180337715Smarkj	uint64_t platformid;
181337715Smarkj	size_t resid;
182337715Smarkj	uint32_t data_size, flags, regs[4], sig, total_size;
183337715Smarkj	int i;
184337715Smarkj
185337715Smarkj	do_cpuid(1, regs);
186337715Smarkj	sig = regs[0];
187337715Smarkj
188337715Smarkj	platformid = rdmsr(MSR_IA32_PLATFORM_ID);
189337715Smarkj	flags = 1 << ((platformid >> 50) & 0x7);
190337715Smarkj
191337715Smarkj	for (resid = *len; resid > 0; data += total_size, resid -= total_size) {
192337715Smarkj		hdr = (struct ucode_intel_header *)data;
193347700Smarkj		if (ucode_intel_verify(hdr, resid) != 0) {
194347700Smarkj			ucode_error = VERIFICATION_FAILED;
195337715Smarkj			break;
196347700Smarkj		}
197337715Smarkj
198337715Smarkj		data_size = hdr->data_size;
199337715Smarkj		total_size = hdr->total_size;
200337715Smarkj		if (data_size == 0)
201337715Smarkj			data_size = UCODE_INTEL_DEFAULT_DATA_SIZE;
202337715Smarkj		if (total_size == 0)
203337715Smarkj			total_size = UCODE_INTEL_DEFAULT_DATA_SIZE +
204337715Smarkj			    sizeof(struct ucode_intel_header);
205337715Smarkj		if (data_size > total_size + sizeof(struct ucode_intel_header))
206337715Smarkj			table = (struct ucode_intel_extsig_table *)
207337715Smarkj			    ((uint8_t *)(hdr + 1) + data_size);
208337715Smarkj		else
209337715Smarkj			table = NULL;
210337715Smarkj
211337715Smarkj		if (hdr->processor_signature == sig) {
212337715Smarkj			if ((hdr->processor_flags & flags) != 0) {
213337715Smarkj				*len = data_size;
214337715Smarkj				return (hdr + 1);
215337715Smarkj			}
216337715Smarkj		} else if (table != NULL) {
217337715Smarkj			for (i = 0; i < table->signature_count; i++) {
218337715Smarkj				entry = &table->entries[i];
219337715Smarkj				if (entry->processor_signature == sig &&
220337715Smarkj				    (entry->processor_flags & flags) != 0) {
221337715Smarkj					*len = data_size;
222337715Smarkj					return (hdr + 1);
223337715Smarkj				}
224337715Smarkj			}
225337715Smarkj		}
226337715Smarkj	}
227337715Smarkj	return (NULL);
228337715Smarkj}
229337715Smarkj
230337715Smarkj/*
231337715Smarkj * Release any memory backing unused microcode blobs back to the system.
232337715Smarkj * We copy the selected update and free the entire microcode file.
233337715Smarkj */
234337715Smarkjstatic void
235337715Smarkjucode_release(void *arg __unused)
236337715Smarkj{
237337715Smarkj	char *name, *type;
238337715Smarkj	caddr_t file;
239337715Smarkj	int release;
240337715Smarkj
241337715Smarkj	if (early_ucode_data == NULL)
242337715Smarkj		return;
243337715Smarkj	release = 1;
244337715Smarkj	TUNABLE_INT_FETCH("debug.ucode.release", &release);
245337715Smarkj	if (!release)
246337715Smarkj		return;
247337715Smarkj
248337715Smarkjrestart:
249337715Smarkj	file = 0;
250337715Smarkj	for (;;) {
251337715Smarkj		file = preload_search_next_name(file);
252337715Smarkj		if (file == 0)
253337715Smarkj			break;
254337715Smarkj		type = (char *)preload_search_info(file, MODINFO_TYPE);
255337715Smarkj		if (type == NULL || strcmp(type, "cpu_microcode") != 0)
256337715Smarkj			continue;
257337715Smarkj
258337715Smarkj		name = preload_search_info(file, MODINFO_NAME);
259337715Smarkj		preload_delete_name(name);
260337715Smarkj		goto restart;
261337715Smarkj	}
262337715Smarkj}
263337715SmarkjSYSINIT(ucode_release, SI_SUB_KMEM + 1, SI_ORDER_ANY, ucode_release, NULL);
264337715Smarkj
265337715Smarkjvoid
266337715Smarkjucode_load_ap(int cpu)
267337715Smarkj{
268347700Smarkj#ifdef SMP
269337715Smarkj	KASSERT(cpu_info[cpu_apic_ids[cpu]].cpu_present,
270337715Smarkj	    ("cpu %d not present", cpu));
271337715Smarkj
272347700Smarkj	if (cpu_info[cpu_apic_ids[cpu]].cpu_hyperthread)
273347700Smarkj		return;
274347700Smarkj#endif
275347700Smarkj
276347700Smarkj	if (ucode_data != NULL)
277347700Smarkj		(void)ucode_loader->load(ucode_data, false, NULL, NULL);
278337715Smarkj}
279337715Smarkj
280337715Smarkjstatic void *
281347700Smarkjmap_ucode(uintptr_t free, size_t len)
282337715Smarkj{
283347700Smarkj#ifdef __i386__
284347700Smarkj	uintptr_t va;
285337715Smarkj
286347700Smarkj	for (va = free; va < free + len; va += PAGE_SIZE)
287347700Smarkj		pmap_kenter(va, (vm_paddr_t)va);
288337715Smarkj#else
289337715Smarkj	(void)len;
290337715Smarkj#endif
291337715Smarkj	return ((void *)free);
292337715Smarkj}
293337715Smarkj
294337715Smarkjstatic void
295347700Smarkjunmap_ucode(uintptr_t free, size_t len)
296337715Smarkj{
297347700Smarkj#ifdef __i386__
298347700Smarkj	uintptr_t va;
299337715Smarkj
300347700Smarkj	for (va = free; va < free + len; va += PAGE_SIZE)
301347700Smarkj		pmap_kremove(va);
302337715Smarkj#else
303337715Smarkj	(void)free;
304337715Smarkj	(void)len;
305337715Smarkj#endif
306337715Smarkj}
307337715Smarkj
308337715Smarkj/*
309337715Smarkj * Search for an applicable microcode update, and load it.  APs will load the
310337715Smarkj * selected update once they come online.
311337715Smarkj *
312337715Smarkj * "free" is the address of the next free physical page.  If a microcode update
313337715Smarkj * is selected, it will be copied to this region prior to loading in order to
314337715Smarkj * satisfy alignment requirements.
315337715Smarkj */
316337715Smarkjsize_t
317337715Smarkjucode_load_bsp(uintptr_t free)
318337715Smarkj{
319337715Smarkj	union {
320337715Smarkj		uint32_t regs[4];
321337715Smarkj		char vendor[13];
322337715Smarkj	} cpuid;
323337715Smarkj	uint8_t *addr, *fileaddr, *match;
324337715Smarkj	char *type;
325347700Smarkj	uint64_t nrev, orev;
326337715Smarkj	caddr_t file;
327347700Smarkj	size_t i, len;
328347700Smarkj	int error;
329337715Smarkj
330337715Smarkj	KASSERT(free % PAGE_SIZE == 0, ("unaligned boundary %p", (void *)free));
331337715Smarkj
332337715Smarkj	do_cpuid(0, cpuid.regs);
333337715Smarkj	cpuid.regs[0] = cpuid.regs[1];
334337715Smarkj	cpuid.regs[1] = cpuid.regs[3];
335337715Smarkj	cpuid.vendor[12] = '\0';
336347700Smarkj	for (i = 0; i < nitems(loaders); i++)
337337715Smarkj		if (strcmp(cpuid.vendor, loaders[i].vendor) == 0) {
338347700Smarkj			ucode_loader = &loaders[i];
339337715Smarkj			break;
340337715Smarkj		}
341347700Smarkj	if (ucode_loader == NULL)
342337715Smarkj		return (0);
343337715Smarkj
344337715Smarkj	file = 0;
345337715Smarkj	fileaddr = match = NULL;
346337715Smarkj	for (;;) {
347337715Smarkj		file = preload_search_next_name(file);
348337715Smarkj		if (file == 0)
349337715Smarkj			break;
350337715Smarkj		type = (char *)preload_search_info(file, MODINFO_TYPE);
351337715Smarkj		if (type == NULL || strcmp(type, "cpu_microcode") != 0)
352337715Smarkj			continue;
353337715Smarkj
354337715Smarkj		fileaddr = preload_fetch_addr(file);
355337715Smarkj		len = preload_fetch_size(file);
356347700Smarkj		match = ucode_loader->match(fileaddr, &len);
357337715Smarkj		if (match != NULL) {
358337715Smarkj			addr = map_ucode(free, len);
359347700Smarkj			/* We can't use memcpy() before ifunc resolution. */
360347700Smarkj			for (i = 0; i < len; i++)
361347700Smarkj				addr[i] = ((volatile uint8_t *)match)[i];
362337715Smarkj			match = addr;
363337715Smarkj
364347700Smarkj			error = ucode_loader->load(match, false, &nrev, &orev);
365347700Smarkj			if (error == 0) {
366347700Smarkj				ucode_data = early_ucode_data = match;
367347700Smarkj				ucode_nrev = nrev;
368347700Smarkj				ucode_orev = orev;
369347700Smarkj				return (len);
370337715Smarkj			}
371337715Smarkj			unmap_ucode(free, len);
372337715Smarkj		}
373337715Smarkj	}
374347700Smarkj	if (fileaddr != NULL && ucode_error == NO_ERROR)
375347700Smarkj		ucode_error = NO_MATCH;
376347700Smarkj	return (0);
377337715Smarkj}
378337715Smarkj
379337715Smarkj/*
380337715Smarkj * Reload microcode following an ACPI resume.
381337715Smarkj */
382337715Smarkjvoid
383337715Smarkjucode_reload(void)
384337715Smarkj{
385337715Smarkj
386337715Smarkj	ucode_load_ap(PCPU_GET(cpuid));
387337715Smarkj}
388337715Smarkj
389337715Smarkj/*
390337715Smarkj * Replace an existing microcode update.
391337715Smarkj */
392337715Smarkjvoid *
393337715Smarkjucode_update(void *newdata)
394337715Smarkj{
395337715Smarkj
396337715Smarkj	newdata = (void *)atomic_swap_ptr((void *)&ucode_data,
397337715Smarkj	    (uintptr_t)newdata);
398337715Smarkj	if (newdata == early_ucode_data)
399337715Smarkj		newdata = NULL;
400337715Smarkj	return (newdata);
401337715Smarkj}
402