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