xref: /illumos-gate/usr/src/uts/intel/os/microcode_intel.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_intel.h>
42 #include <ucode/ucode_errno.h>
43 #include <ucode/ucode_utils_intel.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 
51 static ucode_file_intel_t intel_ucodef;
52 
53 /*
54  * Check whether this module can be used for microcode updates on this
55  * platform.
56  */
57 static bool
ucode_select_intel(cpu_t * cp)58 ucode_select_intel(cpu_t *cp)
59 {
60 	if ((get_hwenv() & HW_VIRTUAL) != 0)
61 		return (false);
62 
63 	return (cpuid_getvendor(cp) == X86_VENDOR_Intel);
64 }
65 
66 /*
67  * Check whether or not a processor is capable of microcode operations
68  *
69  * At this point we only support microcode update for:
70  * - Intel processors family 6 and above.
71  */
72 static bool
ucode_capable_intel(cpu_t * cp)73 ucode_capable_intel(cpu_t *cp)
74 {
75 	return (cpuid_getfamily(cp) >= 6);
76 }
77 
78 static void
ucode_file_reset_intel(void)79 ucode_file_reset_intel(void)
80 {
81 	ucode_file_intel_t *ucodefp = &intel_ucodef;
82 	int total_size, body_size;
83 
84 	if (ucodefp->uf_header == NULL)
85 		return;
86 
87 	total_size = UCODE_TOTAL_SIZE_INTEL(ucodefp->uf_header->uh_total_size);
88 	body_size = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size);
89 
90 	if (ucodefp->uf_body != NULL) {
91 		ucode_free(ucodefp->uf_body, body_size);
92 		ucodefp->uf_body = NULL;
93 	}
94 
95 	if (ucodefp->uf_ext_table != NULL) {
96 		int size = total_size - body_size - UCODE_HEADER_SIZE_INTEL;
97 
98 		ucode_free(ucodefp->uf_ext_table, size);
99 		ucodefp->uf_ext_table = NULL;
100 	}
101 
102 	ucode_free(ucodefp->uf_header, UCODE_HEADER_SIZE_INTEL);
103 	ucodefp->uf_header = NULL;
104 }
105 
106 /*
107  * Checks if the microcode is for this processor.
108  */
109 static ucode_errno_t
ucode_match_intel(int cpi_sig,cpu_ucode_info_t * uinfop,ucode_header_intel_t * uhp,ucode_ext_table_intel_t * uetp)110 ucode_match_intel(int cpi_sig, cpu_ucode_info_t *uinfop,
111     ucode_header_intel_t *uhp, ucode_ext_table_intel_t *uetp)
112 {
113 	if (uhp == NULL)
114 		return (EM_NOMATCH);
115 
116 	if (UCODE_MATCH_INTEL(cpi_sig, uhp->uh_signature,
117 	    uinfop->cui_platid, uhp->uh_proc_flags)) {
118 
119 		if (uinfop->cui_rev >= uhp->uh_rev && !ucode_force_update)
120 			return (EM_HIGHERREV);
121 
122 		return (EM_OK);
123 	}
124 
125 	if (uetp != NULL) {
126 		for (uint_t i = 0; i < uetp->uet_count; i++) {
127 			ucode_ext_sig_intel_t *uesp;
128 
129 			uesp = &uetp->uet_ext_sig[i];
130 
131 			if (UCODE_MATCH_INTEL(cpi_sig, uesp->ues_signature,
132 			    uinfop->cui_platid, uesp->ues_proc_flags)) {
133 
134 				if (uinfop->cui_rev >= uhp->uh_rev &&
135 				    !ucode_force_update)
136 					return (EM_HIGHERREV);
137 
138 				return (EM_OK);
139 			}
140 		}
141 	}
142 
143 	return (EM_NOMATCH);
144 }
145 
146 /*
147  * Copy the given ucode into cpu_ucode_info_t in preparation for loading onto
148  * the corresponding CPU via ucode_load_intel().
149  */
150 static ucode_errno_t
ucode_copy_intel(const ucode_file_intel_t * ucodefp,cpu_ucode_info_t * uinfop)151 ucode_copy_intel(const ucode_file_intel_t *ucodefp, cpu_ucode_info_t *uinfop)
152 {
153 	ASSERT3P(ucodefp->uf_header, !=, NULL);
154 	ASSERT3P(ucodefp->uf_body, !=, NULL);
155 	ASSERT3P(uinfop->cui_pending_ucode, ==, NULL);
156 
157 	/*
158 	 * Allocate memory for the pending microcode update and copy the body.
159 	 * We don't need the header or extended signature table which are only
160 	 * used for matching.
161 	 */
162 	size_t sz = UCODE_BODY_SIZE_INTEL(ucodefp->uf_header->uh_body_size);
163 	uinfop->cui_pending_ucode = ucode_zalloc(sz);
164 	if (uinfop->cui_pending_ucode == NULL)
165 		return (EM_NOMEM);
166 	memcpy(uinfop->cui_pending_ucode, ucodefp->uf_body, sz);
167 
168 	uinfop->cui_pending_size = sz;
169 	uinfop->cui_pending_rev = ucodefp->uf_header->uh_rev;
170 
171 	return (EM_OK);
172 }
173 
174 static ucode_errno_t
ucode_locate_intel(cpu_t * cp,cpu_ucode_info_t * uinfop)175 ucode_locate_intel(cpu_t *cp, cpu_ucode_info_t *uinfop)
176 {
177 	char		name[MAXPATHLEN];
178 	intptr_t	fd;
179 	int		count;
180 	int		header_size = UCODE_HEADER_SIZE_INTEL;
181 	int		cpi_sig = cpuid_getsig(cp);
182 	ucode_errno_t	rc = EM_OK;
183 	ucode_file_intel_t *ucodefp = &intel_ucodef;
184 
185 	/*
186 	 * If the cached microcode matches the CPU we are processing, use it.
187 	 */
188 	if (ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header,
189 	    ucodefp->uf_ext_table) == EM_OK && ucodefp->uf_body != NULL) {
190 		return (ucode_copy_intel(ucodefp, uinfop));
191 	}
192 
193 	/*
194 	 * Look for microcode file with the right name.
195 	 */
196 	(void) snprintf(name, MAXPATHLEN, "%s/%s/%08X-%02X",
197 	    ucode_path(), cpuid_getvendorstr(cp), cpi_sig,
198 	    uinfop->cui_platid);
199 	if ((fd = kobj_open(name)) == -1) {
200 		return (EM_OPENFILE);
201 	}
202 
203 	/*
204 	 * We found a microcode file for the CPU we are processing,
205 	 * reset the microcode data structure and read in the new
206 	 * file.
207 	 */
208 	ucode_file_reset_intel();
209 
210 	ucodefp->uf_header = ucode_zalloc(header_size);
211 	if (ucodefp->uf_header == NULL)
212 		return (EM_NOMEM);
213 
214 	count = kobj_read(fd, (char *)ucodefp->uf_header, header_size, 0);
215 
216 	switch (count) {
217 	case UCODE_HEADER_SIZE_INTEL: {
218 
219 		ucode_header_intel_t	*uhp = ucodefp->uf_header;
220 		uint32_t	offset = header_size;
221 		int		total_size, body_size, ext_size;
222 		uint32_t	sum = 0;
223 
224 		/*
225 		 * Make sure that the header contains valid fields.
226 		 */
227 		if ((rc = ucode_header_validate_intel(uhp)) == EM_OK) {
228 			total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
229 			body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
230 			ucodefp->uf_body = ucode_zalloc(body_size);
231 			if (ucodefp->uf_body == NULL) {
232 				rc = EM_NOMEM;
233 				break;
234 			}
235 
236 			if (kobj_read(fd, (char *)ucodefp->uf_body,
237 			    body_size, offset) != body_size)
238 				rc = EM_FILESIZE;
239 		}
240 
241 		if (rc)
242 			break;
243 
244 		sum = ucode_checksum_intel(0, header_size,
245 		    (uint8_t *)ucodefp->uf_header);
246 		if (ucode_checksum_intel(sum, body_size, ucodefp->uf_body)) {
247 			rc = EM_CHECKSUM;
248 			break;
249 		}
250 
251 		/*
252 		 * Check to see if there is extended signature table.
253 		 */
254 		offset = body_size + header_size;
255 		ext_size = total_size - offset;
256 
257 		if (ext_size <= 0)
258 			break;
259 
260 		ucodefp->uf_ext_table = ucode_zalloc(ext_size);
261 		if (ucodefp->uf_ext_table == NULL) {
262 			rc = EM_NOMEM;
263 			break;
264 		}
265 
266 		if (kobj_read(fd, (char *)ucodefp->uf_ext_table,
267 		    ext_size, offset) != ext_size) {
268 			rc = EM_FILESIZE;
269 		} else if (ucode_checksum_intel(0, ext_size,
270 		    (uint8_t *)(ucodefp->uf_ext_table))) {
271 			rc = EM_EXTCHECKSUM;
272 		} else {
273 			int i;
274 
275 			for (i = 0; i < ucodefp->uf_ext_table->uet_count; i++) {
276 				ucode_ext_sig_intel_t *sig;
277 
278 				sig = &ucodefp->uf_ext_table->uet_ext_sig[i];
279 
280 				if (ucode_checksum_intel_extsig(uhp,
281 				    sig) != 0) {
282 					rc = EM_SIGCHECKSUM;
283 					break;
284 				}
285 			}
286 		}
287 		break;
288 	}
289 
290 	default:
291 		rc = EM_FILESIZE;
292 		break;
293 	}
294 
295 	kobj_close(fd);
296 
297 	if (rc != EM_OK)
298 		return (rc);
299 
300 	rc = ucode_match_intel(cpi_sig, uinfop, ucodefp->uf_header,
301 	    ucodefp->uf_ext_table);
302 	if (rc == EM_OK) {
303 		return (ucode_copy_intel(ucodefp, uinfop));
304 	}
305 
306 	return (rc);
307 }
308 
309 static void
ucode_read_rev_intel(cpu_ucode_info_t * uinfop)310 ucode_read_rev_intel(cpu_ucode_info_t *uinfop)
311 {
312 	struct cpuid_regs crs;
313 
314 	/*
315 	 * The Intel 64 and IA-32 Architecture Software Developer's Manual
316 	 * recommends that MSR_INTC_UCODE_REV be loaded with 0 first, then
317 	 * execute cpuid to guarantee the correct reading of this register.
318 	 */
319 	wrmsr(MSR_INTC_UCODE_REV, 0);
320 	(void) __cpuid_insn(&crs);
321 	uinfop->cui_rev = (rdmsr(MSR_INTC_UCODE_REV) >> INTC_UCODE_REV_SHIFT);
322 
323 	/*
324 	 * The MSR_INTC_PLATFORM_ID is supported in Celeron and Xeon
325 	 * (Family 6, model 5 and above) and all processors after.
326 	 */
327 	if ((cpuid_getmodel(CPU) >= 5 || cpuid_getfamily(CPU) > 6)) {
328 		uinfop->cui_platid = 1 << ((rdmsr(MSR_INTC_PLATFORM_ID) >>
329 		    INTC_PLATFORM_ID_SHIFT) & INTC_PLATFORM_ID_MASK);
330 	}
331 }
332 
333 static void
ucode_load_intel(cpu_ucode_info_t * uinfop)334 ucode_load_intel(cpu_ucode_info_t *uinfop)
335 {
336 	VERIFY3P(uinfop->cui_pending_ucode, !=, NULL);
337 
338 	kpreempt_disable();
339 	/*
340 	 * On some platforms a cache invalidation is required for the
341 	 * ucode update to be successful due to the parts of the
342 	 * processor that the microcode is updating.
343 	 */
344 	invalidate_cache();
345 	wrmsr(MSR_INTC_UCODE_WRITE, (uintptr_t)uinfop->cui_pending_ucode);
346 	kpreempt_enable();
347 }
348 
349 static ucode_errno_t
ucode_extract_intel(ucode_update_t * uusp,uint8_t * ucodep,int size)350 ucode_extract_intel(ucode_update_t *uusp, uint8_t *ucodep, int size)
351 {
352 	uint32_t	header_size = UCODE_HEADER_SIZE_INTEL;
353 	int		remaining;
354 	int		found = 0;
355 	ucode_errno_t	search_rc = EM_NOMATCH; /* search result */
356 
357 	/*
358 	 * Go through the whole buffer in case there are
359 	 * multiple versions of matching microcode for this
360 	 * processor.
361 	 */
362 	for (remaining = size; remaining > 0; ) {
363 		int	total_size, body_size, ext_size;
364 		uint8_t	*curbuf = &ucodep[size - remaining];
365 		ucode_header_intel_t *uhp = (ucode_header_intel_t *)curbuf;
366 		ucode_ext_table_intel_t *uetp = NULL;
367 		ucode_errno_t tmprc;
368 
369 		total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
370 		body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
371 		ext_size = total_size - (header_size + body_size);
372 
373 		if (ext_size > 0)
374 			uetp = (ucode_ext_table_intel_t *)
375 			    &curbuf[header_size + body_size];
376 
377 		tmprc = ucode_match_intel(uusp->sig, &uusp->info, uhp, uetp);
378 
379 		/*
380 		 * Since we are searching through a big file
381 		 * containing microcode for pretty much all the
382 		 * processors, we are bound to get EM_NOMATCH
383 		 * at one point.  However, if we return
384 		 * EM_NOMATCH to users, it will really confuse
385 		 * them.  Therefore, if we ever find a match of
386 		 * a lower rev, we will set return code to
387 		 * EM_HIGHERREV.
388 		 */
389 		if (tmprc == EM_HIGHERREV)
390 			search_rc = EM_HIGHERREV;
391 
392 		if (tmprc == EM_OK &&
393 		    uusp->expected_rev < uhp->uh_rev) {
394 			uusp->ucodep = (uint8_t *)&curbuf[header_size];
395 			uusp->usize =
396 			    UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
397 			uusp->expected_rev = uhp->uh_rev;
398 			found = 1;
399 		}
400 
401 		remaining -= total_size;
402 	}
403 
404 	if (!found)
405 		return (search_rc);
406 
407 	return (EM_OK);
408 }
409 
410 static const ucode_source_t ucode_intel = {
411 	.us_name	= "Intel microcode updater",
412 	.us_write_msr	= MSR_INTC_UCODE_WRITE,
413 	.us_invalidate	= true,
414 	.us_select	= ucode_select_intel,
415 	.us_capable	= ucode_capable_intel,
416 	.us_file_reset	= ucode_file_reset_intel,
417 	.us_read_rev	= ucode_read_rev_intel,
418 	.us_load	= ucode_load_intel,
419 	.us_validate	= ucode_validate_intel,
420 	.us_extract	= ucode_extract_intel,
421 	.us_locate	= ucode_locate_intel
422 };
423 UCODE_SOURCE(ucode_intel);
424