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