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