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
ucode_init(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
ucode_alloc_space(cpu_t * cp)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
ucode_free_space(cpu_t * cp)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 *
ucode_path(void)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 *
ucode_zalloc(processorid_t id,size_t size)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
ucode_free(processorid_t id,void * buf,size_t size)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
ucode_cleanup(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
ucode_write(xc_arg_t arg1,xc_arg_t unused2,xc_arg_t unused3)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
ucode_validate(uint8_t * ucodep,int size)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
ucode_update(uint8_t * ucodep,int size)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
ucode_check(cpu_t * cp)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 goto out;
329
330 #ifdef DEBUG
331 cmn_err(CE_CONT, "?ucode: selected %s\n",
332 ucode->us_name);
333 #endif
334 }
335 }
336
337 if (ucode == NULL)
338 goto out;
339
340 if (ucodepath == NULL) {
341 size_t sz;
342 char *plat;
343
344 if (bsp) {
345 const char *prop = "impl-arch-name";
346 int len;
347
348 len = BOP_GETPROPLEN(bootops, prop);
349
350 if (len <= 0) {
351 cmn_err(CE_WARN,
352 "ucode: could not find %s property", prop);
353 goto out;
354 }
355
356 /*
357 * On the BSP, this memory is allocated via BOP_ALLOC()
358 * -- which panics on failure -- and does not need to
359 * be explicitly freed.
360 */
361 plat = ucode_zalloc(cp->cpu_id, len + 1);
362 (void) BOP_GETPROP(bootops, prop, plat);
363 } else {
364 /*
365 * from common/conf/param.c, already filled in by
366 * setup_ddi() by this point.
367 */
368 plat = platform;
369 }
370 if (plat[0] == '\0') {
371 /*
372 * If we can't determine the architecture name,
373 * we cannot find microcode files for it.
374 * Return without setting 'ucode'.
375 */
376 cmn_err(CE_WARN, "ucode: could not determine arch");
377 goto out;
378 }
379
380 sz = snprintf(NULL, 0, "/platform/%s/ucode", plat) + 1;
381 /*
382 * Note that on the boot CPU, this allocation will be satisfied
383 * via BOP_ALLOC() and the returned address will not be valid
384 * once we come back into this function for the remaining CPUs.
385 * To deal with this, we throw the memory away at the end of
386 * this function if we are the BSP. The next CPU through here
387 * will re-create it using kmem and then it persists.
388 */
389 ucodepath = ucode_zalloc(cp->cpu_id, sz);
390 if (ucodepath == NULL) {
391 cmn_err(CE_WARN,
392 "ucode: could not allocate memory for path");
393 goto out;
394 }
395 (void) snprintf(ucodepath, sz, "/platform/%s/ucode", plat);
396 }
397
398 uinfop = cp->cpu_m.mcpu_ucode_info;
399 ASSERT(uinfop != NULL);
400
401 if (!ucode->us_capable(cp))
402 goto out;
403
404 ucode->us_read_rev(uinfop);
405
406 /*
407 * Check to see if we need ucode update
408 */
409 if ((rc = ucode->us_locate(cp, uinfop)) == EM_OK) {
410 uint32_t old_rev, new_rev;
411
412 old_rev = uinfop->cui_rev;
413 new_rev = ucode->us_load(uinfop);
414
415 if (uinfop->cui_rev != new_rev) {
416 cmn_err(CE_WARN, ucode_failure_fmt, cp->cpu_id,
417 old_rev, new_rev);
418 } else {
419 cmn_err(CE_CONT, ucode_success_fmt, cp->cpu_id,
420 old_rev, new_rev);
421 }
422 }
423
424 /*
425 * If we fail to find a match for any reason, free the file structure
426 * just in case we have read in a partial file.
427 *
428 * Since the scratch memory for holding the microcode for the boot CPU
429 * came from BOP_ALLOC, we will reset the data structure as if we
430 * never did the allocation so we don't have to keep track of this
431 * special chunk of memory. We free the memory used for the rest
432 * of the CPUs in start_other_cpus().
433 *
434 * In case we end up here after ucode_cleanup() has been called, such
435 * as could occur with CPU hotplug, we also clear the memory and reset
436 * the data structure as nothing else will call ucode_cleanup() and we
437 * don't need to cache the data as we do during boot when starting the
438 * APs.
439 */
440 if (rc != EM_OK || bsp || ucode_cleanup_done)
441 ucode->us_file_reset(cp->cpu_id);
442
443 out:
444 /*
445 * If this is the boot CPU, discard the memory that came from BOP_ALLOC
446 * and was used to build the ucode path.
447 */
448 if (bsp)
449 ucodepath = NULL;
450
451 mutex_exit(&ucode_lock);
452 }
453
454 /*
455 * Returns microcode revision from the machcpu structure.
456 */
457 ucode_errno_t
ucode_get_rev(uint32_t * revp)458 ucode_get_rev(uint32_t *revp)
459 {
460 int i;
461
462 ASSERT(revp != NULL);
463
464 if (ucode == NULL || !ucode->us_capable(CPU))
465 return (EM_NOTSUP);
466
467 mutex_enter(&cpu_lock);
468 for (i = 0; i < max_ncpus; i++) {
469 cpu_t *cpu;
470
471 if ((cpu = cpu_get(i)) == NULL)
472 continue;
473
474 revp[i] = cpu->cpu_m.mcpu_ucode_info->cui_rev;
475 }
476 mutex_exit(&cpu_lock);
477
478 return (EM_OK);
479 }
480