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