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