xref: /freebsd/usr.sbin/cpucontrol/amd10h.c (revision dee401e833d262fdce8df2ef0976ca057acaeb9a)
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
91*dee401e8SConrad Meyer amd10h_update(const struct ucode_update_params *params)
920112b52bSAndriy Gapon {
930112b52bSAndriy Gapon 	cpuctl_cpuid_args_t idargs;
940112b52bSAndriy Gapon 	cpuctl_msr_args_t msrargs;
950112b52bSAndriy Gapon 	cpuctl_update_args_t args;
960112b52bSAndriy Gapon 	const amd_10h_fw_header_t *fw_header;
970112b52bSAndriy Gapon 	const amd_10h_fw_header_t *selected_fw;
980112b52bSAndriy Gapon 	const equiv_cpu_entry_t *equiv_cpu_table;
990112b52bSAndriy Gapon 	const section_header_t *section_header;
1000112b52bSAndriy Gapon 	const container_header_t *container_header;
1010112b52bSAndriy Gapon 	const uint8_t *fw_data;
102*dee401e8SConrad Meyer 	const uint8_t *fw_image;
103*dee401e8SConrad Meyer 	const char *dev, *path;
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;
110*dee401e8SConrad Meyer 	int devfd;
1110112b52bSAndriy Gapon 	unsigned int i;
1120112b52bSAndriy Gapon 	int error;
1130112b52bSAndriy Gapon 
114*dee401e8SConrad Meyer 	dev = params->dev_path;
115*dee401e8SConrad Meyer 	path = params->fw_path;
116*dee401e8SConrad Meyer 	devfd = params->devfd;
117*dee401e8SConrad Meyer 	fw_image = params->fwimage;
118*dee401e8SConrad Meyer 	fw_size = params->fwsize;
119*dee401e8SConrad Meyer 
1200112b52bSAndriy Gapon 	assert(path);
1210112b52bSAndriy Gapon 	assert(dev);
1220112b52bSAndriy Gapon 
1230112b52bSAndriy Gapon 	idargs.level = 1;
1240112b52bSAndriy Gapon 	error = ioctl(devfd, CPUCTL_CPUID, &idargs);
1250112b52bSAndriy Gapon 	if (error < 0) {
1260112b52bSAndriy Gapon 		WARN(0, "ioctl()");
1270112b52bSAndriy Gapon 		goto done;
1280112b52bSAndriy Gapon 	}
1290112b52bSAndriy Gapon 	signature = idargs.data[0];
1300112b52bSAndriy Gapon 
131a18e40aaSMark Johnston 	msrargs.msr = MSR_BIOS_SIGN;
1320112b52bSAndriy Gapon 	error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
1330112b52bSAndriy Gapon 	if (error < 0) {
1340112b52bSAndriy Gapon 		WARN(0, "ioctl(%s)", dev);
1350112b52bSAndriy Gapon 		goto done;
1360112b52bSAndriy Gapon 	}
1370112b52bSAndriy Gapon 	revision = (uint32_t)msrargs.data;
1380112b52bSAndriy Gapon 
1390112b52bSAndriy Gapon 	WARNX(1, "found cpu family %#x model %#x "
1400112b52bSAndriy Gapon 	    "stepping %#x extfamily %#x extmodel %#x.",
1416e9d170dSSean Bruno 	    ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff),
1426e9d170dSSean Bruno 	    (signature >> 4) & 0x0f,
1430112b52bSAndriy Gapon 	    (signature >> 0) & 0x0f, (signature >> 20) & 0xff,
1440112b52bSAndriy Gapon 	    (signature >> 16) & 0x0f);
1450112b52bSAndriy Gapon 	WARNX(1, "microcode revision %#x", revision);
1460112b52bSAndriy Gapon 
1470112b52bSAndriy Gapon 	/*
1480112b52bSAndriy Gapon 	 * Open the firmware file.
1490112b52bSAndriy Gapon 	 */
1506e9d170dSSean Bruno 	WARNX(1, "checking %s for update.", path);
151*dee401e8SConrad Meyer 	if (fw_size <
1520112b52bSAndriy Gapon 	    (sizeof(*container_header) + sizeof(*section_header))) {
1530112b52bSAndriy Gapon 		WARNX(2, "file too short: %s", path);
1540112b52bSAndriy Gapon 		goto done;
1550112b52bSAndriy Gapon 	}
1560112b52bSAndriy Gapon 
1570112b52bSAndriy Gapon 	/*
1580112b52bSAndriy Gapon 	 * mmap the whole image.
1590112b52bSAndriy Gapon 	 */
1600112b52bSAndriy Gapon 	fw_data = fw_image;
1610112b52bSAndriy Gapon 	container_header = (const container_header_t *)fw_data;
1620112b52bSAndriy Gapon 	if (container_header->magic != AMD_10H_MAGIC) {
1630112b52bSAndriy Gapon 		WARNX(2, "%s is not a valid amd firmware: bad magic", path);
1640112b52bSAndriy Gapon 		goto done;
1650112b52bSAndriy Gapon 	}
1660112b52bSAndriy Gapon 	fw_data += sizeof(*container_header);
1670112b52bSAndriy Gapon 	fw_size -= sizeof(*container_header);
1680112b52bSAndriy Gapon 
1690112b52bSAndriy Gapon 	section_header = (const section_header_t *)fw_data;
1700112b52bSAndriy Gapon 	if (section_header->type != AMD_10H_EQUIV_TABLE_TYPE) {
1710112b52bSAndriy Gapon 		WARNX(2, "%s is not a valid amd firmware: "
1720112b52bSAndriy Gapon 		    "first section is not CPU equivalence table", path);
1730112b52bSAndriy Gapon 		goto done;
1740112b52bSAndriy Gapon 	}
1750112b52bSAndriy Gapon 	if (section_header->size == 0) {
1760112b52bSAndriy Gapon 		WARNX(2, "%s is not a valid amd firmware: "
1770112b52bSAndriy Gapon 		    "first section is empty", path);
1780112b52bSAndriy Gapon 		goto done;
1790112b52bSAndriy Gapon 	}
1800112b52bSAndriy Gapon 	fw_data += sizeof(*section_header);
1810112b52bSAndriy Gapon 	fw_size -= sizeof(*section_header);
1820112b52bSAndriy Gapon 
1830112b52bSAndriy Gapon 	if (section_header->size > fw_size) {
1840112b52bSAndriy Gapon 		WARNX(2, "%s is not a valid amd firmware: "
1850112b52bSAndriy Gapon 		    "file is truncated", path);
1860112b52bSAndriy Gapon 		goto done;
1870112b52bSAndriy Gapon 	}
1880112b52bSAndriy Gapon 	if (section_header->size < sizeof(*equiv_cpu_table)) {
1890112b52bSAndriy Gapon 		WARNX(2, "%s is not a valid amd firmware: "
1900112b52bSAndriy Gapon 		    "first section is too short", path);
1910112b52bSAndriy Gapon 		goto done;
1920112b52bSAndriy Gapon 	}
1930112b52bSAndriy Gapon 	equiv_cpu_table = (const equiv_cpu_entry_t *)fw_data;
1940112b52bSAndriy Gapon 	fw_data += section_header->size;
1950112b52bSAndriy Gapon 	fw_size -= section_header->size;
1960112b52bSAndriy Gapon 
1970112b52bSAndriy Gapon 	equiv_id = 0;
1980112b52bSAndriy Gapon 	for (i = 0; equiv_cpu_table[i].installed_cpu != 0; i++) {
1990112b52bSAndriy Gapon 		if (signature == equiv_cpu_table[i].installed_cpu) {
2000112b52bSAndriy Gapon 			equiv_id = equiv_cpu_table[i].equiv_cpu;
2016e9d170dSSean Bruno 			WARNX(3, "equiv_id: %x, signature %8x,"
2026e9d170dSSean Bruno 			    " equiv_cpu_table[%d] %8x", equiv_id, signature,
2036e9d170dSSean Bruno 			    i, equiv_cpu_table[i].installed_cpu);
2040112b52bSAndriy Gapon 			break;
2050112b52bSAndriy Gapon 		}
2060112b52bSAndriy Gapon 	}
2070112b52bSAndriy Gapon 	if (equiv_id == 0) {
2080112b52bSAndriy Gapon 		WARNX(2, "CPU is not found in the equivalence table");
2090112b52bSAndriy Gapon 		goto done;
2100112b52bSAndriy Gapon 	}
2110112b52bSAndriy Gapon 
2120112b52bSAndriy Gapon 	selected_fw = NULL;
2130112b52bSAndriy Gapon 	selected_size = 0;
2140112b52bSAndriy Gapon 	while (fw_size >= sizeof(*section_header)) {
2150112b52bSAndriy Gapon 		section_header = (const section_header_t *)fw_data;
2160112b52bSAndriy Gapon 		fw_data += sizeof(*section_header);
2170112b52bSAndriy Gapon 		fw_size -= sizeof(*section_header);
2180112b52bSAndriy Gapon 		if (section_header->type != AMD_10H_uCODE_TYPE) {
2190112b52bSAndriy Gapon 			WARNX(2, "%s is not a valid amd firmware: "
2200112b52bSAndriy Gapon 			    "section has incorret type", path);
2210112b52bSAndriy Gapon 			goto done;
2220112b52bSAndriy Gapon 		}
2230112b52bSAndriy Gapon 		if (section_header->size > fw_size) {
2240112b52bSAndriy Gapon 			WARNX(2, "%s is not a valid amd firmware: "
2250112b52bSAndriy Gapon 			    "file is truncated", path);
2260112b52bSAndriy Gapon 			goto done;
2270112b52bSAndriy Gapon 		}
2280112b52bSAndriy Gapon 		if (section_header->size < sizeof(*fw_header)) {
2290112b52bSAndriy Gapon 			WARNX(2, "%s is not a valid amd firmware: "
2300112b52bSAndriy Gapon 			    "section is too short", path);
2310112b52bSAndriy Gapon 			goto done;
2320112b52bSAndriy Gapon 		}
2330112b52bSAndriy Gapon 		fw_header = (const amd_10h_fw_header_t *)fw_data;
2340112b52bSAndriy Gapon 		fw_data += section_header->size;
2350112b52bSAndriy Gapon 		fw_size -= section_header->size;
2360112b52bSAndriy Gapon 
2376e9d170dSSean Bruno 		if (fw_header->processor_rev_id != equiv_id) {
23826dfa867SEd Maste 			WARNX(1, "firmware processor_rev_id %x, equiv_id %x",
2396e9d170dSSean Bruno 			    fw_header->processor_rev_id, equiv_id);
2400112b52bSAndriy Gapon 			continue; /* different cpu */
2416e9d170dSSean Bruno 		}
2426e9d170dSSean Bruno 		if (fw_header->patch_id <= revision) {
2436e9d170dSSean Bruno 			WARNX(1, "patch_id %x, revision %x",
2446e9d170dSSean Bruno 			    fw_header->patch_id, revision);
2450112b52bSAndriy Gapon 			continue; /* not newer revision */
2466e9d170dSSean Bruno 		}
2470112b52bSAndriy Gapon 		if (fw_header->nb_dev_id != 0 || fw_header->sb_dev_id != 0) {
2480112b52bSAndriy Gapon 			WARNX(2, "Chipset-specific microcode is not supported");
2490112b52bSAndriy Gapon 		}
2500112b52bSAndriy Gapon 
2510112b52bSAndriy Gapon 		WARNX(3, "selecting revision: %x", fw_header->patch_id);
2520112b52bSAndriy Gapon 		revision = fw_header->patch_id;
2530112b52bSAndriy Gapon 		selected_fw = fw_header;
2540112b52bSAndriy Gapon 		selected_size = section_header->size;
2550112b52bSAndriy Gapon 	}
2560112b52bSAndriy Gapon 
2570112b52bSAndriy Gapon 	if (fw_size != 0) {
2580112b52bSAndriy Gapon 		WARNX(2, "%s is not a valid amd firmware: "
2590112b52bSAndriy Gapon 		    "file is truncated", path);
2600112b52bSAndriy Gapon 		goto done;
2610112b52bSAndriy Gapon 	}
2620112b52bSAndriy Gapon 
2630112b52bSAndriy Gapon 	if (selected_fw != NULL) {
2640112b52bSAndriy Gapon 		WARNX(1, "selected ucode size is %zu", selected_size);
2650112b52bSAndriy Gapon 		fprintf(stderr, "%s: updating cpu %s to revision %#x... ",
2660112b52bSAndriy Gapon 		    path, dev, revision);
2670112b52bSAndriy Gapon 
2680112b52bSAndriy Gapon 		args.data = __DECONST(void *, selected_fw);
2690112b52bSAndriy Gapon 		args.size = selected_size;
2700112b52bSAndriy Gapon 		error = ioctl(devfd, CPUCTL_UPDATE, &args);
2710112b52bSAndriy Gapon 		if (error < 0) {
2720112b52bSAndriy Gapon 			fprintf(stderr, "failed.\n");
2730112b52bSAndriy Gapon 			warn("ioctl()");
2740112b52bSAndriy Gapon 			goto done;
2750112b52bSAndriy Gapon 		}
2760112b52bSAndriy Gapon 		fprintf(stderr, "done.\n");
2770112b52bSAndriy Gapon 	}
2780112b52bSAndriy Gapon 
279a18e40aaSMark Johnston 	msrargs.msr = MSR_BIOS_SIGN;
2800112b52bSAndriy Gapon 	error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
2810112b52bSAndriy Gapon 	if (error < 0) {
2820112b52bSAndriy Gapon 		WARN(0, "ioctl(%s)", dev);
2830112b52bSAndriy Gapon 		goto done;
2840112b52bSAndriy Gapon 	}
2850112b52bSAndriy Gapon 	new_rev = (uint32_t)msrargs.data;
2860112b52bSAndriy Gapon 	if (new_rev != revision)
2870112b52bSAndriy Gapon 		WARNX(0, "revision after update %#x", new_rev);
2880112b52bSAndriy Gapon 
2890112b52bSAndriy Gapon done:
2900112b52bSAndriy Gapon 	return;
2910112b52bSAndriy Gapon }
292