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