xref: /illumos-gate/usr/src/uts/intel/os/microcode_amd.c (revision d39975c637b3d0cf582256e9d3084dc02c3ab68f)
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