1/*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2018 The FreeBSD Foundation 5 * 6 * This software was developed by Mark Johnston under sponsorship from 7 * the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#include <sys/param.h> 32#include <sys/cpuset.h> 33#include <sys/kernel.h> 34#include <sys/linker.h> 35#include <sys/malloc.h> 36#include <sys/pcpu.h> 37#include <sys/smp.h> 38#include <sys/systm.h> 39 40#include <machine/atomic.h> 41#include <machine/cpufunc.h> 42#include <x86/specialreg.h> 43#include <machine/stdarg.h> 44#include <x86/ucode.h> 45#include <x86/x86_smp.h> 46 47#include <vm/vm.h> 48#include <vm/pmap.h> 49#include <vm/vm_extern.h> 50#include <vm/vm_kern.h> 51#include <vm/vm_param.h> 52 53static const void *ucode_intel_match(const uint8_t *data, size_t *len); 54static int ucode_intel_verify(const struct ucode_intel_header *hdr, 55 size_t resid); 56 57static const void *ucode_amd_match(const uint8_t *data, size_t *len); 58 59static struct ucode_ops { 60 const char *vendor; 61 int (*load)(const void *, bool, uint64_t *, uint64_t *); 62 const void *(*match)(const uint8_t *, size_t *); 63} loaders[] = { 64 { 65 .vendor = INTEL_VENDOR_ID, 66 .load = ucode_intel_load, 67 .match = ucode_intel_match, 68 }, 69 { 70 .vendor = AMD_VENDOR_ID, 71 .load = ucode_amd_load, 72 .match = ucode_amd_match, 73 }, 74}; 75 76/* Selected microcode update data. */ 77static const void *early_ucode_data; 78static const void *ucode_data; 79static struct ucode_ops *ucode_loader; 80 81/* Variables used for reporting success or failure. */ 82enum { 83 NO_ERROR, 84 NO_MATCH, 85 VERIFICATION_FAILED, 86} ucode_error = NO_ERROR; 87static uint64_t ucode_nrev, ucode_orev; 88 89static void 90log_msg(void *arg __unused) 91{ 92 93 if (ucode_nrev != 0) { 94 printf("CPU microcode: updated from %#jx to %#jx\n", 95 (uintmax_t)ucode_orev, (uintmax_t)ucode_nrev); 96 return; 97 } 98 99 switch (ucode_error) { 100 case NO_MATCH: 101 printf("CPU microcode: no matching update found\n"); 102 break; 103 case VERIFICATION_FAILED: 104 printf("CPU microcode: microcode verification failed\n"); 105 break; 106 default: 107 break; 108 } 109} 110SYSINIT(ucode_log, SI_SUB_CPU, SI_ORDER_FIRST, log_msg, NULL); 111 112int 113ucode_intel_load(const void *data, bool unsafe, uint64_t *nrevp, uint64_t *orevp) 114{ 115 uint64_t nrev, orev; 116 uint32_t cpuid[4]; 117 118 orev = rdmsr(MSR_BIOS_SIGN) >> 32; 119 120 /* 121 * Perform update. Flush caches first to work around seemingly 122 * undocumented errata applying to some Broadwell CPUs. 123 */ 124 wbinvd(); 125 if (unsafe) 126 wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data); 127 else 128 wrmsr(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data); 129 wrmsr(MSR_BIOS_SIGN, 0); 130 131 /* 132 * Serialize instruction flow. 133 */ 134 do_cpuid(0, cpuid); 135 136 /* 137 * Verify that the microcode revision changed. 138 */ 139 nrev = rdmsr(MSR_BIOS_SIGN) >> 32; 140 if (nrevp != NULL) 141 *nrevp = nrev; 142 if (orevp != NULL) 143 *orevp = orev; 144 if (nrev <= orev) 145 return (EEXIST); 146 return (0); 147} 148 149static int 150ucode_intel_verify(const struct ucode_intel_header *hdr, size_t resid) 151{ 152 const uint32_t *data; 153 uint32_t cksum, size; 154 int i; 155 156 if (resid < sizeof(struct ucode_intel_header)) 157 return (1); 158 size = hdr->total_size; 159 if (size == 0) 160 size = UCODE_INTEL_DEFAULT_DATA_SIZE + 161 sizeof(struct ucode_intel_header); 162 163 if (hdr->header_version != 1) 164 return (1); 165 if (size % 16 != 0) 166 return (1); 167 if (resid < size) 168 return (1); 169 170 cksum = 0; 171 data = (const uint32_t *)hdr; 172 for (i = 0; i < size / sizeof(uint32_t); i++) 173 cksum += data[i]; 174 if (cksum != 0) 175 return (1); 176 return (0); 177} 178 179static const void * 180ucode_intel_match(const uint8_t *data, size_t *len) 181{ 182 const struct ucode_intel_header *hdr; 183 const struct ucode_intel_extsig_table *table; 184 const struct ucode_intel_extsig *entry; 185 uint64_t platformid; 186 size_t resid; 187 uint32_t data_size, flags, regs[4], sig, total_size; 188 int i; 189 190 do_cpuid(1, regs); 191 sig = regs[0]; 192 193 platformid = rdmsr(MSR_IA32_PLATFORM_ID); 194 flags = 1 << ((platformid >> 50) & 0x7); 195 196 for (resid = *len; resid > 0; data += total_size, resid -= total_size) { 197 hdr = (const struct ucode_intel_header *)data; 198 if (ucode_intel_verify(hdr, resid) != 0) { 199 ucode_error = VERIFICATION_FAILED; 200 break; 201 } 202 203 data_size = hdr->data_size; 204 total_size = hdr->total_size; 205 if (data_size == 0) 206 data_size = UCODE_INTEL_DEFAULT_DATA_SIZE; 207 if (total_size == 0) 208 total_size = UCODE_INTEL_DEFAULT_DATA_SIZE + 209 sizeof(struct ucode_intel_header); 210 if (data_size > total_size + sizeof(struct ucode_intel_header)) 211 table = (const struct ucode_intel_extsig_table *) 212 ((const uint8_t *)(hdr + 1) + data_size); 213 else 214 table = NULL; 215 216 if (hdr->processor_signature == sig) { 217 if ((hdr->processor_flags & flags) != 0) { 218 *len = data_size; 219 return (hdr + 1); 220 } 221 } else if (table != NULL) { 222 for (i = 0; i < table->signature_count; i++) { 223 entry = &table->entries[i]; 224 if (entry->processor_signature == sig && 225 (entry->processor_flags & flags) != 0) { 226 *len = data_size; 227 return (hdr + 1); 228 } 229 } 230 } 231 } 232 return (NULL); 233} 234 235int 236ucode_amd_load(const void *data, bool unsafe, uint64_t *nrevp, uint64_t *orevp) 237{ 238 uint64_t nrev, orev; 239 uint32_t cpuid[4]; 240 241 orev = rdmsr(MSR_BIOS_SIGN); 242 243 /* 244 * Perform update. 245 */ 246 if (unsafe) 247 wrmsr_safe(MSR_K8_UCODE_UPDATE, (uint64_t)(uintptr_t)data); 248 else 249 wrmsr(MSR_K8_UCODE_UPDATE, (uint64_t)(uintptr_t)data); 250 251 /* 252 * Serialize instruction flow. 253 */ 254 do_cpuid(0, cpuid); 255 256 /* 257 * Verify that the microcode revision changed. 258 */ 259 nrev = rdmsr(MSR_BIOS_SIGN); 260 if (nrevp != NULL) 261 *nrevp = nrev; 262 if (orevp != NULL) 263 *orevp = orev; 264 if (nrev <= orev) 265 return (EEXIST); 266 return (0); 267 268} 269 270static const void * 271ucode_amd_match(const uint8_t *data, size_t *len) 272{ 273 uint32_t signature, revision; 274 uint32_t regs[4]; 275 276 do_cpuid(1, regs); 277 signature = regs[0]; 278 revision = rdmsr(MSR_BIOS_SIGN); 279 280 return (ucode_amd_find("loader blob", signature, revision, data, *len, len)); 281} 282 283/* 284 * Release any memory backing unused microcode blobs back to the system. 285 * We copy the selected update and free the entire microcode file. 286 */ 287static void 288ucode_release(void *arg __unused) 289{ 290 char *name, *type; 291 caddr_t file; 292 int release; 293 294 if (early_ucode_data == NULL) 295 return; 296 release = 1; 297 TUNABLE_INT_FETCH("debug.ucode.release", &release); 298 if (!release) 299 return; 300 301restart: 302 file = 0; 303 for (;;) { 304 file = preload_search_next_name(file); 305 if (file == 0) 306 break; 307 type = (char *)preload_search_info(file, MODINFO_TYPE); 308 if (type == NULL || strcmp(type, "cpu_microcode") != 0) 309 continue; 310 311 name = preload_search_info(file, MODINFO_NAME); 312 preload_delete_name(name); 313 goto restart; 314 } 315} 316SYSINIT(ucode_release, SI_SUB_SMP + 1, SI_ORDER_ANY, ucode_release, NULL); 317 318void 319ucode_load_ap(int cpu) 320{ 321#ifdef SMP 322 KASSERT(cpu_info[cpu_apic_ids[cpu]].cpu_present, 323 ("cpu %d not present", cpu)); 324 325 if (cpu_info[cpu_apic_ids[cpu]].cpu_hyperthread) 326 return; 327#endif 328 329 if (ucode_data != NULL) 330 (void)ucode_loader->load(ucode_data, false, NULL, NULL); 331} 332 333static void * 334map_ucode(uintptr_t free, size_t len) 335{ 336#ifdef __i386__ 337 uintptr_t va; 338 339 for (va = free; va < free + len; va += PAGE_SIZE) 340 pmap_kenter(va, (vm_paddr_t)va); 341#else 342 (void)len; 343#endif 344 return ((void *)free); 345} 346 347static void 348unmap_ucode(uintptr_t free, size_t len) 349{ 350#ifdef __i386__ 351 uintptr_t va; 352 353 for (va = free; va < free + len; va += PAGE_SIZE) 354 pmap_kremove(va); 355#else 356 (void)free; 357 (void)len; 358#endif 359} 360 361/* 362 * Search for an applicable microcode update, and load it. APs will load the 363 * selected update once they come online. 364 * 365 * "free" is the address of the next free physical page. If a microcode update 366 * is selected, it will be copied to this region prior to loading in order to 367 * satisfy alignment requirements. 368 */ 369size_t 370ucode_load_bsp(uintptr_t free) 371{ 372 union { 373 uint32_t regs[4]; 374 char vendor[13]; 375 } cpuid; 376 const uint8_t *fileaddr, *match; 377 uint8_t *addr; 378 char *type; 379 uint64_t nrev, orev; 380 caddr_t file; 381 size_t i, len; 382 int error; 383 384 KASSERT(free % PAGE_SIZE == 0, ("unaligned boundary %p", (void *)free)); 385 386 do_cpuid(0, cpuid.regs); 387 cpuid.regs[0] = cpuid.regs[1]; 388 cpuid.regs[1] = cpuid.regs[3]; 389 cpuid.vendor[12] = '\0'; 390 for (i = 0; i < nitems(loaders); i++) 391 if (strcmp(cpuid.vendor, loaders[i].vendor) == 0) { 392 ucode_loader = &loaders[i]; 393 break; 394 } 395 if (ucode_loader == NULL) 396 return (0); 397 398 file = 0; 399 fileaddr = match = NULL; 400 for (;;) { 401 file = preload_search_next_name(file); 402 if (file == 0) 403 break; 404 type = (char *)preload_search_info(file, MODINFO_TYPE); 405 if (type == NULL || strcmp(type, "cpu_microcode") != 0) 406 continue; 407 408 fileaddr = preload_fetch_addr(file); 409 len = preload_fetch_size(file); 410 match = ucode_loader->match(fileaddr, &len); 411 if (match != NULL) { 412 addr = map_ucode(free, len); 413 /* We can't use memcpy() before ifunc resolution. */ 414 memcpy_early(addr, match, len); 415 match = addr; 416 417 error = ucode_loader->load(match, false, &nrev, &orev); 418 if (error == 0) { 419 ucode_data = early_ucode_data = match; 420 ucode_nrev = nrev; 421 ucode_orev = orev; 422 return (len); 423 } 424 unmap_ucode(free, len); 425 } 426 } 427 if (fileaddr != NULL && ucode_error == NO_ERROR) 428 ucode_error = NO_MATCH; 429 return (0); 430} 431 432/* 433 * Reload microcode following an ACPI resume. 434 */ 435void 436ucode_reload(void) 437{ 438 439 ucode_load_ap(PCPU_GET(cpuid)); 440} 441 442/* 443 * Replace an existing microcode update. 444 */ 445void * 446ucode_update(void *newdata) 447{ 448 449 newdata = (void *)atomic_swap_ptr((void *)&ucode_data, 450 (uintptr_t)newdata); 451 if (newdata == early_ucode_data) 452 newdata = NULL; 453 return (newdata); 454} 455