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