svn commit: r308760 - in stable/11: sys/dev/cpuctl usr.sbin/cpucontrol

Andriy Gapon avg at FreeBSD.org
Thu Nov 17 15:16:54 UTC 2016


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

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

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

Modified: stable/11/sys/dev/cpuctl/cpuctl.c
==============================================================================
--- stable/11/sys/dev/cpuctl/cpuctl.c	Thu Nov 17 15:14:59 2016	(r308759)
+++ stable/11/sys/dev/cpuctl/cpuctl.c	Thu Nov 17 15:16:52 2016	(r308760)
@@ -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/11/usr.sbin/cpucontrol/Makefile
==============================================================================
--- stable/11/usr.sbin/cpucontrol/Makefile	Thu Nov 17 15:14:59 2016	(r308759)
+++ stable/11/usr.sbin/cpucontrol/Makefile	Thu Nov 17 15:16:52 2016	(r308760)
@@ -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/11/usr.sbin/cpucontrol/amd.h
==============================================================================
--- stable/11/usr.sbin/cpucontrol/amd.h	Thu Nov 17 15:14:59 2016	(r308759)
+++ stable/11/usr.sbin/cpucontrol/amd.h	Thu Nov 17 15:16:52 2016	(r308760)
@@ -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/11/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/11/usr.sbin/cpucontrol/amd10h.c	Thu Nov 17 15:16:52 2016	(r308760, copy of r308218, head/usr.sbin/cpucontrol/amd10h.c)
@@ -0,0 +1,307 @@
+/*-
+ * Copyright (c) 2012 Andriy Gapon <avg at 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/11/usr.sbin/cpucontrol/cpucontrol.c
==============================================================================
--- stable/11/usr.sbin/cpucontrol/cpucontrol.c	Thu Nov 17 15:14:59 2016	(r308759)
+++ stable/11/usr.sbin/cpucontrol/cpucontrol.c	Thu Nov 17 15:16:52 2016	(r308760)
@@ -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 },
 };


More information about the svn-src-all mailing list