xref: /freebsd/usr.sbin/cpucontrol/amd10h.c (revision a18e40aad4122d027529aee9a273eeedb87cda1c)
10112b52bSAndriy Gapon /*-
20112b52bSAndriy Gapon  * Copyright (c) 2012 Andriy Gapon <avg@FreeBSD.org>.
30112b52bSAndriy Gapon  * All rights reserved.
40112b52bSAndriy Gapon  *
50112b52bSAndriy Gapon  * Redistribution and use in source and binary forms, with or without
60112b52bSAndriy Gapon  * modification, are permitted provided that the following conditions
70112b52bSAndriy Gapon  * are met:
80112b52bSAndriy Gapon  * 1. Redistributions of source code must retain the above copyright
90112b52bSAndriy Gapon  *    notice, this list of conditions and the following disclaimer.
100112b52bSAndriy Gapon  * 2. Redistributions in binary form must reproduce the above copyright
110112b52bSAndriy Gapon  *    notice, this list of conditions and the following disclaimer in the
120112b52bSAndriy Gapon  *    documentation and/or other materials provided with the distribution.
130112b52bSAndriy Gapon  *
140112b52bSAndriy Gapon  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
150112b52bSAndriy Gapon  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
160112b52bSAndriy Gapon  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
170112b52bSAndriy Gapon  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
180112b52bSAndriy Gapon  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
190112b52bSAndriy Gapon  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
200112b52bSAndriy Gapon  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
210112b52bSAndriy Gapon  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
220112b52bSAndriy Gapon  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
230112b52bSAndriy Gapon  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
240112b52bSAndriy Gapon  */
250112b52bSAndriy Gapon 
260112b52bSAndriy Gapon #include <sys/cdefs.h>
270112b52bSAndriy Gapon __FBSDID("$FreeBSD$");
280112b52bSAndriy Gapon 
290112b52bSAndriy Gapon #include <sys/types.h>
300112b52bSAndriy Gapon #include <sys/stat.h>
310112b52bSAndriy Gapon #include <sys/mman.h>
320112b52bSAndriy Gapon #include <sys/ioctl.h>
330112b52bSAndriy Gapon #include <sys/ioccom.h>
340112b52bSAndriy Gapon #include <sys/cpuctl.h>
350112b52bSAndriy Gapon 
360112b52bSAndriy Gapon #include <machine/cpufunc.h>
370112b52bSAndriy Gapon #include <machine/specialreg.h>
380112b52bSAndriy Gapon 
390112b52bSAndriy Gapon #include <assert.h>
400112b52bSAndriy Gapon #include <stdio.h>
410112b52bSAndriy Gapon #include <stdlib.h>
420112b52bSAndriy Gapon #include <string.h>
430112b52bSAndriy Gapon #include <unistd.h>
440112b52bSAndriy Gapon #include <fcntl.h>
450112b52bSAndriy Gapon #include <err.h>
460112b52bSAndriy Gapon 
470112b52bSAndriy Gapon #include "cpucontrol.h"
480112b52bSAndriy Gapon #include "amd.h"
490112b52bSAndriy Gapon 
500112b52bSAndriy Gapon int
510112b52bSAndriy Gapon amd10h_probe(int fd)
520112b52bSAndriy Gapon {
530112b52bSAndriy Gapon 	char vendor[13];
540112b52bSAndriy Gapon 	cpuctl_cpuid_args_t idargs;
550112b52bSAndriy Gapon 	uint32_t family;
560112b52bSAndriy Gapon 	uint32_t signature;
570112b52bSAndriy Gapon 	int error;
580112b52bSAndriy Gapon 
590112b52bSAndriy Gapon 	idargs.level = 0;
600112b52bSAndriy Gapon 	error = ioctl(fd, CPUCTL_CPUID, &idargs);
610112b52bSAndriy Gapon 	if (error < 0) {
620112b52bSAndriy Gapon 		WARN(0, "ioctl()");
630112b52bSAndriy Gapon 		return (1);
640112b52bSAndriy Gapon 	}
650112b52bSAndriy Gapon 	((uint32_t *)vendor)[0] = idargs.data[1];
660112b52bSAndriy Gapon 	((uint32_t *)vendor)[1] = idargs.data[3];
670112b52bSAndriy Gapon 	((uint32_t *)vendor)[2] = idargs.data[2];
680112b52bSAndriy Gapon 	vendor[12] = '\0';
690112b52bSAndriy Gapon 	if (strncmp(vendor, AMD_VENDOR_ID, sizeof(AMD_VENDOR_ID)) != 0)
700112b52bSAndriy Gapon 		return (1);
710112b52bSAndriy Gapon 
720112b52bSAndriy Gapon 	idargs.level = 1;
730112b52bSAndriy Gapon 	error = ioctl(fd, CPUCTL_CPUID, &idargs);
740112b52bSAndriy Gapon 	if (error < 0) {
750112b52bSAndriy Gapon 		WARN(0, "ioctl()");
760112b52bSAndriy Gapon 		return (1);
770112b52bSAndriy Gapon 	}
780112b52bSAndriy Gapon 	signature = idargs.data[0];
790112b52bSAndriy Gapon 	family = ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff);
800112b52bSAndriy Gapon 	if (family < 0x10)
810112b52bSAndriy Gapon 		return (1);
820112b52bSAndriy Gapon 	return (0);
830112b52bSAndriy Gapon }
840112b52bSAndriy Gapon 
850112b52bSAndriy Gapon /*
860112b52bSAndriy Gapon  * NB: the format of microcode update files is not documented by AMD.
870112b52bSAndriy Gapon  * It has been reverse engineered from studying Coreboot, illumos and Linux
880112b52bSAndriy Gapon  * source code.
890112b52bSAndriy Gapon  */
900112b52bSAndriy Gapon void
910112b52bSAndriy Gapon amd10h_update(const char *dev, const char *path)
920112b52bSAndriy Gapon {
930112b52bSAndriy Gapon 	struct stat st;
940112b52bSAndriy Gapon 	cpuctl_cpuid_args_t idargs;
950112b52bSAndriy Gapon 	cpuctl_msr_args_t msrargs;
960112b52bSAndriy Gapon 	cpuctl_update_args_t args;
970112b52bSAndriy Gapon 	const amd_10h_fw_header_t *fw_header;
980112b52bSAndriy Gapon 	const amd_10h_fw_header_t *selected_fw;
990112b52bSAndriy Gapon 	const equiv_cpu_entry_t *equiv_cpu_table;
1000112b52bSAndriy Gapon 	const section_header_t *section_header;
1010112b52bSAndriy Gapon 	const container_header_t *container_header;
1020112b52bSAndriy Gapon 	const uint8_t *fw_data;
1030112b52bSAndriy Gapon 	uint8_t *fw_image;
1040112b52bSAndriy Gapon 	size_t fw_size;
1050112b52bSAndriy Gapon 	size_t selected_size;
1060112b52bSAndriy Gapon 	uint32_t revision;
1070112b52bSAndriy Gapon 	uint32_t new_rev;
1080112b52bSAndriy Gapon 	uint32_t signature;
1090112b52bSAndriy Gapon 	uint16_t equiv_id;
1100112b52bSAndriy Gapon 	int fd, devfd;
1110112b52bSAndriy Gapon 	unsigned int i;
1120112b52bSAndriy Gapon 	int error;
1130112b52bSAndriy Gapon 
1140112b52bSAndriy Gapon 	assert(path);
1150112b52bSAndriy Gapon 	assert(dev);
1160112b52bSAndriy Gapon 
1170112b52bSAndriy Gapon 	fd = -1;
1180112b52bSAndriy Gapon 	fw_image = MAP_FAILED;
1190112b52bSAndriy Gapon 	devfd = open(dev, O_RDWR);
1200112b52bSAndriy Gapon 	if (devfd < 0) {
1210112b52bSAndriy Gapon 		WARN(0, "could not open %s for writing", dev);
1220112b52bSAndriy Gapon 		return;
1230112b52bSAndriy Gapon 	}
1240112b52bSAndriy Gapon 	idargs.level = 1;
1250112b52bSAndriy Gapon 	error = ioctl(devfd, CPUCTL_CPUID, &idargs);
1260112b52bSAndriy Gapon 	if (error < 0) {
1270112b52bSAndriy Gapon 		WARN(0, "ioctl()");
1280112b52bSAndriy Gapon 		goto done;
1290112b52bSAndriy Gapon 	}
1300112b52bSAndriy Gapon 	signature = idargs.data[0];
1310112b52bSAndriy Gapon 
132*a18e40aaSMark Johnston 	msrargs.msr = MSR_BIOS_SIGN;
1330112b52bSAndriy Gapon 	error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
1340112b52bSAndriy Gapon 	if (error < 0) {
1350112b52bSAndriy Gapon 		WARN(0, "ioctl(%s)", dev);
1360112b52bSAndriy Gapon 		goto done;
1370112b52bSAndriy Gapon 	}
1380112b52bSAndriy Gapon 	revision = (uint32_t)msrargs.data;
1390112b52bSAndriy Gapon 
1400112b52bSAndriy Gapon 	WARNX(1, "found cpu family %#x model %#x "
1410112b52bSAndriy Gapon 	    "stepping %#x extfamily %#x extmodel %#x.",
1426e9d170dSSean Bruno 	    ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff),
1436e9d170dSSean Bruno 	    (signature >> 4) & 0x0f,
1440112b52bSAndriy Gapon 	    (signature >> 0) & 0x0f, (signature >> 20) & 0xff,
1450112b52bSAndriy Gapon 	    (signature >> 16) & 0x0f);
1460112b52bSAndriy Gapon 	WARNX(1, "microcode revision %#x", revision);
1470112b52bSAndriy Gapon 
1480112b52bSAndriy Gapon 	/*
1490112b52bSAndriy Gapon 	 * Open the firmware file.
1500112b52bSAndriy Gapon 	 */
1516e9d170dSSean Bruno 	WARNX(1, "checking %s for update.", path);
1520112b52bSAndriy Gapon 	fd = open(path, O_RDONLY, 0);
1530112b52bSAndriy Gapon 	if (fd < 0) {
1540112b52bSAndriy Gapon 		WARN(0, "open(%s)", path);
1550112b52bSAndriy Gapon 		goto done;
1560112b52bSAndriy Gapon 	}
1570112b52bSAndriy Gapon 	error = fstat(fd, &st);
1580112b52bSAndriy Gapon 	if (error != 0) {
1590112b52bSAndriy Gapon 		WARN(0, "fstat(%s)", path);
1600112b52bSAndriy Gapon 		goto done;
1610112b52bSAndriy Gapon 	}
1620112b52bSAndriy Gapon 	if (st.st_size < 0 || (size_t)st.st_size <
1630112b52bSAndriy Gapon 	    (sizeof(*container_header) + sizeof(*section_header))) {
1640112b52bSAndriy Gapon 		WARNX(2, "file too short: %s", path);
1650112b52bSAndriy Gapon 		goto done;
1660112b52bSAndriy Gapon 	}
1670112b52bSAndriy Gapon 	fw_size = st.st_size;
1680112b52bSAndriy Gapon 
1690112b52bSAndriy Gapon 	/*
1700112b52bSAndriy Gapon 	 * mmap the whole image.
1710112b52bSAndriy Gapon 	 */
1720112b52bSAndriy Gapon 	fw_image = (uint8_t *)mmap(NULL, st.st_size, PROT_READ,
1730112b52bSAndriy Gapon 	    MAP_PRIVATE, fd, 0);
1740112b52bSAndriy Gapon 	if (fw_image == MAP_FAILED) {
1750112b52bSAndriy Gapon 		WARN(0, "mmap(%s)", path);
1760112b52bSAndriy Gapon 		goto done;
1770112b52bSAndriy Gapon 	}
1780112b52bSAndriy Gapon 
1790112b52bSAndriy Gapon 	fw_data = fw_image;
1800112b52bSAndriy Gapon 	container_header = (const container_header_t *)fw_data;
1810112b52bSAndriy Gapon 	if (container_header->magic != AMD_10H_MAGIC) {
1820112b52bSAndriy Gapon 		WARNX(2, "%s is not a valid amd firmware: bad magic", path);
1830112b52bSAndriy Gapon 		goto done;
1840112b52bSAndriy Gapon 	}
1850112b52bSAndriy Gapon 	fw_data += sizeof(*container_header);
1860112b52bSAndriy Gapon 	fw_size -= sizeof(*container_header);
1870112b52bSAndriy Gapon 
1880112b52bSAndriy Gapon 	section_header = (const section_header_t *)fw_data;
1890112b52bSAndriy Gapon 	if (section_header->type != AMD_10H_EQUIV_TABLE_TYPE) {
1900112b52bSAndriy Gapon 		WARNX(2, "%s is not a valid amd firmware: "
1910112b52bSAndriy Gapon 		    "first section is not CPU equivalence table", path);
1920112b52bSAndriy Gapon 		goto done;
1930112b52bSAndriy Gapon 	}
1940112b52bSAndriy Gapon 	if (section_header->size == 0) {
1950112b52bSAndriy Gapon 		WARNX(2, "%s is not a valid amd firmware: "
1960112b52bSAndriy Gapon 		    "first section is empty", path);
1970112b52bSAndriy Gapon 		goto done;
1980112b52bSAndriy Gapon 	}
1990112b52bSAndriy Gapon 	fw_data += sizeof(*section_header);
2000112b52bSAndriy Gapon 	fw_size -= sizeof(*section_header);
2010112b52bSAndriy Gapon 
2020112b52bSAndriy Gapon 	if (section_header->size > fw_size) {
2030112b52bSAndriy Gapon 		WARNX(2, "%s is not a valid amd firmware: "
2040112b52bSAndriy Gapon 		    "file is truncated", path);
2050112b52bSAndriy Gapon 		goto done;
2060112b52bSAndriy Gapon 	}
2070112b52bSAndriy Gapon 	if (section_header->size < sizeof(*equiv_cpu_table)) {
2080112b52bSAndriy Gapon 		WARNX(2, "%s is not a valid amd firmware: "
2090112b52bSAndriy Gapon 		    "first section is too short", path);
2100112b52bSAndriy Gapon 		goto done;
2110112b52bSAndriy Gapon 	}
2120112b52bSAndriy Gapon 	equiv_cpu_table = (const equiv_cpu_entry_t *)fw_data;
2130112b52bSAndriy Gapon 	fw_data += section_header->size;
2140112b52bSAndriy Gapon 	fw_size -= section_header->size;
2150112b52bSAndriy Gapon 
2160112b52bSAndriy Gapon 	equiv_id = 0;
2170112b52bSAndriy Gapon 	for (i = 0; equiv_cpu_table[i].installed_cpu != 0; i++) {
2180112b52bSAndriy Gapon 		if (signature == equiv_cpu_table[i].installed_cpu) {
2190112b52bSAndriy Gapon 			equiv_id = equiv_cpu_table[i].equiv_cpu;
2206e9d170dSSean Bruno 			WARNX(3, "equiv_id: %x, signature %8x,"
2216e9d170dSSean Bruno 			    " equiv_cpu_table[%d] %8x", equiv_id, signature,
2226e9d170dSSean Bruno 			    i, equiv_cpu_table[i].installed_cpu);
2230112b52bSAndriy Gapon 			break;
2240112b52bSAndriy Gapon 		}
2250112b52bSAndriy Gapon 	}
2260112b52bSAndriy Gapon 	if (equiv_id == 0) {
2270112b52bSAndriy Gapon 		WARNX(2, "CPU is not found in the equivalence table");
2280112b52bSAndriy Gapon 		goto done;
2290112b52bSAndriy Gapon 	}
2300112b52bSAndriy Gapon 
2310112b52bSAndriy Gapon 	selected_fw = NULL;
2320112b52bSAndriy Gapon 	selected_size = 0;
2330112b52bSAndriy Gapon 	while (fw_size >= sizeof(*section_header)) {
2340112b52bSAndriy Gapon 		section_header = (const section_header_t *)fw_data;
2350112b52bSAndriy Gapon 		fw_data += sizeof(*section_header);
2360112b52bSAndriy Gapon 		fw_size -= sizeof(*section_header);
2370112b52bSAndriy Gapon 		if (section_header->type != AMD_10H_uCODE_TYPE) {
2380112b52bSAndriy Gapon 			WARNX(2, "%s is not a valid amd firmware: "
2390112b52bSAndriy Gapon 			    "section has incorret type", path);
2400112b52bSAndriy Gapon 			goto done;
2410112b52bSAndriy Gapon 		}
2420112b52bSAndriy Gapon 		if (section_header->size > fw_size) {
2430112b52bSAndriy Gapon 			WARNX(2, "%s is not a valid amd firmware: "
2440112b52bSAndriy Gapon 			    "file is truncated", path);
2450112b52bSAndriy Gapon 			goto done;
2460112b52bSAndriy Gapon 		}
2470112b52bSAndriy Gapon 		if (section_header->size < sizeof(*fw_header)) {
2480112b52bSAndriy Gapon 			WARNX(2, "%s is not a valid amd firmware: "
2490112b52bSAndriy Gapon 			    "section is too short", path);
2500112b52bSAndriy Gapon 			goto done;
2510112b52bSAndriy Gapon 		}
2520112b52bSAndriy Gapon 		fw_header = (const amd_10h_fw_header_t *)fw_data;
2530112b52bSAndriy Gapon 		fw_data += section_header->size;
2540112b52bSAndriy Gapon 		fw_size -= section_header->size;
2550112b52bSAndriy Gapon 
2566e9d170dSSean Bruno 		if (fw_header->processor_rev_id != equiv_id) {
2576e9d170dSSean Bruno 			WARNX(1, "firmware processort_rev_id %x, equiv_id %x",
2586e9d170dSSean Bruno 			    fw_header->processor_rev_id, equiv_id);
2590112b52bSAndriy Gapon 			continue; /* different cpu */
2606e9d170dSSean Bruno 		}
2616e9d170dSSean Bruno 		if (fw_header->patch_id <= revision) {
2626e9d170dSSean Bruno 			WARNX(1, "patch_id %x, revision %x",
2636e9d170dSSean Bruno 			    fw_header->patch_id, revision);
2640112b52bSAndriy Gapon 			continue; /* not newer revision */
2656e9d170dSSean Bruno 		}
2660112b52bSAndriy Gapon 		if (fw_header->nb_dev_id != 0 || fw_header->sb_dev_id != 0) {
2670112b52bSAndriy Gapon 			WARNX(2, "Chipset-specific microcode is not supported");
2680112b52bSAndriy Gapon 		}
2690112b52bSAndriy Gapon 
2700112b52bSAndriy Gapon 		WARNX(3, "selecting revision: %x", fw_header->patch_id);
2710112b52bSAndriy Gapon 		revision = fw_header->patch_id;
2720112b52bSAndriy Gapon 		selected_fw = fw_header;
2730112b52bSAndriy Gapon 		selected_size = section_header->size;
2740112b52bSAndriy Gapon 	}
2750112b52bSAndriy Gapon 
2760112b52bSAndriy Gapon 	if (fw_size != 0) {
2770112b52bSAndriy Gapon 		WARNX(2, "%s is not a valid amd firmware: "
2780112b52bSAndriy Gapon 		    "file is truncated", path);
2790112b52bSAndriy Gapon 		goto done;
2800112b52bSAndriy Gapon 	}
2810112b52bSAndriy Gapon 
2820112b52bSAndriy Gapon 	if (selected_fw != NULL) {
2830112b52bSAndriy Gapon 		WARNX(1, "selected ucode size is %zu", selected_size);
2840112b52bSAndriy Gapon 		fprintf(stderr, "%s: updating cpu %s to revision %#x... ",
2850112b52bSAndriy Gapon 		    path, dev, revision);
2860112b52bSAndriy Gapon 
2870112b52bSAndriy Gapon 		args.data = __DECONST(void *, selected_fw);
2880112b52bSAndriy Gapon 		args.size = selected_size;
2890112b52bSAndriy Gapon 		error = ioctl(devfd, CPUCTL_UPDATE, &args);
2900112b52bSAndriy Gapon 		if (error < 0) {
2910112b52bSAndriy Gapon 			fprintf(stderr, "failed.\n");
2920112b52bSAndriy Gapon 			warn("ioctl()");
2930112b52bSAndriy Gapon 			goto done;
2940112b52bSAndriy Gapon 		}
2950112b52bSAndriy Gapon 		fprintf(stderr, "done.\n");
2960112b52bSAndriy Gapon 	}
2970112b52bSAndriy Gapon 
298*a18e40aaSMark Johnston 	msrargs.msr = MSR_BIOS_SIGN;
2990112b52bSAndriy Gapon 	error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
3000112b52bSAndriy Gapon 	if (error < 0) {
3010112b52bSAndriy Gapon 		WARN(0, "ioctl(%s)", dev);
3020112b52bSAndriy Gapon 		goto done;
3030112b52bSAndriy Gapon 	}
3040112b52bSAndriy Gapon 	new_rev = (uint32_t)msrargs.data;
3050112b52bSAndriy Gapon 	if (new_rev != revision)
3060112b52bSAndriy Gapon 		WARNX(0, "revision after update %#x", new_rev);
3070112b52bSAndriy Gapon 
3080112b52bSAndriy Gapon done:
3090112b52bSAndriy Gapon 	if (fd >= 0)
3100112b52bSAndriy Gapon 		close(fd);
3110112b52bSAndriy Gapon 	if (devfd >= 0)
3120112b52bSAndriy Gapon 		close(devfd);
3130112b52bSAndriy Gapon 	if (fw_image != MAP_FAILED)
3140112b52bSAndriy Gapon 		if (munmap(fw_image, st.st_size) != 0)
3150112b52bSAndriy Gapon 			warn("munmap(%s)", path);
3160112b52bSAndriy Gapon 	return;
3170112b52bSAndriy Gapon }
318