cpuctl.c revision 302372
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 302372 2016-07-06 14:09:49Z nwhitehorn $"); 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 66275960Skib#define UCODE_SIZE_MAX (32 * 1024) 67181430Sstas 68181430Sstasstatic int cpuctl_do_msr(int cpu, cpuctl_msr_args_t *data, u_long cmd, 69181430Sstas struct thread *td); 70301962Skibstatic int cpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data, 71181430Sstas struct thread *td); 72301962Skibstatic int cpuctl_do_cpuid_count(int cpu, cpuctl_cpuid_count_args_t *data, 73267651Sattilio struct thread *td); 74181430Sstasstatic int cpuctl_do_update(int cpu, cpuctl_update_args_t *data, 75181430Sstas struct thread *td); 76181430Sstasstatic int update_intel(int cpu, cpuctl_update_args_t *args, 77181430Sstas struct thread *td); 78181430Sstasstatic int update_amd(int cpu, cpuctl_update_args_t *args, struct thread *td); 79228436Sfabientstatic int update_via(int cpu, cpuctl_update_args_t *args, 80228436Sfabient struct thread *td); 81181430Sstas 82181430Sstasstatic struct cdev **cpuctl_devs; 83181430Sstasstatic MALLOC_DEFINE(M_CPUCTL, "cpuctl", "CPUCTL buffer"); 84181430Sstas 85181430Sstasstatic struct cdevsw cpuctl_cdevsw = { 86181430Sstas .d_version = D_VERSION, 87181430Sstas .d_open = cpuctl_open, 88181430Sstas .d_ioctl = cpuctl_ioctl, 89181430Sstas .d_name = "cpuctl", 90181430Sstas}; 91181430Sstas 92181430Sstas/* 93181430Sstas * This function checks if specified cpu enabled or not. 94181430Sstas */ 95181430Sstasstatic int 96181430Sstascpu_enabled(int cpu) 97181430Sstas{ 98181430Sstas 99181430Sstas return (pmc_cpu_is_disabled(cpu) == 0); 100181430Sstas} 101181430Sstas 102181430Sstas/* 103181430Sstas * Check if the current thread is bound to a specific cpu. 104181430Sstas */ 105181430Sstasstatic int 106181430Sstascpu_sched_is_bound(struct thread *td) 107181430Sstas{ 108181430Sstas int ret; 109181430Sstas 110181430Sstas thread_lock(td); 111181430Sstas ret = sched_is_bound(td); 112181430Sstas thread_unlock(td); 113181430Sstas return (ret); 114181430Sstas} 115181430Sstas 116181430Sstas/* 117181430Sstas * Switch to target cpu to run. 118181430Sstas */ 119181430Sstasstatic void 120181430Sstasset_cpu(int cpu, struct thread *td) 121181430Sstas{ 122181430Sstas 123302372Snwhitehorn KASSERT(cpu >= 0 && cpu <= mp_maxid && cpu_enabled(cpu), 124181430Sstas ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu)); 125181430Sstas thread_lock(td); 126181430Sstas sched_bind(td, cpu); 127181430Sstas thread_unlock(td); 128181430Sstas KASSERT(td->td_oncpu == cpu, 129300424Sache ("[cpuctl,%d]: cannot bind to target cpu %d on cpu %d", __LINE__, cpu, td->td_oncpu)); 130181430Sstas} 131181430Sstas 132181430Sstasstatic void 133181430Sstasrestore_cpu(int oldcpu, int is_bound, struct thread *td) 134181430Sstas{ 135181430Sstas 136302372Snwhitehorn KASSERT(oldcpu >= 0 && oldcpu <= mp_maxid && cpu_enabled(oldcpu), 137181430Sstas ("[cpuctl,%d]: bad cpu number %d", __LINE__, oldcpu)); 138181430Sstas thread_lock(td); 139181430Sstas if (is_bound == 0) 140181430Sstas sched_unbind(td); 141181430Sstas else 142181430Sstas sched_bind(td, oldcpu); 143181430Sstas thread_unlock(td); 144181430Sstas} 145181430Sstas 146181430Sstasint 147181430Sstascpuctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, 148181430Sstas int flags, struct thread *td) 149181430Sstas{ 150181430Sstas int ret; 151183397Sed int cpu = dev2unit(dev); 152181430Sstas 153302372Snwhitehorn if (cpu > mp_maxid || !cpu_enabled(cpu)) { 154181430Sstas DPRINTF("[cpuctl,%d]: bad cpu number %d\n", __LINE__, cpu); 155181430Sstas return (ENXIO); 156181430Sstas } 157181430Sstas /* Require write flag for "write" requests. */ 158181430Sstas if ((cmd == CPUCTL_WRMSR || cmd == CPUCTL_UPDATE) && 159181430Sstas ((flags & FWRITE) == 0)) 160181430Sstas return (EPERM); 161181430Sstas switch (cmd) { 162181430Sstas case CPUCTL_RDMSR: 163181430Sstas ret = cpuctl_do_msr(cpu, (cpuctl_msr_args_t *)data, cmd, td); 164181430Sstas break; 165195189Sstas case CPUCTL_MSRSBIT: 166195189Sstas case CPUCTL_MSRCBIT: 167181430Sstas case CPUCTL_WRMSR: 168181430Sstas ret = priv_check(td, PRIV_CPUCTL_WRMSR); 169181430Sstas if (ret != 0) 170181430Sstas goto fail; 171181430Sstas ret = cpuctl_do_msr(cpu, (cpuctl_msr_args_t *)data, cmd, td); 172181430Sstas break; 173181430Sstas case CPUCTL_CPUID: 174301962Skib ret = cpuctl_do_cpuid(cpu, (cpuctl_cpuid_args_t *)data, td); 175181430Sstas break; 176181430Sstas case CPUCTL_UPDATE: 177181430Sstas ret = priv_check(td, PRIV_CPUCTL_UPDATE); 178181430Sstas if (ret != 0) 179181430Sstas goto fail; 180181430Sstas ret = cpuctl_do_update(cpu, (cpuctl_update_args_t *)data, td); 181181430Sstas break; 182267651Sattilio case CPUCTL_CPUID_COUNT: 183301962Skib ret = cpuctl_do_cpuid_count(cpu, 184301962Skib (cpuctl_cpuid_count_args_t *)data, td); 185267651Sattilio break; 186181430Sstas default: 187181430Sstas ret = EINVAL; 188181430Sstas break; 189181430Sstas } 190181430Sstasfail: 191181430Sstas return (ret); 192181430Sstas} 193181430Sstas 194181430Sstas/* 195181430Sstas * Actually perform cpuid operation. 196181430Sstas */ 197301962Skibstatic int 198267673Skibcpuctl_do_cpuid_count(int cpu, cpuctl_cpuid_count_args_t *data, 199267673Skib struct thread *td) 200181430Sstas{ 201181430Sstas int is_bound = 0; 202181430Sstas int oldcpu; 203181430Sstas 204302372Snwhitehorn KASSERT(cpu >= 0 && cpu <= mp_maxid, 205181430Sstas ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu)); 206181430Sstas 207181430Sstas /* Explicitly clear cpuid data to avoid returning stale info. */ 208181430Sstas bzero(data->data, sizeof(data->data)); 209267651Sattilio DPRINTF("[cpuctl,%d]: retrieving cpuid lev %#0x type %#0x for %d cpu\n", 210267651Sattilio __LINE__, data->level, data->level_type, cpu); 211301962Skib#ifdef __i386__ 212301962Skib if (cpu_id == 0) 213301962Skib return (ENODEV); 214301962Skib#endif 215181430Sstas oldcpu = td->td_oncpu; 216181430Sstas is_bound = cpu_sched_is_bound(td); 217181430Sstas set_cpu(cpu, td); 218267651Sattilio cpuid_count(data->level, data->level_type, data->data); 219181430Sstas restore_cpu(oldcpu, is_bound, td); 220301962Skib return (0); 221181430Sstas} 222181430Sstas 223301962Skibstatic int 224267651Sattiliocpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data, struct thread *td) 225267651Sattilio{ 226267673Skib cpuctl_cpuid_count_args_t cdata; 227301962Skib int error; 228267651Sattilio 229267673Skib cdata.level = data->level; 230267651Sattilio /* Override the level type. */ 231267673Skib cdata.level_type = 0; 232301962Skib error = cpuctl_do_cpuid_count(cpu, &cdata, td); 233267673Skib bcopy(cdata.data, data->data, sizeof(data->data)); /* Ignore error */ 234301962Skib return (error); 235267651Sattilio} 236267651Sattilio 237181430Sstas/* 238181430Sstas * Actually perform MSR operations. 239181430Sstas */ 240181430Sstasstatic int 241181430Sstascpuctl_do_msr(int cpu, cpuctl_msr_args_t *data, u_long cmd, struct thread *td) 242181430Sstas{ 243195189Sstas uint64_t reg; 244181430Sstas int is_bound = 0; 245181430Sstas int oldcpu; 246181430Sstas int ret; 247181430Sstas 248302372Snwhitehorn KASSERT(cpu >= 0 && cpu <= mp_maxid, 249181430Sstas ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu)); 250181430Sstas 251181430Sstas /* 252181430Sstas * Explicitly clear cpuid data to avoid returning stale 253181430Sstas * info 254181430Sstas */ 255181430Sstas DPRINTF("[cpuctl,%d]: operating on MSR %#0x for %d cpu\n", __LINE__, 256181430Sstas data->msr, cpu); 257301962Skib#ifdef __i386__ 258301962Skib if ((cpu_feature & CPUID_MSR) == 0) 259301962Skib return (ENODEV); 260301962Skib#endif 261181430Sstas oldcpu = td->td_oncpu; 262181430Sstas is_bound = cpu_sched_is_bound(td); 263181430Sstas set_cpu(cpu, td); 264195081Sstas if (cmd == CPUCTL_RDMSR) { 265195081Sstas data->data = 0; 266195081Sstas ret = rdmsr_safe(data->msr, &data->data); 267195189Sstas } else if (cmd == CPUCTL_WRMSR) { 268195081Sstas ret = wrmsr_safe(data->msr, data->data); 269195189Sstas } else if (cmd == CPUCTL_MSRSBIT) { 270195189Sstas critical_enter(); 271195189Sstas ret = rdmsr_safe(data->msr, ®); 272195189Sstas if (ret == 0) 273195189Sstas ret = wrmsr_safe(data->msr, reg | data->data); 274195189Sstas critical_exit(); 275195189Sstas } else if (cmd == CPUCTL_MSRCBIT) { 276195189Sstas critical_enter(); 277195189Sstas ret = rdmsr_safe(data->msr, ®); 278195189Sstas if (ret == 0) 279195189Sstas ret = wrmsr_safe(data->msr, reg & ~data->data); 280195189Sstas critical_exit(); 281195189Sstas } else 282195189Sstas panic("[cpuctl,%d]: unknown operation requested: %lu", __LINE__, cmd); 283181430Sstas restore_cpu(oldcpu, is_bound, td); 284181430Sstas return (ret); 285181430Sstas} 286181430Sstas 287181430Sstas/* 288181430Sstas * Actually perform microcode update. 289181430Sstas */ 290181430Sstasstatic int 291181430Sstascpuctl_do_update(int cpu, cpuctl_update_args_t *data, struct thread *td) 292181430Sstas{ 293181430Sstas cpuctl_cpuid_args_t args = { 294181430Sstas .level = 0, 295181430Sstas }; 296181430Sstas char vendor[13]; 297181430Sstas int ret; 298181430Sstas 299302372Snwhitehorn KASSERT(cpu >= 0 && cpu <= mp_maxid, 300181430Sstas ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu)); 301181430Sstas DPRINTF("[cpuctl,%d]: XXX %d", __LINE__, cpu); 302181430Sstas 303301962Skib ret = cpuctl_do_cpuid(cpu, &args, td); 304301962Skib if (ret != 0) 305301962Skib return (ret); 306181430Sstas ((uint32_t *)vendor)[0] = args.data[1]; 307181430Sstas ((uint32_t *)vendor)[1] = args.data[3]; 308181430Sstas ((uint32_t *)vendor)[2] = args.data[2]; 309181430Sstas vendor[12] = '\0'; 310181430Sstas if (strncmp(vendor, INTEL_VENDOR_ID, sizeof(INTEL_VENDOR_ID)) == 0) 311181430Sstas ret = update_intel(cpu, data, td); 312228436Sfabient else if(strncmp(vendor, AMD_VENDOR_ID, sizeof(AMD_VENDOR_ID)) == 0) 313181430Sstas ret = update_amd(cpu, data, td); 314228436Sfabient else if(strncmp(vendor, CENTAUR_VENDOR_ID, sizeof(CENTAUR_VENDOR_ID)) == 0) 315228436Sfabient ret = update_via(cpu, data, td); 316181430Sstas else 317181430Sstas ret = ENXIO; 318181430Sstas return (ret); 319181430Sstas} 320181430Sstas 321181430Sstasstatic int 322181430Sstasupdate_intel(int cpu, cpuctl_update_args_t *args, struct thread *td) 323181430Sstas{ 324255439Skib void *ptr; 325181430Sstas uint64_t rev0, rev1; 326181430Sstas uint32_t tmp[4]; 327255439Skib int is_bound; 328181430Sstas int oldcpu; 329181430Sstas int ret; 330181430Sstas 331181430Sstas if (args->size == 0 || args->data == NULL) { 332181430Sstas DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__); 333181430Sstas return (EINVAL); 334181430Sstas } 335181430Sstas if (args->size > UCODE_SIZE_MAX) { 336181430Sstas DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__); 337181430Sstas return (EINVAL); 338181430Sstas } 339181430Sstas 340181430Sstas /* 341255439Skib * 16 byte alignment required. Rely on the fact that 342255439Skib * malloc(9) always returns the pointer aligned at least on 343255439Skib * the size of the allocation. 344181430Sstas */ 345181430Sstas ptr = malloc(args->size + 16, M_CPUCTL, M_WAITOK); 346181430Sstas if (copyin(args->data, ptr, args->size) != 0) { 347181430Sstas DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed", 348181430Sstas __LINE__, args->data, ptr, args->size); 349181430Sstas ret = EFAULT; 350181430Sstas goto fail; 351181430Sstas } 352181430Sstas oldcpu = td->td_oncpu; 353181430Sstas is_bound = cpu_sched_is_bound(td); 354181430Sstas set_cpu(cpu, td); 355181430Sstas critical_enter(); 356252592Srpaulo rdmsr_safe(MSR_BIOS_SIGN, &rev0); /* Get current microcode revision. */ 357181430Sstas 358181430Sstas /* 359181430Sstas * Perform update. 360181430Sstas */ 361181430Sstas wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uintptr_t)(ptr)); 362181430Sstas wrmsr_safe(MSR_BIOS_SIGN, 0); 363181430Sstas 364181430Sstas /* 365181430Sstas * Serialize instruction flow. 366181430Sstas */ 367181430Sstas do_cpuid(0, tmp); 368181430Sstas critical_exit(); 369252592Srpaulo rdmsr_safe(MSR_BIOS_SIGN, &rev1); /* Get new microcode revision. */ 370181430Sstas restore_cpu(oldcpu, is_bound, td); 371181430Sstas if (rev1 > rev0) 372181430Sstas ret = 0; 373181430Sstas else 374181430Sstas ret = EEXIST; 375181430Sstasfail: 376254191Skib free(ptr, M_CPUCTL); 377181430Sstas return (ret); 378181430Sstas} 379181430Sstas 380181430Sstasstatic int 381181430Sstasupdate_amd(int cpu, cpuctl_update_args_t *args, struct thread *td) 382181430Sstas{ 383181430Sstas void *ptr = NULL; 384181430Sstas uint32_t tmp[4]; 385181430Sstas int is_bound = 0; 386181430Sstas int oldcpu; 387181430Sstas int ret; 388181430Sstas 389181430Sstas if (args->size == 0 || args->data == NULL) { 390181430Sstas DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__); 391181430Sstas return (EINVAL); 392181430Sstas } 393181430Sstas if (args->size > UCODE_SIZE_MAX) { 394181430Sstas DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__); 395181430Sstas return (EINVAL); 396181430Sstas } 397181430Sstas /* 398181430Sstas * XXX Might not require contignous address space - needs check 399181430Sstas */ 400181430Sstas ptr = contigmalloc(args->size, M_CPUCTL, 0, 0, 0xffffffff, 16, 0); 401181430Sstas if (ptr == NULL) { 402181430Sstas DPRINTF("[cpuctl,%d]: cannot allocate %zd bytes of memory", 403181430Sstas __LINE__, args->size); 404181430Sstas return (ENOMEM); 405181430Sstas } 406181430Sstas if (copyin(args->data, ptr, args->size) != 0) { 407181430Sstas DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed", 408181430Sstas __LINE__, args->data, ptr, args->size); 409181430Sstas ret = EFAULT; 410181430Sstas goto fail; 411181430Sstas } 412181430Sstas oldcpu = td->td_oncpu; 413181430Sstas is_bound = cpu_sched_is_bound(td); 414181430Sstas set_cpu(cpu, td); 415181430Sstas critical_enter(); 416181430Sstas 417181430Sstas /* 418181430Sstas * Perform update. 419181430Sstas */ 420195081Sstas wrmsr_safe(MSR_K8_UCODE_UPDATE, (uintptr_t)ptr); 421181430Sstas 422181430Sstas /* 423181430Sstas * Serialize instruction flow. 424181430Sstas */ 425181430Sstas do_cpuid(0, tmp); 426181430Sstas critical_exit(); 427181430Sstas restore_cpu(oldcpu, is_bound, td); 428181430Sstas ret = 0; 429181430Sstasfail: 430181430Sstas if (ptr != NULL) 431181430Sstas contigfree(ptr, args->size, M_CPUCTL); 432181430Sstas return (ret); 433181430Sstas} 434181430Sstas 435228436Sfabientstatic int 436228436Sfabientupdate_via(int cpu, cpuctl_update_args_t *args, struct thread *td) 437228436Sfabient{ 438255439Skib void *ptr; 439228436Sfabient uint64_t rev0, rev1, res; 440228436Sfabient uint32_t tmp[4]; 441255439Skib int is_bound; 442228436Sfabient int oldcpu; 443228436Sfabient int ret; 444228436Sfabient 445228436Sfabient if (args->size == 0 || args->data == NULL) { 446228436Sfabient DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__); 447228436Sfabient return (EINVAL); 448228436Sfabient } 449228436Sfabient if (args->size > UCODE_SIZE_MAX) { 450228436Sfabient DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__); 451228436Sfabient return (EINVAL); 452228436Sfabient } 453228436Sfabient 454228436Sfabient /* 455228436Sfabient * 4 byte alignment required. 456228436Sfabient */ 457255439Skib ptr = malloc(args->size, M_CPUCTL, M_WAITOK); 458228436Sfabient if (copyin(args->data, ptr, args->size) != 0) { 459228436Sfabient DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed", 460228436Sfabient __LINE__, args->data, ptr, args->size); 461228436Sfabient ret = EFAULT; 462228436Sfabient goto fail; 463228436Sfabient } 464228436Sfabient oldcpu = td->td_oncpu; 465228436Sfabient is_bound = cpu_sched_is_bound(td); 466228436Sfabient set_cpu(cpu, td); 467228436Sfabient critical_enter(); 468252592Srpaulo rdmsr_safe(MSR_BIOS_SIGN, &rev0); /* Get current microcode revision. */ 469228436Sfabient 470228436Sfabient /* 471228436Sfabient * Perform update. 472228436Sfabient */ 473228436Sfabient wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uintptr_t)(ptr)); 474228436Sfabient do_cpuid(1, tmp); 475228436Sfabient 476228436Sfabient /* 477228436Sfabient * Result are in low byte of MSR FCR5: 478228436Sfabient * 0x00: No update has been attempted since RESET. 479228436Sfabient * 0x01: The last attempted update was successful. 480228436Sfabient * 0x02: The last attempted update was unsuccessful due to a bad 481228436Sfabient * environment. No update was loaded and any preexisting 482228436Sfabient * patches are still active. 483228436Sfabient * 0x03: The last attempted update was not applicable to this processor. 484228436Sfabient * No update was loaded and any preexisting patches are still 485228436Sfabient * active. 486228436Sfabient * 0x04: The last attempted update was not successful due to an invalid 487228436Sfabient * update data block. No update was loaded and any preexisting 488228436Sfabient * patches are still active 489228436Sfabient */ 490228436Sfabient rdmsr_safe(0x1205, &res); 491228436Sfabient res &= 0xff; 492228436Sfabient critical_exit(); 493228436Sfabient rdmsr_safe(MSR_BIOS_SIGN, &rev1); /* Get new microcode revision. */ 494228436Sfabient restore_cpu(oldcpu, is_bound, td); 495228436Sfabient 496228436Sfabient DPRINTF("[cpu,%d]: rev0=%x rev1=%x res=%x\n", __LINE__, 497228436Sfabient (unsigned)(rev0 >> 32), (unsigned)(rev1 >> 32), (unsigned)res); 498228436Sfabient 499228436Sfabient if (res != 0x01) 500228436Sfabient ret = EINVAL; 501228436Sfabient else 502228436Sfabient ret = 0; 503228436Sfabientfail: 504254191Skib free(ptr, M_CPUCTL); 505228436Sfabient return (ret); 506228436Sfabient} 507228436Sfabient 508181430Sstasint 509181430Sstascpuctl_open(struct cdev *dev, int flags, int fmt __unused, struct thread *td) 510181430Sstas{ 511181430Sstas int ret = 0; 512181430Sstas int cpu; 513181430Sstas 514183397Sed cpu = dev2unit(dev); 515302372Snwhitehorn if (cpu > mp_maxid || !cpu_enabled(cpu)) { 516181430Sstas DPRINTF("[cpuctl,%d]: incorrect cpu number %d\n", __LINE__, 517181430Sstas cpu); 518181430Sstas return (ENXIO); 519181430Sstas } 520181430Sstas if (flags & FWRITE) 521181430Sstas ret = securelevel_gt(td->td_ucred, 0); 522181430Sstas return (ret); 523181430Sstas} 524181430Sstas 525181430Sstasstatic int 526181430Sstascpuctl_modevent(module_t mod __unused, int type, void *data __unused) 527181430Sstas{ 528181430Sstas int cpu; 529181430Sstas 530181430Sstas switch(type) { 531181430Sstas case MOD_LOAD: 532181430Sstas if (bootverbose) 533181430Sstas printf("cpuctl: access to MSR registers/cpuid info.\n"); 534302372Snwhitehorn cpuctl_devs = malloc(sizeof(*cpuctl_devs) * (mp_maxid + 1), M_CPUCTL, 535263080Skib M_WAITOK | M_ZERO); 536302372Snwhitehorn CPU_FOREACH(cpu) 537181430Sstas if (cpu_enabled(cpu)) 538181430Sstas cpuctl_devs[cpu] = make_dev(&cpuctl_cdevsw, cpu, 539181430Sstas UID_ROOT, GID_KMEM, 0640, "cpuctl%d", cpu); 540181430Sstas break; 541181430Sstas case MOD_UNLOAD: 542302372Snwhitehorn CPU_FOREACH(cpu) { 543181430Sstas if (cpuctl_devs[cpu] != NULL) 544181430Sstas destroy_dev(cpuctl_devs[cpu]); 545181430Sstas } 546181430Sstas free(cpuctl_devs, M_CPUCTL); 547181430Sstas break; 548181430Sstas case MOD_SHUTDOWN: 549181430Sstas break; 550181430Sstas default: 551181430Sstas return (EOPNOTSUPP); 552181430Sstas } 553181430Sstas return (0); 554181430Sstas} 555181430Sstas 556181430SstasDEV_MODULE(cpuctl, cpuctl_modevent, NULL); 557181430SstasMODULE_VERSION(cpuctl, CPUCTL_VERSION); 558