Author: avg
Date: Thu Nov 17 15:17:01 2016
New Revision: 308761
URL: https://svnweb.freebsd.org/changeset/base/308761

Log:
  MFC r308218: Add support for microcode update on newer AMD CPUs (10h+)

Added:
  stable/10/usr.sbin/cpucontrol/amd10h.c
     - copied unchanged from r308218, head/usr.sbin/cpucontrol/amd10h.c
Modified:
  stable/10/sys/dev/cpuctl/cpuctl.c
  stable/10/usr.sbin/cpucontrol/Makefile
  stable/10/usr.sbin/cpucontrol/amd.h
  stable/10/usr.sbin/cpucontrol/cpucontrol.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/dev/cpuctl/cpuctl.c
==============================================================================
--- stable/10/sys/dev/cpuctl/cpuctl.c   Thu Nov 17 15:16:52 2016        
(r308760)
+++ stable/10/sys/dev/cpuctl/cpuctl.c   Thu Nov 17 15:17:01 2016        
(r308761)
@@ -377,13 +377,24 @@ fail:
        return (ret);
 }
 
+/*
+ * NB: MSR 0xc0010020, MSR_K8_UCODE_UPDATE, is not documented by AMD.
+ * Coreboot, illumos and Linux source code was used to understand
+ * its workings.
+ */
+static void
+amd_ucode_wrmsr(void *ucode_ptr)
+{
+       uint32_t tmp[4];
+
+       wrmsr_safe(MSR_K8_UCODE_UPDATE, (uintptr_t)ucode_ptr);
+       do_cpuid(0, tmp);
+}
+
 static int
 update_amd(int cpu, cpuctl_update_args_t *args, struct thread *td)
 {
-       void *ptr = NULL;
-       uint32_t tmp[4];
-       int is_bound = 0;
-       int oldcpu;
+       void *ptr;
        int ret;
 
        if (args->size == 0 || args->data == NULL) {
@@ -394,41 +405,23 @@ update_amd(int cpu, cpuctl_update_args_t
                DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__);
                return (EINVAL);
        }
+
        /*
-        * XXX Might not require contignous address space - needs check
+        * 16 byte alignment required.  Rely on the fact that
+        * malloc(9) always returns the pointer aligned at least on
+        * the size of the allocation.
         */
-       ptr = contigmalloc(args->size, M_CPUCTL, 0, 0, 0xffffffff, 16, 0);
-       if (ptr == NULL) {
-               DPRINTF("[cpuctl,%d]: cannot allocate %zd bytes of memory",
-                   __LINE__, args->size);
-               return (ENOMEM);
-       }
+       ptr = malloc(args->size + 16, M_CPUCTL, M_ZERO | M_WAITOK);
        if (copyin(args->data, ptr, args->size) != 0) {
                DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed",
                    __LINE__, args->data, ptr, args->size);
                ret = EFAULT;
                goto fail;
        }
-       oldcpu = td->td_oncpu;
-       is_bound = cpu_sched_is_bound(td);
-       set_cpu(cpu, td);
-       critical_enter();
-
-       /*
-        * Perform update.
-        */
-       wrmsr_safe(MSR_K8_UCODE_UPDATE, (uintptr_t)ptr);
-
-       /*
-        * Serialize instruction flow.
-        */
-       do_cpuid(0, tmp);
-       critical_exit();
-       restore_cpu(oldcpu, is_bound, td);
+       smp_rendezvous(NULL, amd_ucode_wrmsr, NULL, ptr);
        ret = 0;
 fail:
-       if (ptr != NULL)
-               contigfree(ptr, args->size, M_CPUCTL);
+       free(ptr, M_CPUCTL);
        return (ret);
 }
 

Modified: stable/10/usr.sbin/cpucontrol/Makefile
==============================================================================
--- stable/10/usr.sbin/cpucontrol/Makefile      Thu Nov 17 15:16:52 2016        
(r308760)
+++ stable/10/usr.sbin/cpucontrol/Makefile      Thu Nov 17 15:17:01 2016        
(r308761)
@@ -2,7 +2,7 @@
 
 PROG=  cpucontrol
 MAN=   cpucontrol.8
-SRCS=  cpucontrol.c intel.c amd.c via.c
+SRCS=  cpucontrol.c intel.c amd.c amd10h.c via.c
 
 NO_WCAST_ALIGN=
 

Modified: stable/10/usr.sbin/cpucontrol/amd.h
==============================================================================
--- stable/10/usr.sbin/cpucontrol/amd.h Thu Nov 17 15:16:52 2016        
(r308760)
+++ stable/10/usr.sbin/cpucontrol/amd.h Thu Nov 17 15:17:01 2016        
(r308761)
@@ -33,6 +33,8 @@
  */
 ucode_probe_t  amd_probe;
 ucode_update_t amd_update;
+ucode_probe_t  amd10h_probe;
+ucode_update_t amd10h_update;
 
 typedef struct amd_fw_header {
        uint32_t        date;           /* Update creation date. */
@@ -46,4 +48,45 @@ typedef struct amd_fw_header {
 
 #define        AMD_MAGIC       0xaaaaaa
 
+/*
+ * AMD family 10h and later.
+ */
+typedef struct amd_10h_fw_header {
+       uint32_t        data_code;
+       uint32_t        patch_id;
+       uint16_t        mc_patch_data_id;
+       uint8_t         mc_patch_data_len;
+       uint8_t         init_flag;
+       uint32_t        mc_patch_data_checksum;
+       uint32_t        nb_dev_id;
+       uint32_t        sb_dev_id;
+       uint16_t        processor_rev_id;
+       uint8_t         nb_rev_id;
+       uint8_t         sb_rev_id;
+       uint8_t         bios_api_rev;
+       uint8_t         reserved1[3];
+       uint32_t        match_reg[8];
+} amd_10h_fw_header_t;
+
+typedef struct equiv_cpu_entry {
+       uint32_t        installed_cpu;
+       uint32_t        fixed_errata_mask;
+       uint32_t        fixed_errata_compare;
+       uint16_t        equiv_cpu;
+       uint16_t        res;
+} equiv_cpu_entry_t;
+
+typedef struct section_header {
+       uint32_t        type;
+       uint32_t        size;
+} section_header_t;
+
+typedef struct container_header {
+       uint32_t        magic;
+} container_header_t;
+
+#define        AMD_10H_MAGIC                   0x414d44
+#define AMD_10H_EQUIV_TABLE_TYPE       0
+#define AMD_10H_uCODE_TYPE             1
+
 #endif /* !AMD_H */

Copied: stable/10/usr.sbin/cpucontrol/amd10h.c (from r308218, 
head/usr.sbin/cpucontrol/amd10h.c)
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ stable/10/usr.sbin/cpucontrol/amd10h.c      Thu Nov 17 15:17:01 2016        
(r308761, copy of r308218, head/usr.sbin/cpucontrol/amd10h.c)
@@ -0,0 +1,307 @@
+/*-
+ * Copyright (c) 2012 Andriy Gapon <a...@freebsd.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/ioccom.h>
+#include <sys/cpuctl.h>
+
+#include <machine/cpufunc.h>
+#include <machine/specialreg.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+
+#include "cpucontrol.h"
+#include "amd.h"
+
+int
+amd10h_probe(int fd)
+{
+       char vendor[13];
+       cpuctl_cpuid_args_t idargs;
+       uint32_t family;
+       uint32_t signature;
+       int error;
+
+       idargs.level = 0;
+       error = ioctl(fd, CPUCTL_CPUID, &idargs);
+       if (error < 0) {
+               WARN(0, "ioctl()");
+               return (1);
+       }
+       ((uint32_t *)vendor)[0] = idargs.data[1];
+       ((uint32_t *)vendor)[1] = idargs.data[3];
+       ((uint32_t *)vendor)[2] = idargs.data[2];
+       vendor[12] = '\0';
+       if (strncmp(vendor, AMD_VENDOR_ID, sizeof(AMD_VENDOR_ID)) != 0)
+               return (1);
+
+       idargs.level = 1;
+       error = ioctl(fd, CPUCTL_CPUID, &idargs);
+       if (error < 0) {
+               WARN(0, "ioctl()");
+               return (1);
+       }
+       signature = idargs.data[0];
+       family = ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff);
+       if (family < 0x10)
+               return (1);
+       return (0);
+}
+
+/*
+ * NB: the format of microcode update files is not documented by AMD.
+ * It has been reverse engineered from studying Coreboot, illumos and Linux
+ * source code.
+ */
+void
+amd10h_update(const char *dev, const char *path)
+{
+       struct stat st;
+       cpuctl_cpuid_args_t idargs;
+       cpuctl_msr_args_t msrargs;
+       cpuctl_update_args_t args;
+       const amd_10h_fw_header_t *fw_header;
+       const amd_10h_fw_header_t *selected_fw;
+       const equiv_cpu_entry_t *equiv_cpu_table;
+       const section_header_t *section_header;
+       const container_header_t *container_header;
+       const uint8_t *fw_data;
+       uint8_t *fw_image;
+       size_t fw_size;
+       size_t selected_size;
+       uint32_t revision;
+       uint32_t new_rev;
+       uint32_t signature;
+       uint16_t equiv_id;
+       int fd, devfd;
+       unsigned int i;
+       int error;
+
+       assert(path);
+       assert(dev);
+
+       fd = -1;
+       fw_image = MAP_FAILED;
+       devfd = open(dev, O_RDWR);
+       if (devfd < 0) {
+               WARN(0, "could not open %s for writing", dev);
+               return;
+       }
+       idargs.level = 1;
+       error = ioctl(devfd, CPUCTL_CPUID, &idargs);
+       if (error < 0) {
+               WARN(0, "ioctl()");
+               goto done;
+       }
+       signature = idargs.data[0];
+
+       msrargs.msr = 0x0000008b;
+       error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
+       if (error < 0) {
+               WARN(0, "ioctl(%s)", dev);
+               goto done;
+       }
+       revision = (uint32_t)msrargs.data;
+
+       WARNX(1, "found cpu family %#x model %#x "
+           "stepping %#x extfamily %#x extmodel %#x.",
+           (signature >> 8) & 0x0f, (signature >> 4) & 0x0f,
+           (signature >> 0) & 0x0f, (signature >> 20) & 0xff,
+           (signature >> 16) & 0x0f);
+       WARNX(1, "microcode revision %#x", revision);
+
+       /*
+        * Open the firmware file.
+        */
+       fd = open(path, O_RDONLY, 0);
+       if (fd < 0) {
+               WARN(0, "open(%s)", path);
+               goto done;
+       }
+       error = fstat(fd, &st);
+       if (error != 0) {
+               WARN(0, "fstat(%s)", path);
+               goto done;
+       }
+       if (st.st_size < 0 || (size_t)st.st_size <
+           (sizeof(*container_header) + sizeof(*section_header))) {
+               WARNX(2, "file too short: %s", path);
+               goto done;
+       }
+       fw_size = st.st_size;
+
+       /*
+        * mmap the whole image.
+        */
+       fw_image = (uint8_t *)mmap(NULL, st.st_size, PROT_READ,
+           MAP_PRIVATE, fd, 0);
+       if (fw_image == MAP_FAILED) {
+               WARN(0, "mmap(%s)", path);
+               goto done;
+       }
+
+       fw_data = fw_image;
+       container_header = (const container_header_t *)fw_data;
+       if (container_header->magic != AMD_10H_MAGIC) {
+               WARNX(2, "%s is not a valid amd firmware: bad magic", path);
+               goto done;
+       }
+       fw_data += sizeof(*container_header);
+       fw_size -= sizeof(*container_header);
+
+       section_header = (const section_header_t *)fw_data;
+       if (section_header->type != AMD_10H_EQUIV_TABLE_TYPE) {
+               WARNX(2, "%s is not a valid amd firmware: "
+                   "first section is not CPU equivalence table", path);
+               goto done;
+       }
+       if (section_header->size == 0) {
+               WARNX(2, "%s is not a valid amd firmware: "
+                   "first section is empty", path);
+               goto done;
+       }
+       fw_data += sizeof(*section_header);
+       fw_size -= sizeof(*section_header);
+
+       if (section_header->size > fw_size) {
+               WARNX(2, "%s is not a valid amd firmware: "
+                   "file is truncated", path);
+               goto done;
+       }
+       if (section_header->size < sizeof(*equiv_cpu_table)) {
+               WARNX(2, "%s is not a valid amd firmware: "
+                   "first section is too short", path);
+               goto done;
+       }
+       equiv_cpu_table = (const equiv_cpu_entry_t *)fw_data;
+       fw_data += section_header->size;
+       fw_size -= section_header->size;
+
+       equiv_id = 0;
+       for (i = 0; equiv_cpu_table[i].installed_cpu != 0; i++) {
+               if (signature == equiv_cpu_table[i].installed_cpu) {
+                       equiv_id = equiv_cpu_table[i].equiv_cpu;
+                       WARNX(3, "equiv_id: %x", equiv_id);
+                       break;
+               }
+       }
+       if (equiv_id == 0) {
+               WARNX(2, "CPU is not found in the equivalence table");
+               goto done;
+       }
+
+       selected_fw = NULL;
+       selected_size = 0;
+       while (fw_size >= sizeof(*section_header)) {
+               section_header = (const section_header_t *)fw_data;
+               fw_data += sizeof(*section_header);
+               fw_size -= sizeof(*section_header);
+               if (section_header->type != AMD_10H_uCODE_TYPE) {
+                       WARNX(2, "%s is not a valid amd firmware: "
+                           "section has incorret type", path);
+                       goto done;
+               }
+               if (section_header->size > fw_size) {
+                       WARNX(2, "%s is not a valid amd firmware: "
+                           "file is truncated", path);
+                       goto done;
+               }
+               if (section_header->size < sizeof(*fw_header)) {
+                       WARNX(2, "%s is not a valid amd firmware: "
+                           "section is too short", path);
+                       goto done;
+               }
+               fw_header = (const amd_10h_fw_header_t *)fw_data;
+               fw_data += section_header->size;
+               fw_size -= section_header->size;
+
+               if (fw_header->processor_rev_id != equiv_id)
+                       continue; /* different cpu */
+               if (fw_header->patch_id <= revision)
+                       continue; /* not newer revision */
+               if (fw_header->nb_dev_id != 0 || fw_header->sb_dev_id != 0) {
+                       WARNX(2, "Chipset-specific microcode is not supported");
+               }
+
+               WARNX(3, "selecting revision: %x", fw_header->patch_id);
+               revision = fw_header->patch_id;
+               selected_fw = fw_header;
+               selected_size = section_header->size;
+       }
+
+       if (fw_size != 0) {
+               WARNX(2, "%s is not a valid amd firmware: "
+                   "file is truncated", path);
+               goto done;
+       }
+
+       if (selected_fw != NULL) {
+               WARNX(1, "selected ucode size is %zu", selected_size);
+               fprintf(stderr, "%s: updating cpu %s to revision %#x... ",
+                   path, dev, revision);
+
+               args.data = __DECONST(void *, selected_fw);
+               args.size = selected_size;
+               error = ioctl(devfd, CPUCTL_UPDATE, &args);
+               if (error < 0) {
+                       fprintf(stderr, "failed.\n");
+                       warn("ioctl()");
+                       goto done;
+               }
+               fprintf(stderr, "done.\n");
+       }
+
+       msrargs.msr = 0x0000008b;
+       error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
+       if (error < 0) {
+               WARN(0, "ioctl(%s)", dev);
+               goto done;
+       }
+       new_rev = (uint32_t)msrargs.data;
+       if (new_rev != revision)
+               WARNX(0, "revision after update %#x", new_rev);
+
+done:
+       if (fd >= 0)
+               close(fd);
+       if (devfd >= 0)
+               close(devfd);
+       if (fw_image != MAP_FAILED)
+               if (munmap(fw_image, st.st_size) != 0)
+                       warn("munmap(%s)", path);
+       return;
+}

Modified: stable/10/usr.sbin/cpucontrol/cpucontrol.c
==============================================================================
--- stable/10/usr.sbin/cpucontrol/cpucontrol.c  Thu Nov 17 15:16:52 2016        
(r308760)
+++ stable/10/usr.sbin/cpucontrol/cpucontrol.c  Thu Nov 17 15:17:01 2016        
(r308761)
@@ -91,6 +91,7 @@ static struct ucode_handler {
        ucode_update_t *update;
 } handlers[] = {
        { intel_probe, intel_update },
+       { amd10h_probe, amd10h_update },
        { amd_probe, amd_update },
        { via_probe, via_update },
 };
_______________________________________________
svn-src-stable-10@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-stable-10
To unsubscribe, send any mail to "svn-src-stable-10-unsubscr...@freebsd.org"

Reply via email to