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