Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/cpuctl/cpuctl.c
Show First 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | |||||
#include <machine/md_var.h> | #include <machine/md_var.h> | ||||
#include <machine/specialreg.h> | #include <machine/specialreg.h> | ||||
static d_open_t cpuctl_open; | static d_open_t cpuctl_open; | ||||
static d_ioctl_t cpuctl_ioctl; | static d_ioctl_t cpuctl_ioctl; | ||||
#define CPUCTL_VERSION 1 | #define CPUCTL_VERSION 1 | ||||
#ifdef DEBUG | #ifdef CPUCTL_DEBUG | ||||
kib: This is unrelated, commit separately (now ?). | |||||
# define DPRINTF(format,...) printf(format, __VA_ARGS__); | # define DPRINTF(format,...) printf(format, __VA_ARGS__); | ||||
#else | #else | ||||
# define DPRINTF(...) | # define DPRINTF(...) | ||||
#endif | #endif | ||||
#define UCODE_SIZE_MAX (32 * 1024) | #define UCODE_SIZE_MAX (32 * 1024) | ||||
static int cpuctl_do_msr(int cpu, cpuctl_msr_args_t *data, u_long cmd, | static int cpuctl_do_msr(int cpu, cpuctl_msr_args_t *data, u_long cmd, | ||||
▲ Show 20 Lines • Show All 303 Lines • ▼ Show 20 Lines | if (rev1 > rev0) | ||||
ret = 0; | ret = 0; | ||||
else | else | ||||
ret = EEXIST; | ret = EEXIST; | ||||
fail: | fail: | ||||
free(ptr, M_CPUCTL); | free(ptr, M_CPUCTL); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
/* | |||||
* NB: MSR 0xc0010020, MSR_K8_UCODE_UPDATE, is not documented by AMD. | |||||
* Coreboot, illumos and Linux source code was used to understand | |||||
* its workings. | |||||
*/ | |||||
static void | |||||
amd_ucode_wrmsr(void *ucode_ptr) | |||||
{ | |||||
uint32_t tmp[4]; | |||||
wrmsr_safe(MSR_K8_UCODE_UPDATE, (uintptr_t)ucode_ptr); | |||||
do_cpuid(0, tmp); | |||||
} | |||||
static int | static int | ||||
update_amd(int cpu, cpuctl_update_args_t *args, struct thread *td) | update_amd(int cpu, cpuctl_update_args_t *args, struct thread *td) | ||||
{ | { | ||||
void *ptr = NULL; | void *ptr; | ||||
uint32_t tmp[4]; | |||||
int is_bound = 0; | |||||
int oldcpu; | |||||
int ret; | int ret; | ||||
if (args->size == 0 || args->data == NULL) { | if (args->size == 0 || args->data == NULL) { | ||||
DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__); | DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__); | ||||
Not Done Inline ActionsWhy is this required ? I do not see any harm from allowing the update to run from arbitrary /dev/cpuctlN. kib: Why is this required ? I do not see any harm from allowing the update to run from arbitrary… | |||||
Not Done Inline ActionsI agree. Unless there are restrictions on which cores the update can be performed on... But in that case the check will have to be a bit more sophisticated to account for multi-socket configurations. stas: I agree. Unless there are restrictions on which cores the update can be performed on... But… | |||||
Not Done Inline ActionsThere is really no restriction. I just wanted to skip unnecessary writes to the update MSR in the default configuration with microcode_cpus="ALL". But that was not needed anyway, because the first invocation of cpucontrol would update all CPUs and subsequent invocations would see that the CPUs have the latest version. avg: There is really no restriction. I just wanted to skip unnecessary writes to the update MSR in… | |||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
if (args->size > UCODE_SIZE_MAX) { | if (args->size > UCODE_SIZE_MAX) { | ||||
DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__); | DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
/* | /* | ||||
* XXX Might not require contignous address space - needs check | * 16 byte alignment required. Rely on the fact that | ||||
* malloc(9) always returns the pointer aligned at least on | |||||
* the size of the allocation. | |||||
*/ | */ | ||||
ptr = contigmalloc(args->size, M_CPUCTL, 0, 0, 0xffffffff, 16, 0); | ptr = malloc(args->size + 16, M_CPUCTL, M_ZERO | M_WAITOK); | ||||
if (ptr == NULL) { | |||||
DPRINTF("[cpuctl,%d]: cannot allocate %zd bytes of memory", | |||||
__LINE__, args->size); | |||||
return (ENOMEM); | |||||
} | |||||
if (copyin(args->data, ptr, args->size) != 0) { | if (copyin(args->data, ptr, args->size) != 0) { | ||||
DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed", | DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed", | ||||
__LINE__, args->data, ptr, args->size); | __LINE__, args->data, ptr, args->size); | ||||
ret = EFAULT; | ret = EFAULT; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
oldcpu = td->td_oncpu; | smp_rendezvous(NULL, amd_ucode_wrmsr, NULL, ptr); | ||||
is_bound = cpu_sched_is_bound(td); | |||||
set_cpu(cpu, td); | |||||
critical_enter(); | |||||
/* | |||||
* Perform update. | |||||
*/ | |||||
wrmsr_safe(MSR_K8_UCODE_UPDATE, (uintptr_t)ptr); | |||||
/* | |||||
* Serialize instruction flow. | |||||
*/ | |||||
do_cpuid(0, tmp); | |||||
critical_exit(); | |||||
restore_cpu(oldcpu, is_bound, td); | |||||
ret = 0; | ret = 0; | ||||
fail: | fail: | ||||
if (ptr != NULL) | free(ptr, M_CPUCTL); | ||||
Not Done Inline ActionsCheck for ptr != NULL is not needed. Use plain free(9). kib: Check for ptr != NULL is not needed. Use plain free(9). | |||||
contigfree(ptr, args->size, M_CPUCTL); | |||||
return (ret); | return (ret); | ||||
} | } | ||||
static int | static int | ||||
update_via(int cpu, cpuctl_update_args_t *args, struct thread *td) | update_via(int cpu, cpuctl_update_args_t *args, struct thread *td) | ||||
{ | { | ||||
void *ptr; | void *ptr; | ||||
uint64_t rev0, rev1, res; | uint64_t rev0, rev1, res; | ||||
▲ Show 20 Lines • Show All 118 Lines • Show Last 20 Lines |
This is unrelated, commit separately (now ?).