ucode.c revision 337715
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: head/sys/x86/x86/ucode.c 337715 2018-08-13 17:13:09Z 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; 62337715Smarkj int (*load)(void *, bool); 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; 75337715Smarkj 76337715Smarkjstatic char errbuf[128]; 77337715Smarkj 78337715Smarkjstatic void __printflike(1, 2) 79337715Smarkjlog_err(const char *fmt, ...) 80337715Smarkj{ 81337715Smarkj va_list ap; 82337715Smarkj 83337715Smarkj va_start(ap, fmt); 84337715Smarkj vsnprintf(errbuf, sizeof(errbuf), fmt, ap); 85337715Smarkj va_end(ap); 86337715Smarkj} 87337715Smarkj 88337715Smarkjstatic void 89337715Smarkjprint_err(void *arg __unused) 90337715Smarkj{ 91337715Smarkj 92337715Smarkj if (errbuf[0] != '\0') 93337715Smarkj printf("microcode load error: %s\n", errbuf); 94337715Smarkj} 95337715SmarkjSYSINIT(ucode_print_err, SI_SUB_CPU, SI_ORDER_FIRST, print_err, NULL); 96337715Smarkj 97337715Smarkjint 98337715Smarkjucode_intel_load(void *data, bool unsafe) 99337715Smarkj{ 100337715Smarkj uint64_t rev0, rev1; 101337715Smarkj uint32_t cpuid[4]; 102337715Smarkj 103337715Smarkj rev0 = rdmsr(MSR_BIOS_SIGN); 104337715Smarkj 105337715Smarkj /* 106337715Smarkj * Perform update. Flush caches first to work around seemingly 107337715Smarkj * undocumented errata applying to some Broadwell CPUs. 108337715Smarkj */ 109337715Smarkj wbinvd(); 110337715Smarkj if (unsafe) 111337715Smarkj wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data); 112337715Smarkj else 113337715Smarkj wrmsr(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data); 114337715Smarkj wrmsr(MSR_BIOS_SIGN, 0); 115337715Smarkj 116337715Smarkj /* 117337715Smarkj * Serialize instruction flow. 118337715Smarkj */ 119337715Smarkj do_cpuid(0, cpuid); 120337715Smarkj 121337715Smarkj rev1 = rdmsr(MSR_BIOS_SIGN); 122337715Smarkj if (rev1 <= rev0) 123337715Smarkj return (EEXIST); 124337715Smarkj return (0); 125337715Smarkj} 126337715Smarkj 127337715Smarkjstatic int 128337715Smarkjucode_intel_verify(struct ucode_intel_header *hdr, size_t resid) 129337715Smarkj{ 130337715Smarkj uint32_t cksum, *data, size; 131337715Smarkj int i; 132337715Smarkj 133337715Smarkj if (resid < sizeof(struct ucode_intel_header)) { 134337715Smarkj log_err("truncated update header"); 135337715Smarkj return (1); 136337715Smarkj } 137337715Smarkj size = hdr->total_size; 138337715Smarkj if (size == 0) 139337715Smarkj size = UCODE_INTEL_DEFAULT_DATA_SIZE + 140337715Smarkj sizeof(struct ucode_intel_header); 141337715Smarkj 142337715Smarkj if (hdr->header_version != 1) { 143337715Smarkj log_err("unexpected header version %u", hdr->header_version); 144337715Smarkj return (1); 145337715Smarkj } 146337715Smarkj if (size % 16 != 0) { 147337715Smarkj log_err("unexpected update size %u", hdr->total_size); 148337715Smarkj return (1); 149337715Smarkj } 150337715Smarkj if (resid < size) { 151337715Smarkj log_err("truncated update"); 152337715Smarkj return (1); 153337715Smarkj } 154337715Smarkj 155337715Smarkj cksum = 0; 156337715Smarkj data = (uint32_t *)hdr; 157337715Smarkj for (i = 0; i < size / sizeof(uint32_t); i++) 158337715Smarkj cksum += data[i]; 159337715Smarkj if (cksum != 0) { 160337715Smarkj log_err("checksum failed"); 161337715Smarkj return (1); 162337715Smarkj } 163337715Smarkj return (0); 164337715Smarkj} 165337715Smarkj 166337715Smarkjstatic void * 167337715Smarkjucode_intel_match(uint8_t *data, size_t *len) 168337715Smarkj{ 169337715Smarkj struct ucode_intel_header *hdr; 170337715Smarkj struct ucode_intel_extsig_table *table; 171337715Smarkj struct ucode_intel_extsig *entry; 172337715Smarkj uint64_t platformid; 173337715Smarkj size_t resid; 174337715Smarkj uint32_t data_size, flags, regs[4], sig, total_size; 175337715Smarkj int i; 176337715Smarkj 177337715Smarkj do_cpuid(1, regs); 178337715Smarkj sig = regs[0]; 179337715Smarkj 180337715Smarkj platformid = rdmsr(MSR_IA32_PLATFORM_ID); 181337715Smarkj flags = 1 << ((platformid >> 50) & 0x7); 182337715Smarkj 183337715Smarkj for (resid = *len; resid > 0; data += total_size, resid -= total_size) { 184337715Smarkj hdr = (struct ucode_intel_header *)data; 185337715Smarkj if (ucode_intel_verify(hdr, resid) != 0) 186337715Smarkj break; 187337715Smarkj 188337715Smarkj data_size = hdr->data_size; 189337715Smarkj total_size = hdr->total_size; 190337715Smarkj if (data_size == 0) 191337715Smarkj data_size = UCODE_INTEL_DEFAULT_DATA_SIZE; 192337715Smarkj if (total_size == 0) 193337715Smarkj total_size = UCODE_INTEL_DEFAULT_DATA_SIZE + 194337715Smarkj sizeof(struct ucode_intel_header); 195337715Smarkj if (data_size > total_size + sizeof(struct ucode_intel_header)) 196337715Smarkj table = (struct ucode_intel_extsig_table *) 197337715Smarkj ((uint8_t *)(hdr + 1) + data_size); 198337715Smarkj else 199337715Smarkj table = NULL; 200337715Smarkj 201337715Smarkj if (hdr->processor_signature == sig) { 202337715Smarkj if ((hdr->processor_flags & flags) != 0) { 203337715Smarkj *len = data_size; 204337715Smarkj return (hdr + 1); 205337715Smarkj } 206337715Smarkj } else if (table != NULL) { 207337715Smarkj for (i = 0; i < table->signature_count; i++) { 208337715Smarkj entry = &table->entries[i]; 209337715Smarkj if (entry->processor_signature == sig && 210337715Smarkj (entry->processor_flags & flags) != 0) { 211337715Smarkj *len = data_size; 212337715Smarkj return (hdr + 1); 213337715Smarkj } 214337715Smarkj } 215337715Smarkj } 216337715Smarkj } 217337715Smarkj return (NULL); 218337715Smarkj} 219337715Smarkj 220337715Smarkj/* 221337715Smarkj * Release any memory backing unused microcode blobs back to the system. 222337715Smarkj * We copy the selected update and free the entire microcode file. 223337715Smarkj */ 224337715Smarkjstatic void 225337715Smarkjucode_release(void *arg __unused) 226337715Smarkj{ 227337715Smarkj char *name, *type; 228337715Smarkj caddr_t file; 229337715Smarkj int release; 230337715Smarkj 231337715Smarkj if (early_ucode_data == NULL) 232337715Smarkj return; 233337715Smarkj release = 1; 234337715Smarkj TUNABLE_INT_FETCH("debug.ucode.release", &release); 235337715Smarkj if (!release) 236337715Smarkj return; 237337715Smarkj 238337715Smarkjrestart: 239337715Smarkj file = 0; 240337715Smarkj for (;;) { 241337715Smarkj file = preload_search_next_name(file); 242337715Smarkj if (file == 0) 243337715Smarkj break; 244337715Smarkj type = (char *)preload_search_info(file, MODINFO_TYPE); 245337715Smarkj if (type == NULL || strcmp(type, "cpu_microcode") != 0) 246337715Smarkj continue; 247337715Smarkj 248337715Smarkj name = preload_search_info(file, MODINFO_NAME); 249337715Smarkj preload_delete_name(name); 250337715Smarkj goto restart; 251337715Smarkj } 252337715Smarkj} 253337715SmarkjSYSINIT(ucode_release, SI_SUB_KMEM + 1, SI_ORDER_ANY, ucode_release, NULL); 254337715Smarkj 255337715Smarkjvoid 256337715Smarkjucode_load_ap(int cpu) 257337715Smarkj{ 258337715Smarkj 259337715Smarkj KASSERT(cpu_info[cpu_apic_ids[cpu]].cpu_present, 260337715Smarkj ("cpu %d not present", cpu)); 261337715Smarkj 262337715Smarkj if (ucode_data != NULL && !cpu_info[cpu_apic_ids[cpu]].cpu_hyperthread) 263337715Smarkj (void)ucode_intel_load(ucode_data, false); 264337715Smarkj} 265337715Smarkj 266337715Smarkjstatic void * 267337715Smarkjmap_ucode(vm_paddr_t free, size_t len) 268337715Smarkj{ 269337715Smarkj 270337715Smarkj#ifdef __i386__ 271337715Smarkj for (vm_paddr_t pa = free; pa < free + len; pa += PAGE_SIZE) 272337715Smarkj pmap_kenter(pa, pa); 273337715Smarkj#else 274337715Smarkj (void)len; 275337715Smarkj#endif 276337715Smarkj return ((void *)free); 277337715Smarkj} 278337715Smarkj 279337715Smarkjstatic void 280337715Smarkjunmap_ucode(vm_paddr_t free, size_t len) 281337715Smarkj{ 282337715Smarkj 283337715Smarkj#ifdef __i386__ 284337715Smarkj for (vm_paddr_t pa = free; pa < free + len; pa += PAGE_SIZE) 285337715Smarkj pmap_kremove((vm_offset_t)pa); 286337715Smarkj#else 287337715Smarkj (void)free; 288337715Smarkj (void)len; 289337715Smarkj#endif 290337715Smarkj} 291337715Smarkj 292337715Smarkj/* 293337715Smarkj * Search for an applicable microcode update, and load it. APs will load the 294337715Smarkj * selected update once they come online. 295337715Smarkj * 296337715Smarkj * "free" is the address of the next free physical page. If a microcode update 297337715Smarkj * is selected, it will be copied to this region prior to loading in order to 298337715Smarkj * satisfy alignment requirements. 299337715Smarkj */ 300337715Smarkjsize_t 301337715Smarkjucode_load_bsp(uintptr_t free) 302337715Smarkj{ 303337715Smarkj union { 304337715Smarkj uint32_t regs[4]; 305337715Smarkj char vendor[13]; 306337715Smarkj } cpuid; 307337715Smarkj struct ucode_ops *loader; 308337715Smarkj uint8_t *addr, *fileaddr, *match; 309337715Smarkj char *type; 310337715Smarkj caddr_t file; 311337715Smarkj size_t len, ucode_len; 312337715Smarkj int i; 313337715Smarkj 314337715Smarkj KASSERT(free % PAGE_SIZE == 0, ("unaligned boundary %p", (void *)free)); 315337715Smarkj 316337715Smarkj do_cpuid(0, cpuid.regs); 317337715Smarkj cpuid.regs[0] = cpuid.regs[1]; 318337715Smarkj cpuid.regs[1] = cpuid.regs[3]; 319337715Smarkj cpuid.vendor[12] = '\0'; 320337715Smarkj for (i = 0, loader = NULL; i < nitems(loaders); i++) 321337715Smarkj if (strcmp(cpuid.vendor, loaders[i].vendor) == 0) { 322337715Smarkj loader = &loaders[i]; 323337715Smarkj break; 324337715Smarkj } 325337715Smarkj if (loader == NULL) 326337715Smarkj return (0); 327337715Smarkj 328337715Smarkj file = 0; 329337715Smarkj fileaddr = match = NULL; 330337715Smarkj ucode_len = 0; 331337715Smarkj for (;;) { 332337715Smarkj file = preload_search_next_name(file); 333337715Smarkj if (file == 0) 334337715Smarkj break; 335337715Smarkj type = (char *)preload_search_info(file, MODINFO_TYPE); 336337715Smarkj if (type == NULL || strcmp(type, "cpu_microcode") != 0) 337337715Smarkj continue; 338337715Smarkj 339337715Smarkj fileaddr = preload_fetch_addr(file); 340337715Smarkj len = preload_fetch_size(file); 341337715Smarkj match = loader->match(fileaddr, &len); 342337715Smarkj if (match != NULL) { 343337715Smarkj addr = map_ucode(free, len); 344337715Smarkj memcpy(addr, match, len); 345337715Smarkj match = addr; 346337715Smarkj 347337715Smarkj if (loader->load(match, false) == 0) { 348337715Smarkj ucode_data = match; 349337715Smarkj ucode_len = len; 350337715Smarkj early_ucode_data = ucode_data; 351337715Smarkj break; 352337715Smarkj } 353337715Smarkj unmap_ucode(free, len); 354337715Smarkj } 355337715Smarkj } 356337715Smarkj if (fileaddr != NULL && ucode_data == NULL) 357337715Smarkj log_err("no matching update found"); 358337715Smarkj return (ucode_len); 359337715Smarkj} 360337715Smarkj 361337715Smarkj/* 362337715Smarkj * Reload microcode following an ACPI resume. 363337715Smarkj */ 364337715Smarkjvoid 365337715Smarkjucode_reload(void) 366337715Smarkj{ 367337715Smarkj 368337715Smarkj ucode_load_ap(PCPU_GET(cpuid)); 369337715Smarkj} 370337715Smarkj 371337715Smarkj/* 372337715Smarkj * Replace an existing microcode update. 373337715Smarkj */ 374337715Smarkjvoid * 375337715Smarkjucode_update(void *newdata) 376337715Smarkj{ 377337715Smarkj 378337715Smarkj newdata = (void *)atomic_swap_ptr((void *)&ucode_data, 379337715Smarkj (uintptr_t)newdata); 380337715Smarkj if (newdata == early_ucode_data) 381337715Smarkj newdata = NULL; 382337715Smarkj return (newdata); 383337715Smarkj} 384