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$"); 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); 77229471Sfabientstatic int update_via(int cpu, cpuctl_update_args_t *args, 78229471Sfabient struct thread *td); 79181430Sstas 80181430Sstasstatic struct cdev **cpuctl_devs; 81181430Sstasstatic MALLOC_DEFINE(M_CPUCTL, "cpuctl", "CPUCTL buffer"); 82181430Sstas 83181430Sstasstatic struct cdevsw cpuctl_cdevsw = { 84181430Sstas .d_version = D_VERSION, 85181430Sstas .d_open = cpuctl_open, 86181430Sstas .d_ioctl = cpuctl_ioctl, 87181430Sstas .d_name = "cpuctl", 88181430Sstas}; 89181430Sstas 90181430Sstas/* 91181430Sstas * This function checks if specified cpu enabled or not. 92181430Sstas */ 93181430Sstasstatic int 94181430Sstascpu_enabled(int cpu) 95181430Sstas{ 96181430Sstas 97181430Sstas return (pmc_cpu_is_disabled(cpu) == 0); 98181430Sstas} 99181430Sstas 100181430Sstas/* 101181430Sstas * Check if the current thread is bound to a specific cpu. 102181430Sstas */ 103181430Sstasstatic int 104181430Sstascpu_sched_is_bound(struct thread *td) 105181430Sstas{ 106181430Sstas int ret; 107181430Sstas 108181430Sstas thread_lock(td); 109181430Sstas ret = sched_is_bound(td); 110181430Sstas thread_unlock(td); 111181430Sstas return (ret); 112181430Sstas} 113181430Sstas 114181430Sstas/* 115181430Sstas * Switch to target cpu to run. 116181430Sstas */ 117181430Sstasstatic void 118181430Sstasset_cpu(int cpu, struct thread *td) 119181430Sstas{ 120181430Sstas 121181430Sstas KASSERT(cpu >= 0 && cpu < mp_ncpus && cpu_enabled(cpu), 122181430Sstas ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu)); 123181430Sstas thread_lock(td); 124181430Sstas sched_bind(td, cpu); 125181430Sstas thread_unlock(td); 126181430Sstas KASSERT(td->td_oncpu == cpu, 127181430Sstas ("[cpuctl,%d]: cannot bind to target cpu %d", __LINE__, cpu)); 128181430Sstas} 129181430Sstas 130181430Sstasstatic void 131181430Sstasrestore_cpu(int oldcpu, int is_bound, struct thread *td) 132181430Sstas{ 133181430Sstas 134181430Sstas KASSERT(oldcpu >= 0 && oldcpu < mp_ncpus && cpu_enabled(oldcpu), 135181430Sstas ("[cpuctl,%d]: bad cpu number %d", __LINE__, oldcpu)); 136181430Sstas thread_lock(td); 137181430Sstas if (is_bound == 0) 138181430Sstas sched_unbind(td); 139181430Sstas else 140181430Sstas sched_bind(td, oldcpu); 141181430Sstas thread_unlock(td); 142181430Sstas} 143181430Sstas 144181430Sstasint 145181430Sstascpuctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, 146181430Sstas int flags, struct thread *td) 147181430Sstas{ 148181430Sstas int ret; 149183397Sed int cpu = dev2unit(dev); 150181430Sstas 151181430Sstas if (cpu >= mp_ncpus || !cpu_enabled(cpu)) { 152181430Sstas DPRINTF("[cpuctl,%d]: bad cpu number %d\n", __LINE__, cpu); 153181430Sstas return (ENXIO); 154181430Sstas } 155181430Sstas /* Require write flag for "write" requests. */ 156181430Sstas if ((cmd == CPUCTL_WRMSR || cmd == CPUCTL_UPDATE) && 157181430Sstas ((flags & FWRITE) == 0)) 158181430Sstas return (EPERM); 159181430Sstas switch (cmd) { 160181430Sstas case CPUCTL_RDMSR: 161181430Sstas ret = cpuctl_do_msr(cpu, (cpuctl_msr_args_t *)data, cmd, td); 162181430Sstas break; 163195189Sstas case CPUCTL_MSRSBIT: 164195189Sstas case CPUCTL_MSRCBIT: 165181430Sstas case CPUCTL_WRMSR: 166181430Sstas ret = priv_check(td, PRIV_CPUCTL_WRMSR); 167181430Sstas if (ret != 0) 168181430Sstas goto fail; 169181430Sstas ret = cpuctl_do_msr(cpu, (cpuctl_msr_args_t *)data, cmd, td); 170181430Sstas break; 171181430Sstas case CPUCTL_CPUID: 172181430Sstas ret = cpuctl_do_cpuid(cpu, (cpuctl_cpuid_args_t *)data, td); 173181430Sstas break; 174181430Sstas case CPUCTL_UPDATE: 175181430Sstas ret = priv_check(td, PRIV_CPUCTL_UPDATE); 176181430Sstas if (ret != 0) 177181430Sstas goto fail; 178181430Sstas ret = cpuctl_do_update(cpu, (cpuctl_update_args_t *)data, td); 179181430Sstas break; 180181430Sstas default: 181181430Sstas ret = EINVAL; 182181430Sstas break; 183181430Sstas } 184181430Sstasfail: 185181430Sstas return (ret); 186181430Sstas} 187181430Sstas 188181430Sstas/* 189181430Sstas * Actually perform cpuid operation. 190181430Sstas */ 191181430Sstasstatic int 192181430Sstascpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data, struct thread *td) 193181430Sstas{ 194181430Sstas int is_bound = 0; 195181430Sstas int oldcpu; 196181430Sstas 197181430Sstas KASSERT(cpu >= 0 && cpu < mp_ncpus, 198181430Sstas ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu)); 199181430Sstas 200181430Sstas /* Explicitly clear cpuid data to avoid returning stale info. */ 201181430Sstas bzero(data->data, sizeof(data->data)); 202181430Sstas DPRINTF("[cpuctl,%d]: retriving cpuid level %#0x for %d cpu\n", 203181430Sstas __LINE__, data->level, cpu); 204181430Sstas oldcpu = td->td_oncpu; 205181430Sstas is_bound = cpu_sched_is_bound(td); 206181430Sstas set_cpu(cpu, td); 207242860Savg cpuid_count(data->level, 0, data->data); 208181430Sstas restore_cpu(oldcpu, is_bound, td); 209181430Sstas return (0); 210181430Sstas} 211181430Sstas 212181430Sstas/* 213181430Sstas * Actually perform MSR operations. 214181430Sstas */ 215181430Sstasstatic int 216181430Sstascpuctl_do_msr(int cpu, cpuctl_msr_args_t *data, u_long cmd, struct thread *td) 217181430Sstas{ 218195189Sstas uint64_t reg; 219181430Sstas int is_bound = 0; 220181430Sstas int oldcpu; 221181430Sstas int ret; 222181430Sstas 223181430Sstas KASSERT(cpu >= 0 && cpu < mp_ncpus, 224181430Sstas ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu)); 225181430Sstas 226181430Sstas /* 227181430Sstas * Explicitly clear cpuid data to avoid returning stale 228181430Sstas * info 229181430Sstas */ 230181430Sstas DPRINTF("[cpuctl,%d]: operating on MSR %#0x for %d cpu\n", __LINE__, 231181430Sstas data->msr, cpu); 232181430Sstas oldcpu = td->td_oncpu; 233181430Sstas is_bound = cpu_sched_is_bound(td); 234181430Sstas set_cpu(cpu, td); 235195081Sstas if (cmd == CPUCTL_RDMSR) { 236195081Sstas data->data = 0; 237195081Sstas ret = rdmsr_safe(data->msr, &data->data); 238195189Sstas } else if (cmd == CPUCTL_WRMSR) { 239195081Sstas ret = wrmsr_safe(data->msr, data->data); 240195189Sstas } else if (cmd == CPUCTL_MSRSBIT) { 241195189Sstas critical_enter(); 242195189Sstas ret = rdmsr_safe(data->msr, ®); 243195189Sstas if (ret == 0) 244195189Sstas ret = wrmsr_safe(data->msr, reg | data->data); 245195189Sstas critical_exit(); 246195189Sstas } else if (cmd == CPUCTL_MSRCBIT) { 247195189Sstas critical_enter(); 248195189Sstas ret = rdmsr_safe(data->msr, ®); 249195189Sstas if (ret == 0) 250195189Sstas ret = wrmsr_safe(data->msr, reg & ~data->data); 251195189Sstas critical_exit(); 252195189Sstas } else 253195189Sstas panic("[cpuctl,%d]: unknown operation requested: %lu", __LINE__, cmd); 254181430Sstas restore_cpu(oldcpu, is_bound, td); 255181430Sstas return (ret); 256181430Sstas} 257181430Sstas 258181430Sstas/* 259181430Sstas * Actually perform microcode update. 260181430Sstas */ 261181430Sstasstatic int 262181430Sstascpuctl_do_update(int cpu, cpuctl_update_args_t *data, struct thread *td) 263181430Sstas{ 264181430Sstas cpuctl_cpuid_args_t args = { 265181430Sstas .level = 0, 266181430Sstas }; 267181430Sstas char vendor[13]; 268181430Sstas int ret; 269181430Sstas 270181430Sstas KASSERT(cpu >= 0 && cpu < mp_ncpus, 271181430Sstas ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu)); 272181430Sstas DPRINTF("[cpuctl,%d]: XXX %d", __LINE__, cpu); 273181430Sstas 274181430Sstas ret = cpuctl_do_cpuid(cpu, &args, td); 275181430Sstas if (ret != 0) { 276181430Sstas DPRINTF("[cpuctl,%d]: cannot retrive cpuid info for cpu %d", 277181430Sstas __LINE__, cpu); 278181430Sstas return (ENXIO); 279181430Sstas } 280181430Sstas ((uint32_t *)vendor)[0] = args.data[1]; 281181430Sstas ((uint32_t *)vendor)[1] = args.data[3]; 282181430Sstas ((uint32_t *)vendor)[2] = args.data[2]; 283181430Sstas vendor[12] = '\0'; 284181430Sstas if (strncmp(vendor, INTEL_VENDOR_ID, sizeof(INTEL_VENDOR_ID)) == 0) 285181430Sstas ret = update_intel(cpu, data, td); 286229471Sfabient else if(strncmp(vendor, AMD_VENDOR_ID, sizeof(AMD_VENDOR_ID)) == 0) 287181430Sstas ret = update_amd(cpu, data, td); 288229471Sfabient else if(strncmp(vendor, CENTAUR_VENDOR_ID, sizeof(CENTAUR_VENDOR_ID)) == 0) 289229471Sfabient ret = update_via(cpu, data, td); 290181430Sstas else 291181430Sstas ret = ENXIO; 292181430Sstas return (ret); 293181430Sstas} 294181430Sstas 295181430Sstasstatic int 296181430Sstasupdate_intel(int cpu, cpuctl_update_args_t *args, struct thread *td) 297181430Sstas{ 298255507Skib void *ptr; 299181430Sstas uint64_t rev0, rev1; 300181430Sstas uint32_t tmp[4]; 301255507Skib int is_bound; 302181430Sstas int oldcpu; 303181430Sstas int ret; 304181430Sstas 305181430Sstas if (args->size == 0 || args->data == NULL) { 306181430Sstas DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__); 307181430Sstas return (EINVAL); 308181430Sstas } 309181430Sstas if (args->size > UCODE_SIZE_MAX) { 310181430Sstas DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__); 311181430Sstas return (EINVAL); 312181430Sstas } 313181430Sstas 314181430Sstas /* 315255507Skib * 16 byte alignment required. Rely on the fact that 316255507Skib * malloc(9) always returns the pointer aligned at least on 317255507Skib * the size of the allocation. 318181430Sstas */ 319181430Sstas ptr = malloc(args->size + 16, M_CPUCTL, M_WAITOK); 320181430Sstas if (copyin(args->data, ptr, args->size) != 0) { 321181430Sstas DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed", 322181430Sstas __LINE__, args->data, ptr, args->size); 323181430Sstas ret = EFAULT; 324181430Sstas goto fail; 325181430Sstas } 326181430Sstas oldcpu = td->td_oncpu; 327181430Sstas is_bound = cpu_sched_is_bound(td); 328181430Sstas set_cpu(cpu, td); 329181430Sstas critical_enter(); 330181430Sstas rdmsr_safe(MSR_BIOS_SIGN, &rev0); /* Get current micorcode revision. */ 331181430Sstas 332181430Sstas /* 333181430Sstas * Perform update. 334181430Sstas */ 335181430Sstas wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uintptr_t)(ptr)); 336181430Sstas wrmsr_safe(MSR_BIOS_SIGN, 0); 337181430Sstas 338181430Sstas /* 339181430Sstas * Serialize instruction flow. 340181430Sstas */ 341181430Sstas do_cpuid(0, tmp); 342181430Sstas critical_exit(); 343181430Sstas rdmsr_safe(MSR_BIOS_SIGN, &rev1); /* Get new micorcode revision. */ 344181430Sstas restore_cpu(oldcpu, is_bound, td); 345181430Sstas if (rev1 > rev0) 346181430Sstas ret = 0; 347181430Sstas else 348181430Sstas ret = EEXIST; 349181430Sstasfail: 350254442Skib free(ptr, M_CPUCTL); 351181430Sstas return (ret); 352181430Sstas} 353181430Sstas 354181430Sstasstatic int 355181430Sstasupdate_amd(int cpu, cpuctl_update_args_t *args, struct thread *td) 356181430Sstas{ 357181430Sstas void *ptr = NULL; 358181430Sstas uint32_t tmp[4]; 359181430Sstas int is_bound = 0; 360181430Sstas int oldcpu; 361181430Sstas int ret; 362181430Sstas 363181430Sstas if (args->size == 0 || args->data == NULL) { 364181430Sstas DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__); 365181430Sstas return (EINVAL); 366181430Sstas } 367181430Sstas if (args->size > UCODE_SIZE_MAX) { 368181430Sstas DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__); 369181430Sstas return (EINVAL); 370181430Sstas } 371181430Sstas /* 372181430Sstas * XXX Might not require contignous address space - needs check 373181430Sstas */ 374181430Sstas ptr = contigmalloc(args->size, M_CPUCTL, 0, 0, 0xffffffff, 16, 0); 375181430Sstas if (ptr == NULL) { 376181430Sstas DPRINTF("[cpuctl,%d]: cannot allocate %zd bytes of memory", 377181430Sstas __LINE__, args->size); 378181430Sstas return (ENOMEM); 379181430Sstas } 380181430Sstas if (copyin(args->data, ptr, args->size) != 0) { 381181430Sstas DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed", 382181430Sstas __LINE__, args->data, ptr, args->size); 383181430Sstas ret = EFAULT; 384181430Sstas goto fail; 385181430Sstas } 386181430Sstas oldcpu = td->td_oncpu; 387181430Sstas is_bound = cpu_sched_is_bound(td); 388181430Sstas set_cpu(cpu, td); 389181430Sstas critical_enter(); 390181430Sstas 391181430Sstas /* 392181430Sstas * Perform update. 393181430Sstas */ 394195081Sstas wrmsr_safe(MSR_K8_UCODE_UPDATE, (uintptr_t)ptr); 395181430Sstas 396181430Sstas /* 397181430Sstas * Serialize instruction flow. 398181430Sstas */ 399181430Sstas do_cpuid(0, tmp); 400181430Sstas critical_exit(); 401181430Sstas restore_cpu(oldcpu, is_bound, td); 402181430Sstas ret = 0; 403181430Sstasfail: 404181430Sstas if (ptr != NULL) 405181430Sstas contigfree(ptr, args->size, M_CPUCTL); 406181430Sstas return (ret); 407181430Sstas} 408181430Sstas 409229471Sfabientstatic int 410229471Sfabientupdate_via(int cpu, cpuctl_update_args_t *args, struct thread *td) 411229471Sfabient{ 412255507Skib void *ptr; 413229471Sfabient uint64_t rev0, rev1, res; 414229471Sfabient uint32_t tmp[4]; 415255507Skib int is_bound; 416229471Sfabient int oldcpu; 417229471Sfabient int ret; 418229471Sfabient 419229471Sfabient if (args->size == 0 || args->data == NULL) { 420229471Sfabient DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__); 421229471Sfabient return (EINVAL); 422229471Sfabient } 423229471Sfabient if (args->size > UCODE_SIZE_MAX) { 424229471Sfabient DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__); 425229471Sfabient return (EINVAL); 426229471Sfabient } 427229471Sfabient 428229471Sfabient /* 429229471Sfabient * 4 byte alignment required. 430229471Sfabient */ 431255507Skib ptr = malloc(args->size, M_CPUCTL, M_WAITOK); 432229471Sfabient if (copyin(args->data, ptr, args->size) != 0) { 433229471Sfabient DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed", 434229471Sfabient __LINE__, args->data, ptr, args->size); 435229471Sfabient ret = EFAULT; 436229471Sfabient goto fail; 437229471Sfabient } 438229471Sfabient oldcpu = td->td_oncpu; 439229471Sfabient is_bound = cpu_sched_is_bound(td); 440229471Sfabient set_cpu(cpu, td); 441229471Sfabient critical_enter(); 442229471Sfabient rdmsr_safe(MSR_BIOS_SIGN, &rev0); /* Get current micorcode revision. */ 443229471Sfabient 444229471Sfabient /* 445229471Sfabient * Perform update. 446229471Sfabient */ 447229471Sfabient wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uintptr_t)(ptr)); 448229471Sfabient do_cpuid(1, tmp); 449229471Sfabient 450229471Sfabient /* 451229471Sfabient * Result are in low byte of MSR FCR5: 452229471Sfabient * 0x00: No update has been attempted since RESET. 453229471Sfabient * 0x01: The last attempted update was successful. 454229471Sfabient * 0x02: The last attempted update was unsuccessful due to a bad 455229471Sfabient * environment. No update was loaded and any preexisting 456229471Sfabient * patches are still active. 457229471Sfabient * 0x03: The last attempted update was not applicable to this processor. 458229471Sfabient * No update was loaded and any preexisting patches are still 459229471Sfabient * active. 460229471Sfabient * 0x04: The last attempted update was not successful due to an invalid 461229471Sfabient * update data block. No update was loaded and any preexisting 462229471Sfabient * patches are still active 463229471Sfabient */ 464229471Sfabient rdmsr_safe(0x1205, &res); 465229471Sfabient res &= 0xff; 466229471Sfabient critical_exit(); 467229471Sfabient rdmsr_safe(MSR_BIOS_SIGN, &rev1); /* Get new microcode revision. */ 468229471Sfabient restore_cpu(oldcpu, is_bound, td); 469229471Sfabient 470229471Sfabient DPRINTF("[cpu,%d]: rev0=%x rev1=%x res=%x\n", __LINE__, 471229471Sfabient (unsigned)(rev0 >> 32), (unsigned)(rev1 >> 32), (unsigned)res); 472229471Sfabient 473229471Sfabient if (res != 0x01) 474229471Sfabient ret = EINVAL; 475229471Sfabient else 476229471Sfabient ret = 0; 477229471Sfabientfail: 478254442Skib free(ptr, M_CPUCTL); 479229471Sfabient return (ret); 480229471Sfabient} 481229471Sfabient 482181430Sstasint 483181430Sstascpuctl_open(struct cdev *dev, int flags, int fmt __unused, struct thread *td) 484181430Sstas{ 485181430Sstas int ret = 0; 486181430Sstas int cpu; 487181430Sstas 488183397Sed cpu = dev2unit(dev); 489181430Sstas if (cpu >= mp_ncpus || !cpu_enabled(cpu)) { 490181430Sstas DPRINTF("[cpuctl,%d]: incorrect cpu number %d\n", __LINE__, 491181430Sstas cpu); 492181430Sstas return (ENXIO); 493181430Sstas } 494181430Sstas if (flags & FWRITE) 495181430Sstas ret = securelevel_gt(td->td_ucred, 0); 496181430Sstas return (ret); 497181430Sstas} 498181430Sstas 499181430Sstasstatic int 500181430Sstascpuctl_modevent(module_t mod __unused, int type, void *data __unused) 501181430Sstas{ 502181430Sstas int cpu; 503181430Sstas 504181430Sstas switch(type) { 505181430Sstas case MOD_LOAD: 506181430Sstas if ((cpu_feature & CPUID_MSR) == 0) { 507181430Sstas if (bootverbose) 508181430Sstas printf("cpuctl: not available.\n"); 509181430Sstas return (ENODEV); 510181430Sstas } 511181430Sstas if (bootverbose) 512181430Sstas printf("cpuctl: access to MSR registers/cpuid info.\n"); 513263358Skib cpuctl_devs = malloc(sizeof(*cpuctl_devs) * mp_ncpus, M_CPUCTL, 514263358Skib M_WAITOK | M_ZERO); 515181430Sstas for (cpu = 0; cpu < mp_ncpus; cpu++) 516181430Sstas if (cpu_enabled(cpu)) 517181430Sstas cpuctl_devs[cpu] = make_dev(&cpuctl_cdevsw, cpu, 518181430Sstas UID_ROOT, GID_KMEM, 0640, "cpuctl%d", cpu); 519181430Sstas break; 520181430Sstas case MOD_UNLOAD: 521181430Sstas for (cpu = 0; cpu < mp_ncpus; cpu++) { 522181430Sstas if (cpuctl_devs[cpu] != NULL) 523181430Sstas destroy_dev(cpuctl_devs[cpu]); 524181430Sstas } 525181430Sstas free(cpuctl_devs, M_CPUCTL); 526181430Sstas break; 527181430Sstas case MOD_SHUTDOWN: 528181430Sstas break; 529181430Sstas default: 530181430Sstas return (EOPNOTSUPP); 531181430Sstas } 532181430Sstas return (0); 533181430Sstas} 534181430Sstas 535181430SstasDEV_MODULE(cpuctl, cpuctl_modevent, NULL); 536181430SstasMODULE_VERSION(cpuctl, CPUCTL_VERSION); 537