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