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