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