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 53 static const void *ucode_intel_match(const uint8_t *data, size_t *len); 54 static int ucode_intel_verify(const struct ucode_intel_header *hdr, 55 size_t resid); 56 57 static struct ucode_ops { 58 const char *vendor; 59 int (*load)(const void *, bool, uint64_t *, uint64_t *); 60 const void *(*match)(const uint8_t *, size_t *); 61 } loaders[] = { 62 { 63 .vendor = INTEL_VENDOR_ID, 64 .load = ucode_intel_load, 65 .match = ucode_intel_match, 66 }, 67 }; 68 69 /* Selected microcode update data. */ 70 static const void *early_ucode_data; 71 static const void *ucode_data; 72 static struct ucode_ops *ucode_loader; 73 74 /* Variables used for reporting success or failure. */ 75 enum { 76 NO_ERROR, 77 NO_MATCH, 78 VERIFICATION_FAILED, 79 } ucode_error = NO_ERROR; 80 static uint64_t ucode_nrev, ucode_orev; 81 82 static void 83 log_msg(void *arg __unused) 84 { 85 86 if (ucode_nrev != 0) { 87 printf("CPU microcode: updated from %#jx to %#jx\n", 88 (uintmax_t)ucode_orev, (uintmax_t)ucode_nrev); 89 return; 90 } 91 92 switch (ucode_error) { 93 case NO_MATCH: 94 printf("CPU microcode: no matching update found\n"); 95 break; 96 case VERIFICATION_FAILED: 97 printf("CPU microcode: microcode verification failed\n"); 98 break; 99 default: 100 break; 101 } 102 } 103 SYSINIT(ucode_log, SI_SUB_CPU, SI_ORDER_FIRST, log_msg, NULL); 104 105 int 106 ucode_intel_load(const void *data, bool unsafe, uint64_t *nrevp, uint64_t *orevp) 107 { 108 uint64_t nrev, orev; 109 uint32_t cpuid[4]; 110 111 orev = rdmsr(MSR_BIOS_SIGN) >> 32; 112 113 /* 114 * Perform update. Flush caches first to work around seemingly 115 * undocumented errata applying to some Broadwell CPUs. 116 */ 117 wbinvd(); 118 if (unsafe) 119 wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data); 120 else 121 wrmsr(MSR_BIOS_UPDT_TRIG, (uint64_t)(uintptr_t)data); 122 wrmsr(MSR_BIOS_SIGN, 0); 123 124 /* 125 * Serialize instruction flow. 126 */ 127 do_cpuid(0, cpuid); 128 129 /* 130 * Verify that the microcode revision changed. 131 */ 132 nrev = rdmsr(MSR_BIOS_SIGN) >> 32; 133 if (nrevp != NULL) 134 *nrevp = nrev; 135 if (orevp != NULL) 136 *orevp = orev; 137 if (nrev <= orev) 138 return (EEXIST); 139 return (0); 140 } 141 142 static int 143 ucode_intel_verify(const struct ucode_intel_header *hdr, size_t resid) 144 { 145 const uint32_t *data; 146 uint32_t cksum, size; 147 int i; 148 149 if (resid < sizeof(struct ucode_intel_header)) 150 return (1); 151 size = hdr->total_size; 152 if (size == 0) 153 size = UCODE_INTEL_DEFAULT_DATA_SIZE + 154 sizeof(struct ucode_intel_header); 155 156 if (hdr->header_version != 1) 157 return (1); 158 if (size % 16 != 0) 159 return (1); 160 if (resid < size) 161 return (1); 162 163 cksum = 0; 164 data = (const uint32_t *)hdr; 165 for (i = 0; i < size / sizeof(uint32_t); i++) 166 cksum += data[i]; 167 if (cksum != 0) 168 return (1); 169 return (0); 170 } 171 172 static const void * 173 ucode_intel_match(const uint8_t *data, size_t *len) 174 { 175 const struct ucode_intel_header *hdr; 176 const struct ucode_intel_extsig_table *table; 177 const struct ucode_intel_extsig *entry; 178 uint64_t platformid; 179 size_t resid; 180 uint32_t data_size, flags, regs[4], sig, total_size; 181 int i; 182 183 do_cpuid(1, regs); 184 sig = regs[0]; 185 186 platformid = rdmsr(MSR_IA32_PLATFORM_ID); 187 flags = 1 << ((platformid >> 50) & 0x7); 188 189 for (resid = *len; resid > 0; data += total_size, resid -= total_size) { 190 hdr = (const struct ucode_intel_header *)data; 191 if (ucode_intel_verify(hdr, resid) != 0) { 192 ucode_error = VERIFICATION_FAILED; 193 break; 194 } 195 196 data_size = hdr->data_size; 197 total_size = hdr->total_size; 198 if (data_size == 0) 199 data_size = UCODE_INTEL_DEFAULT_DATA_SIZE; 200 if (total_size == 0) 201 total_size = UCODE_INTEL_DEFAULT_DATA_SIZE + 202 sizeof(struct ucode_intel_header); 203 if (data_size > total_size + sizeof(struct ucode_intel_header)) 204 table = (const struct ucode_intel_extsig_table *) 205 ((const uint8_t *)(hdr + 1) + data_size); 206 else 207 table = NULL; 208 209 if (hdr->processor_signature == sig) { 210 if ((hdr->processor_flags & flags) != 0) { 211 *len = data_size; 212 return (hdr + 1); 213 } 214 } else if (table != NULL) { 215 for (i = 0; i < table->signature_count; i++) { 216 entry = &table->entries[i]; 217 if (entry->processor_signature == sig && 218 (entry->processor_flags & flags) != 0) { 219 *len = data_size; 220 return (hdr + 1); 221 } 222 } 223 } 224 } 225 return (NULL); 226 } 227 228 /* 229 * Release any memory backing unused microcode blobs back to the system. 230 * We copy the selected update and free the entire microcode file. 231 */ 232 static void 233 ucode_release(void *arg __unused) 234 { 235 char *name, *type; 236 caddr_t file; 237 int release; 238 239 if (early_ucode_data == NULL) 240 return; 241 release = 1; 242 TUNABLE_INT_FETCH("debug.ucode.release", &release); 243 if (!release) 244 return; 245 246 restart: 247 file = 0; 248 for (;;) { 249 file = preload_search_next_name(file); 250 if (file == 0) 251 break; 252 type = (char *)preload_search_info(file, MODINFO_TYPE); 253 if (type == NULL || strcmp(type, "cpu_microcode") != 0) 254 continue; 255 256 name = preload_search_info(file, MODINFO_NAME); 257 preload_delete_name(name); 258 goto restart; 259 } 260 } 261 SYSINIT(ucode_release, SI_SUB_SMP + 1, SI_ORDER_ANY, ucode_release, NULL); 262 263 void 264 ucode_load_ap(int cpu) 265 { 266 #ifdef SMP 267 KASSERT(cpu_info[cpu_apic_ids[cpu]].cpu_present, 268 ("cpu %d not present", cpu)); 269 270 if (cpu_info[cpu_apic_ids[cpu]].cpu_hyperthread) 271 return; 272 #endif 273 274 if (ucode_data != NULL) 275 (void)ucode_loader->load(ucode_data, false, NULL, NULL); 276 } 277 278 static void * 279 map_ucode(uintptr_t free, size_t len) 280 { 281 #ifdef __i386__ 282 uintptr_t va; 283 284 for (va = free; va < free + len; va += PAGE_SIZE) 285 pmap_kenter(va, (vm_paddr_t)va); 286 #else 287 (void)len; 288 #endif 289 return ((void *)free); 290 } 291 292 static void 293 unmap_ucode(uintptr_t free, size_t len) 294 { 295 #ifdef __i386__ 296 uintptr_t va; 297 298 for (va = free; va < free + len; va += PAGE_SIZE) 299 pmap_kremove(va); 300 #else 301 (void)free; 302 (void)len; 303 #endif 304 } 305 306 /* 307 * Search for an applicable microcode update, and load it. APs will load the 308 * selected update once they come online. 309 * 310 * "free" is the address of the next free physical page. If a microcode update 311 * is selected, it will be copied to this region prior to loading in order to 312 * satisfy alignment requirements. 313 */ 314 size_t 315 ucode_load_bsp(uintptr_t free) 316 { 317 union { 318 uint32_t regs[4]; 319 char vendor[13]; 320 } cpuid; 321 const uint8_t *fileaddr, *match; 322 uint8_t *addr; 323 char *type; 324 uint64_t nrev, orev; 325 caddr_t file; 326 size_t i, len; 327 int error; 328 329 KASSERT(free % PAGE_SIZE == 0, ("unaligned boundary %p", (void *)free)); 330 331 do_cpuid(0, cpuid.regs); 332 cpuid.regs[0] = cpuid.regs[1]; 333 cpuid.regs[1] = cpuid.regs[3]; 334 cpuid.vendor[12] = '\0'; 335 for (i = 0; i < nitems(loaders); i++) 336 if (strcmp(cpuid.vendor, loaders[i].vendor) == 0) { 337 ucode_loader = &loaders[i]; 338 break; 339 } 340 if (ucode_loader == NULL) 341 return (0); 342 343 file = 0; 344 fileaddr = match = NULL; 345 for (;;) { 346 file = preload_search_next_name(file); 347 if (file == 0) 348 break; 349 type = (char *)preload_search_info(file, MODINFO_TYPE); 350 if (type == NULL || strcmp(type, "cpu_microcode") != 0) 351 continue; 352 353 fileaddr = preload_fetch_addr(file); 354 len = preload_fetch_size(file); 355 match = ucode_loader->match(fileaddr, &len); 356 if (match != NULL) { 357 addr = map_ucode(free, len); 358 /* We can't use memcpy() before ifunc resolution. */ 359 memcpy_early(addr, match, len); 360 match = addr; 361 362 error = ucode_loader->load(match, false, &nrev, &orev); 363 if (error == 0) { 364 ucode_data = early_ucode_data = match; 365 ucode_nrev = nrev; 366 ucode_orev = orev; 367 return (len); 368 } 369 unmap_ucode(free, len); 370 } 371 } 372 if (fileaddr != NULL && ucode_error == NO_ERROR) 373 ucode_error = NO_MATCH; 374 return (0); 375 } 376 377 /* 378 * Reload microcode following an ACPI resume. 379 */ 380 void 381 ucode_reload(void) 382 { 383 384 ucode_load_ap(PCPU_GET(cpuid)); 385 } 386 387 /* 388 * Replace an existing microcode update. 389 */ 390 void * 391 ucode_update(void *newdata) 392 { 393 394 newdata = (void *)atomic_swap_ptr((void *)&ucode_data, 395 (uintptr_t)newdata); 396 if (newdata == early_ucode_data) 397 newdata = NULL; 398 return (newdata); 399 } 400