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 2025 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/machparam.h> 38 #include <sys/ontrap.h> 39 #include <sys/sysmacros.h> 40 #include <sys/systm.h> 41 #include <sys/ucode.h> 42 #include <sys/ucode_amd.h> 43 #include <ucode/ucode_errno.h> 44 #include <ucode/ucode_utils_amd.h> 45 #include <sys/x86_archext.h> 46 47 extern void *ucode_zalloc(size_t); 48 extern void ucode_free(void *, size_t); 49 extern const char *ucode_path(void); 50 extern int ucode_force_update; 51 extern bool ucode_use_kmem; 52 53 static ucode_file_amd_t *amd_ucodef; 54 static size_t amd_ucodef_len, amd_ucodef_buflen; 55 static ucode_eqtbl_amd_t *ucode_eqtbl_amd; 56 static uint_t ucode_eqtbl_amd_entries; 57 static bool ucode_amd_fallback = false; 58 59 /* 60 * Check whether this module can be used for microcode updates on this 61 * platform. 62 */ 63 static bool 64 ucode_select_amd(cpu_t *cp) 65 { 66 if ((get_hwenv() & HW_VIRTUAL) != 0) 67 return (false); 68 69 return (cpuid_getvendor(cp) == X86_VENDOR_AMD); 70 } 71 72 /* 73 * Check whether or not a processor is capable of microcode operations 74 * 75 * At this point we only support microcode update for: 76 * - AMD processors family 0x10 and above. 77 */ 78 static bool 79 ucode_capable_amd(cpu_t *cp) 80 { 81 return (cpuid_getfamily(cp) >= 0x10); 82 } 83 84 /* 85 * Called when it is no longer necessary to keep the microcode around, 86 * or when the cached microcode doesn't match the CPU being processed. 87 */ 88 static void 89 ucode_file_reset_amd(void) 90 { 91 if (amd_ucodef == NULL) 92 return; 93 94 ucode_free(amd_ucodef, amd_ucodef_buflen); 95 amd_ucodef = NULL; 96 amd_ucodef_buflen = amd_ucodef_len = 0; 97 } 98 99 /* 100 * Find the equivalent CPU id in the equivalence table. 101 */ 102 static ucode_errno_t 103 ucode_equiv_cpu_amd(cpu_t *cp, uint16_t *eq_sig) 104 { 105 char *name = NULL; 106 int cpi_sig = cpuid_getsig(cp); 107 ucode_errno_t ret = EM_OK; 108 109 if (ucode_eqtbl_amd == NULL) { 110 name = ucode_zalloc(MAXPATHLEN); 111 if (name == NULL) 112 return (EM_NOMEM); 113 114 (void) snprintf(name, MAXPATHLEN, "%s/%s/%s", 115 ucode_path(), cpuid_getvendorstr(cp), 116 UCODE_AMD_EQUIVALENCE_TABLE_NAME); 117 } 118 119 if (!ucode_use_kmem) { 120 /* 121 * No kmem_zalloc() etc. available yet. 122 */ 123 ucode_eqtbl_amd_t eqtbl; 124 int count, offset = 0; 125 intptr_t fd; 126 127 ASSERT3P(name, !=, NULL); 128 129 if ((fd = kobj_open(name)) == -1) { 130 ret = EM_OPENFILE; 131 goto out; 132 } 133 do { 134 count = kobj_read(fd, (int8_t *)&eqtbl, 135 sizeof (eqtbl), offset); 136 if (count != sizeof (eqtbl)) { 137 (void) kobj_close(fd); 138 ret = EM_HIGHERREV; 139 goto out; 140 } 141 offset += count; 142 } while (eqtbl.ue_inst_cpu != 0 && 143 eqtbl.ue_inst_cpu != cpi_sig); 144 (void) kobj_close(fd); 145 *eq_sig = eqtbl.ue_equiv_cpu; 146 } else { 147 ucode_eqtbl_amd_t *eqtbl; 148 149 /* 150 * If not already done, load the equivalence table. 151 */ 152 if (ucode_eqtbl_amd == NULL) { 153 struct _buf *eq; 154 uint64_t size; 155 int count; 156 157 ASSERT3P(name, !=, NULL); 158 159 if ((eq = kobj_open_file(name)) == (struct _buf *)-1) { 160 ret = EM_OPENFILE; 161 goto out; 162 } 163 164 if (kobj_get_filesize(eq, &size) < 0) { 165 kobj_close_file(eq); 166 ret = EM_OPENFILE; 167 goto out; 168 } 169 170 if (size == 0 || 171 size % sizeof (*ucode_eqtbl_amd) != 0) { 172 kobj_close_file(eq); 173 ret = EM_HIGHERREV; 174 goto out; 175 } 176 177 ucode_eqtbl_amd = kmem_zalloc(size, KM_NOSLEEP); 178 if (ucode_eqtbl_amd == NULL) { 179 kobj_close_file(eq); 180 ret = EM_NOMEM; 181 goto out; 182 } 183 count = kobj_read_file(eq, (char *)ucode_eqtbl_amd, 184 size, 0); 185 kobj_close_file(eq); 186 187 if (count != size) { 188 ucode_eqtbl_amd_entries = 0; 189 ret = EM_FILESIZE; 190 goto out; 191 } 192 193 ucode_eqtbl_amd_entries = 194 size / sizeof (*ucode_eqtbl_amd); 195 } 196 197 eqtbl = ucode_eqtbl_amd; 198 *eq_sig = 0; 199 for (uint_t i = 0; i < ucode_eqtbl_amd_entries; i++, eqtbl++) { 200 if (eqtbl->ue_inst_cpu == 0) { 201 /* End of table */ 202 ret = EM_HIGHERREV; 203 goto out; 204 } 205 if (eqtbl->ue_inst_cpu == cpi_sig) { 206 *eq_sig = eqtbl->ue_equiv_cpu; 207 ret = EM_OK; 208 goto out; 209 } 210 } 211 /* 212 * No equivalent CPU id found, assume outdated microcode file. 213 */ 214 ret = EM_HIGHERREV; 215 } 216 217 out: 218 ucode_free(name, MAXPATHLEN); 219 220 return (ret); 221 } 222 223 static ucode_errno_t 224 ucode_match_amd(uint16_t eq_sig, cpu_ucode_info_t *uinfop, 225 ucode_file_amd_t *ucodefp, size_t size) 226 { 227 ucode_header_amd_t *uh; 228 229 if (ucodefp == NULL || size < sizeof (ucode_header_amd_t)) 230 return (EM_NOMATCH); 231 232 uh = &ucodefp->uf_header; 233 234 /* 235 * Don't even think about loading patches that would require code 236 * execution. Does not apply to patches for family 0x14 and beyond. 237 */ 238 if (uh->uh_cpu_rev < 0x5000 && 239 size > offsetof(ucode_file_amd_t, uf_code_present) && 240 ucodefp->uf_code_present) { 241 return (EM_NOMATCH); 242 } 243 244 if (eq_sig != uh->uh_cpu_rev) 245 return (EM_NOMATCH); 246 247 if (uh->uh_nb_id) { 248 cmn_err(CE_WARN, "ignoring northbridge-specific ucode: " 249 "chipset id %x, revision %x", uh->uh_nb_id, uh->uh_nb_rev); 250 return (EM_NOMATCH); 251 } 252 253 if (uh->uh_sb_id) { 254 cmn_err(CE_WARN, "ignoring southbridge-specific ucode: " 255 "chipset id %x, revision %x", uh->uh_sb_id, uh->uh_sb_rev); 256 return (EM_NOMATCH); 257 } 258 259 if (uh->uh_patch_id <= uinfop->cui_rev && !ucode_force_update) 260 return (EM_HIGHERREV); 261 262 return (EM_OK); 263 } 264 265 /* 266 * Copy the given ucode into cpu_ucode_info_t in preparation for loading onto 267 * the corresponding CPU via ucode_load_amd(). 268 */ 269 static ucode_errno_t 270 ucode_copy_amd(cpu_ucode_info_t *uinfop, const ucode_file_amd_t *ucodefp, 271 size_t size) 272 { 273 ASSERT3P(uinfop->cui_pending_ucode, ==, NULL); 274 ASSERT3U(size, <=, UCODE_AMD_MAXSIZE); 275 276 uinfop->cui_pending_ucode = ucode_zalloc(size); 277 if (uinfop->cui_pending_ucode == NULL) 278 return (EM_NOMEM); 279 280 (void) memcpy(uinfop->cui_pending_ucode, ucodefp, size); 281 uinfop->cui_pending_size = size; 282 uinfop->cui_pending_rev = ucodefp->uf_header.uh_patch_id; 283 284 return (EM_OK); 285 } 286 287 /* 288 * Populate the ucode file structure from the microcode file corresponding to 289 * this CPU, if exists. 290 * 291 * Return EM_OK on success, corresponding error code on failure. 292 */ 293 static ucode_errno_t 294 i_ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop, bool fallback) 295 { 296 uint16_t eq_sig; 297 ucode_errno_t rc; 298 299 /* get equivalent CPU id */ 300 eq_sig = 0; 301 if ((rc = ucode_equiv_cpu_amd(cp, &eq_sig)) != EM_OK) 302 return (rc); 303 304 /* 305 * Allocate a buffer for the microcode patch. If the buffer has been 306 * allocated before, check for a matching microcode to avoid loading 307 * the file again. 308 */ 309 if (amd_ucodef == NULL) { 310 size_t len = PAGESIZE; 311 312 amd_ucodef = ucode_zalloc(len); 313 if (amd_ucodef == NULL) 314 return (EM_NOMEM); 315 amd_ucodef_buflen = len; 316 } else { 317 rc = ucode_match_amd(eq_sig, uinfop, amd_ucodef, 318 amd_ucodef_len); 319 if (rc == EM_HIGHERREV) 320 return (rc); 321 if (rc == EM_OK) { 322 return (ucode_copy_amd(uinfop, amd_ucodef, 323 amd_ucodef_len)); 324 } 325 } 326 327 /* 328 * Find the patch for this CPU. The patch files are named XXXX-YY, where 329 * XXXX is the equivalent CPU id and YY is the running patch number. 330 * Patches specific to certain chipsets are guaranteed to have lower 331 * numbers than less specific patches, so we can just load the first 332 * patch that matches. 333 */ 334 for (uint_t i = 0; i < 0xff; i++) { 335 char name[MAXPATHLEN]; 336 intptr_t fd; 337 int count; 338 /* This is a uint_t to match the signature of kobj_read() */ 339 uint_t size; 340 341 (void) snprintf(name, MAXPATHLEN, "%s/%s/%s%04X-%02X", 342 ucode_path(), cpuid_getvendorstr(cp), 343 fallback ? "fallback/" : "", eq_sig, i); 344 345 if ((fd = kobj_open(name)) == -1) 346 return (EM_NOMATCH); 347 348 /* 349 * Since this code will run for the boot CPU before kmem is 350 * initialised we can't use the kobj_*_file() functions. 351 * In the case where the archive contains compressed files, 352 * kobj_fstat() will return the compressed size and so we must 353 * read the entire file through to determine its size. 354 */ 355 size = 0; 356 do { 357 count = kobj_read(fd, (char *)amd_ucodef, 358 amd_ucodef_buflen, size); 359 if (count < 0) { 360 (void) kobj_close(fd); 361 return (EM_OPENFILE); 362 } 363 size += count; 364 } while (count == amd_ucodef_buflen && 365 size <= UCODE_AMD_MAXSIZE); 366 367 if (size > UCODE_AMD_MAXSIZE) { 368 (void) kobj_close(fd); 369 cmn_err(CE_WARN, "ucode: microcode file %s is " 370 "too large (over 0x%x bytes)", name, 371 UCODE_AMD_MAXSIZE); 372 return (EM_FILESIZE); 373 } 374 375 if (size > amd_ucodef_buflen) { 376 size_t len = P2ROUNDUP(size, PAGESIZE); 377 378 ucode_file_reset_amd(); 379 amd_ucodef = ucode_zalloc(len); 380 if (amd_ucodef == NULL) { 381 (void) kobj_close(fd); 382 return (EM_NOMEM); 383 } 384 amd_ucodef_buflen = len; 385 } 386 387 count = kobj_read(fd, (char *)amd_ucodef, amd_ucodef_buflen, 0); 388 (void) kobj_close(fd); 389 if (count < 0 || count != size) 390 return (EM_OPENFILE); 391 392 amd_ucodef_len = count; 393 394 rc = ucode_match_amd(eq_sig, uinfop, amd_ucodef, 395 amd_ucodef_len); 396 if (rc == EM_HIGHERREV) 397 return (rc); 398 if (rc == EM_OK) { 399 return (ucode_copy_amd(uinfop, amd_ucodef, 400 amd_ucodef_len)); 401 } 402 } 403 return (EM_NOMATCH); 404 } 405 406 ucode_errno_t 407 ucode_locate_amd(cpu_t *cp, cpu_ucode_info_t *uinfop) 408 { 409 return (i_ucode_locate_amd(cp, uinfop, ucode_amd_fallback)); 410 } 411 412 ucode_errno_t 413 ucode_locate_fallback_amd(cpu_t *cp, cpu_ucode_info_t *uinfop) 414 { 415 /* Once we have switched to the fallback microcode, stick with it */ 416 ucode_amd_fallback = true; 417 return (i_ucode_locate_amd(cp, uinfop, ucode_amd_fallback)); 418 } 419 420 static void 421 ucode_read_rev_amd(cpu_ucode_info_t *uinfop) 422 { 423 uinfop->cui_rev = rdmsr(MSR_AMD_PATCHLEVEL); 424 } 425 426 static void 427 ucode_load_amd(cpu_ucode_info_t *uinfop) 428 { 429 ucode_file_amd_t *ucodefp = uinfop->cui_pending_ucode; 430 on_trap_data_t otd; 431 432 VERIFY3P(ucodefp, !=, NULL); 433 VERIFY3U(ucodefp->uf_header.uh_patch_id, ==, uinfop->cui_pending_rev); 434 435 kpreempt_disable(); 436 if (on_trap(&otd, OT_DATA_ACCESS)) { 437 no_trap(); 438 goto out; 439 } 440 wrmsr(MSR_AMD_PATCHLOADER, (uintptr_t)ucodefp); 441 no_trap(); 442 443 out: 444 kpreempt_enable(); 445 } 446 447 static ucode_errno_t 448 ucode_extract_amd(ucode_update_t *uusp, uint8_t *ucodep, size_t size) 449 { 450 uint32_t *ptr = (uint32_t *)ucodep; 451 ucode_eqtbl_amd_t *eqtbl; 452 ucode_file_amd_t *ufp; 453 uint32_t count; 454 bool higher = false; 455 ucode_errno_t rc = EM_NOMATCH; 456 uint16_t eq_sig; 457 458 /* skip over magic number & equivalence table header */ 459 ptr += 2; size -= 8; 460 461 count = *ptr++; size -= 4; 462 for (eqtbl = (ucode_eqtbl_amd_t *)ptr; 463 eqtbl->ue_inst_cpu && eqtbl->ue_inst_cpu != uusp->sig; 464 eqtbl++) 465 ; 466 467 eq_sig = eqtbl->ue_equiv_cpu; 468 469 /* No equivalent CPU id found, assume outdated microcode file. */ 470 if (eq_sig == 0) 471 return (EM_HIGHERREV); 472 473 /* Use the first microcode patch that matches. */ 474 do { 475 ptr += count >> 2; size -= count; 476 477 if (size == 0) 478 return (higher ? EM_HIGHERREV : EM_NOMATCH); 479 480 ptr++; size -= 4; 481 count = *ptr++; size -= 4; 482 ufp = (ucode_file_amd_t *)ptr; 483 484 rc = ucode_match_amd(eq_sig, &uusp->info, ufp, count); 485 if (rc == EM_HIGHERREV) 486 higher = true; 487 } while (rc != EM_OK); 488 489 uusp->ucodep = (uint8_t *)ufp; 490 uusp->usize = count; 491 uusp->expected_rev = ufp->uf_header.uh_patch_id; 492 493 return (EM_OK); 494 } 495 496 static const ucode_source_t ucode_amd = { 497 .us_name = "AMD microcode updater", 498 .us_write_msr = MSR_AMD_PATCHLOADER, 499 .us_invalidate = false, 500 .us_select = ucode_select_amd, 501 .us_capable = ucode_capable_amd, 502 .us_file_reset = ucode_file_reset_amd, 503 .us_read_rev = ucode_read_rev_amd, 504 .us_load = ucode_load_amd, 505 .us_validate = ucode_validate_amd, 506 .us_extract = ucode_extract_amd, 507 .us_locate = ucode_locate_amd, 508 .us_locate_fallback = ucode_locate_fallback_amd 509 }; 510 UCODE_SOURCE(ucode_amd); 511