Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F149475050
D8384.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
12 KB
Referenced Files
None
Subscribers
None
D8384.diff
View Options
Index: head/sys/dev/cpuctl/cpuctl.c
===================================================================
--- head/sys/dev/cpuctl/cpuctl.c
+++ head/sys/dev/cpuctl/cpuctl.c
@@ -377,13 +377,24 @@
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 @@
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);
}
Index: head/usr.sbin/cpucontrol/Makefile
===================================================================
--- head/usr.sbin/cpucontrol/Makefile
+++ head/usr.sbin/cpucontrol/Makefile
@@ -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=
Index: head/usr.sbin/cpucontrol/amd.h
===================================================================
--- head/usr.sbin/cpucontrol/amd.h
+++ head/usr.sbin/cpucontrol/amd.h
@@ -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 @@
#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 */
Index: head/usr.sbin/cpucontrol/amd10h.c
===================================================================
--- head/usr.sbin/cpucontrol/amd10h.c
+++ head/usr.sbin/cpucontrol/amd10h.c
@@ -0,0 +1,307 @@
+/*-
+ * Copyright (c) 2012 Andriy Gapon <avg@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;
+}
Index: head/usr.sbin/cpucontrol/cpucontrol.c
===================================================================
--- head/usr.sbin/cpucontrol/cpucontrol.c
+++ head/usr.sbin/cpucontrol/cpucontrol.c
@@ -91,6 +91,7 @@
ucode_update_t *update;
} handlers[] = {
{ intel_probe, intel_update },
+ { amd10h_probe, amd10h_update },
{ amd_probe, amd_update },
{ via_probe, via_update },
};
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Mar 25, 5:11 PM (49 m, 51 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30335792
Default Alt Text
D8384.diff (12 KB)
Attached To
Mode
D8384: Add support for microcode update on newer AMD CPUs (10h+)
Attached
Detach File
Event Timeline
Log In to Comment