xref: /illumos-gate/usr/src/uts/intel/os/microcode.c (revision d32f26ee)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
27  * Copyright (c) 2018, Joyent, Inc.
28  * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
29  * Copyright 2023 Oxide Computer Company
30  */
31 
32 #include <sys/bootconf.h>
33 #include <sys/cmn_err.h>
34 #include <sys/controlregs.h>
35 #include <sys/utsname.h>
36 #include <sys/debug.h>
37 #include <sys/kobj.h>
38 #include <sys/kobj_impl.h>
39 #include <sys/ontrap.h>
40 #include <sys/systeminfo.h>
41 #include <sys/systm.h>
42 #include <sys/ucode.h>
43 #include <sys/x86_archext.h>
44 #include <sys/x_call.h>
45 
46 /*
47  * mcpu_ucode_info for the boot CPU.  Statically allocated.
48  */
49 static struct cpu_ucode_info cpu_ucode_info0;
50 static const ucode_source_t *ucode;
51 static char *ucodepath;
52 static kmutex_t ucode_lock;
53 static bool ucode_cleanup_done = false;
54 
55 static const char ucode_failure_fmt[] =
56 	"cpu%d: failed to update microcode from version 0x%x to 0x%x";
57 static const char ucode_success_fmt[] =
58 	"?cpu%d: microcode has been updated from version 0x%x to 0x%x\n";
59 
60 SET_DECLARE(ucode_source_set, ucode_source_t);
61 
62 /*
63  * Force flag.  If set, the first microcode binary that matches
64  * signature and platform id will be used for microcode update,
65  * regardless of version.  Should only be used for debugging.
66  */
67 int ucode_force_update = 0;
68 
69 void
70 ucode_init(void)
71 {
72 	mutex_init(&ucode_lock, NULL, MUTEX_DEFAULT, NULL);
73 }
74 
75 /*
76  * Allocate space for mcpu_ucode_info in the machcpu structure
77  * for all non-boot CPUs.
78  */
79 void
80 ucode_alloc_space(cpu_t *cp)
81 {
82 	ASSERT(cp->cpu_id != 0);
83 	ASSERT(cp->cpu_m.mcpu_ucode_info == NULL);
84 	cp->cpu_m.mcpu_ucode_info =
85 	    kmem_zalloc(sizeof (*cp->cpu_m.mcpu_ucode_info), KM_SLEEP);
86 }
87 
88 void
89 ucode_free_space(cpu_t *cp)
90 {
91 	ASSERT(cp->cpu_m.mcpu_ucode_info != NULL);
92 	ASSERT(cp->cpu_m.mcpu_ucode_info != &cpu_ucode_info0);
93 	kmem_free(cp->cpu_m.mcpu_ucode_info,
94 	    sizeof (*cp->cpu_m.mcpu_ucode_info));
95 	cp->cpu_m.mcpu_ucode_info = NULL;
96 }
97 
98 const char *
99 ucode_path(void)
100 {
101 	ASSERT(ucodepath != NULL);
102 	return (ucodepath);
103 }
104 
105 /*
106  * Allocate/free a buffer used to hold ucode data. Space for the boot CPU is
107  * allocated with BOP_ALLOC() and does not require a free.
108  */
109 void *
110 ucode_zalloc(processorid_t id, size_t size)
111 {
112 	if (id != 0)
113 		return (kmem_zalloc(size, KM_NOSLEEP));
114 
115 	/* BOP_ALLOC() failure results in panic */
116 	return (BOP_ALLOC(bootops, NULL, size, MMU_PAGESIZE));
117 }
118 
119 void
120 ucode_free(processorid_t id, void *buf, size_t size)
121 {
122 	if (id != 0 && buf != NULL)
123 		kmem_free(buf, size);
124 }
125 
126 /*
127  * Called to free up space allocated for the microcode file. This is called
128  * from start_other_cpus() after an update attempt has been performed on all
129  * CPUs.
130  */
131 void
132 ucode_cleanup(void)
133 {
134 	mutex_enter(&ucode_lock);
135 	if (ucode != NULL)
136 		ucode->us_file_reset(-1);
137 	ucode_cleanup_done = true;
138 	mutex_exit(&ucode_lock);
139 
140 	/*
141 	 * We purposefully do not free 'ucodepath' here so that it persists for
142 	 * any future callers to ucode_check(), such as could occur on systems
143 	 * that support DR.
144 	 */
145 }
146 
147 static int
148 ucode_write(xc_arg_t arg1, xc_arg_t unused2, xc_arg_t unused3)
149 {
150 	ucode_update_t *uusp = (ucode_update_t *)arg1;
151 	cpu_ucode_info_t *uinfop = CPU->cpu_m.mcpu_ucode_info;
152 	on_trap_data_t otd;
153 
154 	ASSERT(ucode != NULL);
155 	ASSERT(uusp->ucodep != NULL);
156 
157 	/*
158 	 * Check one more time to see if it is really necessary to update
159 	 * microcode just in case this is a hyperthreaded processor where
160 	 * the threads share the same microcode.
161 	 */
162 	if (!ucode_force_update) {
163 		ucode->us_read_rev(uinfop);
164 		uusp->new_rev = uinfop->cui_rev;
165 		if (uinfop->cui_rev >= uusp->expected_rev)
166 			return (0);
167 	}
168 
169 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
170 		if (ucode->us_invalidate) {
171 			/*
172 			 * On some platforms a cache invalidation is required
173 			 * for the ucode update to be successful due to the
174 			 * parts of the processor that the microcode is
175 			 * updating.
176 			 */
177 			invalidate_cache();
178 		}
179 		wrmsr(ucode->us_write_msr, (uintptr_t)uusp->ucodep);
180 	}
181 
182 	no_trap();
183 	ucode->us_read_rev(uinfop);
184 	uusp->new_rev = uinfop->cui_rev;
185 
186 	return (0);
187 }
188 
189 /*
190  * Entry points to microcode update from the 'ucode' driver.
191  */
192 
193 ucode_errno_t
194 ucode_validate(uint8_t *ucodep, int size)
195 {
196 	if (ucode == NULL)
197 		return (EM_NOTSUP);
198 	return (ucode->us_validate(ucodep, size));
199 }
200 
201 ucode_errno_t
202 ucode_update(uint8_t *ucodep, int size)
203 {
204 	int		found = 0;
205 	ucode_update_t	cached = { 0 };
206 	ucode_update_t	*cachedp = NULL;
207 	ucode_errno_t	rc = EM_OK;
208 	ucode_errno_t	search_rc = EM_NOMATCH; /* search result */
209 	cpuset_t cpuset;
210 
211 	ASSERT(ucode != 0);
212 	ASSERT(ucodep != 0);
213 	CPUSET_ZERO(cpuset);
214 
215 	if (!ucode->us_capable(CPU))
216 		return (EM_NOTSUP);
217 
218 	mutex_enter(&cpu_lock);
219 
220 	for (processorid_t id = 0; id < max_ncpus; id++) {
221 		cpu_t *cpu;
222 		ucode_update_t uus = { 0 };
223 		ucode_update_t *uusp = &uus;
224 
225 		/*
226 		 * If there is no such CPU or it is not xcall ready, skip it.
227 		 */
228 		if ((cpu = cpu_get(id)) == NULL ||
229 		    !(cpu->cpu_flags & CPU_READY)) {
230 			continue;
231 		}
232 
233 		uusp->sig = cpuid_getsig(cpu);
234 		bcopy(cpu->cpu_m.mcpu_ucode_info, &uusp->info,
235 		    sizeof (uusp->info));
236 
237 		/*
238 		 * If the current CPU has the same signature and platform
239 		 * id as the previous one we processed, reuse the information.
240 		 */
241 		if (cachedp && cachedp->sig == cpuid_getsig(cpu) &&
242 		    cachedp->info.cui_platid == uusp->info.cui_platid) {
243 			uusp->ucodep = cachedp->ucodep;
244 			uusp->expected_rev = cachedp->expected_rev;
245 			/*
246 			 * Intuitively we should check here to see whether the
247 			 * running microcode rev is >= the expected rev, and
248 			 * quit if it is.  But we choose to proceed with the
249 			 * xcall regardless of the running version so that
250 			 * the other threads in an HT processor can update
251 			 * the cpu_ucode_info structure in machcpu.
252 			 */
253 		} else if ((search_rc = ucode->us_extract(uusp, ucodep, size))
254 		    == EM_OK) {
255 			bcopy(uusp, &cached, sizeof (cached));
256 			cachedp = &cached;
257 			found = 1;
258 		}
259 
260 		/* Nothing to do */
261 		if (uusp->ucodep == NULL)
262 			continue;
263 
264 		CPUSET_ADD(cpuset, id);
265 		kpreempt_disable();
266 		xc_sync((xc_arg_t)uusp, 0, 0, CPUSET2BV(cpuset), ucode_write);
267 		kpreempt_enable();
268 		CPUSET_DEL(cpuset, id);
269 
270 		if (uusp->new_rev != 0 && uusp->info.cui_rev == uusp->new_rev &&
271 		    !ucode_force_update) {
272 			rc = EM_HIGHERREV;
273 		} else if ((uusp->new_rev == 0) || (uusp->expected_rev != 0 &&
274 		    uusp->expected_rev != uusp->new_rev)) {
275 			cmn_err(CE_WARN, ucode_failure_fmt,
276 			    id, uusp->info.cui_rev, uusp->expected_rev);
277 			rc = EM_UPDATE;
278 		} else {
279 			cmn_err(CE_CONT, ucode_success_fmt,
280 			    id, uusp->info.cui_rev, uusp->new_rev);
281 		}
282 	}
283 
284 	mutex_exit(&cpu_lock);
285 
286 	if (!found) {
287 		rc = search_rc;
288 	} else if (rc == EM_OK) {
289 		cpuid_post_ucodeadm();
290 	}
291 
292 	return (rc);
293 }
294 
295 /*
296  * Entry point to microcode update from mlsetup() and mp_startup()
297  * Initialize mcpu_ucode_info, and perform microcode update if necessary.
298  * cpuid_info must be initialized before ucode_check can be called.
299  */
300 void
301 ucode_check(cpu_t *cp)
302 {
303 	cpu_ucode_info_t *uinfop;
304 	ucode_errno_t rc = EM_OK;
305 	bool bsp = (cp->cpu_id == 0);
306 
307 	ASSERT(cp != NULL);
308 
309 	mutex_enter(&ucode_lock);
310 
311 	if (bsp) {
312 		/* Space statically allocated for BSP; ensure pointer is set */
313 		if (cp->cpu_m.mcpu_ucode_info == NULL)
314 			cp->cpu_m.mcpu_ucode_info = &cpu_ucode_info0;
315 
316 		/* Set up function pointers if not already done */
317 		if (ucode == NULL) {
318 			ucode_source_t **src;
319 
320 			SET_FOREACH(src, ucode_source_set) {
321 				if ((*src)->us_select(cp)) {
322 					ucode = *src;
323 					break;
324 				}
325 			}
326 
327 			if (ucode == NULL) {
328 				cmn_err(CE_CONT,
329 				    "?ucode: unsupported processor\n");
330 				goto out;
331 			}
332 
333 			cmn_err(CE_CONT, "?ucode: selected %s\n",
334 			    ucode->us_name);
335 		}
336 	}
337 
338 	if (ucode == NULL)
339 		goto out;
340 
341 	if (ucodepath == NULL) {
342 		size_t sz;
343 		char *plat;
344 
345 		if (bsp) {
346 			const char *prop = "impl-arch-name";
347 			int len;
348 
349 			len = BOP_GETPROPLEN(bootops, prop);
350 
351 			if (len <= 0) {
352 				cmn_err(CE_WARN,
353 				    "ucode: could not find %s property", prop);
354 				goto out;
355 			}
356 
357 			/*
358 			 * On the BSP, this memory is allocated via BOP_ALLOC()
359 			 * -- which panics on failure -- and does not need to
360 			 * be explicitly freed.
361 			 */
362 			plat = ucode_zalloc(cp->cpu_id, len + 1);
363 			(void) BOP_GETPROP(bootops, prop, plat);
364 		} else {
365 			/*
366 			 * from common/conf/param.c, already filled in by
367 			 * setup_ddi() by this point.
368 			 */
369 			plat = platform;
370 		}
371 		if (plat[0] == '\0') {
372 			/*
373 			 * If we can't determine the architecture name,
374 			 * we cannot find microcode files for it.
375 			 * Return without setting 'ucode'.
376 			 */
377 			cmn_err(CE_WARN, "ucode: could not determine arch");
378 			goto out;
379 		}
380 
381 		sz = snprintf(NULL, 0, "/platform/%s/ucode", plat) + 1;
382 		/*
383 		 * Note that on the boot CPU, this allocation will be satisfied
384 		 * via BOP_ALLOC() and the returned address will not be valid
385 		 * once we come back into this function for the remaining CPUs.
386 		 * To deal with this, we throw the memory away at the end of
387 		 * this function if we are the BSP. The next CPU through here
388 		 * will re-create it using kmem and then it persists.
389 		 */
390 		ucodepath = ucode_zalloc(cp->cpu_id, sz);
391 		if (ucodepath == NULL) {
392 			cmn_err(CE_WARN,
393 			    "ucode: could not allocate memory for path");
394 			goto out;
395 		}
396 		(void) snprintf(ucodepath, sz, "/platform/%s/ucode", plat);
397 	}
398 
399 	uinfop = cp->cpu_m.mcpu_ucode_info;
400 	ASSERT(uinfop != NULL);
401 
402 	if (!ucode->us_capable(cp))
403 		goto out;
404 
405 	ucode->us_read_rev(uinfop);
406 
407 	/*
408 	 * Check to see if we need ucode update
409 	 */
410 	if ((rc = ucode->us_locate(cp, uinfop)) == EM_OK) {
411 		uint32_t old_rev, new_rev;
412 
413 		old_rev = uinfop->cui_rev;
414 		new_rev = ucode->us_load(uinfop);
415 
416 		if (uinfop->cui_rev != new_rev) {
417 			cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id,
418 			    old_rev, new_rev);
419 		} else {
420 			cmn_err(CE_CONT, ucode_success_fmt, cp->cpu_id,
421 			    old_rev, new_rev);
422 		}
423 	}
424 
425 	/*
426 	 * If we fail to find a match for any reason, free the file structure
427 	 * just in case we have read in a partial file.
428 	 *
429 	 * Since the scratch memory for holding the microcode for the boot CPU
430 	 * came from BOP_ALLOC, we will reset the data structure as if we
431 	 * never did the allocation so we don't have to keep track of this
432 	 * special chunk of memory.  We free the memory used for the rest
433 	 * of the CPUs in start_other_cpus().
434 	 *
435 	 * In case we end up here after ucode_cleanup() has been called, such
436 	 * as could occur with CPU hotplug, we also clear the memory and reset
437 	 * the data structure as nothing else will call ucode_cleanup() and we
438 	 * don't need to cache the data as we do during boot when starting the
439 	 * APs.
440 	 */
441 	if (rc != EM_OK || bsp || ucode_cleanup_done)
442 		ucode->us_file_reset(cp->cpu_id);
443 
444 out:
445 	/*
446 	 * If this is the boot CPU, discard the memory that came from BOP_ALLOC
447 	 * and was used to build the ucode path.
448 	 */
449 	if (bsp)
450 		ucodepath = NULL;
451 
452 	mutex_exit(&ucode_lock);
453 }
454 
455 /*
456  * Returns microcode revision from the machcpu structure.
457  */
458 ucode_errno_t
459 ucode_get_rev(uint32_t *revp)
460 {
461 	int i;
462 
463 	ASSERT(ucode != NULL);
464 	ASSERT(revp != NULL);
465 
466 	if (!ucode->us_capable(CPU))
467 		return (EM_NOTSUP);
468 
469 	mutex_enter(&cpu_lock);
470 	for (i = 0; i < max_ncpus; i++) {
471 		cpu_t *cpu;
472 
473 		if ((cpu = cpu_get(i)) == NULL)
474 			continue;
475 
476 		revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev;
477 	}
478 	mutex_exit(&cpu_lock);
479 
480 	return (EM_OK);
481 }
482