The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/x86/x86/ucode.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 6b273fe2f10c8c9d293eeb54f5db72c3


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.