xref: /dflybsd-src/sys/dev/misc/cpuctl/cpuctl.c (revision 2b3f93ea6d1f70880f3e87f3c2cbe0dc0bfc9332)
1d4ef6694SJoris Giovannangeli /*-
2d4ef6694SJoris Giovannangeli  * Copyright (c) 2006-2008 Stanislav Sedov <stas@FreeBSD.org>
3d4ef6694SJoris Giovannangeli  * All rights reserved.
4d4ef6694SJoris Giovannangeli  *
5d4ef6694SJoris Giovannangeli  * Redistribution and use in source and binary forms, with or without
6d4ef6694SJoris Giovannangeli  * modification, are permitted provided that the following conditions
7d4ef6694SJoris Giovannangeli  * are met:
8d4ef6694SJoris Giovannangeli  * 1. Redistributions of source code must retain the above copyright
9d4ef6694SJoris Giovannangeli  *    notice, this list of conditions and the following disclaimer.
10d4ef6694SJoris Giovannangeli  * 2. Redistributions in binary form must reproduce the above copyright
11d4ef6694SJoris Giovannangeli  *    notice, this list of conditions and the following disclaimer in the
12d4ef6694SJoris Giovannangeli  *    documentation and/or other materials provided with the distribution.
13d4ef6694SJoris Giovannangeli  *
14d4ef6694SJoris Giovannangeli  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15d4ef6694SJoris Giovannangeli  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16d4ef6694SJoris Giovannangeli  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17d4ef6694SJoris Giovannangeli  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18d4ef6694SJoris Giovannangeli  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19d4ef6694SJoris Giovannangeli  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20d4ef6694SJoris Giovannangeli  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21d4ef6694SJoris Giovannangeli  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22d4ef6694SJoris Giovannangeli  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23d4ef6694SJoris Giovannangeli  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24d4ef6694SJoris Giovannangeli  * SUCH DAMAGE.
25d4ef6694SJoris Giovannangeli  *
261246b87eSzrj  * $FreeBSD: head/sys/dev/cpuctl/cpuctl.c 275960 2014-12-20 16:40:49Z kib $
27d4ef6694SJoris Giovannangeli  */
28d4ef6694SJoris Giovannangeli 
29d4ef6694SJoris Giovannangeli 
30d4ef6694SJoris Giovannangeli #include <sys/param.h>
31d4ef6694SJoris Giovannangeli #include <sys/systm.h>
32d4ef6694SJoris Giovannangeli #include <sys/conf.h>
33d4ef6694SJoris Giovannangeli #include <sys/fcntl.h>
34d4ef6694SJoris Giovannangeli #include <sys/malloc.h>
35d4ef6694SJoris Giovannangeli #include <sys/module.h>
36*2b3f93eaSMatthew Dillon #include <sys/caps.h>
37d4ef6694SJoris Giovannangeli #include <sys/proc.h>
38d4ef6694SJoris Giovannangeli #include <sys/queue.h>
39d4ef6694SJoris Giovannangeli #include <sys/sched.h>
40d4ef6694SJoris Giovannangeli #include <sys/kernel.h>
41d4ef6694SJoris Giovannangeli #include <sys/sysctl.h>
42d4ef6694SJoris Giovannangeli #include <sys/uio.h>
43d4ef6694SJoris Giovannangeli #include <sys/cpuctl.h>
44d4ef6694SJoris Giovannangeli #include <sys/device.h>
45d4ef6694SJoris Giovannangeli #include <sys/thread2.h>
46d4ef6694SJoris Giovannangeli 
47d4ef6694SJoris Giovannangeli #include <machine/cpufunc.h>
48d4ef6694SJoris Giovannangeli #include <machine/md_var.h>
49d4ef6694SJoris Giovannangeli #include <machine/specialreg.h>
50d4ef6694SJoris Giovannangeli 
51d4ef6694SJoris Giovannangeli static d_open_t cpuctl_open;
522798dc7bSMatthew Dillon static d_close_t cpuctl_close;
53d4ef6694SJoris Giovannangeli static d_ioctl_t cpuctl_ioctl;
54d4ef6694SJoris Giovannangeli 
55d4ef6694SJoris Giovannangeli #define	CPUCTL_VERSION 1
56d4ef6694SJoris Giovannangeli 
57d4ef6694SJoris Giovannangeli #ifdef DEBUG
58d4ef6694SJoris Giovannangeli # define	DPRINTF(format,...) kprintf(format, __VA_ARGS__);
59d4ef6694SJoris Giovannangeli #else
60d4ef6694SJoris Giovannangeli # define	DPRINTF(format,...)
61d4ef6694SJoris Giovannangeli #endif
62d4ef6694SJoris Giovannangeli 
6337d142deSMatthew Dillon #define	UCODE_SIZE_MAX	(4 * 1024 * 1024)
64d4ef6694SJoris Giovannangeli 
65d4ef6694SJoris Giovannangeli static int cpuctl_do_msr(int cpu, cpuctl_msr_args_t *data, u_long cmd);
661246b87eSzrj static void cpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data);
671246b87eSzrj static void cpuctl_do_cpuid_count(int cpu, cpuctl_cpuid_count_args_t *data);
68d4ef6694SJoris Giovannangeli static int cpuctl_do_update(int cpu, cpuctl_update_args_t *data);
69d4ef6694SJoris Giovannangeli static int update_intel(int cpu, cpuctl_update_args_t *args);
70d4ef6694SJoris Giovannangeli static int update_amd(int cpu, cpuctl_update_args_t *args);
71d4ef6694SJoris Giovannangeli static int update_via(int cpu, cpuctl_update_args_t *args);
72d4ef6694SJoris Giovannangeli 
73d4ef6694SJoris Giovannangeli static cdev_t *cpuctl_devs;
74d4ef6694SJoris Giovannangeli static MALLOC_DEFINE(M_CPUCTL, "cpuctl", "CPUCTL buffer");
75481d12aaSMatthew Dillon static struct lock cpuctl_lock = LOCK_INITIALIZER("cpuctl", 0, 0);
76d4ef6694SJoris Giovannangeli 
77d4ef6694SJoris Giovannangeli static struct dev_ops cpuctl_cdevsw = {
78481d12aaSMatthew Dillon         .head = { .name = "cpuctl", .flags = D_MPSAFE },
79d4ef6694SJoris Giovannangeli         .d_open =       cpuctl_open,
802798dc7bSMatthew Dillon 	.d_close =	cpuctl_close,
81d4ef6694SJoris Giovannangeli         .d_ioctl =      cpuctl_ioctl,
82d4ef6694SJoris Giovannangeli };
83d4ef6694SJoris Giovannangeli 
84d4ef6694SJoris Giovannangeli int
cpuctl_ioctl(struct dev_ioctl_args * ap)85d4ef6694SJoris Giovannangeli cpuctl_ioctl(struct dev_ioctl_args *ap)
86d4ef6694SJoris Giovannangeli {
87d4ef6694SJoris Giovannangeli 	int ret;
88d4ef6694SJoris Giovannangeli 	int cpu = dev2unit(ap->a_head.a_dev);
89d4ef6694SJoris Giovannangeli 	u_long cmd = ap->a_cmd;
90d4ef6694SJoris Giovannangeli 	int flags = ap->a_fflag;
91d4ef6694SJoris Giovannangeli 	caddr_t data = ap->a_data;
92d4ef6694SJoris Giovannangeli 
93d4ef6694SJoris Giovannangeli 	if (cpu >= ncpus) {
94d4ef6694SJoris Giovannangeli 		DPRINTF("[cpuctl,%d]: bad cpu number %d\n", __LINE__, cpu);
95d4ef6694SJoris Giovannangeli 		return (ENXIO);
96d4ef6694SJoris Giovannangeli 	}
97d4ef6694SJoris Giovannangeli 	/* Require write flag for "write" requests. */
985a9835aeSImre Vadász 	if ((cmd == CPUCTL_WRMSR || cmd == CPUCTL_UPDATE ||
995a9835aeSImre Vadász 	     cmd == CPUCTL_MSRSBIT || cmd == CPUCTL_MSRCBIT) &&
100d4ef6694SJoris Giovannangeli 	    ((flags & FWRITE) == 0))
101d4ef6694SJoris Giovannangeli 		return (EPERM);
102481d12aaSMatthew Dillon 
103481d12aaSMatthew Dillon 	lockmgr(&cpuctl_lock, LK_EXCLUSIVE);
104481d12aaSMatthew Dillon 
105d4ef6694SJoris Giovannangeli 	switch (cmd) {
106d4ef6694SJoris Giovannangeli 	case CPUCTL_RDMSR:
107d4ef6694SJoris Giovannangeli 		ret = cpuctl_do_msr(cpu, (cpuctl_msr_args_t *)data, cmd);
108d4ef6694SJoris Giovannangeli 		break;
109d4ef6694SJoris Giovannangeli 	case CPUCTL_MSRSBIT:
110d4ef6694SJoris Giovannangeli 	case CPUCTL_MSRCBIT:
111d4ef6694SJoris Giovannangeli 	case CPUCTL_WRMSR:
112*2b3f93eaSMatthew Dillon 		ret = caps_priv_check_self(SYSCAP_NOCPUCTL_WRMSR);
113d4ef6694SJoris Giovannangeli 		if (ret != 0)
114d4ef6694SJoris Giovannangeli 			goto fail;
115d4ef6694SJoris Giovannangeli 		ret = cpuctl_do_msr(cpu, (cpuctl_msr_args_t *)data, cmd);
116d4ef6694SJoris Giovannangeli 		break;
117d4ef6694SJoris Giovannangeli 	case CPUCTL_CPUID:
1181246b87eSzrj 		cpuctl_do_cpuid(cpu, (cpuctl_cpuid_args_t *)data);
1191246b87eSzrj 		ret = 0;
120d4ef6694SJoris Giovannangeli 		break;
121d4ef6694SJoris Giovannangeli 	case CPUCTL_UPDATE:
122*2b3f93eaSMatthew Dillon 		ret = caps_priv_check_self(SYSCAP_NOCPUCTL_UPDATE);
123d4ef6694SJoris Giovannangeli 		if (ret != 0)
124d4ef6694SJoris Giovannangeli 			goto fail;
125d4ef6694SJoris Giovannangeli 		ret = cpuctl_do_update(cpu, (cpuctl_update_args_t *)data);
126d4ef6694SJoris Giovannangeli 		break;
1271246b87eSzrj 	case CPUCTL_CPUID_COUNT:
1281246b87eSzrj 		cpuctl_do_cpuid_count(cpu, (cpuctl_cpuid_count_args_t *)data);
1291246b87eSzrj 		ret = 0;
1301246b87eSzrj 		break;
131d4ef6694SJoris Giovannangeli 	default:
132d4ef6694SJoris Giovannangeli 		ret = EINVAL;
133d4ef6694SJoris Giovannangeli 		break;
134d4ef6694SJoris Giovannangeli 	}
135d4ef6694SJoris Giovannangeli fail:
136481d12aaSMatthew Dillon 	lockmgr(&cpuctl_lock, LK_RELEASE);
137481d12aaSMatthew Dillon 
138d4ef6694SJoris Giovannangeli 	return (ret);
139d4ef6694SJoris Giovannangeli }
140d4ef6694SJoris Giovannangeli 
141d4ef6694SJoris Giovannangeli /*
142d4ef6694SJoris Giovannangeli  * Actually perform cpuid operation.
143d4ef6694SJoris Giovannangeli  */
1441246b87eSzrj static void
cpuctl_do_cpuid_count(int cpu,cpuctl_cpuid_count_args_t * data)1451246b87eSzrj cpuctl_do_cpuid_count(int cpu, cpuctl_cpuid_count_args_t *data)
146d4ef6694SJoris Giovannangeli {
147d4ef6694SJoris Giovannangeli 	int oldcpu;
148d4ef6694SJoris Giovannangeli 
149d4ef6694SJoris Giovannangeli 	KASSERT(cpu >= 0 && cpu < ncpus,
150d4ef6694SJoris Giovannangeli 	    ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu));
151d4ef6694SJoris Giovannangeli 
152d4ef6694SJoris Giovannangeli 	/* Explicitly clear cpuid data to avoid returning stale info. */
153d4ef6694SJoris Giovannangeli 	bzero(data->data, sizeof(data->data));
1541246b87eSzrj 	DPRINTF("[cpuctl,%d]: retrieving cpuid lev %#0x type %#0x for %d cpu\n",
1551246b87eSzrj 	    __LINE__, data->level, data->level_type, cpu);
156d4ef6694SJoris Giovannangeli 	oldcpu = mycpuid;
157d4ef6694SJoris Giovannangeli 	lwkt_migratecpu(cpu);
1581246b87eSzrj 	cpuid_count(data->level, data->level_type, data->data);
159d4ef6694SJoris Giovannangeli 	lwkt_migratecpu(oldcpu);
1601246b87eSzrj }
1611246b87eSzrj 
1621246b87eSzrj static void
cpuctl_do_cpuid(int cpu,cpuctl_cpuid_args_t * data)1631246b87eSzrj cpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data)
1641246b87eSzrj {
1651246b87eSzrj 	cpuctl_cpuid_count_args_t cdata;
1661246b87eSzrj 
1671246b87eSzrj 	cdata.level = data->level;
1681246b87eSzrj 	/* Override the level type. */
1691246b87eSzrj 	cdata.level_type = 0;
1701246b87eSzrj 	cpuctl_do_cpuid_count(cpu, &cdata);
1711246b87eSzrj 	bcopy(cdata.data, data->data, sizeof(data->data)); /* Ignore error */
172d4ef6694SJoris Giovannangeli }
173d4ef6694SJoris Giovannangeli 
174d4ef6694SJoris Giovannangeli /*
175d4ef6694SJoris Giovannangeli  * Actually perform MSR operations.
176d4ef6694SJoris Giovannangeli  */
177d4ef6694SJoris Giovannangeli static int
cpuctl_do_msr(int cpu,cpuctl_msr_args_t * data,u_long cmd)178d4ef6694SJoris Giovannangeli cpuctl_do_msr(int cpu, cpuctl_msr_args_t *data, u_long cmd)
179d4ef6694SJoris Giovannangeli {
180d4ef6694SJoris Giovannangeli 	uint64_t reg;
181d4ef6694SJoris Giovannangeli 	int oldcpu;
182d4ef6694SJoris Giovannangeli 	int ret;
183d4ef6694SJoris Giovannangeli 
184d4ef6694SJoris Giovannangeli 	KASSERT(cpu >= 0 && cpu < ncpus,
185d4ef6694SJoris Giovannangeli 	    ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu));
186d4ef6694SJoris Giovannangeli 
187d4ef6694SJoris Giovannangeli 	/*
188d4ef6694SJoris Giovannangeli 	 * Explicitly clear cpuid data to avoid returning stale
189d4ef6694SJoris Giovannangeli 	 * info
190d4ef6694SJoris Giovannangeli 	 */
191d4ef6694SJoris Giovannangeli 	DPRINTF("[cpuctl,%d]: operating on MSR %#0x for %d cpu\n", __LINE__,
192d4ef6694SJoris Giovannangeli 	    data->msr, cpu);
193d4ef6694SJoris Giovannangeli 	oldcpu = mycpuid;
194d4ef6694SJoris Giovannangeli 	lwkt_migratecpu(cpu);
195d4ef6694SJoris Giovannangeli 	if (cmd == CPUCTL_RDMSR) {
196d4ef6694SJoris Giovannangeli 		data->data = 0;
197d4ef6694SJoris Giovannangeli 		ret = rdmsr_safe(data->msr, &data->data);
198d4ef6694SJoris Giovannangeli 	} else if (cmd == CPUCTL_WRMSR) {
199d4ef6694SJoris Giovannangeli 		ret = wrmsr_safe(data->msr, data->data);
200d4ef6694SJoris Giovannangeli 	} else if (cmd == CPUCTL_MSRSBIT) {
201d4ef6694SJoris Giovannangeli 		crit_enter();
202d4ef6694SJoris Giovannangeli 		ret = rdmsr_safe(data->msr, &reg);
203d4ef6694SJoris Giovannangeli 		if (ret == 0)
204d4ef6694SJoris Giovannangeli 			ret = wrmsr_safe(data->msr, reg | data->data);
205d4ef6694SJoris Giovannangeli 		crit_exit();
206d4ef6694SJoris Giovannangeli 	} else if (cmd == CPUCTL_MSRCBIT) {
207d4ef6694SJoris Giovannangeli 		crit_enter();
208d4ef6694SJoris Giovannangeli 		ret = rdmsr_safe(data->msr, &reg);
209d4ef6694SJoris Giovannangeli 		if (ret == 0)
210d4ef6694SJoris Giovannangeli 			ret = wrmsr_safe(data->msr, reg & ~data->data);
211d4ef6694SJoris Giovannangeli 		crit_exit();
212d4ef6694SJoris Giovannangeli 	} else
213d4ef6694SJoris Giovannangeli 		panic("[cpuctl,%d]: unknown operation requested: %lu", __LINE__, cmd);
214d4ef6694SJoris Giovannangeli 	lwkt_migratecpu(oldcpu);
215d4ef6694SJoris Giovannangeli 	return (ret);
216d4ef6694SJoris Giovannangeli }
217d4ef6694SJoris Giovannangeli 
218d4ef6694SJoris Giovannangeli /*
219d4ef6694SJoris Giovannangeli  * Actually perform microcode update.
220d4ef6694SJoris Giovannangeli  */
221cd89a7ceSMatthew Dillon extern void mitigation_vm_setup(void *arg);
2228ed06571SMatthew Dillon 
223d4ef6694SJoris Giovannangeli static int
cpuctl_do_update(int cpu,cpuctl_update_args_t * data)224d4ef6694SJoris Giovannangeli cpuctl_do_update(int cpu, cpuctl_update_args_t *data)
225d4ef6694SJoris Giovannangeli {
226d4ef6694SJoris Giovannangeli 	cpuctl_cpuid_args_t args = {
227d4ef6694SJoris Giovannangeli 		.level = 0,
228d4ef6694SJoris Giovannangeli 	};
229d4ef6694SJoris Giovannangeli 	char vendor[13];
230d4ef6694SJoris Giovannangeli 	int ret;
231d4ef6694SJoris Giovannangeli 
232d4ef6694SJoris Giovannangeli 	KASSERT(cpu >= 0 && cpu < ncpus,
233d4ef6694SJoris Giovannangeli 	    ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu));
234d4ef6694SJoris Giovannangeli 	DPRINTF("[cpuctl,%d]: XXX %d", __LINE__, cpu);
235d4ef6694SJoris Giovannangeli 
2361246b87eSzrj 	cpuctl_do_cpuid(cpu, &args);
237d4ef6694SJoris Giovannangeli 	((uint32_t *)vendor)[0] = args.data[1];
238d4ef6694SJoris Giovannangeli 	((uint32_t *)vendor)[1] = args.data[3];
239d4ef6694SJoris Giovannangeli 	((uint32_t *)vendor)[2] = args.data[2];
240d4ef6694SJoris Giovannangeli 	vendor[12] = '\0';
241d4ef6694SJoris Giovannangeli 	if (strncmp(vendor, INTEL_VENDOR_ID, sizeof(INTEL_VENDOR_ID)) == 0)
242d4ef6694SJoris Giovannangeli 		ret = update_intel(cpu, data);
243d4ef6694SJoris Giovannangeli 	else if(strncmp(vendor, AMD_VENDOR_ID, sizeof(AMD_VENDOR_ID)) == 0)
244d4ef6694SJoris Giovannangeli 		ret = update_amd(cpu, data);
245d4ef6694SJoris Giovannangeli 	else if(strncmp(vendor, CENTAUR_VENDOR_ID, sizeof(CENTAUR_VENDOR_ID)) == 0)
246d4ef6694SJoris Giovannangeli 		ret = update_via(cpu, data);
247d4ef6694SJoris Giovannangeli 	else
248d4ef6694SJoris Giovannangeli 		ret = ENXIO;
2498ed06571SMatthew Dillon 
2508ed06571SMatthew Dillon 	if (ret == 0)
251cd89a7ceSMatthew Dillon 		mitigation_vm_setup((void *)(intptr_t)1);
2528ed06571SMatthew Dillon 
253d4ef6694SJoris Giovannangeli 	return (ret);
254d4ef6694SJoris Giovannangeli }
255d4ef6694SJoris Giovannangeli 
256d4ef6694SJoris Giovannangeli static int
update_intel(int cpu,cpuctl_update_args_t * args)257d4ef6694SJoris Giovannangeli update_intel(int cpu, cpuctl_update_args_t *args)
258d4ef6694SJoris Giovannangeli {
259d4ef6694SJoris Giovannangeli 	void *ptr;
260d4ef6694SJoris Giovannangeli 	uint64_t rev0, rev1;
261d4ef6694SJoris Giovannangeli 	uint32_t tmp[4];
262d4ef6694SJoris Giovannangeli 	int oldcpu;
263d4ef6694SJoris Giovannangeli 	int ret;
264d4ef6694SJoris Giovannangeli 
265d4ef6694SJoris Giovannangeli 	if (args->size == 0 || args->data == NULL) {
266d4ef6694SJoris Giovannangeli 		DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__);
267d4ef6694SJoris Giovannangeli 		return (EINVAL);
268d4ef6694SJoris Giovannangeli 	}
269d4ef6694SJoris Giovannangeli 	if (args->size > UCODE_SIZE_MAX) {
270d4ef6694SJoris Giovannangeli 		DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__);
271d4ef6694SJoris Giovannangeli 		return (EINVAL);
272d4ef6694SJoris Giovannangeli 	}
273d4ef6694SJoris Giovannangeli 
274d4ef6694SJoris Giovannangeli 	/*
275d4ef6694SJoris Giovannangeli 	 * 16 byte alignment required.  Rely on the fact that
276d4ef6694SJoris Giovannangeli 	 * malloc(9) always returns the pointer aligned at least on
277d4ef6694SJoris Giovannangeli 	 * the size of the allocation.
278d4ef6694SJoris Giovannangeli 	 */
279d4ef6694SJoris Giovannangeli 	ptr = kmalloc(args->size + 16, M_CPUCTL, M_WAITOK);
280d4ef6694SJoris Giovannangeli 	if (copyin(args->data, ptr, args->size) != 0) {
281d4ef6694SJoris Giovannangeli 		DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed",
282d4ef6694SJoris Giovannangeli 		    __LINE__, args->data, ptr, args->size);
283d4ef6694SJoris Giovannangeli 		ret = EFAULT;
284d4ef6694SJoris Giovannangeli 		goto fail;
285d4ef6694SJoris Giovannangeli 	}
286d4ef6694SJoris Giovannangeli 	oldcpu = mycpuid;
287d4ef6694SJoris Giovannangeli 	lwkt_migratecpu(cpu);
288d4ef6694SJoris Giovannangeli 	crit_enter();
289d4ef6694SJoris Giovannangeli 	rdmsr_safe(MSR_BIOS_SIGN, &rev0); /* Get current microcode revision. */
290d4ef6694SJoris Giovannangeli 
291d4ef6694SJoris Giovannangeli 	/*
292d4ef6694SJoris Giovannangeli 	 * Perform update.
293d4ef6694SJoris Giovannangeli 	 */
294d4ef6694SJoris Giovannangeli 	wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uintptr_t)(ptr));
295d4ef6694SJoris Giovannangeli 	wrmsr_safe(MSR_BIOS_SIGN, 0);
296d4ef6694SJoris Giovannangeli 
297d4ef6694SJoris Giovannangeli 	/*
298d4ef6694SJoris Giovannangeli 	 * Serialize instruction flow.
299d4ef6694SJoris Giovannangeli 	 */
300d4ef6694SJoris Giovannangeli 	do_cpuid(0, tmp);
301d4ef6694SJoris Giovannangeli 	crit_exit();
302d4ef6694SJoris Giovannangeli 	rdmsr_safe(MSR_BIOS_SIGN, &rev1); /* Get new microcode revision. */
303d4ef6694SJoris Giovannangeli 	lwkt_migratecpu(oldcpu);
304d4ef6694SJoris Giovannangeli 	kprintf("[cpu %d]: updated microcode from rev=0x%x to rev=0x%x\n", cpu,
305d4ef6694SJoris Giovannangeli 	    (unsigned)(rev0 >> 32), (unsigned)(rev1 >> 32));
306d4ef6694SJoris Giovannangeli 
307d4ef6694SJoris Giovannangeli 	if (rev1 > rev0)
308d4ef6694SJoris Giovannangeli 		ret = 0;
309d4ef6694SJoris Giovannangeli 	else
310d4ef6694SJoris Giovannangeli 		ret = EEXIST;
311d4ef6694SJoris Giovannangeli fail:
312d4ef6694SJoris Giovannangeli 	kfree(ptr, M_CPUCTL);
313d4ef6694SJoris Giovannangeli 	return (ret);
314d4ef6694SJoris Giovannangeli }
315d4ef6694SJoris Giovannangeli 
316d4ef6694SJoris Giovannangeli static int
update_amd(int cpu,cpuctl_update_args_t * args)317d4ef6694SJoris Giovannangeli update_amd(int cpu, cpuctl_update_args_t *args)
318d4ef6694SJoris Giovannangeli {
319d4ef6694SJoris Giovannangeli 	void *ptr = NULL;
320d4ef6694SJoris Giovannangeli 	uint32_t tmp[4];
321d4ef6694SJoris Giovannangeli 	int oldcpu;
322d4ef6694SJoris Giovannangeli 	int ret;
323d4ef6694SJoris Giovannangeli 
324d4ef6694SJoris Giovannangeli 	if (args->size == 0 || args->data == NULL) {
325d4ef6694SJoris Giovannangeli 		DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__);
326d4ef6694SJoris Giovannangeli 		return (EINVAL);
327d4ef6694SJoris Giovannangeli 	}
328d4ef6694SJoris Giovannangeli 	if (args->size > UCODE_SIZE_MAX) {
329d4ef6694SJoris Giovannangeli 		DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__);
330d4ef6694SJoris Giovannangeli 		return (EINVAL);
331d4ef6694SJoris Giovannangeli 	}
332d4ef6694SJoris Giovannangeli 	/*
333d4ef6694SJoris Giovannangeli 	 * XXX Might not require contignous address space - needs check
334d4ef6694SJoris Giovannangeli 	 */
335d4ef6694SJoris Giovannangeli 	ptr = contigmalloc(args->size, M_CPUCTL, 0, 0, 0xffffffff, 16, 0);
336d4ef6694SJoris Giovannangeli 	if (ptr == NULL) {
337d4ef6694SJoris Giovannangeli 		DPRINTF("[cpuctl,%d]: cannot allocate %zd bytes of memory",
338d4ef6694SJoris Giovannangeli 		    __LINE__, args->size);
339d4ef6694SJoris Giovannangeli 		return (ENOMEM);
340d4ef6694SJoris Giovannangeli 	}
341d4ef6694SJoris Giovannangeli 	if (copyin(args->data, ptr, args->size) != 0) {
342d4ef6694SJoris Giovannangeli 		DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed",
343d4ef6694SJoris Giovannangeli 		    __LINE__, args->data, ptr, args->size);
344d4ef6694SJoris Giovannangeli 		ret = EFAULT;
345d4ef6694SJoris Giovannangeli 		goto fail;
346d4ef6694SJoris Giovannangeli 	}
347d4ef6694SJoris Giovannangeli 	oldcpu = mycpuid;
348d4ef6694SJoris Giovannangeli 	lwkt_migratecpu(cpu);
349d4ef6694SJoris Giovannangeli 	crit_enter();
350d4ef6694SJoris Giovannangeli 
351d4ef6694SJoris Giovannangeli 	/*
352d4ef6694SJoris Giovannangeli 	 * Perform update.
353d4ef6694SJoris Giovannangeli 	 */
35496abf295SAaron LI 	wrmsr_safe(MSR_AMD_PATCH_LOADER, (uintptr_t)ptr);
355d4ef6694SJoris Giovannangeli 
356d4ef6694SJoris Giovannangeli 	/*
357d4ef6694SJoris Giovannangeli 	 * Serialize instruction flow.
358d4ef6694SJoris Giovannangeli 	 */
359d4ef6694SJoris Giovannangeli 	do_cpuid(0, tmp);
360d4ef6694SJoris Giovannangeli 	crit_exit();
361d4ef6694SJoris Giovannangeli 	lwkt_migratecpu(oldcpu);
362d4ef6694SJoris Giovannangeli 	ret = 0;
363d4ef6694SJoris Giovannangeli fail:
364d4ef6694SJoris Giovannangeli 	if (ptr != NULL)
365d4ef6694SJoris Giovannangeli 		contigfree(ptr, args->size, M_CPUCTL);
366d4ef6694SJoris Giovannangeli 	return (ret);
367d4ef6694SJoris Giovannangeli }
368d4ef6694SJoris Giovannangeli 
369d4ef6694SJoris Giovannangeli static int
update_via(int cpu,cpuctl_update_args_t * args)370d4ef6694SJoris Giovannangeli update_via(int cpu, cpuctl_update_args_t *args)
371d4ef6694SJoris Giovannangeli {
372d4ef6694SJoris Giovannangeli 	void *ptr;
373d4ef6694SJoris Giovannangeli 	uint64_t rev0, rev1, res;
374d4ef6694SJoris Giovannangeli 	uint32_t tmp[4];
375d4ef6694SJoris Giovannangeli 	int oldcpu;
376d4ef6694SJoris Giovannangeli 	int ret;
377d4ef6694SJoris Giovannangeli 
378d4ef6694SJoris Giovannangeli 	if (args->size == 0 || args->data == NULL) {
379d4ef6694SJoris Giovannangeli 		DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__);
380d4ef6694SJoris Giovannangeli 		return (EINVAL);
381d4ef6694SJoris Giovannangeli 	}
382d4ef6694SJoris Giovannangeli 	if (args->size > UCODE_SIZE_MAX) {
383d4ef6694SJoris Giovannangeli 		DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__);
384d4ef6694SJoris Giovannangeli 		return (EINVAL);
385d4ef6694SJoris Giovannangeli 	}
386d4ef6694SJoris Giovannangeli 
387d4ef6694SJoris Giovannangeli 	/*
388d4ef6694SJoris Giovannangeli 	 * 4 byte alignment required.
389d4ef6694SJoris Giovannangeli 	 */
390d4ef6694SJoris Giovannangeli 	ptr = kmalloc(args->size, M_CPUCTL, M_WAITOK);
391d4ef6694SJoris Giovannangeli 	if (copyin(args->data, ptr, args->size) != 0) {
392d4ef6694SJoris Giovannangeli 		DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed",
393d4ef6694SJoris Giovannangeli 		    __LINE__, args->data, ptr, args->size);
394d4ef6694SJoris Giovannangeli 		ret = EFAULT;
395d4ef6694SJoris Giovannangeli 		goto fail;
396d4ef6694SJoris Giovannangeli 	}
397d4ef6694SJoris Giovannangeli 	oldcpu = mycpuid;
398d4ef6694SJoris Giovannangeli 	lwkt_migratecpu(cpu);
399d4ef6694SJoris Giovannangeli 	crit_enter();
400d4ef6694SJoris Giovannangeli 	rdmsr_safe(MSR_BIOS_SIGN, &rev0); /* Get current microcode revision. */
401d4ef6694SJoris Giovannangeli 
402d4ef6694SJoris Giovannangeli 	/*
403d4ef6694SJoris Giovannangeli 	 * Perform update.
404d4ef6694SJoris Giovannangeli 	 */
405d4ef6694SJoris Giovannangeli 	wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uintptr_t)(ptr));
406d4ef6694SJoris Giovannangeli 	do_cpuid(1, tmp);
407d4ef6694SJoris Giovannangeli 
408d4ef6694SJoris Giovannangeli 	/*
409d4ef6694SJoris Giovannangeli 	 * Result are in low byte of MSR FCR5:
410d4ef6694SJoris Giovannangeli 	 * 0x00: No update has been attempted since RESET.
411d4ef6694SJoris Giovannangeli 	 * 0x01: The last attempted update was successful.
412d4ef6694SJoris Giovannangeli 	 * 0x02: The last attempted update was unsuccessful due to a bad
413d4ef6694SJoris Giovannangeli 	 *       environment. No update was loaded and any preexisting
414d4ef6694SJoris Giovannangeli 	 *       patches are still active.
415d4ef6694SJoris Giovannangeli 	 * 0x03: The last attempted update was not applicable to this processor.
416d4ef6694SJoris Giovannangeli 	 *       No update was loaded and any preexisting patches are still
417d4ef6694SJoris Giovannangeli 	 *       active.
418d4ef6694SJoris Giovannangeli 	 * 0x04: The last attempted update was not successful due to an invalid
419d4ef6694SJoris Giovannangeli 	 *       update data block. No update was loaded and any preexisting
420d4ef6694SJoris Giovannangeli 	 *       patches are still active
421d4ef6694SJoris Giovannangeli 	 */
422d4ef6694SJoris Giovannangeli 	rdmsr_safe(0x1205, &res);
423d4ef6694SJoris Giovannangeli 	res &= 0xff;
424d4ef6694SJoris Giovannangeli 	crit_exit();
425d4ef6694SJoris Giovannangeli 	rdmsr_safe(MSR_BIOS_SIGN, &rev1); /* Get new microcode revision. */
426d4ef6694SJoris Giovannangeli 	lwkt_migratecpu(oldcpu);
427d4ef6694SJoris Giovannangeli 
428d4ef6694SJoris Giovannangeli 	DPRINTF("[cpu,%d]: rev0=%x rev1=%x res=%x\n", __LINE__,
429d4ef6694SJoris Giovannangeli 	    (unsigned)(rev0 >> 32), (unsigned)(rev1 >> 32), (unsigned)res);
430d4ef6694SJoris Giovannangeli 
431d4ef6694SJoris Giovannangeli 	if (res != 0x01)
432d4ef6694SJoris Giovannangeli 		ret = EINVAL;
433d4ef6694SJoris Giovannangeli 	else
434d4ef6694SJoris Giovannangeli 		ret = 0;
435d4ef6694SJoris Giovannangeli fail:
436d4ef6694SJoris Giovannangeli 	kfree(ptr, M_CPUCTL);
437d4ef6694SJoris Giovannangeli 	return (ret);
438d4ef6694SJoris Giovannangeli }
439d4ef6694SJoris Giovannangeli 
440d4ef6694SJoris Giovannangeli int
cpuctl_open(struct dev_open_args * ap)441d4ef6694SJoris Giovannangeli cpuctl_open(struct dev_open_args *ap)
442d4ef6694SJoris Giovannangeli {
443d4ef6694SJoris Giovannangeli 	int ret = 0;
444d4ef6694SJoris Giovannangeli 	int cpu;
445d4ef6694SJoris Giovannangeli 
446d4ef6694SJoris Giovannangeli 	cpu = dev2unit(ap->a_head.a_dev);
4472798dc7bSMatthew Dillon 	if (cpu < 0 || cpu >= ncpus) {
4482798dc7bSMatthew Dillon 		DPRINTF("[cpuctl,%d]: incorrect cpu number %d\n",
4492798dc7bSMatthew Dillon 			__LINE__, cpu);
450d4ef6694SJoris Giovannangeli 		return (ENXIO);
451d4ef6694SJoris Giovannangeli 	}
452d4ef6694SJoris Giovannangeli 	if (ap->a_oflags & FWRITE)
453d4ef6694SJoris Giovannangeli 		ret = securelevel > 0 ? EPERM : 0;
454d4ef6694SJoris Giovannangeli 	return (ret);
455d4ef6694SJoris Giovannangeli }
456d4ef6694SJoris Giovannangeli 
457d4ef6694SJoris Giovannangeli static int
cpuctl_close(struct dev_close_args * ap)4582798dc7bSMatthew Dillon cpuctl_close(struct dev_close_args *ap)
4592798dc7bSMatthew Dillon {
4602798dc7bSMatthew Dillon 	return 0;
4612798dc7bSMatthew Dillon }
4622798dc7bSMatthew Dillon 
4632798dc7bSMatthew Dillon static int
cpuctl_modevent(module_t mod __unused,int type,void * data __unused)464d4ef6694SJoris Giovannangeli cpuctl_modevent(module_t mod __unused, int type, void *data __unused)
465d4ef6694SJoris Giovannangeli {
466d4ef6694SJoris Giovannangeli 	int cpu;
467d4ef6694SJoris Giovannangeli 
468d4ef6694SJoris Giovannangeli 	switch(type) {
469d4ef6694SJoris Giovannangeli 	case MOD_LOAD:
470d4ef6694SJoris Giovannangeli 		if ((cpu_feature & CPUID_MSR) == 0) {
471d4ef6694SJoris Giovannangeli 			if (bootverbose)
472d4ef6694SJoris Giovannangeli 				kprintf("cpuctl: not available.\n");
473d4ef6694SJoris Giovannangeli 			return (ENODEV);
474d4ef6694SJoris Giovannangeli 		}
475d4ef6694SJoris Giovannangeli 		if (bootverbose)
476d4ef6694SJoris Giovannangeli 			kprintf("cpuctl: access to MSR registers/cpuid info.\n");
4771246b87eSzrj 		cpuctl_devs = kmalloc(sizeof(*cpuctl_devs) * ncpus, M_CPUCTL,
4781246b87eSzrj 		    M_WAITOK | M_ZERO);
479d4ef6694SJoris Giovannangeli 		for (cpu = 0; cpu < ncpus; cpu++)
480d4ef6694SJoris Giovannangeli 			cpuctl_devs[cpu] = make_dev(&cpuctl_cdevsw, cpu,
481d4ef6694SJoris Giovannangeli 			    UID_ROOT, GID_KMEM, 0640, "cpuctl%d", cpu);
482d4ef6694SJoris Giovannangeli 		break;
483d4ef6694SJoris Giovannangeli 	case MOD_UNLOAD:
484d4ef6694SJoris Giovannangeli 		for (cpu = 0; cpu < ncpus; cpu++) {
485d4ef6694SJoris Giovannangeli 			if (cpuctl_devs[cpu] != NULL)
486d4ef6694SJoris Giovannangeli 				destroy_dev(cpuctl_devs[cpu]);
487d4ef6694SJoris Giovannangeli 		}
488d4ef6694SJoris Giovannangeli 		kfree(cpuctl_devs, M_CPUCTL);
489d4ef6694SJoris Giovannangeli 		break;
490d4ef6694SJoris Giovannangeli 	case MOD_SHUTDOWN:
491d4ef6694SJoris Giovannangeli 		break;
492d4ef6694SJoris Giovannangeli 	default:
493d4ef6694SJoris Giovannangeli 		return (EOPNOTSUPP);
494d4ef6694SJoris Giovannangeli 	}
495d4ef6694SJoris Giovannangeli 	return (0);
496d4ef6694SJoris Giovannangeli }
497d4ef6694SJoris Giovannangeli 
498d4ef6694SJoris Giovannangeli DEV_MODULE(cpuctl, cpuctl_modevent, NULL);
499d4ef6694SJoris Giovannangeli MODULE_VERSION(cpuctl, CPUCTL_VERSION);
500