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