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