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, ®);
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, ®);
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