The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/cpuctl/cpuctl.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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  */
   27 
   28 #include <sys/cdefs.h>
   29 __FBSDID("$FreeBSD: releng/8.1/sys/dev/cpuctl/cpuctl.c 195189 2009-06-30 12:35:47Z stas $");
   30 
   31 #include <sys/param.h>
   32 #include <sys/systm.h>
   33 #include <sys/conf.h>
   34 #include <sys/fcntl.h>
   35 #include <sys/ioccom.h>
   36 #include <sys/malloc.h>
   37 #include <sys/module.h>
   38 #include <sys/mutex.h>
   39 #include <sys/priv.h>
   40 #include <sys/proc.h>
   41 #include <sys/queue.h>
   42 #include <sys/sched.h>
   43 #include <sys/kernel.h>
   44 #include <sys/sysctl.h>
   45 #include <sys/uio.h>
   46 #include <sys/pcpu.h>
   47 #include <sys/smp.h>
   48 #include <sys/pmckern.h>
   49 #include <sys/cpuctl.h>
   50 
   51 #include <machine/cpufunc.h>
   52 #include <machine/md_var.h>
   53 #include <machine/specialreg.h>
   54 
   55 static d_open_t cpuctl_open;
   56 static d_ioctl_t cpuctl_ioctl;
   57 
   58 #define CPUCTL_VERSION 1
   59 
   60 #ifdef DEBUG
   61 # define        DPRINTF(format,...) printf(format, __VA_ARGS__);
   62 #else
   63 # define        DPRINTF(...)
   64 #endif
   65 
   66 #define UCODE_SIZE_MAX  (10 * 1024)
   67 
   68 static int cpuctl_do_msr(int cpu, cpuctl_msr_args_t *data, u_long cmd,
   69     struct thread *td);
   70 static int cpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data,
   71     struct thread *td);
   72 static int cpuctl_do_update(int cpu, cpuctl_update_args_t *data,
   73     struct thread *td);
   74 static int update_intel(int cpu, cpuctl_update_args_t *args,
   75     struct thread *td);
   76 static int update_amd(int cpu, cpuctl_update_args_t *args, struct thread *td);
   77 
   78 static struct cdev **cpuctl_devs;
   79 static MALLOC_DEFINE(M_CPUCTL, "cpuctl", "CPUCTL buffer");
   80 
   81 static struct cdevsw cpuctl_cdevsw = {
   82         .d_version =    D_VERSION,
   83         .d_open =       cpuctl_open,
   84         .d_ioctl =      cpuctl_ioctl,
   85         .d_name =       "cpuctl",
   86 };
   87 
   88 /*
   89  * This function checks if specified cpu enabled or not.
   90  */
   91 static int
   92 cpu_enabled(int cpu)
   93 {
   94 
   95         return (pmc_cpu_is_disabled(cpu) == 0);
   96 }
   97 
   98 /*
   99  * Check if the current thread is bound to a specific cpu.
  100  */
  101 static int
  102 cpu_sched_is_bound(struct thread *td)
  103 {
  104         int ret;
  105 
  106         thread_lock(td);
  107         ret = sched_is_bound(td);
  108         thread_unlock(td);
  109         return (ret);
  110 }
  111 
  112 /*
  113  * Switch to target cpu to run.
  114  */
  115 static void
  116 set_cpu(int cpu, struct thread *td)
  117 {
  118 
  119         KASSERT(cpu >= 0 && cpu < mp_ncpus && cpu_enabled(cpu),
  120             ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu));
  121         thread_lock(td);
  122         sched_bind(td, cpu);
  123         thread_unlock(td);
  124         KASSERT(td->td_oncpu == cpu,
  125             ("[cpuctl,%d]: cannot bind to target cpu %d", __LINE__, cpu));
  126 }
  127 
  128 static void
  129 restore_cpu(int oldcpu, int is_bound, struct thread *td)
  130 {
  131 
  132         KASSERT(oldcpu >= 0 && oldcpu < mp_ncpus && cpu_enabled(oldcpu),
  133             ("[cpuctl,%d]: bad cpu number %d", __LINE__, oldcpu));
  134         thread_lock(td);
  135         if (is_bound == 0)
  136                 sched_unbind(td);
  137         else
  138                 sched_bind(td, oldcpu);
  139         thread_unlock(td);
  140 }
  141 
  142 int
  143 cpuctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
  144         int flags, struct thread *td)
  145 {
  146         int ret;
  147         int cpu = dev2unit(dev);
  148 
  149         if (cpu >= mp_ncpus || !cpu_enabled(cpu)) {
  150                 DPRINTF("[cpuctl,%d]: bad cpu number %d\n", __LINE__, cpu);
  151                 return (ENXIO);
  152         }
  153         /* Require write flag for "write" requests. */
  154         if ((cmd == CPUCTL_WRMSR || cmd == CPUCTL_UPDATE) &&
  155             ((flags & FWRITE) == 0))
  156                 return (EPERM);
  157         switch (cmd) {
  158         case CPUCTL_RDMSR:
  159                 ret = cpuctl_do_msr(cpu, (cpuctl_msr_args_t *)data, cmd, td);
  160                 break;
  161         case CPUCTL_MSRSBIT:
  162         case CPUCTL_MSRCBIT:
  163         case CPUCTL_WRMSR:
  164                 ret = priv_check(td, PRIV_CPUCTL_WRMSR);
  165                 if (ret != 0)
  166                         goto fail;
  167                 ret = cpuctl_do_msr(cpu, (cpuctl_msr_args_t *)data, cmd, td);
  168                 break;
  169         case CPUCTL_CPUID:
  170                 ret = cpuctl_do_cpuid(cpu, (cpuctl_cpuid_args_t *)data, td);
  171                 break;
  172         case CPUCTL_UPDATE:
  173                 ret = priv_check(td, PRIV_CPUCTL_UPDATE);
  174                 if (ret != 0)
  175                         goto fail;
  176                 ret = cpuctl_do_update(cpu, (cpuctl_update_args_t *)data, td);
  177                 break;
  178         default:
  179                 ret = EINVAL;
  180                 break;
  181         }
  182 fail:
  183         return (ret);
  184 }
  185 
  186 /*
  187  * Actually perform cpuid operation.
  188  */
  189 static int
  190 cpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data, struct thread *td)
  191 {
  192         int is_bound = 0;
  193         int oldcpu;
  194 
  195         KASSERT(cpu >= 0 && cpu < mp_ncpus,
  196             ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu));
  197 
  198         /* Explicitly clear cpuid data to avoid returning stale info. */
  199         bzero(data->data, sizeof(data->data));
  200         DPRINTF("[cpuctl,%d]: retriving cpuid level %#0x for %d cpu\n",
  201             __LINE__, data->level, cpu);
  202         oldcpu = td->td_oncpu;
  203         is_bound = cpu_sched_is_bound(td);
  204         set_cpu(cpu, td);
  205         do_cpuid(data->level, data->data);
  206         restore_cpu(oldcpu, is_bound, td);
  207         return (0);
  208 }
  209 
  210 /*
  211  * Actually perform MSR operations.
  212  */
  213 static int
  214 cpuctl_do_msr(int cpu, cpuctl_msr_args_t *data, u_long cmd, struct thread *td)
  215 {
  216         uint64_t reg;
  217         int is_bound = 0;
  218         int oldcpu;
  219         int ret;
  220 
  221         KASSERT(cpu >= 0 && cpu < mp_ncpus,
  222             ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu));
  223 
  224         /*
  225          * Explicitly clear cpuid data to avoid returning stale
  226          * info
  227          */
  228         DPRINTF("[cpuctl,%d]: operating on MSR %#0x for %d cpu\n", __LINE__,
  229             data->msr, cpu);
  230         oldcpu = td->td_oncpu;
  231         is_bound = cpu_sched_is_bound(td);
  232         set_cpu(cpu, td);
  233         if (cmd == CPUCTL_RDMSR) {
  234                 data->data = 0;
  235                 ret = rdmsr_safe(data->msr, &data->data);
  236         } else if (cmd == CPUCTL_WRMSR) {
  237                 ret = wrmsr_safe(data->msr, data->data);
  238         } else if (cmd == CPUCTL_MSRSBIT) {
  239                 critical_enter();
  240                 ret = rdmsr_safe(data->msr, &reg);
  241                 if (ret == 0)
  242                         ret = wrmsr_safe(data->msr, reg | data->data);
  243                 critical_exit();
  244         } else if (cmd == CPUCTL_MSRCBIT) {
  245                 critical_enter();
  246                 ret = rdmsr_safe(data->msr, &reg);
  247                 if (ret == 0)
  248                         ret = wrmsr_safe(data->msr, reg & ~data->data);
  249                 critical_exit();
  250         } else
  251                 panic("[cpuctl,%d]: unknown operation requested: %lu", __LINE__, cmd);
  252         restore_cpu(oldcpu, is_bound, td);
  253         return (ret);
  254 }
  255 
  256 /*
  257  * Actually perform microcode update.
  258  */
  259 static int
  260 cpuctl_do_update(int cpu, cpuctl_update_args_t *data, struct thread *td)
  261 {
  262         cpuctl_cpuid_args_t args = {
  263                 .level = 0,
  264         };
  265         char vendor[13];
  266         int ret;
  267 
  268         KASSERT(cpu >= 0 && cpu < mp_ncpus,
  269             ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu));
  270         DPRINTF("[cpuctl,%d]: XXX %d", __LINE__, cpu);
  271 
  272         ret = cpuctl_do_cpuid(cpu, &args, td);
  273         if (ret != 0) {
  274                 DPRINTF("[cpuctl,%d]: cannot retrive cpuid info for cpu %d",
  275                     __LINE__, cpu);
  276                 return (ENXIO);
  277         }
  278         ((uint32_t *)vendor)[0] = args.data[1];
  279         ((uint32_t *)vendor)[1] = args.data[3];
  280         ((uint32_t *)vendor)[2] = args.data[2];
  281         vendor[12] = '\0';
  282         if (strncmp(vendor, INTEL_VENDOR_ID, sizeof(INTEL_VENDOR_ID)) == 0)
  283                 ret = update_intel(cpu, data, td);
  284         else if(strncmp(vendor, INTEL_VENDOR_ID, sizeof(AMD_VENDOR_ID)) == 0)
  285                 ret = update_amd(cpu, data, td);
  286         else
  287                 ret = ENXIO;
  288         return (ret);
  289 }
  290 
  291 static int
  292 update_intel(int cpu, cpuctl_update_args_t *args, struct thread *td)
  293 {
  294         void *ptr = NULL;
  295         uint64_t rev0, rev1;
  296         uint32_t tmp[4];
  297         int is_bound = 0;
  298         int oldcpu;
  299         int ret;
  300 
  301         if (args->size == 0 || args->data == NULL) {
  302                 DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__);
  303                 return (EINVAL);
  304         }
  305         if (args->size > UCODE_SIZE_MAX) {
  306                 DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__);
  307                 return (EINVAL);
  308         }
  309 
  310         /*
  311          * 16 byte alignment required.
  312          */
  313         ptr = malloc(args->size + 16, M_CPUCTL, M_WAITOK);
  314         ptr = (void *)(16 + ((intptr_t)ptr & ~0xf));
  315         if (copyin(args->data, ptr, args->size) != 0) {
  316                 DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed",
  317                     __LINE__, args->data, ptr, args->size);
  318                 ret = EFAULT;
  319                 goto fail;
  320         }
  321         oldcpu = td->td_oncpu;
  322         is_bound = cpu_sched_is_bound(td);
  323         set_cpu(cpu, td);
  324         critical_enter();
  325         rdmsr_safe(MSR_BIOS_SIGN, &rev0); /* Get current micorcode revision. */
  326 
  327         /*
  328          * Perform update.
  329          */
  330         wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uintptr_t)(ptr));
  331         wrmsr_safe(MSR_BIOS_SIGN, 0);
  332 
  333         /*
  334          * Serialize instruction flow.
  335          */
  336         do_cpuid(0, tmp);
  337         critical_exit();
  338         rdmsr_safe(MSR_BIOS_SIGN, &rev1); /* Get new micorcode revision. */
  339         restore_cpu(oldcpu, is_bound, td);
  340         if (rev1 > rev0)
  341                 ret = 0;
  342         else
  343                 ret = EEXIST;
  344 fail:
  345         if (ptr != NULL)
  346                 contigfree(ptr, args->size, M_CPUCTL);
  347         return (ret);
  348 }
  349 
  350 static int
  351 update_amd(int cpu, cpuctl_update_args_t *args, struct thread *td)
  352 {
  353         void *ptr = NULL;
  354         uint32_t tmp[4];
  355         int is_bound = 0;
  356         int oldcpu;
  357         int ret;
  358 
  359         if (args->size == 0 || args->data == NULL) {
  360                 DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__);
  361                 return (EINVAL);
  362         }
  363         if (args->size > UCODE_SIZE_MAX) {
  364                 DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__);
  365                 return (EINVAL);
  366         }
  367         /*
  368          * XXX Might not require contignous address space - needs check
  369          */
  370         ptr = contigmalloc(args->size, M_CPUCTL, 0, 0, 0xffffffff, 16, 0);
  371         if (ptr == NULL) {
  372                 DPRINTF("[cpuctl,%d]: cannot allocate %zd bytes of memory",
  373                     __LINE__, args->size);
  374                 return (ENOMEM);
  375         }
  376         if (copyin(args->data, ptr, args->size) != 0) {
  377                 DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed",
  378                     __LINE__, args->data, ptr, args->size);
  379                 ret = EFAULT;
  380                 goto fail;
  381         }
  382         oldcpu = td->td_oncpu;
  383         is_bound = cpu_sched_is_bound(td);
  384         set_cpu(cpu, td);
  385         critical_enter();
  386 
  387         /*
  388          * Perform update.
  389          */
  390         wrmsr_safe(MSR_K8_UCODE_UPDATE, (uintptr_t)ptr);
  391 
  392         /*
  393          * Serialize instruction flow.
  394          */
  395         do_cpuid(0, tmp);
  396         critical_exit();
  397         restore_cpu(oldcpu, is_bound, td);
  398         ret = 0;
  399 fail:
  400         if (ptr != NULL)
  401                 contigfree(ptr, args->size, M_CPUCTL);
  402         return (ret);
  403 }
  404 
  405 int
  406 cpuctl_open(struct cdev *dev, int flags, int fmt __unused, struct thread *td)
  407 {
  408         int ret = 0;
  409         int cpu;
  410 
  411         cpu = dev2unit(dev);
  412         if (cpu >= mp_ncpus || !cpu_enabled(cpu)) {
  413                 DPRINTF("[cpuctl,%d]: incorrect cpu number %d\n", __LINE__,
  414                     cpu);
  415                 return (ENXIO);
  416         }
  417         if (flags & FWRITE)
  418                 ret = securelevel_gt(td->td_ucred, 0);
  419         return (ret);
  420 }
  421 
  422 static int
  423 cpuctl_modevent(module_t mod __unused, int type, void *data __unused)
  424 {
  425         int cpu;
  426 
  427         switch(type) {
  428         case MOD_LOAD:
  429                 if ((cpu_feature & CPUID_MSR) == 0) {
  430                         if (bootverbose)
  431                                 printf("cpuctl: not available.\n");
  432                         return (ENODEV);
  433                 }
  434                 if (bootverbose)
  435                         printf("cpuctl: access to MSR registers/cpuid info.\n");
  436                 cpuctl_devs = (struct cdev **)malloc(sizeof(void *) * mp_ncpus,
  437                     M_CPUCTL, M_WAITOK | M_ZERO);
  438                 if (cpuctl_devs == NULL) {
  439                         DPRINTF("[cpuctl,%d]: cannot allocate memory\n",
  440                             __LINE__);
  441                         return (ENOMEM);
  442                 }
  443                 for (cpu = 0; cpu < mp_ncpus; cpu++)
  444                         if (cpu_enabled(cpu))
  445                                 cpuctl_devs[cpu] = make_dev(&cpuctl_cdevsw, cpu,
  446                                     UID_ROOT, GID_KMEM, 0640, "cpuctl%d", cpu);
  447                 break;
  448         case MOD_UNLOAD:
  449                 for (cpu = 0; cpu < mp_ncpus; cpu++) {
  450                         if (cpuctl_devs[cpu] != NULL)
  451                                 destroy_dev(cpuctl_devs[cpu]);
  452                 }
  453                 free(cpuctl_devs, M_CPUCTL);
  454                 break;
  455         case MOD_SHUTDOWN:
  456                 break;
  457         default:
  458                 return (EOPNOTSUPP);
  459         }
  460         return (0);
  461 }
  462 
  463 DEV_MODULE(cpuctl, cpuctl_modevent, NULL);
  464 MODULE_VERSION(cpuctl, CPUCTL_VERSION);

Cache object: 75f6676976f2c4c630553884f0604bd5


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.