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