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 2025 Oxide Computer Company
30 */
31
32 #include <sys/stdbool.h>
33 #include <sys/cmn_err.h>
34 #include <sys/controlregs.h>
35 #include <sys/kobj.h>
36 #include <sys/kobj_impl.h>
37 #include <sys/machparam.h>
38 #include <sys/ontrap.h>
39 #include <sys/sysmacros.h>
40 #include <sys/systm.h>
41 #include <sys/ucode.h>
42 #include <sys/ucode_amd.h>
43 #include <ucode/ucode_errno.h>
44 #include <ucode/ucode_utils_amd.h>
45 #include <sys/x86_archext.h>
46
47 extern void *ucode_zalloc(size_t);
48 extern void ucode_free(void *, size_t);
49 extern const char *ucode_path(void);
50 extern int ucode_force_update;
51 extern bool ucode_use_kmem;
52
53 static ucode_file_amd_t *amd_ucodef;
54 static size_t amd_ucodef_len, amd_ucodef_buflen;
55 static ucode_eqtbl_amd_t *ucode_eqtbl_amd;
56 static uint_t ucode_eqtbl_amd_entries;
57 static bool ucode_amd_fallback = false;
58
59 /*
60 * Check whether this module can be used for microcode updates on this
61 * platform.
62 */
63 static bool
ucode_select_amd(cpu_t * cp)64 ucode_select_amd(cpu_t *cp)
65 {
66 if ((get_hwenv() & HW_VIRTUAL) != 0)
67 return (false);
68
69 return (cpuid_getvendor(cp) == X86_VENDOR_AMD);
70 }
71
72 /*
73 * Check whether or not a processor is capable of microcode operations
74 *
75 * At this point we only support microcode update for:
76 * - AMD processors family 0x10 and above.
77 */
78 static bool
ucode_capable_amd(cpu_t * cp)79 ucode_capable_amd(cpu_t *cp)
80 {
81 return (cpuid_getfamily(cp) >= 0x10);
82 }
83
84 /*
85 * Called when it is no longer necessary to keep the microcode around,
86 * or when the cached microcode doesn't match the CPU being processed.
87 */
88 static void
ucode_file_reset_amd(void)89 ucode_file_reset_amd(void)
90 {
91 if (amd_ucodef == NULL)
92 return;
93
94 ucode_free(amd_ucodef, amd_ucodef_buflen);
95 amd_ucodef = NULL;
96 amd_ucodef_buflen = amd_ucodef_len = 0;
97 }
98
99 /*
100 * Find the equivalent CPU id in the equivalence table.
101 */
102 static ucode_errno_t
ucode_equiv_cpu_amd(cpu_t * cp,uint16_t * eq_sig)103 ucode_equiv_cpu_amd(cpu_t *cp, uint16_t *eq_sig)
104 {
105 char *name = NULL;
106 int cpi_sig = cpuid_getsig(cp);
107 ucode_errno_t ret = EM_OK;
108
109 if (ucode_eqtbl_amd == NULL) {
110 name = ucode_zalloc(MAXPATHLEN);
111 if (name == NULL)
112 return (EM_NOMEM);
113
114 (void) snprintf(name, MAXPATHLEN, "%s/%s/%s",
115 ucode_path(), cpuid_getvendorstr(cp),
116 UCODE_AMD_EQUIVALENCE_TABLE_NAME);
117 }
118
119 if (!ucode_use_kmem) {
120 /*
121 * No kmem_zalloc() etc. available yet.
122 */
123 ucode_eqtbl_amd_t eqtbl;
124 int count, offset = 0;
125 intptr_t fd;
126
127 ASSERT3P(name, !=, NULL);
128
129 if ((fd = kobj_open(name)) == -1) {
130 ret = EM_OPENFILE;
131 goto out;
132 }
133 do {
134 count = kobj_read(fd, (int8_t *)&eqtbl,
135 sizeof (eqtbl), offset);
136 if (count != sizeof (eqtbl)) {
137 (void) kobj_close(fd);
138 ret = EM_HIGHERREV;
139 goto out;
140 }
141 offset += count;
142 } while (eqtbl.ue_inst_cpu != 0 &&
143 eqtbl.ue_inst_cpu != cpi_sig);
144 (void) kobj_close(fd);
145 *eq_sig = eqtbl.ue_equiv_cpu;
146 } else {
147 ucode_eqtbl_amd_t *eqtbl;
148
149 /*
150 * If not already done, load the equivalence table.
151 */
152 if (ucode_eqtbl_amd == NULL) {
153 struct _buf *eq;
154 uint64_t size;
155 int count;
156
157 ASSERT3P(name, !=, NULL);
158
159 if ((eq = kobj_open_file(name)) == (struct _buf *)-1) {
160 ret = EM_OPENFILE;
161 goto out;
162 }
163
164 if (kobj_get_filesize(eq, &size) < 0) {
165 kobj_close_file(eq);
166 ret = EM_OPENFILE;
167 goto out;
168 }
169
170 if (size == 0 ||
171 size % sizeof (*ucode_eqtbl_amd) != 0) {
172 kobj_close_file(eq);
173 ret = EM_HIGHERREV;
174 goto out;
175 }
176
177 ucode_eqtbl_amd = kmem_zalloc(size, KM_NOSLEEP);
178 if (ucode_eqtbl_amd == NULL) {
179 kobj_close_file(eq);
180 ret = EM_NOMEM;
181 goto out;
182 }
183 count = kobj_read_file(eq, (char *)ucode_eqtbl_amd,
184 size, 0);
185 kobj_close_file(eq);
186
187 if (count != size) {
188 ucode_eqtbl_amd_entries = 0;
189 ret = EM_FILESIZE;
190 goto out;
191 }
192
193 ucode_eqtbl_amd_entries =
194 size / sizeof (*ucode_eqtbl_amd);
195 }
196
197 eqtbl = ucode_eqtbl_amd;
198 *eq_sig = 0;
199 for (uint_t i = 0; i < ucode_eqtbl_amd_entries; i++, eqtbl++) {
200 if (eqtbl->ue_inst_cpu == 0) {
201 /* End of table */
202 ret = EM_HIGHERREV;
203 goto out;
204 }
205 if (eqtbl->ue_inst_cpu == cpi_sig) {
206 *eq_sig = eqtbl->ue_equiv_cpu;
207 ret = EM_OK;
208 goto out;
209 }
210 }
211 /*
212 * No equivalent CPU id found, assume outdated microcode file.
213 */
214 ret = EM_HIGHERREV;
215 }
216
217 out:
218 ucode_free(name, MAXPATHLEN);
219
220 return (ret);
221 }
222
223 static ucode_errno_t
ucode_match_amd(uint16_t eq_sig,cpu_ucode_info_t * uinfop,ucode_file_amd_t * ucodefp,size_t size)224 ucode_match_amd(uint16_t eq_sig, cpu_ucode_info_t *uinfop,
225 ucode_file_amd_t *ucodefp, size_t size)
226 {
227 ucode_header_amd_t *uh;
228
229 if (ucodefp == NULL || size < sizeof (ucode_header_amd_t))
230 return (EM_NOMATCH);
231
232 uh = &ucodefp->uf_header;
233
234 /*
235 * Don't even think about loading patches that would require code
236 * execution. Does not apply to patches for family 0x14 and beyond.
237 */
238 if (uh->uh_cpu_rev < 0x5000 &&
239 size > offsetof(ucode_file_amd_t, uf_code_present) &&
240 ucodefp->uf_code_present) {
241 return (EM_NOMATCH);
242 }
243
244 if (eq_sig != uh->uh_cpu_rev)
245 return (EM_NOMATCH);
246
247 if (uh->uh_nb_id) {
248 cmn_err(CE_WARN, "ignoring northbridge-specific ucode: "
249 "chipset id %x, revision %x", uh->uh_nb_id, uh->uh_nb_rev);
250 return (EM_NOMATCH);
251 }
252
253 if (uh->uh_sb_id) {
254 cmn_err(CE_WARN, "ignoring southbridge-specific ucode: "
255 "chipset id %x, revision %x", uh->uh_sb_id, uh->uh_sb_rev);
256 return (EM_NOMATCH);
257 }
258
259 if (uh->uh_patch_id <= uinfop->cui_rev && !ucode_force_update)
260 return (EM_HIGHERREV);
261
262 return (EM_OK);
263 }
264
265 /*
266 * Copy the given ucode into cpu_ucode_info_t in preparation for loading onto
267 * the corresponding CPU via ucode_load_amd().
268 */
269 static ucode_errno_t
ucode_copy_amd(cpu_ucode_info_t * uinfop,const ucode_file_amd_t * ucodefp,size_t size)270 ucode_copy_amd(cpu_ucode_info_t *uinfop, const ucode_file_amd_t *ucodefp,
271 size_t size)
272 {
273 ASSERT3P(uinfop->cui_pending_ucode, ==, NULL);
274 ASSERT3U(size, <=, UCODE_AMD_MAXSIZE);
275
276 uinfop->cui_pending_ucode = ucode_zalloc(size);
277 if (uinfop->cui_pending_ucode == NULL)
278 return (EM_NOMEM);
279
280 (void) memcpy(uinfop->cui_pending_ucode, ucodefp, size);
281 uinfop->cui_pending_size = size;
282 uinfop->cui_pending_rev = ucodefp->uf_header.uh_patch_id;
283
284 return (EM_OK);
285 }
286
287 /*
288 * Populate the ucode file structure from the microcode file corresponding to
289 * this CPU, if exists.
290 *
291 * Return EM_OK on success, corresponding error code on failure.
292 */
293 static ucode_errno_t
i_ucode_locate_amd(cpu_t * cp,cpu_ucode_info_t * uinfop,bool fallback)294 i_ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop, bool fallback)
295 {
296 uint16_t eq_sig;
297 ucode_errno_t rc;
298
299 /* get equivalent CPU id */
300 eq_sig = 0;
301 if ((rc = ucode_equiv_cpu_amd(cp, &eq_sig)) != EM_OK)
302 return (rc);
303
304 /*
305 * Allocate a buffer for the microcode patch. If the buffer has been
306 * allocated before, check for a matching microcode to avoid loading
307 * the file again.
308 */
309 if (amd_ucodef == NULL) {
310 size_t len = PAGESIZE;
311
312 amd_ucodef = ucode_zalloc(len);
313 if (amd_ucodef == NULL)
314 return (EM_NOMEM);
315 amd_ucodef_buflen = len;
316 } else {
317 rc = ucode_match_amd(eq_sig, uinfop, amd_ucodef,
318 amd_ucodef_len);
319 if (rc == EM_HIGHERREV)
320 return (rc);
321 if (rc == EM_OK) {
322 return (ucode_copy_amd(uinfop, amd_ucodef,
323 amd_ucodef_len));
324 }
325 }
326
327 /*
328 * Find the patch for this CPU. The patch files are named XXXX-YY, where
329 * XXXX is the equivalent CPU id and YY is the running patch number.
330 * Patches specific to certain chipsets are guaranteed to have lower
331 * numbers than less specific patches, so we can just load the first
332 * patch that matches.
333 */
334 for (uint_t i = 0; i < 0xff; i++) {
335 char name[MAXPATHLEN];
336 intptr_t fd;
337 int count;
338 /* This is a uint_t to match the signature of kobj_read() */
339 uint_t size;
340
341 (void) snprintf(name, MAXPATHLEN, "%s/%s/%s%04X-%02X",
342 ucode_path(), cpuid_getvendorstr(cp),
343 fallback ? "fallback/" : "", eq_sig, i);
344
345 if ((fd = kobj_open(name)) == -1)
346 return (EM_NOMATCH);
347
348 /*
349 * Since this code will run for the boot CPU before kmem is
350 * initialised we can't use the kobj_*_file() functions.
351 * In the case where the archive contains compressed files,
352 * kobj_fstat() will return the compressed size and so we must
353 * read the entire file through to determine its size.
354 */
355 size = 0;
356 do {
357 count = kobj_read(fd, (char *)amd_ucodef,
358 amd_ucodef_buflen, size);
359 if (count < 0) {
360 (void) kobj_close(fd);
361 return (EM_OPENFILE);
362 }
363 size += count;
364 } while (count == amd_ucodef_buflen &&
365 size <= UCODE_AMD_MAXSIZE);
366
367 if (size > UCODE_AMD_MAXSIZE) {
368 (void) kobj_close(fd);
369 cmn_err(CE_WARN, "ucode: microcode file %s is "
370 "too large (over 0x%x bytes)", name,
371 UCODE_AMD_MAXSIZE);
372 return (EM_FILESIZE);
373 }
374
375 if (size > amd_ucodef_buflen) {
376 size_t len = P2ROUNDUP(size, PAGESIZE);
377
378 ucode_file_reset_amd();
379 amd_ucodef = ucode_zalloc(len);
380 if (amd_ucodef == NULL) {
381 (void) kobj_close(fd);
382 return (EM_NOMEM);
383 }
384 amd_ucodef_buflen = len;
385 }
386
387 count = kobj_read(fd, (char *)amd_ucodef, amd_ucodef_buflen, 0);
388 (void) kobj_close(fd);
389 if (count < 0 || count != size)
390 return (EM_OPENFILE);
391
392 amd_ucodef_len = count;
393
394 rc = ucode_match_amd(eq_sig, uinfop, amd_ucodef,
395 amd_ucodef_len);
396 if (rc == EM_HIGHERREV)
397 return (rc);
398 if (rc == EM_OK) {
399 return (ucode_copy_amd(uinfop, amd_ucodef,
400 amd_ucodef_len));
401 }
402 }
403 return (EM_NOMATCH);
404 }
405
406 ucode_errno_t
ucode_locate_amd(cpu_t * cp,cpu_ucode_info_t * uinfop)407 ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop)
408 {
409 return (i_ucode_locate_amd(cp, uinfop, ucode_amd_fallback));
410 }
411
412 ucode_errno_t
ucode_locate_fallback_amd(cpu_t * cp,cpu_ucode_info_t * uinfop)413 ucode_locate_fallback_amd(cpu_t *cp, cpu_ucode_info_t *uinfop)
414 {
415 /* Once we have switched to the fallback microcode, stick with it */
416 ucode_amd_fallback = true;
417 return (i_ucode_locate_amd(cp, uinfop, ucode_amd_fallback));
418 }
419
420 static void
ucode_read_rev_amd(cpu_ucode_info_t * uinfop)421 ucode_read_rev_amd(cpu_ucode_info_t *uinfop)
422 {
423 uinfop->cui_rev = rdmsr(MSR_AMD_PATCHLEVEL);
424 }
425
426 static void
ucode_load_amd(cpu_ucode_info_t * uinfop)427 ucode_load_amd(cpu_ucode_info_t *uinfop)
428 {
429 ucode_file_amd_t *ucodefp = uinfop->cui_pending_ucode;
430 on_trap_data_t otd;
431
432 VERIFY3P(ucodefp, !=, NULL);
433 VERIFY3U(ucodefp->uf_header.uh_patch_id, ==, uinfop->cui_pending_rev);
434
435 kpreempt_disable();
436 if (on_trap(&otd, OT_DATA_ACCESS)) {
437 no_trap();
438 goto out;
439 }
440 wrmsr(MSR_AMD_PATCHLOADER, (uintptr_t)ucodefp);
441 no_trap();
442
443 out:
444 kpreempt_enable();
445 }
446
447 static ucode_errno_t
ucode_extract_amd(ucode_update_t * uusp,uint8_t * ucodep,size_t size)448 ucode_extract_amd(ucode_update_t *uusp, uint8_t *ucodep, size_t size)
449 {
450 uint32_t *ptr = (uint32_t *)ucodep;
451 ucode_eqtbl_amd_t *eqtbl;
452 ucode_file_amd_t *ufp;
453 uint32_t count;
454 bool higher = false;
455 ucode_errno_t rc = EM_NOMATCH;
456 uint16_t eq_sig;
457
458 /* skip over magic number & equivalence table header */
459 ptr += 2; size -= 8;
460
461 count = *ptr++; size -= 4;
462 for (eqtbl = (ucode_eqtbl_amd_t *)ptr;
463 eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != uusp->sig;
464 eqtbl++)
465 ;
466
467 eq_sig = eqtbl->ue_equiv_cpu;
468
469 /* No equivalent CPU id found, assume outdated microcode file. */
470 if (eq_sig == 0)
471 return (EM_HIGHERREV);
472
473 /* Use the first microcode patch that matches. */
474 do {
475 ptr += count >> 2; size -= count;
476
477 if (size == 0)
478 return (higher ? EM_HIGHERREV : EM_NOMATCH);
479
480 ptr++; size -= 4;
481 count = *ptr++; size -= 4;
482 ufp = (ucode_file_amd_t *)ptr;
483
484 rc = ucode_match_amd(eq_sig, &uusp->info, ufp, count);
485 if (rc == EM_HIGHERREV)
486 higher = true;
487 } while (rc != EM_OK);
488
489 uusp->ucodep = (uint8_t *)ufp;
490 uusp->usize = count;
491 uusp->expected_rev = ufp->uf_header.uh_patch_id;
492
493 return (EM_OK);
494 }
495
496 static const ucode_source_t ucode_amd = {
497 .us_name = "AMD microcode updater",
498 .us_write_msr = MSR_AMD_PATCHLOADER,
499 .us_invalidate = false,
500 .us_select = ucode_select_amd,
501 .us_capable = ucode_capable_amd,
502 .us_file_reset = ucode_file_reset_amd,
503 .us_read_rev = ucode_read_rev_amd,
504 .us_load = ucode_load_amd,
505 .us_validate = ucode_validate_amd,
506 .us_extract = ucode_extract_amd,
507 .us_locate = ucode_locate_amd,
508 .us_locate_fallback = ucode_locate_fallback_amd
509 };
510 UCODE_SOURCE(ucode_amd);
511