cpuctl.c revision 195081
1181430Sstas/*- 2181430Sstas * Copyright (c) 2006-2008 Stanislav Sedov <stas@FreeBSD.org> 3181430Sstas * All rights reserved. 4181430Sstas * 5181430Sstas * Redistribution and use in source and binary forms, with or without 6181430Sstas * modification, are permitted provided that the following conditions 7181430Sstas * are met: 8181430Sstas * 1. Redistributions of source code must retain the above copyright 9181430Sstas * notice, this list of conditions and the following disclaimer. 10181430Sstas * 2. Redistributions in binary form must reproduce the above copyright 11181430Sstas * notice, this list of conditions and the following disclaimer in the 12181430Sstas * documentation and/or other materials provided with the distribution. 13181430Sstas * 14181430Sstas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15181430Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16181430Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17181430Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18181430Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19181430Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20181430Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21181430Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22181430Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23181430Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24181430Sstas * SUCH DAMAGE. 25181430Sstas * 26181430Sstas */ 27181430Sstas 28181430Sstas#include <sys/cdefs.h> 29181430Sstas__FBSDID("$FreeBSD: head/sys/dev/cpuctl/cpuctl.c 195081 2009-06-26 22:13:15Z stas $"); 30181430Sstas 31181430Sstas#include <sys/param.h> 32181430Sstas#include <sys/systm.h> 33181430Sstas#include <sys/conf.h> 34181430Sstas#include <sys/fcntl.h> 35181430Sstas#include <sys/ioccom.h> 36181430Sstas#include <sys/malloc.h> 37181430Sstas#include <sys/module.h> 38181430Sstas#include <sys/mutex.h> 39181430Sstas#include <sys/priv.h> 40181430Sstas#include <sys/proc.h> 41181430Sstas#include <sys/queue.h> 42181430Sstas#include <sys/sched.h> 43181430Sstas#include <sys/kernel.h> 44181430Sstas#include <sys/sysctl.h> 45181430Sstas#include <sys/uio.h> 46181430Sstas#include <sys/pcpu.h> 47181430Sstas#include <sys/smp.h> 48181430Sstas#include <sys/pmckern.h> 49181430Sstas#include <sys/cpuctl.h> 50181430Sstas 51181430Sstas#include <machine/cpufunc.h> 52181430Sstas#include <machine/md_var.h> 53181430Sstas#include <machine/specialreg.h> 54181430Sstas 55181430Sstasstatic d_open_t cpuctl_open; 56181430Sstasstatic d_ioctl_t cpuctl_ioctl; 57181430Sstas 58181430Sstas#define CPUCTL_VERSION 1 59181430Sstas 60181430Sstas#ifdef DEBUG 61181430Sstas# define DPRINTF(format,...) printf(format, __VA_ARGS__); 62181430Sstas#else 63181430Sstas# define DPRINTF(...) 64181430Sstas#endif 65181430Sstas 66181430Sstas#define UCODE_SIZE_MAX (10 * 1024) 67181430Sstas 68181430Sstasstatic int cpuctl_do_msr(int cpu, cpuctl_msr_args_t *data, u_long cmd, 69181430Sstas struct thread *td); 70181430Sstasstatic int cpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data, 71181430Sstas struct thread *td); 72181430Sstasstatic int cpuctl_do_update(int cpu, cpuctl_update_args_t *data, 73181430Sstas struct thread *td); 74181430Sstasstatic int update_intel(int cpu, cpuctl_update_args_t *args, 75181430Sstas struct thread *td); 76181430Sstasstatic int update_amd(int cpu, cpuctl_update_args_t *args, struct thread *td); 77181430Sstas 78181430Sstasstatic struct cdev **cpuctl_devs; 79181430Sstasstatic MALLOC_DEFINE(M_CPUCTL, "cpuctl", "CPUCTL buffer"); 80181430Sstas 81181430Sstasstatic struct cdevsw cpuctl_cdevsw = { 82181430Sstas .d_version = D_VERSION, 83181430Sstas .d_open = cpuctl_open, 84181430Sstas .d_ioctl = cpuctl_ioctl, 85181430Sstas .d_name = "cpuctl", 86181430Sstas}; 87181430Sstas 88181430Sstas/* 89181430Sstas * This function checks if specified cpu enabled or not. 90181430Sstas */ 91181430Sstasstatic int 92181430Sstascpu_enabled(int cpu) 93181430Sstas{ 94181430Sstas 95181430Sstas return (pmc_cpu_is_disabled(cpu) == 0); 96181430Sstas} 97181430Sstas 98181430Sstas/* 99181430Sstas * Check if the current thread is bound to a specific cpu. 100181430Sstas */ 101181430Sstasstatic int 102181430Sstascpu_sched_is_bound(struct thread *td) 103181430Sstas{ 104181430Sstas int ret; 105181430Sstas 106181430Sstas thread_lock(td); 107181430Sstas ret = sched_is_bound(td); 108181430Sstas thread_unlock(td); 109181430Sstas return (ret); 110181430Sstas} 111181430Sstas 112181430Sstas/* 113181430Sstas * Switch to target cpu to run. 114181430Sstas */ 115181430Sstasstatic void 116181430Sstasset_cpu(int cpu, struct thread *td) 117181430Sstas{ 118181430Sstas 119181430Sstas KASSERT(cpu >= 0 && cpu < mp_ncpus && cpu_enabled(cpu), 120181430Sstas ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu)); 121181430Sstas thread_lock(td); 122181430Sstas sched_bind(td, cpu); 123181430Sstas thread_unlock(td); 124181430Sstas KASSERT(td->td_oncpu == cpu, 125181430Sstas ("[cpuctl,%d]: cannot bind to target cpu %d", __LINE__, cpu)); 126181430Sstas} 127181430Sstas 128181430Sstasstatic void 129181430Sstasrestore_cpu(int oldcpu, int is_bound, struct thread *td) 130181430Sstas{ 131181430Sstas 132181430Sstas KASSERT(oldcpu >= 0 && oldcpu < mp_ncpus && cpu_enabled(oldcpu), 133181430Sstas ("[cpuctl,%d]: bad cpu number %d", __LINE__, oldcpu)); 134181430Sstas thread_lock(td); 135181430Sstas if (is_bound == 0) 136181430Sstas sched_unbind(td); 137181430Sstas else 138181430Sstas sched_bind(td, oldcpu); 139181430Sstas thread_unlock(td); 140181430Sstas} 141181430Sstas 142181430Sstasint 143181430Sstascpuctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, 144181430Sstas int flags, struct thread *td) 145181430Sstas{ 146181430Sstas int ret; 147183397Sed int cpu = dev2unit(dev); 148181430Sstas 149181430Sstas if (cpu >= mp_ncpus || !cpu_enabled(cpu)) { 150181430Sstas DPRINTF("[cpuctl,%d]: bad cpu number %d\n", __LINE__, cpu); 151181430Sstas return (ENXIO); 152181430Sstas } 153181430Sstas /* Require write flag for "write" requests. */ 154181430Sstas if ((cmd == CPUCTL_WRMSR || cmd == CPUCTL_UPDATE) && 155181430Sstas ((flags & FWRITE) == 0)) 156181430Sstas return (EPERM); 157181430Sstas switch (cmd) { 158181430Sstas case CPUCTL_RDMSR: 159181430Sstas ret = cpuctl_do_msr(cpu, (cpuctl_msr_args_t *)data, cmd, td); 160181430Sstas break; 161181430Sstas case CPUCTL_WRMSR: 162181430Sstas ret = priv_check(td, PRIV_CPUCTL_WRMSR); 163181430Sstas if (ret != 0) 164181430Sstas goto fail; 165181430Sstas ret = cpuctl_do_msr(cpu, (cpuctl_msr_args_t *)data, cmd, td); 166181430Sstas break; 167181430Sstas case CPUCTL_CPUID: 168181430Sstas ret = cpuctl_do_cpuid(cpu, (cpuctl_cpuid_args_t *)data, td); 169181430Sstas break; 170181430Sstas case CPUCTL_UPDATE: 171181430Sstas ret = priv_check(td, PRIV_CPUCTL_UPDATE); 172181430Sstas if (ret != 0) 173181430Sstas goto fail; 174181430Sstas ret = cpuctl_do_update(cpu, (cpuctl_update_args_t *)data, td); 175181430Sstas break; 176181430Sstas default: 177181430Sstas ret = EINVAL; 178181430Sstas break; 179181430Sstas } 180181430Sstasfail: 181181430Sstas return (ret); 182181430Sstas} 183181430Sstas 184181430Sstas/* 185181430Sstas * Actually perform cpuid operation. 186181430Sstas */ 187181430Sstasstatic int 188181430Sstascpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data, struct thread *td) 189181430Sstas{ 190181430Sstas int is_bound = 0; 191181430Sstas int oldcpu; 192181430Sstas 193181430Sstas KASSERT(cpu >= 0 && cpu < mp_ncpus, 194181430Sstas ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu)); 195181430Sstas 196181430Sstas /* Explicitly clear cpuid data to avoid returning stale info. */ 197181430Sstas bzero(data->data, sizeof(data->data)); 198181430Sstas DPRINTF("[cpuctl,%d]: retriving cpuid level %#0x for %d cpu\n", 199181430Sstas __LINE__, data->level, cpu); 200181430Sstas oldcpu = td->td_oncpu; 201181430Sstas is_bound = cpu_sched_is_bound(td); 202181430Sstas set_cpu(cpu, td); 203181430Sstas do_cpuid(data->level, data->data); 204181430Sstas restore_cpu(oldcpu, is_bound, td); 205181430Sstas return (0); 206181430Sstas} 207181430Sstas 208181430Sstas/* 209181430Sstas * Actually perform MSR operations. 210181430Sstas */ 211181430Sstasstatic int 212181430Sstascpuctl_do_msr(int cpu, cpuctl_msr_args_t *data, u_long cmd, struct thread *td) 213181430Sstas{ 214181430Sstas int is_bound = 0; 215181430Sstas int oldcpu; 216181430Sstas int ret; 217181430Sstas 218181430Sstas KASSERT(cpu >= 0 && cpu < mp_ncpus, 219181430Sstas ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu)); 220181430Sstas 221181430Sstas /* 222181430Sstas * Explicitly clear cpuid data to avoid returning stale 223181430Sstas * info 224181430Sstas */ 225181430Sstas DPRINTF("[cpuctl,%d]: operating on MSR %#0x for %d cpu\n", __LINE__, 226181430Sstas data->msr, cpu); 227181430Sstas oldcpu = td->td_oncpu; 228181430Sstas is_bound = cpu_sched_is_bound(td); 229181430Sstas set_cpu(cpu, td); 230195081Sstas if (cmd == CPUCTL_RDMSR) { 231195081Sstas data->data = 0; 232195081Sstas ret = rdmsr_safe(data->msr, &data->data); 233195081Sstas } else { 234195081Sstas ret = wrmsr_safe(data->msr, data->data); 235195081Sstas } 236181430Sstas restore_cpu(oldcpu, is_bound, td); 237181430Sstas return (ret); 238181430Sstas} 239181430Sstas 240181430Sstas/* 241181430Sstas * Actually perform microcode update. 242181430Sstas */ 243181430Sstasstatic int 244181430Sstascpuctl_do_update(int cpu, cpuctl_update_args_t *data, struct thread *td) 245181430Sstas{ 246181430Sstas cpuctl_cpuid_args_t args = { 247181430Sstas .level = 0, 248181430Sstas }; 249181430Sstas char vendor[13]; 250181430Sstas int ret; 251181430Sstas 252181430Sstas KASSERT(cpu >= 0 && cpu < mp_ncpus, 253181430Sstas ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu)); 254181430Sstas DPRINTF("[cpuctl,%d]: XXX %d", __LINE__, cpu); 255181430Sstas 256181430Sstas ret = cpuctl_do_cpuid(cpu, &args, td); 257181430Sstas if (ret != 0) { 258181430Sstas DPRINTF("[cpuctl,%d]: cannot retrive cpuid info for cpu %d", 259181430Sstas __LINE__, cpu); 260181430Sstas return (ENXIO); 261181430Sstas } 262181430Sstas ((uint32_t *)vendor)[0] = args.data[1]; 263181430Sstas ((uint32_t *)vendor)[1] = args.data[3]; 264181430Sstas ((uint32_t *)vendor)[2] = args.data[2]; 265181430Sstas vendor[12] = '\0'; 266181430Sstas if (strncmp(vendor, INTEL_VENDOR_ID, sizeof(INTEL_VENDOR_ID)) == 0) 267181430Sstas ret = update_intel(cpu, data, td); 268181430Sstas else if(strncmp(vendor, INTEL_VENDOR_ID, sizeof(AMD_VENDOR_ID)) == 0) 269181430Sstas ret = update_amd(cpu, data, td); 270181430Sstas else 271181430Sstas ret = ENXIO; 272181430Sstas return (ret); 273181430Sstas} 274181430Sstas 275181430Sstasstatic int 276181430Sstasupdate_intel(int cpu, cpuctl_update_args_t *args, struct thread *td) 277181430Sstas{ 278181430Sstas void *ptr = NULL; 279181430Sstas uint64_t rev0, rev1; 280181430Sstas uint32_t tmp[4]; 281181430Sstas int is_bound = 0; 282181430Sstas int oldcpu; 283181430Sstas int ret; 284181430Sstas 285181430Sstas if (args->size == 0 || args->data == NULL) { 286181430Sstas DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__); 287181430Sstas return (EINVAL); 288181430Sstas } 289181430Sstas if (args->size > UCODE_SIZE_MAX) { 290181430Sstas DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__); 291181430Sstas return (EINVAL); 292181430Sstas } 293181430Sstas 294181430Sstas /* 295181430Sstas * 16 byte alignment required. 296181430Sstas */ 297181430Sstas ptr = malloc(args->size + 16, M_CPUCTL, M_WAITOK); 298181430Sstas ptr = (void *)(16 + ((intptr_t)ptr & ~0xf)); 299181430Sstas if (copyin(args->data, ptr, args->size) != 0) { 300181430Sstas DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed", 301181430Sstas __LINE__, args->data, ptr, args->size); 302181430Sstas ret = EFAULT; 303181430Sstas goto fail; 304181430Sstas } 305181430Sstas oldcpu = td->td_oncpu; 306181430Sstas is_bound = cpu_sched_is_bound(td); 307181430Sstas set_cpu(cpu, td); 308181430Sstas critical_enter(); 309181430Sstas rdmsr_safe(MSR_BIOS_SIGN, &rev0); /* Get current micorcode revision. */ 310181430Sstas 311181430Sstas /* 312181430Sstas * Perform update. 313181430Sstas */ 314181430Sstas wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uintptr_t)(ptr)); 315181430Sstas wrmsr_safe(MSR_BIOS_SIGN, 0); 316181430Sstas 317181430Sstas /* 318181430Sstas * Serialize instruction flow. 319181430Sstas */ 320181430Sstas do_cpuid(0, tmp); 321181430Sstas critical_exit(); 322181430Sstas rdmsr_safe(MSR_BIOS_SIGN, &rev1); /* Get new micorcode revision. */ 323181430Sstas restore_cpu(oldcpu, is_bound, td); 324181430Sstas if (rev1 > rev0) 325181430Sstas ret = 0; 326181430Sstas else 327181430Sstas ret = EEXIST; 328181430Sstasfail: 329181430Sstas if (ptr != NULL) 330181430Sstas contigfree(ptr, args->size, M_CPUCTL); 331181430Sstas return (ret); 332181430Sstas} 333181430Sstas 334181430Sstasstatic int 335181430Sstasupdate_amd(int cpu, cpuctl_update_args_t *args, struct thread *td) 336181430Sstas{ 337181430Sstas void *ptr = NULL; 338181430Sstas uint32_t tmp[4]; 339181430Sstas int is_bound = 0; 340181430Sstas int oldcpu; 341181430Sstas int ret; 342181430Sstas 343181430Sstas if (args->size == 0 || args->data == NULL) { 344181430Sstas DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__); 345181430Sstas return (EINVAL); 346181430Sstas } 347181430Sstas if (args->size > UCODE_SIZE_MAX) { 348181430Sstas DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__); 349181430Sstas return (EINVAL); 350181430Sstas } 351181430Sstas /* 352181430Sstas * XXX Might not require contignous address space - needs check 353181430Sstas */ 354181430Sstas ptr = contigmalloc(args->size, M_CPUCTL, 0, 0, 0xffffffff, 16, 0); 355181430Sstas if (ptr == NULL) { 356181430Sstas DPRINTF("[cpuctl,%d]: cannot allocate %zd bytes of memory", 357181430Sstas __LINE__, args->size); 358181430Sstas return (ENOMEM); 359181430Sstas } 360181430Sstas if (copyin(args->data, ptr, args->size) != 0) { 361181430Sstas DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed", 362181430Sstas __LINE__, args->data, ptr, args->size); 363181430Sstas ret = EFAULT; 364181430Sstas goto fail; 365181430Sstas } 366181430Sstas oldcpu = td->td_oncpu; 367181430Sstas is_bound = cpu_sched_is_bound(td); 368181430Sstas set_cpu(cpu, td); 369181430Sstas critical_enter(); 370181430Sstas 371181430Sstas /* 372181430Sstas * Perform update. 373181430Sstas */ 374195081Sstas wrmsr_safe(MSR_K8_UCODE_UPDATE, (uintptr_t)ptr); 375181430Sstas 376181430Sstas /* 377181430Sstas * Serialize instruction flow. 378181430Sstas */ 379181430Sstas do_cpuid(0, tmp); 380181430Sstas critical_exit(); 381181430Sstas restore_cpu(oldcpu, is_bound, td); 382181430Sstas ret = 0; 383181430Sstasfail: 384181430Sstas if (ptr != NULL) 385181430Sstas contigfree(ptr, args->size, M_CPUCTL); 386181430Sstas return (ret); 387181430Sstas} 388181430Sstas 389181430Sstasint 390181430Sstascpuctl_open(struct cdev *dev, int flags, int fmt __unused, struct thread *td) 391181430Sstas{ 392181430Sstas int ret = 0; 393181430Sstas int cpu; 394181430Sstas 395183397Sed cpu = dev2unit(dev); 396181430Sstas if (cpu >= mp_ncpus || !cpu_enabled(cpu)) { 397181430Sstas DPRINTF("[cpuctl,%d]: incorrect cpu number %d\n", __LINE__, 398181430Sstas cpu); 399181430Sstas return (ENXIO); 400181430Sstas } 401181430Sstas if (flags & FWRITE) 402181430Sstas ret = securelevel_gt(td->td_ucred, 0); 403181430Sstas return (ret); 404181430Sstas} 405181430Sstas 406181430Sstasstatic int 407181430Sstascpuctl_modevent(module_t mod __unused, int type, void *data __unused) 408181430Sstas{ 409181430Sstas int cpu; 410181430Sstas 411181430Sstas switch(type) { 412181430Sstas case MOD_LOAD: 413181430Sstas if ((cpu_feature & CPUID_MSR) == 0) { 414181430Sstas if (bootverbose) 415181430Sstas printf("cpuctl: not available.\n"); 416181430Sstas return (ENODEV); 417181430Sstas } 418181430Sstas if (bootverbose) 419181430Sstas printf("cpuctl: access to MSR registers/cpuid info.\n"); 420181430Sstas cpuctl_devs = (struct cdev **)malloc(sizeof(void *) * mp_ncpus, 421181430Sstas M_CPUCTL, M_WAITOK | M_ZERO); 422181430Sstas if (cpuctl_devs == NULL) { 423181430Sstas DPRINTF("[cpuctl,%d]: cannot allocate memory\n", 424181430Sstas __LINE__); 425181430Sstas return (ENOMEM); 426181430Sstas } 427181430Sstas for (cpu = 0; cpu < mp_ncpus; cpu++) 428181430Sstas if (cpu_enabled(cpu)) 429181430Sstas cpuctl_devs[cpu] = make_dev(&cpuctl_cdevsw, cpu, 430181430Sstas UID_ROOT, GID_KMEM, 0640, "cpuctl%d", cpu); 431181430Sstas break; 432181430Sstas case MOD_UNLOAD: 433181430Sstas for (cpu = 0; cpu < mp_ncpus; cpu++) { 434181430Sstas if (cpuctl_devs[cpu] != NULL) 435181430Sstas destroy_dev(cpuctl_devs[cpu]); 436181430Sstas } 437181430Sstas free(cpuctl_devs, M_CPUCTL); 438181430Sstas break; 439181430Sstas case MOD_SHUTDOWN: 440181430Sstas break; 441181430Sstas default: 442181430Sstas return (EOPNOTSUPP); 443181430Sstas } 444181430Sstas return (0); 445181430Sstas} 446181430Sstas 447181430SstasDEV_MODULE(cpuctl, cpuctl_modevent, NULL); 448181430SstasMODULE_VERSION(cpuctl, CPUCTL_VERSION); 449