1308218Savg/*-
2308218Savg * Copyright (c) 2012 Andriy Gapon <avg@FreeBSD.org>.
3308218Savg * All rights reserved.
4308218Savg *
5308218Savg * Redistribution and use in source and binary forms, with or without
6308218Savg * modification, are permitted provided that the following conditions
7308218Savg * are met:
8308218Savg * 1. Redistributions of source code must retain the above copyright
9308218Savg *    notice, this list of conditions and the following disclaimer.
10308218Savg * 2. Redistributions in binary form must reproduce the above copyright
11308218Savg *    notice, this list of conditions and the following disclaimer in the
12308218Savg *    documentation and/or other materials provided with the distribution.
13308218Savg *
14308218Savg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15308218Savg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16308218Savg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17308218Savg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18308218Savg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19308218Savg * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20308218Savg * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21308218Savg * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22308218Savg * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23308218Savg * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24308218Savg */
25308218Savg
26308218Savg#include <sys/cdefs.h>
27308218Savg__FBSDID("$FreeBSD: stable/11/usr.sbin/cpucontrol/amd10h.c 336338 2018-07-16 13:21:49Z markj $");
28308218Savg
29308218Savg#include <sys/types.h>
30308218Savg#include <sys/stat.h>
31308218Savg#include <sys/mman.h>
32308218Savg#include <sys/ioctl.h>
33308218Savg#include <sys/ioccom.h>
34308218Savg#include <sys/cpuctl.h>
35308218Savg
36308218Savg#include <machine/cpufunc.h>
37308218Savg#include <machine/specialreg.h>
38308218Savg
39308218Savg#include <assert.h>
40308218Savg#include <stdio.h>
41308218Savg#include <stdlib.h>
42308218Savg#include <string.h>
43308218Savg#include <unistd.h>
44308218Savg#include <fcntl.h>
45308218Savg#include <err.h>
46308218Savg
47308218Savg#include "cpucontrol.h"
48308218Savg#include "amd.h"
49308218Savg
50308218Savgint
51308218Savgamd10h_probe(int fd)
52308218Savg{
53308218Savg	char vendor[13];
54308218Savg	cpuctl_cpuid_args_t idargs;
55308218Savg	uint32_t family;
56308218Savg	uint32_t signature;
57308218Savg	int error;
58308218Savg
59308218Savg	idargs.level = 0;
60308218Savg	error = ioctl(fd, CPUCTL_CPUID, &idargs);
61308218Savg	if (error < 0) {
62308218Savg		WARN(0, "ioctl()");
63308218Savg		return (1);
64308218Savg	}
65308218Savg	((uint32_t *)vendor)[0] = idargs.data[1];
66308218Savg	((uint32_t *)vendor)[1] = idargs.data[3];
67308218Savg	((uint32_t *)vendor)[2] = idargs.data[2];
68308218Savg	vendor[12] = '\0';
69308218Savg	if (strncmp(vendor, AMD_VENDOR_ID, sizeof(AMD_VENDOR_ID)) != 0)
70308218Savg		return (1);
71308218Savg
72308218Savg	idargs.level = 1;
73308218Savg	error = ioctl(fd, CPUCTL_CPUID, &idargs);
74308218Savg	if (error < 0) {
75308218Savg		WARN(0, "ioctl()");
76308218Savg		return (1);
77308218Savg	}
78308218Savg	signature = idargs.data[0];
79308218Savg	family = ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff);
80308218Savg	if (family < 0x10)
81308218Savg		return (1);
82308218Savg	return (0);
83308218Savg}
84308218Savg
85308218Savg/*
86308218Savg * NB: the format of microcode update files is not documented by AMD.
87308218Savg * It has been reverse engineered from studying Coreboot, illumos and Linux
88308218Savg * source code.
89308218Savg */
90308218Savgvoid
91308218Savgamd10h_update(const char *dev, const char *path)
92308218Savg{
93308218Savg	struct stat st;
94308218Savg	cpuctl_cpuid_args_t idargs;
95308218Savg	cpuctl_msr_args_t msrargs;
96308218Savg	cpuctl_update_args_t args;
97308218Savg	const amd_10h_fw_header_t *fw_header;
98308218Savg	const amd_10h_fw_header_t *selected_fw;
99308218Savg	const equiv_cpu_entry_t *equiv_cpu_table;
100308218Savg	const section_header_t *section_header;
101308218Savg	const container_header_t *container_header;
102308218Savg	const uint8_t *fw_data;
103308218Savg	uint8_t *fw_image;
104308218Savg	size_t fw_size;
105308218Savg	size_t selected_size;
106308218Savg	uint32_t revision;
107308218Savg	uint32_t new_rev;
108308218Savg	uint32_t signature;
109308218Savg	uint16_t equiv_id;
110308218Savg	int fd, devfd;
111308218Savg	unsigned int i;
112308218Savg	int error;
113308218Savg
114308218Savg	assert(path);
115308218Savg	assert(dev);
116308218Savg
117308218Savg	fd = -1;
118308218Savg	fw_image = MAP_FAILED;
119308218Savg	devfd = open(dev, O_RDWR);
120308218Savg	if (devfd < 0) {
121308218Savg		WARN(0, "could not open %s for writing", dev);
122308218Savg		return;
123308218Savg	}
124308218Savg	idargs.level = 1;
125308218Savg	error = ioctl(devfd, CPUCTL_CPUID, &idargs);
126308218Savg	if (error < 0) {
127308218Savg		WARN(0, "ioctl()");
128308218Savg		goto done;
129308218Savg	}
130308218Savg	signature = idargs.data[0];
131308218Savg
132336338Smarkj	msrargs.msr = MSR_BIOS_SIGN;
133308218Savg	error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
134308218Savg	if (error < 0) {
135308218Savg		WARN(0, "ioctl(%s)", dev);
136308218Savg		goto done;
137308218Savg	}
138308218Savg	revision = (uint32_t)msrargs.data;
139308218Savg
140308218Savg	WARNX(1, "found cpu family %#x model %#x "
141308218Savg	    "stepping %#x extfamily %#x extmodel %#x.",
142308218Savg	    (signature >> 8) & 0x0f, (signature >> 4) & 0x0f,
143308218Savg	    (signature >> 0) & 0x0f, (signature >> 20) & 0xff,
144308218Savg	    (signature >> 16) & 0x0f);
145308218Savg	WARNX(1, "microcode revision %#x", revision);
146308218Savg
147308218Savg	/*
148308218Savg	 * Open the firmware file.
149308218Savg	 */
150308218Savg	fd = open(path, O_RDONLY, 0);
151308218Savg	if (fd < 0) {
152308218Savg		WARN(0, "open(%s)", path);
153308218Savg		goto done;
154308218Savg	}
155308218Savg	error = fstat(fd, &st);
156308218Savg	if (error != 0) {
157308218Savg		WARN(0, "fstat(%s)", path);
158308218Savg		goto done;
159308218Savg	}
160308218Savg	if (st.st_size < 0 || (size_t)st.st_size <
161308218Savg	    (sizeof(*container_header) + sizeof(*section_header))) {
162308218Savg		WARNX(2, "file too short: %s", path);
163308218Savg		goto done;
164308218Savg	}
165308218Savg	fw_size = st.st_size;
166308218Savg
167308218Savg	/*
168308218Savg	 * mmap the whole image.
169308218Savg	 */
170308218Savg	fw_image = (uint8_t *)mmap(NULL, st.st_size, PROT_READ,
171308218Savg	    MAP_PRIVATE, fd, 0);
172308218Savg	if (fw_image == MAP_FAILED) {
173308218Savg		WARN(0, "mmap(%s)", path);
174308218Savg		goto done;
175308218Savg	}
176308218Savg
177308218Savg	fw_data = fw_image;
178308218Savg	container_header = (const container_header_t *)fw_data;
179308218Savg	if (container_header->magic != AMD_10H_MAGIC) {
180308218Savg		WARNX(2, "%s is not a valid amd firmware: bad magic", path);
181308218Savg		goto done;
182308218Savg	}
183308218Savg	fw_data += sizeof(*container_header);
184308218Savg	fw_size -= sizeof(*container_header);
185308218Savg
186308218Savg	section_header = (const section_header_t *)fw_data;
187308218Savg	if (section_header->type != AMD_10H_EQUIV_TABLE_TYPE) {
188308218Savg		WARNX(2, "%s is not a valid amd firmware: "
189308218Savg		    "first section is not CPU equivalence table", path);
190308218Savg		goto done;
191308218Savg	}
192308218Savg	if (section_header->size == 0) {
193308218Savg		WARNX(2, "%s is not a valid amd firmware: "
194308218Savg		    "first section is empty", path);
195308218Savg		goto done;
196308218Savg	}
197308218Savg	fw_data += sizeof(*section_header);
198308218Savg	fw_size -= sizeof(*section_header);
199308218Savg
200308218Savg	if (section_header->size > fw_size) {
201308218Savg		WARNX(2, "%s is not a valid amd firmware: "
202308218Savg		    "file is truncated", path);
203308218Savg		goto done;
204308218Savg	}
205308218Savg	if (section_header->size < sizeof(*equiv_cpu_table)) {
206308218Savg		WARNX(2, "%s is not a valid amd firmware: "
207308218Savg		    "first section is too short", path);
208308218Savg		goto done;
209308218Savg	}
210308218Savg	equiv_cpu_table = (const equiv_cpu_entry_t *)fw_data;
211308218Savg	fw_data += section_header->size;
212308218Savg	fw_size -= section_header->size;
213308218Savg
214308218Savg	equiv_id = 0;
215308218Savg	for (i = 0; equiv_cpu_table[i].installed_cpu != 0; i++) {
216308218Savg		if (signature == equiv_cpu_table[i].installed_cpu) {
217308218Savg			equiv_id = equiv_cpu_table[i].equiv_cpu;
218308218Savg			WARNX(3, "equiv_id: %x", equiv_id);
219308218Savg			break;
220308218Savg		}
221308218Savg	}
222308218Savg	if (equiv_id == 0) {
223308218Savg		WARNX(2, "CPU is not found in the equivalence table");
224308218Savg		goto done;
225308218Savg	}
226308218Savg
227308218Savg	selected_fw = NULL;
228308218Savg	selected_size = 0;
229308218Savg	while (fw_size >= sizeof(*section_header)) {
230308218Savg		section_header = (const section_header_t *)fw_data;
231308218Savg		fw_data += sizeof(*section_header);
232308218Savg		fw_size -= sizeof(*section_header);
233308218Savg		if (section_header->type != AMD_10H_uCODE_TYPE) {
234308218Savg			WARNX(2, "%s is not a valid amd firmware: "
235308218Savg			    "section has incorret type", path);
236308218Savg			goto done;
237308218Savg		}
238308218Savg		if (section_header->size > fw_size) {
239308218Savg			WARNX(2, "%s is not a valid amd firmware: "
240308218Savg			    "file is truncated", path);
241308218Savg			goto done;
242308218Savg		}
243308218Savg		if (section_header->size < sizeof(*fw_header)) {
244308218Savg			WARNX(2, "%s is not a valid amd firmware: "
245308218Savg			    "section is too short", path);
246308218Savg			goto done;
247308218Savg		}
248308218Savg		fw_header = (const amd_10h_fw_header_t *)fw_data;
249308218Savg		fw_data += section_header->size;
250308218Savg		fw_size -= section_header->size;
251308218Savg
252308218Savg		if (fw_header->processor_rev_id != equiv_id)
253308218Savg			continue; /* different cpu */
254308218Savg		if (fw_header->patch_id <= revision)
255308218Savg			continue; /* not newer revision */
256308218Savg		if (fw_header->nb_dev_id != 0 || fw_header->sb_dev_id != 0) {
257308218Savg			WARNX(2, "Chipset-specific microcode is not supported");
258308218Savg		}
259308218Savg
260308218Savg		WARNX(3, "selecting revision: %x", fw_header->patch_id);
261308218Savg		revision = fw_header->patch_id;
262308218Savg		selected_fw = fw_header;
263308218Savg		selected_size = section_header->size;
264308218Savg	}
265308218Savg
266308218Savg	if (fw_size != 0) {
267308218Savg		WARNX(2, "%s is not a valid amd firmware: "
268308218Savg		    "file is truncated", path);
269308218Savg		goto done;
270308218Savg	}
271308218Savg
272308218Savg	if (selected_fw != NULL) {
273308218Savg		WARNX(1, "selected ucode size is %zu", selected_size);
274308218Savg		fprintf(stderr, "%s: updating cpu %s to revision %#x... ",
275308218Savg		    path, dev, revision);
276308218Savg
277308218Savg		args.data = __DECONST(void *, selected_fw);
278308218Savg		args.size = selected_size;
279308218Savg		error = ioctl(devfd, CPUCTL_UPDATE, &args);
280308218Savg		if (error < 0) {
281308218Savg			fprintf(stderr, "failed.\n");
282308218Savg			warn("ioctl()");
283308218Savg			goto done;
284308218Savg		}
285308218Savg		fprintf(stderr, "done.\n");
286308218Savg	}
287308218Savg
288336338Smarkj	msrargs.msr = MSR_BIOS_SIGN;
289308218Savg	error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
290308218Savg	if (error < 0) {
291308218Savg		WARN(0, "ioctl(%s)", dev);
292308218Savg		goto done;
293308218Savg	}
294308218Savg	new_rev = (uint32_t)msrargs.data;
295308218Savg	if (new_rev != revision)
296308218Savg		WARNX(0, "revision after update %#x", new_rev);
297308218Savg
298308218Savgdone:
299308218Savg	if (fd >= 0)
300308218Savg		close(fd);
301308218Savg	if (devfd >= 0)
302308218Savg		close(devfd);
303308218Savg	if (fw_image != MAP_FAILED)
304308218Savg		if (munmap(fw_image, st.st_size) != 0)
305308218Savg			warn("munmap(%s)", path);
306308218Savg	return;
307308218Savg}
308