xref: /freebsd/sys/x86/x86/ucode.c (revision 4e8d558c)
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/cdefs.h>
32 __FBSDID("$FreeBSD$");
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 
56 static void	*ucode_intel_match(uint8_t *data, size_t *len);
57 static int	ucode_intel_verify(struct ucode_intel_header *hdr,
58 		    size_t resid);
59 
60 static 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. */
73 static void *early_ucode_data;
74 static void *ucode_data;
75 static struct ucode_ops *ucode_loader;
76 
77 /* Variables used for reporting success or failure. */
78 enum {
79 	NO_ERROR,
80 	NO_MATCH,
81 	VERIFICATION_FAILED,
82 } ucode_error = NO_ERROR;
83 static uint64_t ucode_nrev, ucode_orev;
84 
85 static void
86 log_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 }
106 SYSINIT(ucode_log, SI_SUB_CPU, SI_ORDER_FIRST, log_msg, NULL);
107 
108 int
109 ucode_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 
145 static int
146 ucode_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 
174 static void *
175 ucode_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  */
234 static void
235 ucode_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 
248 restart:
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 }
263 SYSINIT(ucode_release, SI_SUB_SMP + 1, SI_ORDER_ANY, ucode_release, NULL);
264 
265 void
266 ucode_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 
280 static void *
281 map_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 
294 static void
295 unmap_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  */
316 size_t
317 ucode_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 			memcpy_early(addr, match, len);
361 			match = addr;
362 
363 			error = ucode_loader->load(match, false, &nrev, &orev);
364 			if (error == 0) {
365 				ucode_data = early_ucode_data = match;
366 				ucode_nrev = nrev;
367 				ucode_orev = orev;
368 				return (len);
369 			}
370 			unmap_ucode(free, len);
371 		}
372 	}
373 	if (fileaddr != NULL && ucode_error == NO_ERROR)
374 		ucode_error = NO_MATCH;
375 	return (0);
376 }
377 
378 /*
379  * Reload microcode following an ACPI resume.
380  */
381 void
382 ucode_reload(void)
383 {
384 
385 	ucode_load_ap(PCPU_GET(cpuid));
386 }
387 
388 /*
389  * Replace an existing microcode update.
390  */
391 void *
392 ucode_update(void *newdata)
393 {
394 
395 	newdata = (void *)atomic_swap_ptr((void *)&ucode_data,
396 	    (uintptr_t)newdata);
397 	if (newdata == early_ucode_data)
398 		newdata = NULL;
399 	return (newdata);
400 }
401