1337715Smarkj/*- 2337715Smarkj * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3337715Smarkj * 4337715Smarkj * Copyright (c) 2018 The FreeBSD Foundation 5337715Smarkj * 6337715Smarkj * This software was developed by Mark Johnston under sponsorship from 7337715Smarkj * the FreeBSD Foundation. 8337715Smarkj * 9337715Smarkj * Redistribution and use in source and binary forms, with or without 10337715Smarkj * modification, are permitted provided that the following conditions 11337715Smarkj * are met: 12337715Smarkj * 1. Redistributions of source code must retain the above copyright 13337715Smarkj * notice, this list of conditions and the following disclaimer. 14337715Smarkj * 2. Redistributions in binary form must reproduce the above copyright 15337715Smarkj * notice, this list of conditions and the following disclaimer in the 16337715Smarkj * documentation and/or other materials provided with the distribution. 17337715Smarkj * 18337715Smarkj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19337715Smarkj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20337715Smarkj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21337715Smarkj * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22337715Smarkj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23337715Smarkj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24337715Smarkj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25337715Smarkj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26337715Smarkj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27337715Smarkj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28337715Smarkj * SUCH DAMAGE. 29337715Smarkj */ 30337715Smarkj 31337715Smarkj#include <sys/cdefs.h> 32337715Smarkj__FBSDID("$FreeBSD: stable/11/sys/x86/x86/ucode.c 347700 2019-05-16 14:42:16Z markj $"); 33337715Smarkj 34337715Smarkj#include <sys/param.h> 35337715Smarkj#include <sys/cpuset.h> 36337715Smarkj#include <sys/kernel.h> 37337715Smarkj#include <sys/linker.h> 38337715Smarkj#include <sys/malloc.h> 39337715Smarkj#include <sys/pcpu.h> 40337715Smarkj#include <sys/smp.h> 41337715Smarkj#include <sys/systm.h> 42337715Smarkj 43337715Smarkj#include <machine/atomic.h> 44337715Smarkj#include <machine/cpufunc.h> 45337715Smarkj#include <x86/specialreg.h> 46337715Smarkj#include <machine/stdarg.h> 47337715Smarkj#include <x86/ucode.h> 48337715Smarkj#include <x86/x86_smp.h> 49337715Smarkj 50337715Smarkj#include <vm/vm.h> 51337715Smarkj#include <vm/pmap.h> 52337715Smarkj#include <vm/vm_extern.h> 53337715Smarkj#include <vm/vm_kern.h> 54337715Smarkj#include <vm/vm_param.h> 55337715Smarkj 56337715Smarkjstatic void *ucode_intel_match(uint8_t *data, size_t *len); 57337715Smarkjstatic int ucode_intel_verify(struct ucode_intel_header *hdr, 58337715Smarkj size_t resid); 59337715Smarkj 60337715Smarkjstatic struct ucode_ops { 61337715Smarkj const char *vendor; 62347700Smarkj int (*load)(void *, bool, uint64_t *, uint64_t *); 63337715Smarkj void *(*match)(uint8_t *, size_t *); 64337715Smarkj} loaders[] = { 65337715Smarkj { 66337715Smarkj .vendor = INTEL_VENDOR_ID, 67337715Smarkj .load = ucode_intel_load, 68337715Smarkj .match = ucode_intel_match, 69337715Smarkj }, 70337715Smarkj}; 71337715Smarkj 72337715Smarkj/* Selected microcode update data. */ 73337715Smarkjstatic void *early_ucode_data; 74337715Smarkjstatic void *ucode_data; 75347700Smarkjstatic struct ucode_ops *ucode_loader; 76337715Smarkj 77347700Smarkj/* Variables used for reporting success or failure. */ 78347700Smarkjenum { 79347700Smarkj NO_ERROR, 80347700Smarkj NO_MATCH, 81347700Smarkj VERIFICATION_FAILED, 82347700Smarkj} ucode_error = NO_ERROR; 83347700Smarkjstatic uint64_t ucode_nrev, ucode_orev; 84337715Smarkj 85347700Smarkjstatic void 86347700Smarkjlog_msg(void *arg __unused) 87337715Smarkj{ 88337715Smarkj 89347700Smarkj if (ucode_nrev != 0) { 90347700Smarkj printf("CPU microcode: updated from %#jx to %#jx\n", 91347700Smarkj (uintmax_t)ucode_orev, (uintmax_t)ucode_nrev); 92347700Smarkj return; 93347700Smarkj } 94337715Smarkj 95347700Smarkj switch (ucode_error) { 96347700Smarkj case NO_MATCH: 97347700Smarkj printf("CPU microcode: no matching update found\n"); 98347700Smarkj break; 99347700Smarkj case VERIFICATION_FAILED: 100347700Smarkj printf("CPU microcode: microcode verification failed\n"); 101347700Smarkj break; 102347700Smarkj default: 103347700Smarkj break; 104347700Smarkj } 105337715Smarkj} 106347700SmarkjSYSINIT(ucode_log, SI_SUB_CPU, SI_ORDER_FIRST, log_msg, NULL); 107337715Smarkj 108337715Smarkjint 109347700Smarkjucode_intel_load(void *data, bool unsafe, uint64_t *nrevp, uint64_t *orevp) 110337715Smarkj{ 111347700Smarkj uint64_t nrev, orev; 112337715Smarkj uint32_t cpuid[4]; 113337715Smarkj 114347700Smarkj orev = rdmsr(MSR_BIOS_SIGN) >> 32; 115337715Smarkj 116337715Smarkj /* 117337715Smarkj * Perform update. Flush caches first to work around seemingly 118337715Smarkj * undocumented errata applying to some Broadwell CPUs. 119337715Smarkj */ 120337715Smarkj wbinvd(); 121337715Smarkj if (unsafe) 122337715Smarkj wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data); 123337715Smarkj else 124337715Smarkj wrmsr(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data); 125337715Smarkj wrmsr(MSR_BIOS_SIGN, 0); 126337715Smarkj 127337715Smarkj /* 128337715Smarkj * Serialize instruction flow. 129337715Smarkj */ 130337715Smarkj do_cpuid(0, cpuid); 131337715Smarkj 132347700Smarkj /* 133347700Smarkj * Verify that the microcode revision changed. 134347700Smarkj */ 135347700Smarkj nrev = rdmsr(MSR_BIOS_SIGN) >> 32; 136347700Smarkj if (nrevp != NULL) 137347700Smarkj *nrevp = nrev; 138347700Smarkj if (orevp != NULL) 139347700Smarkj *orevp = orev; 140347700Smarkj if (nrev <= orev) 141337715Smarkj return (EEXIST); 142337715Smarkj return (0); 143337715Smarkj} 144337715Smarkj 145337715Smarkjstatic int 146337715Smarkjucode_intel_verify(struct ucode_intel_header *hdr, size_t resid) 147337715Smarkj{ 148337715Smarkj uint32_t cksum, *data, size; 149337715Smarkj int i; 150337715Smarkj 151347700Smarkj if (resid < sizeof(struct ucode_intel_header)) 152337715Smarkj return (1); 153337715Smarkj size = hdr->total_size; 154337715Smarkj if (size == 0) 155337715Smarkj size = UCODE_INTEL_DEFAULT_DATA_SIZE + 156337715Smarkj sizeof(struct ucode_intel_header); 157337715Smarkj 158347700Smarkj if (hdr->header_version != 1) 159337715Smarkj return (1); 160347700Smarkj if (size % 16 != 0) 161337715Smarkj return (1); 162347700Smarkj if (resid < size) 163337715Smarkj return (1); 164337715Smarkj 165337715Smarkj cksum = 0; 166337715Smarkj data = (uint32_t *)hdr; 167337715Smarkj for (i = 0; i < size / sizeof(uint32_t); i++) 168337715Smarkj cksum += data[i]; 169347700Smarkj if (cksum != 0) 170337715Smarkj return (1); 171337715Smarkj return (0); 172337715Smarkj} 173337715Smarkj 174337715Smarkjstatic void * 175337715Smarkjucode_intel_match(uint8_t *data, size_t *len) 176337715Smarkj{ 177337715Smarkj struct ucode_intel_header *hdr; 178337715Smarkj struct ucode_intel_extsig_table *table; 179337715Smarkj struct ucode_intel_extsig *entry; 180337715Smarkj uint64_t platformid; 181337715Smarkj size_t resid; 182337715Smarkj uint32_t data_size, flags, regs[4], sig, total_size; 183337715Smarkj int i; 184337715Smarkj 185337715Smarkj do_cpuid(1, regs); 186337715Smarkj sig = regs[0]; 187337715Smarkj 188337715Smarkj platformid = rdmsr(MSR_IA32_PLATFORM_ID); 189337715Smarkj flags = 1 << ((platformid >> 50) & 0x7); 190337715Smarkj 191337715Smarkj for (resid = *len; resid > 0; data += total_size, resid -= total_size) { 192337715Smarkj hdr = (struct ucode_intel_header *)data; 193347700Smarkj if (ucode_intel_verify(hdr, resid) != 0) { 194347700Smarkj ucode_error = VERIFICATION_FAILED; 195337715Smarkj break; 196347700Smarkj } 197337715Smarkj 198337715Smarkj data_size = hdr->data_size; 199337715Smarkj total_size = hdr->total_size; 200337715Smarkj if (data_size == 0) 201337715Smarkj data_size = UCODE_INTEL_DEFAULT_DATA_SIZE; 202337715Smarkj if (total_size == 0) 203337715Smarkj total_size = UCODE_INTEL_DEFAULT_DATA_SIZE + 204337715Smarkj sizeof(struct ucode_intel_header); 205337715Smarkj if (data_size > total_size + sizeof(struct ucode_intel_header)) 206337715Smarkj table = (struct ucode_intel_extsig_table *) 207337715Smarkj ((uint8_t *)(hdr + 1) + data_size); 208337715Smarkj else 209337715Smarkj table = NULL; 210337715Smarkj 211337715Smarkj if (hdr->processor_signature == sig) { 212337715Smarkj if ((hdr->processor_flags & flags) != 0) { 213337715Smarkj *len = data_size; 214337715Smarkj return (hdr + 1); 215337715Smarkj } 216337715Smarkj } else if (table != NULL) { 217337715Smarkj for (i = 0; i < table->signature_count; i++) { 218337715Smarkj entry = &table->entries[i]; 219337715Smarkj if (entry->processor_signature == sig && 220337715Smarkj (entry->processor_flags & flags) != 0) { 221337715Smarkj *len = data_size; 222337715Smarkj return (hdr + 1); 223337715Smarkj } 224337715Smarkj } 225337715Smarkj } 226337715Smarkj } 227337715Smarkj return (NULL); 228337715Smarkj} 229337715Smarkj 230337715Smarkj/* 231337715Smarkj * Release any memory backing unused microcode blobs back to the system. 232337715Smarkj * We copy the selected update and free the entire microcode file. 233337715Smarkj */ 234337715Smarkjstatic void 235337715Smarkjucode_release(void *arg __unused) 236337715Smarkj{ 237337715Smarkj char *name, *type; 238337715Smarkj caddr_t file; 239337715Smarkj int release; 240337715Smarkj 241337715Smarkj if (early_ucode_data == NULL) 242337715Smarkj return; 243337715Smarkj release = 1; 244337715Smarkj TUNABLE_INT_FETCH("debug.ucode.release", &release); 245337715Smarkj if (!release) 246337715Smarkj return; 247337715Smarkj 248337715Smarkjrestart: 249337715Smarkj file = 0; 250337715Smarkj for (;;) { 251337715Smarkj file = preload_search_next_name(file); 252337715Smarkj if (file == 0) 253337715Smarkj break; 254337715Smarkj type = (char *)preload_search_info(file, MODINFO_TYPE); 255337715Smarkj if (type == NULL || strcmp(type, "cpu_microcode") != 0) 256337715Smarkj continue; 257337715Smarkj 258337715Smarkj name = preload_search_info(file, MODINFO_NAME); 259337715Smarkj preload_delete_name(name); 260337715Smarkj goto restart; 261337715Smarkj } 262337715Smarkj} 263337715SmarkjSYSINIT(ucode_release, SI_SUB_KMEM + 1, SI_ORDER_ANY, ucode_release, NULL); 264337715Smarkj 265337715Smarkjvoid 266337715Smarkjucode_load_ap(int cpu) 267337715Smarkj{ 268347700Smarkj#ifdef SMP 269337715Smarkj KASSERT(cpu_info[cpu_apic_ids[cpu]].cpu_present, 270337715Smarkj ("cpu %d not present", cpu)); 271337715Smarkj 272347700Smarkj if (cpu_info[cpu_apic_ids[cpu]].cpu_hyperthread) 273347700Smarkj return; 274347700Smarkj#endif 275347700Smarkj 276347700Smarkj if (ucode_data != NULL) 277347700Smarkj (void)ucode_loader->load(ucode_data, false, NULL, NULL); 278337715Smarkj} 279337715Smarkj 280337715Smarkjstatic void * 281347700Smarkjmap_ucode(uintptr_t free, size_t len) 282337715Smarkj{ 283347700Smarkj#ifdef __i386__ 284347700Smarkj uintptr_t va; 285337715Smarkj 286347700Smarkj for (va = free; va < free + len; va += PAGE_SIZE) 287347700Smarkj pmap_kenter(va, (vm_paddr_t)va); 288337715Smarkj#else 289337715Smarkj (void)len; 290337715Smarkj#endif 291337715Smarkj return ((void *)free); 292337715Smarkj} 293337715Smarkj 294337715Smarkjstatic void 295347700Smarkjunmap_ucode(uintptr_t free, size_t len) 296337715Smarkj{ 297347700Smarkj#ifdef __i386__ 298347700Smarkj uintptr_t va; 299337715Smarkj 300347700Smarkj for (va = free; va < free + len; va += PAGE_SIZE) 301347700Smarkj pmap_kremove(va); 302337715Smarkj#else 303337715Smarkj (void)free; 304337715Smarkj (void)len; 305337715Smarkj#endif 306337715Smarkj} 307337715Smarkj 308337715Smarkj/* 309337715Smarkj * Search for an applicable microcode update, and load it. APs will load the 310337715Smarkj * selected update once they come online. 311337715Smarkj * 312337715Smarkj * "free" is the address of the next free physical page. If a microcode update 313337715Smarkj * is selected, it will be copied to this region prior to loading in order to 314337715Smarkj * satisfy alignment requirements. 315337715Smarkj */ 316337715Smarkjsize_t 317337715Smarkjucode_load_bsp(uintptr_t free) 318337715Smarkj{ 319337715Smarkj union { 320337715Smarkj uint32_t regs[4]; 321337715Smarkj char vendor[13]; 322337715Smarkj } cpuid; 323337715Smarkj uint8_t *addr, *fileaddr, *match; 324337715Smarkj char *type; 325347700Smarkj uint64_t nrev, orev; 326337715Smarkj caddr_t file; 327347700Smarkj size_t i, len; 328347700Smarkj int error; 329337715Smarkj 330337715Smarkj KASSERT(free % PAGE_SIZE == 0, ("unaligned boundary %p", (void *)free)); 331337715Smarkj 332337715Smarkj do_cpuid(0, cpuid.regs); 333337715Smarkj cpuid.regs[0] = cpuid.regs[1]; 334337715Smarkj cpuid.regs[1] = cpuid.regs[3]; 335337715Smarkj cpuid.vendor[12] = '\0'; 336347700Smarkj for (i = 0; i < nitems(loaders); i++) 337337715Smarkj if (strcmp(cpuid.vendor, loaders[i].vendor) == 0) { 338347700Smarkj ucode_loader = &loaders[i]; 339337715Smarkj break; 340337715Smarkj } 341347700Smarkj if (ucode_loader == NULL) 342337715Smarkj return (0); 343337715Smarkj 344337715Smarkj file = 0; 345337715Smarkj fileaddr = match = NULL; 346337715Smarkj for (;;) { 347337715Smarkj file = preload_search_next_name(file); 348337715Smarkj if (file == 0) 349337715Smarkj break; 350337715Smarkj type = (char *)preload_search_info(file, MODINFO_TYPE); 351337715Smarkj if (type == NULL || strcmp(type, "cpu_microcode") != 0) 352337715Smarkj continue; 353337715Smarkj 354337715Smarkj fileaddr = preload_fetch_addr(file); 355337715Smarkj len = preload_fetch_size(file); 356347700Smarkj match = ucode_loader->match(fileaddr, &len); 357337715Smarkj if (match != NULL) { 358337715Smarkj addr = map_ucode(free, len); 359347700Smarkj /* We can't use memcpy() before ifunc resolution. */ 360347700Smarkj for (i = 0; i < len; i++) 361347700Smarkj addr[i] = ((volatile uint8_t *)match)[i]; 362337715Smarkj match = addr; 363337715Smarkj 364347700Smarkj error = ucode_loader->load(match, false, &nrev, &orev); 365347700Smarkj if (error == 0) { 366347700Smarkj ucode_data = early_ucode_data = match; 367347700Smarkj ucode_nrev = nrev; 368347700Smarkj ucode_orev = orev; 369347700Smarkj return (len); 370337715Smarkj } 371337715Smarkj unmap_ucode(free, len); 372337715Smarkj } 373337715Smarkj } 374347700Smarkj if (fileaddr != NULL && ucode_error == NO_ERROR) 375347700Smarkj ucode_error = NO_MATCH; 376347700Smarkj return (0); 377337715Smarkj} 378337715Smarkj 379337715Smarkj/* 380337715Smarkj * Reload microcode following an ACPI resume. 381337715Smarkj */ 382337715Smarkjvoid 383337715Smarkjucode_reload(void) 384337715Smarkj{ 385337715Smarkj 386337715Smarkj ucode_load_ap(PCPU_GET(cpuid)); 387337715Smarkj} 388337715Smarkj 389337715Smarkj/* 390337715Smarkj * Replace an existing microcode update. 391337715Smarkj */ 392337715Smarkjvoid * 393337715Smarkjucode_update(void *newdata) 394337715Smarkj{ 395337715Smarkj 396337715Smarkj newdata = (void *)atomic_swap_ptr((void *)&ucode_data, 397337715Smarkj (uintptr_t)newdata); 398337715Smarkj if (newdata == early_ucode_data) 399337715Smarkj newdata = NULL; 400337715Smarkj return (newdata); 401337715Smarkj} 402