summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--share/man/man4/Makefile2
-rw-r--r--share/man/man4/cpuctl.4146
-rw-r--r--sys/amd64/amd64/support.S44
-rw-r--r--sys/amd64/conf/NOTES6
-rw-r--r--sys/amd64/include/cpufunc.h5
-rw-r--r--sys/amd64/include/specialreg.h7
-rw-r--r--sys/conf/files.amd641
-rw-r--r--sys/conf/files.i3861
-rw-r--r--sys/dev/cpuctl/cpuctl.c446
-rw-r--r--sys/i386/conf/NOTES6
-rw-r--r--sys/i386/i386/support.s49
-rw-r--r--sys/i386/include/cpufunc.h5
-rw-r--r--sys/i386/include/specialreg.h7
-rw-r--r--sys/modules/Makefile3
-rw-r--r--sys/modules/cpuctl/Makefile8
-rw-r--r--sys/sys/cpuctl.h52
-rw-r--r--sys/sys/priv.h8
-rw-r--r--usr.sbin/Makefile3
-rw-r--r--usr.sbin/cpucontrol/Makefile9
-rw-r--r--usr.sbin/cpucontrol/amd.c183
-rw-r--r--usr.sbin/cpucontrol/amd.h49
-rw-r--r--usr.sbin/cpucontrol/cpucontrol.8116
-rw-r--r--usr.sbin/cpucontrol/cpucontrol.c362
-rw-r--r--usr.sbin/cpucontrol/cpucontrol.h56
-rw-r--r--usr.sbin/cpucontrol/intel.c285
-rw-r--r--usr.sbin/cpucontrol/intel.h70
26 files changed, 1928 insertions, 1 deletions
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 69d9dc8..544a542 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -52,6 +52,7 @@ MAN= aac.4 \
cmx.4 \
coda.4 \
${_coretemp.4} \
+ ${_cpuctl.4} \
cpufreq.4 \
crypto.4 \
cue.4 \
@@ -551,6 +552,7 @@ _acpi_dock.4= acpi_dock.4
_amdsmb.4= amdsmb.4
_asmc.4= asmc.4
_coretemp.4= coretemp.4
+_cpuctl.4= cpuctl.4
_hptiop.4= hptiop.4
_hptmv.4= hptmv.4
_hptrr.4= hptrr.4
diff --git a/share/man/man4/cpuctl.4 b/share/man/man4/cpuctl.4
new file mode 100644
index 0000000..0e60801
--- /dev/null
+++ b/share/man/man4/cpuctl.4
@@ -0,0 +1,146 @@
+.\" Copyright (c) 2006-2008 Stanislav Sedov <stas@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 31, 2008
+.Dt CPUCTL 4
+.Os
+.Sh NAME
+.Nm cpuctl
+.Nd cpuctl pseudo device
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your kernel
+configuration file:
+.Bd -ragged -offset indent
+.Cd "device cpuctl"
+.Ed
+.Pp
+Alternatively, to load the driver as a module
+at boot time, place the following in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+cpuctl_load="YES"
+.Ed
+.Sh DESCRIPTION
+The special file
+.Pa /dev/cpuctl
+presents interace to the system CPU. It provides functionality to retrieve
+CPUID information, read/write machine specific registers (MSR) and perform
+cpu firmware updates.
+.Pp
+For each cpu present in the system, special file
+.Pa /dev/cpuctl%d
+with the appropriate index will be created. For multicore cpus the
+special file will be created for each core.
+.Pp
+Currently, only i386 and amd64 processors are
+supported.
+.Sh IOCTL INTERFACE
+All of the supported operations are invoked using the
+.Fr ioctl 2
+system call. Refer to that manpage for further information about
+this interface. Currently, the following ioctls are defined:
+.Bl -tag -width CPUCTL_UPDATE
+.It Dv CPUCTL_RDMSR Fa cpuctl_msr_args_t *args
+.It Dv CPUCTL_WRMSR Fa cpuctl_msr_args_t *args
+Read/write cpu machine specific register. The
+.Vt cpuctl_msr_args_t
+structure defined in
+.In sys/cpuctl.h
+as:
+.Pp
+.Bd -literal
+typedef struct {
+ int msr; /* MSR to read */
+ uint64_t data;
+} cpuctl_msr_args_t;
+.Ed
+.It Dv CPUCTL_CPUID Fa cpuctl_cpuid_args_t *args
+Retrieve CPUID information. Arguments are supplied in
+the following struct:
+.Pp
+.Bd -literal
+typedef struct {
+ int level; /* CPUID level */
+ uint32_t data[4];
+} cpuctl_cpuid_args_t;
+.Ed
+.Pp
+The
+.Va level
+field indicates the CPUID level to retrieve information for, while the
+.Va data
+used to store CPUID data received.
+.It Dv CPUCTL_UPDATE cpuctl_update_args_t *args
+Update cpu firmware (microcode). The structure defined in
+.In sys/cpuctl.h
+as:
+.Pp
+.Bd -literal
+typedef struct {
+ void *data;
+ size_t size;
+} cpuctl_update_args_t;
+.Ed
+.Pp
+The
+.Va data
+field should point to the firmware image of size
+.Va size .
+.El
+.Pp
+For additional information refer to
+.Pa cpuctl.h .
+.Sh RETURN VALUES
+.Bl -tag -width Er
+.It Bq Er ENXIO
+The operation requested is not supported by device (e.g. unsupported
+architecture or cpu was disabled)
+.It Bq Er EINVAL
+Incorrect request was supplied, or microcode image is not correct.
+.It Bq Er ENOMEM
+No physical memory was available to complete the request.
+.It Bq Er EFAULT
+The firmware image address points outside process address space.
+.El
+.Sh FILES
+.Bl -tag -width /dev/cpuctl -compact
+.It Pa /dev/cpuctl
+.El
+.Sh SEE ALSO
+.Xr hwpmc 4
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Fx 8.0
+.Sh BUGS
+Yes, probably, report if any.
+.Sh AUTHORS
+The
+.Nm
+module and this manual page was written by
+.An Stanislav Sedov Aq stas@FreeBSD.org .
diff --git a/sys/amd64/amd64/support.S b/sys/amd64/amd64/support.S
index 2f2a3f1..35f5920 100644
--- a/sys/amd64/amd64/support.S
+++ b/sys/amd64/amd64/support.S
@@ -716,3 +716,47 @@ NON_GPROF_ENTRY(__bb_init_func)
movq %rax,32(%rdi)
movq %rdi,bbhead
NON_GPROF_RET
+
+/*
+ * Support for reading MSRs in the safe manner.
+ */
+ENTRY(rdmsr_safe)
+/* int rdmsr_safe(u_int msr, uint64_t *data) */
+ movq PCPU(CURPCB),%r8
+ movq $msr_onfault,PCB_ONFAULT(%r8)
+ movl %edi,%ecx
+ rdmsr /* Read MSR pointed by %ecx. Returns
+ hi byte in edx, lo in %eax */
+ salq $32,%rdx /* sign-shift %rdx left */
+ cltq /* sign-extend %eax -> %rax */
+ orq %rdx,%rax
+ movq %rax,(%rsi)
+ xorq %rax,%rax
+ movq %rax,PCB_ONFAULT(%r8)
+ ret
+
+/*
+ * Support for writing MSRs in the safe manner.
+ */
+ENTRY(wrmsr_safe)
+/* int wrmsr_safe(u_int msr, uint64_t data) */
+ movq PCPU(CURPCB),%r8
+ movq $msr_onfault,PCB_ONFAULT(%r8)
+ movl %edi,%ecx
+ movl %esi,%eax
+ sarq $32,%rsi
+ movl %esi,%edx
+ wrmsr /* Write MSR pointed by %ecx. Accepts
+ hi byte in edx, lo in %eax. */
+ xorq %rax,%rax
+ movq %rax,PCB_ONFAULT(%r8)
+ ret
+
+/*
+ * MSR operations fault handler
+ */
+ ALIGN_TEXT
+msr_onfault:
+ movq $0,PCB_ONFAULT(%r8)
+ movl $EFAULT,%eax
+ ret
diff --git a/sys/amd64/conf/NOTES b/sys/amd64/conf/NOTES
index 1e0aae1..ac3cc42 100644
--- a/sys/amd64/conf/NOTES
+++ b/sys/amd64/conf/NOTES
@@ -401,6 +401,12 @@ device coretemp
device k8temp
#
+# CPU control pseudo-device. Provides access to MSRs, CPUID info and
+# microcode update feature.
+#
+device cpuctl
+
+#
# System Management Bus (SMB)
#
options ENABLE_ALART # Control alarm on Intel intpm driver
diff --git a/sys/amd64/include/cpufunc.h b/sys/amd64/include/cpufunc.h
index ac0db87..a4a84b7 100644
--- a/sys/amd64/include/cpufunc.h
+++ b/sys/amd64/include/cpufunc.h
@@ -790,4 +790,9 @@ void wrmsr(u_int msr, u_int64_t newval);
void reset_dbregs(void);
+#ifdef _KERNEL
+int rdmsr_safe(u_int msr, uint64_t *val);
+int wrmsr_safe(u_int msr, uint64_t newval);
+#endif
+
#endif /* !_MACHINE_CPUFUNC_H_ */
diff --git a/sys/amd64/include/specialreg.h b/sys/amd64/include/specialreg.h
index 760d2c5..4fd32ae 100644
--- a/sys/amd64/include/specialreg.h
+++ b/sys/amd64/include/specialreg.h
@@ -166,6 +166,12 @@
#define AMDID_CMP_CORES 0x000000ff
/*
+ * CPUID manufacturers identifiers
+ */
+#define INTEL_VENDOR_ID "GenuineIntel"
+#define AMD_VENDOR_ID "AuthenticAMD"
+
+/*
* Model-specific registers for the i386 family
*/
#define MSR_P5_MC_ADDR 0x000
@@ -414,5 +420,6 @@
#define MSR_IORRMASK1 0xc0010019
#define MSR_TOP_MEM 0xc001001a /* boundary for ram below 4G */
#define MSR_TOP_MEM2 0xc001001d /* boundary for ram above 4G */
+#define MSR_K8_UCODE_UPDATE 0xc0010020 /* update microcode */
#endif /* !_MACHINE_SPECIALREG_H_ */
diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64
index 8dbcdc3..ee838a7 100644
--- a/sys/conf/files.amd64
+++ b/sys/conf/files.amd64
@@ -151,6 +151,7 @@ dev/atkbdc/atkbdc_isa.c optional atkbdc isa
dev/atkbdc/atkbdc_subr.c optional atkbdc
dev/atkbdc/psm.c optional psm atkbdc
dev/coretemp/coretemp.c optional coretemp
+dev/cpuctl/cpuctl.c optional cpuctl
# There are no systems with isa slots, so all ed isa entries should go..
dev/ed/if_ed_3c503.c optional ed isa ed_3c503
dev/ed/if_ed_isa.c optional ed isa
diff --git a/sys/conf/files.i386 b/sys/conf/files.i386
index 385e63d..bfc9aac 100644
--- a/sys/conf/files.i386
+++ b/sys/conf/files.i386
@@ -156,6 +156,7 @@ dev/cm/if_cm_isa.c optional cm isa
dev/coretemp/coretemp.c optional coretemp
dev/cp/cpddk.c optional cp
dev/cp/if_cp.c optional cp
+dev/cpuctl/cpuctl.c optional cpuctl
dev/ctau/ctau.c optional ctau
dev/ctau/ctddk.c optional ctau
dev/ctau/if_ct.c optional ctau
diff --git a/sys/dev/cpuctl/cpuctl.c b/sys/dev/cpuctl/cpuctl.c
new file mode 100644
index 0000000..207c721
--- /dev/null
+++ b/sys/dev/cpuctl/cpuctl.c
@@ -0,0 +1,446 @@
+/*-
+ * Copyright (c) 2006-2008 Stanislav Sedov <stas@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/ioccom.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sched.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+#include <sys/pcpu.h>
+#include <sys/smp.h>
+#include <sys/pmckern.h>
+#include <sys/cpuctl.h>
+
+#include <machine/cpufunc.h>
+#include <machine/md_var.h>
+#include <machine/specialreg.h>
+
+static d_open_t cpuctl_open;
+static d_ioctl_t cpuctl_ioctl;
+
+#define CPUCTL_VERSION 1
+
+#ifdef DEBUG
+# define DPRINTF(format,...) printf(format, __VA_ARGS__);
+#else
+# define DPRINTF(...)
+#endif
+
+#define UCODE_SIZE_MAX (10 * 1024)
+
+static int cpuctl_do_msr(int cpu, cpuctl_msr_args_t *data, u_long cmd,
+ struct thread *td);
+static int cpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data,
+ struct thread *td);
+static int cpuctl_do_update(int cpu, cpuctl_update_args_t *data,
+ struct thread *td);
+static int update_intel(int cpu, cpuctl_update_args_t *args,
+ struct thread *td);
+static int update_amd(int cpu, cpuctl_update_args_t *args, struct thread *td);
+
+static struct cdev **cpuctl_devs;
+static MALLOC_DEFINE(M_CPUCTL, "cpuctl", "CPUCTL buffer");
+
+static struct cdevsw cpuctl_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDMINOR,
+ .d_open = cpuctl_open,
+ .d_ioctl = cpuctl_ioctl,
+ .d_name = "cpuctl",
+};
+
+/*
+ * This function checks if specified cpu enabled or not.
+ */
+static int
+cpu_enabled(int cpu)
+{
+
+ return (pmc_cpu_is_disabled(cpu) == 0);
+}
+
+/*
+ * Check if the current thread is bound to a specific cpu.
+ */
+static int
+cpu_sched_is_bound(struct thread *td)
+{
+ int ret;
+
+ thread_lock(td);
+ ret = sched_is_bound(td);
+ thread_unlock(td);
+ return (ret);
+}
+
+/*
+ * Switch to target cpu to run.
+ */
+static void
+set_cpu(int cpu, struct thread *td)
+{
+
+ KASSERT(cpu >= 0 && cpu < mp_ncpus && cpu_enabled(cpu),
+ ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu));
+ thread_lock(td);
+ sched_bind(td, cpu);
+ thread_unlock(td);
+ KASSERT(td->td_oncpu == cpu,
+ ("[cpuctl,%d]: cannot bind to target cpu %d", __LINE__, cpu));
+}
+
+static void
+restore_cpu(int oldcpu, int is_bound, struct thread *td)
+{
+
+ KASSERT(oldcpu >= 0 && oldcpu < mp_ncpus && cpu_enabled(oldcpu),
+ ("[cpuctl,%d]: bad cpu number %d", __LINE__, oldcpu));
+ thread_lock(td);
+ if (is_bound == 0)
+ sched_unbind(td);
+ else
+ sched_bind(td, oldcpu);
+ thread_unlock(td);
+}
+
+int
+cpuctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
+ int flags, struct thread *td)
+{
+ int ret;
+ int cpu = minor(dev);
+
+ if (cpu >= mp_ncpus || !cpu_enabled(cpu)) {
+ DPRINTF("[cpuctl,%d]: bad cpu number %d\n", __LINE__, cpu);
+ return (ENXIO);
+ }
+ /* Require write flag for "write" requests. */
+ if ((cmd == CPUCTL_WRMSR || cmd == CPUCTL_UPDATE) &&
+ ((flags & FWRITE) == 0))
+ return (EPERM);
+ switch (cmd) {
+ case CPUCTL_RDMSR:
+ ret = cpuctl_do_msr(cpu, (cpuctl_msr_args_t *)data, cmd, td);
+ break;
+ case CPUCTL_WRMSR:
+ ret = priv_check(td, PRIV_CPUCTL_WRMSR);
+ if (ret != 0)
+ goto fail;
+ ret = cpuctl_do_msr(cpu, (cpuctl_msr_args_t *)data, cmd, td);
+ break;
+ case CPUCTL_CPUID:
+ ret = cpuctl_do_cpuid(cpu, (cpuctl_cpuid_args_t *)data, td);
+ break;
+ case CPUCTL_UPDATE:
+ ret = priv_check(td, PRIV_CPUCTL_UPDATE);
+ if (ret != 0)
+ goto fail;
+ ret = cpuctl_do_update(cpu, (cpuctl_update_args_t *)data, td);
+ break;
+ default:
+ ret = EINVAL;
+ break;
+ }
+fail:
+ return (ret);
+}
+
+/*
+ * Actually perform cpuid operation.
+ */
+static int
+cpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data, struct thread *td)
+{
+ int is_bound = 0;
+ int oldcpu;
+
+ KASSERT(cpu >= 0 && cpu < mp_ncpus,
+ ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu));
+
+ /* Explicitly clear cpuid data to avoid returning stale info. */
+ bzero(data->data, sizeof(data->data));
+ DPRINTF("[cpuctl,%d]: retriving cpuid level %#0x for %d cpu\n",
+ __LINE__, data->level, cpu);
+ oldcpu = td->td_oncpu;
+ is_bound = cpu_sched_is_bound(td);
+ set_cpu(cpu, td);
+ do_cpuid(data->level, data->data);
+ restore_cpu(oldcpu, is_bound, td);
+ return (0);
+}
+
+/*
+ * Actually perform MSR operations.
+ */
+static int
+cpuctl_do_msr(int cpu, cpuctl_msr_args_t *data, u_long cmd, struct thread *td)
+{
+ int is_bound = 0;
+ int oldcpu;
+ int ret;
+
+ KASSERT(cpu >= 0 && cpu < mp_ncpus,
+ ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu));
+
+ /*
+ * Explicitly clear cpuid data to avoid returning stale
+ * info
+ */
+ data->data = 0;
+ DPRINTF("[cpuctl,%d]: operating on MSR %#0x for %d cpu\n", __LINE__,
+ data->msr, cpu);
+ oldcpu = td->td_oncpu;
+ is_bound = cpu_sched_is_bound(td);
+ set_cpu(cpu, td);
+ ret = cmd == CPUCTL_RDMSR ? rdmsr_safe(data->msr, &data->data) :
+ wrmsr_safe(data->msr, data->data);
+ restore_cpu(oldcpu, is_bound, td);
+ return (ret);
+}
+
+/*
+ * Actually perform microcode update.
+ */
+static int
+cpuctl_do_update(int cpu, cpuctl_update_args_t *data, struct thread *td)
+{
+ cpuctl_cpuid_args_t args = {
+ .level = 0,
+ };
+ char vendor[13];
+ int ret;
+
+ KASSERT(cpu >= 0 && cpu < mp_ncpus,
+ ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu));
+ DPRINTF("[cpuctl,%d]: XXX %d", __LINE__, cpu);
+
+ ret = cpuctl_do_cpuid(cpu, &args, td);
+ if (ret != 0) {
+ DPRINTF("[cpuctl,%d]: cannot retrive cpuid info for cpu %d",
+ __LINE__, cpu);
+ return (ENXIO);
+ }
+ ((uint32_t *)vendor)[0] = args.data[1];
+ ((uint32_t *)vendor)[1] = args.data[3];
+ ((uint32_t *)vendor)[2] = args.data[2];
+ vendor[12] = '\0';
+ if (strncmp(vendor, INTEL_VENDOR_ID, sizeof(INTEL_VENDOR_ID)) == 0)
+ ret = update_intel(cpu, data, td);
+ else if(strncmp(vendor, INTEL_VENDOR_ID, sizeof(AMD_VENDOR_ID)) == 0)
+ ret = update_amd(cpu, data, td);
+ else
+ ret = ENXIO;
+ return (ret);
+}
+
+static int
+update_intel(int cpu, cpuctl_update_args_t *args, struct thread *td)
+{
+ void *ptr = NULL;
+ uint64_t rev0, rev1;
+ uint32_t tmp[4];
+ int is_bound = 0;
+ int oldcpu;
+ int ret;
+
+ if (args->size == 0 || args->data == NULL) {
+ DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__);
+ return (EINVAL);
+ }
+ if (args->size > UCODE_SIZE_MAX) {
+ DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__);
+ return (EINVAL);
+ }
+
+ /*
+ * 16 byte alignment required.
+ */
+ ptr = malloc(args->size + 16, M_CPUCTL, M_WAITOK);
+ ptr = (void *)(16 + ((intptr_t)ptr & ~0xf));
+ if (copyin(args->data, ptr, args->size) != 0) {
+ DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed",
+ __LINE__, args->data, ptr, args->size);
+ ret = EFAULT;
+ goto fail;
+ }
+ oldcpu = td->td_oncpu;
+ is_bound = cpu_sched_is_bound(td);
+ set_cpu(cpu, td);
+ critical_enter();
+ rdmsr_safe(MSR_BIOS_SIGN, &rev0); /* Get current micorcode revision. */
+
+ /*
+ * Perform update.
+ */
+ wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uintptr_t)(ptr));
+ wrmsr_safe(MSR_BIOS_SIGN, 0);
+
+ /*
+ * Serialize instruction flow.
+ */
+ do_cpuid(0, tmp);
+ critical_exit();
+ rdmsr_safe(MSR_BIOS_SIGN, &rev1); /* Get new micorcode revision. */
+ restore_cpu(oldcpu, is_bound, td);
+ if (rev1 > rev0)
+ ret = 0;
+ else
+ ret = EEXIST;
+fail:
+ if (ptr != NULL)
+ contigfree(ptr, args->size, M_CPUCTL);
+ return (ret);
+}
+
+static int
+update_amd(int cpu, cpuctl_update_args_t *args, struct thread *td)
+{
+ void *ptr = NULL;
+ uint32_t tmp[4];
+ int is_bound = 0;
+ int oldcpu;
+ int ret;
+
+ if (args->size == 0 || args->data == NULL) {
+ DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__);
+ return (EINVAL);
+ }
+ if (args->size > UCODE_SIZE_MAX) {
+ DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__);
+ return (EINVAL);
+ }
+ /*
+ * XXX Might not require contignous address space - needs check
+ */
+ ptr = contigmalloc(args->size, M_CPUCTL, 0, 0, 0xffffffff, 16, 0);
+ 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) {
+ DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed",
+ __LINE__, args->data, ptr, args->size);
+ ret = EFAULT;
+ goto fail;
+ }
+ oldcpu = td->td_oncpu;
+ is_bound = cpu_sched_is_bound(td);
+ set_cpu(cpu, td);
+ critical_enter();
+
+ /*
+ * Perform update.
+ */
+ wrmsr_safe(MSR_K8_UCODE_UPDATE, (uintptr_t)args->data);
+
+ /*
+ * Serialize instruction flow.
+ */
+ do_cpuid(0, tmp);
+ critical_exit();
+ restore_cpu(oldcpu, is_bound, td);
+ ret = 0;
+fail:
+ if (ptr != NULL)
+ contigfree(ptr, args->size, M_CPUCTL);
+ return (ret);
+}
+
+int
+cpuctl_open(struct cdev *dev, int flags, int fmt __unused, struct thread *td)
+{
+ int ret = 0;
+ int cpu;
+
+ cpu = minor(dev);
+ if (cpu >= mp_ncpus || !cpu_enabled(cpu)) {
+ DPRINTF("[cpuctl,%d]: incorrect cpu number %d\n", __LINE__,
+ cpu);
+ return (ENXIO);
+ }
+ if (flags & FWRITE)
+ ret = securelevel_gt(td->td_ucred, 0);
+ return (ret);
+}
+
+static int
+cpuctl_modevent(module_t mod __unused, int type, void *data __unused)
+{
+ int cpu;
+
+ switch(type) {
+ case MOD_LOAD:
+ if ((cpu_feature & CPUID_MSR) == 0) {
+ if (bootverbose)
+ printf("cpuctl: not available.\n");
+ return (ENODEV);
+ }
+ if (bootverbose)
+ printf("cpuctl: access to MSR registers/cpuid info.\n");
+ cpuctl_devs = (struct cdev **)malloc(sizeof(void *) * mp_ncpus,
+ M_CPUCTL, M_WAITOK | M_ZERO);
+ if (cpuctl_devs == NULL) {
+ DPRINTF("[cpuctl,%d]: cannot allocate memory\n",
+ __LINE__);
+ return (ENOMEM);
+ }
+ for (cpu = 0; cpu < mp_ncpus; cpu++)
+ if (cpu_enabled(cpu))
+ cpuctl_devs[cpu] = make_dev(&cpuctl_cdevsw, cpu,
+ UID_ROOT, GID_KMEM, 0640, "cpuctl%d", cpu);
+ break;
+ case MOD_UNLOAD:
+ for (cpu = 0; cpu < mp_ncpus; cpu++) {
+ if (cpuctl_devs[cpu] != NULL)
+ destroy_dev(cpuctl_devs[cpu]);
+ }
+ free(cpuctl_devs, M_CPUCTL);
+ break;
+ case MOD_SHUTDOWN:
+ break;
+ default:
+ return (EOPNOTSUPP);
+ }
+ return (0);
+}
+
+DEV_MODULE(cpuctl, cpuctl_modevent, NULL);
+MODULE_VERSION(cpuctl, CPUCTL_VERSION);
diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES
index 93c4954..ddff062 100644
--- a/sys/i386/conf/NOTES
+++ b/sys/i386/conf/NOTES
@@ -778,6 +778,12 @@ device ichwd
device coretemp
#
+# CPU control pseudo-device. Provides access to MSRs, CPUID info and
+# microcode update feature.
+#
+device cpuctl
+
+#
# System Management Bus (SMB)
#
options ENABLE_ALART # Control alarm on Intel intpm driver
diff --git a/sys/i386/i386/support.s b/sys/i386/i386/support.s
index d5c5096..5c8cd85 100644
--- a/sys/i386/i386/support.s
+++ b/sys/i386/i386/support.s
@@ -1568,3 +1568,52 @@ NON_GPROF_ENTRY(__bb_init_func)
movl %edx,16(%eax)
movl %eax,bbhead
NON_GPROF_RET
+
+/*
+ * Support for reading MSRs in the safe manner.
+ */
+ENTRY(rdmsr_safe)
+/* int rdmsr_safe(u_int msr, uint64_t *data) */
+ movl PCPU(CURPCB),%ecx
+ movl $msr_onfault,PCB_ONFAULT(%ecx)
+
+ movl 4(%esp),%ecx
+ rdmsr
+ movl 8(%esp),%ecx
+ movl %eax,(%ecx)
+ movl %edx,4(%ecx)
+ xorl %eax,%eax
+
+ movl PCPU(CURPCB),%ecx
+ movl %eax,PCB_ONFAULT(%ecx)
+
+ ret
+
+/*
+ * Support for writing MSRs in the safe manner.
+ */
+ENTRY(wrmsr_safe)
+/* int wrmsr_safe(u_int msr, uint64_t data) */
+ movl PCPU(CURPCB),%ecx
+ movl $msr_onfault,PCB_ONFAULT(%ecx)
+
+ movl 4(%esp),%ecx
+ movl 8(%esp),%eax
+ movl 12(%esp),%edx
+ wrmsr
+ xorl %eax,%eax
+
+ movl PCPU(CURPCB),%ecx
+ movl %eax,PCB_ONFAULT(%ecx)
+
+ ret
+
+/*
+ * MSR operations fault handler
+ */
+ ALIGN_TEXT
+msr_onfault:
+ movl PCPU(CURPCB),%ecx
+ movl $0,PCB_ONFAULT(%ecx)
+ movl $EFAULT,%eax
+ ret
diff --git a/sys/i386/include/cpufunc.h b/sys/i386/include/cpufunc.h
index 85b3f3d..fc61eab 100644
--- a/sys/i386/include/cpufunc.h
+++ b/sys/i386/include/cpufunc.h
@@ -748,4 +748,9 @@ void wrmsr(u_int msr, uint64_t newval);
void reset_dbregs(void);
+#ifdef _KERNEL
+int rdmsr_safe(u_int msr, uint64_t *val);
+int wrmsr_safe(u_int msr, uint64_t newval);
+#endif
+
#endif /* !_MACHINE_CPUFUNC_H_ */
diff --git a/sys/i386/include/specialreg.h b/sys/i386/include/specialreg.h
index 07a74ad..8835c05 100644
--- a/sys/i386/include/specialreg.h
+++ b/sys/i386/include/specialreg.h
@@ -157,6 +157,12 @@
#define AMDID_CMP_CORES 0x000000ff
/*
+ * CPUID manufacturers identifiers
+ */
+#define INTEL_VENDOR_ID "GenuineIntel"
+#define AMD_VENDOR_ID "AuthenticAMD"
+
+/*
* Model-specific registers for the i386 family
*/
#define MSR_P5_MC_ADDR 0x000
@@ -444,6 +450,7 @@
/* AMD64 MSR's */
#define MSR_EFER 0xc0000080 /* extended features */
+#define MSR_K8_UCODE_UPDATE 0xc0010020 /* update microcode */
/* VIA ACE crypto featureset: for via_feature_rng */
#define VIA_HAS_RNG 1 /* cpu has RNG */
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index 4b15997..ae11611 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -59,6 +59,7 @@ SUBDIR= ${_3dfx} \
${_coff} \
${_coretemp} \
${_cp} \
+ ${_cpuctl} \
${_cpufreq} \
${_crypto} \
${_cryptodev} \
@@ -368,6 +369,7 @@ _cbb= cbb
_ce= ce
_coff= coff
_cp= cp
+_cpuctl= cpuctl
_cpufreq= cpufreq
_cs= cs
.if ${MK_CDDL} != "no" || defined(ALL_MODULES)
@@ -498,6 +500,7 @@ _cbb= cbb
_cmx= cmx
_ciss= ciss
_coretemp= coretemp
+_cpuctl= cpuctl
_cpufreq= cpufreq
.if ${MK_CDDL} != "no" || defined(ALL_MODULES)
_cyclic= cyclic
diff --git a/sys/modules/cpuctl/Makefile b/sys/modules/cpuctl/Makefile
new file mode 100644
index 0000000..74c76b9
--- /dev/null
+++ b/sys/modules/cpuctl/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../dev/cpuctl
+
+KMOD= cpuctl
+SRCS= cpuctl.c
+
+.include <bsd.kmod.mk>
diff --git a/sys/sys/cpuctl.h b/sys/sys/cpuctl.h
new file mode 100644
index 0000000..57b5acf
--- /dev/null
+++ b/sys/sys/cpuctl.h
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2006-2008 Stanislav Sedov <stas@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _CPUCTL_H_
+#define _CPUCTL_H_
+
+typedef struct {
+ int msr; /* MSR to read */
+ uint64_t data;
+} cpuctl_msr_args_t;
+
+typedef struct {
+ int level; /* CPUID level */
+ uint32_t data[4];
+} cpuctl_cpuid_args_t;
+
+typedef struct {
+ void *data;
+ size_t size;
+} cpuctl_update_args_t;
+
+#define CPUCTL_RDMSR _IOWR('c', 1, cpuctl_msr_args_t)
+#define CPUCTL_WRMSR _IOWR('c', 2, cpuctl_msr_args_t)
+#define CPUCTL_CPUID _IOWR('c', 3, cpuctl_cpuid_args_t)
+#define CPUCTL_UPDATE _IOWR('c', 4, cpuctl_update_args_t)
+
+#endif /* _CPUCTL_H_ */
diff --git a/sys/sys/priv.h b/sys/sys/priv.h
index 70706bc..4c0fdca 100644
--- a/sys/sys/priv.h
+++ b/sys/sys/priv.h
@@ -455,9 +455,15 @@
#define PRIV_NNPFS_DEBUG 630 /* Perforn ARLA_VIOC_NNPFSDEBUG. */
/*
+ * cpuctl(4) privileges.
+ */
+#define PRIV_CPUCTL_WRMSR 640 /* Write model-specific register. */
+#define PRIV_CPUCTL_UPDATE 641 /* Update cpu microcode. */
+
+/*
* Track end of privilege list.
*/
-#define _PRIV_HIGHEST 631
+#define _PRIV_HIGHEST 642
/*
* Validate that a named privilege is known by the privilege system. Invalid
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
index 9a73744..44ccefb 100644
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -33,6 +33,7 @@ SUBDIR= ac \
ckdist \
clear_locks \
config \
+ ${_cpucontrol} \
crashinfo \
cron \
crunch \
@@ -300,6 +301,7 @@ _apm= apm
_apmd= apmd
_asf= asf
_btxld= btxld
+_cpucontrol= cpucontrol
_kgmon= kgmon
_kgzip= kgzip
_lptcontrol= lptcontrol
@@ -335,6 +337,7 @@ _acpi= acpi
_asf= asf
_boot0cfg= boot0cfg
_btxld= btxld
+_cpucontrol= cpucontrol
_kgmon= kgmon
_lptcontrol= lptcontrol
.if ${MK_NCP} != "no"
diff --git a/usr.sbin/cpucontrol/Makefile b/usr.sbin/cpucontrol/Makefile
new file mode 100644
index 0000000..00e7214
--- /dev/null
+++ b/usr.sbin/cpucontrol/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= cpucontrol
+MAN= cpucontrol.8
+SRCS= cpucontrol.c intel.c amd.c
+
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/cpucontrol/amd.c b/usr.sbin/cpucontrol/amd.c
new file mode 100644
index 0000000..bd36c9c
--- /dev/null
+++ b/usr.sbin/cpucontrol/amd.c
@@ -0,0 +1,183 @@
+/*-
+ * Copyright (c) 2006, 2008 Stanislav Sedov <stas@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/ioccom.h>
+#include <sys/cpuctl.h>
+
+#include <machine/cpufunc.h>
+#include <machine/specialreg.h>
+
+#include "cpucontrol.h"
+#include "amd.h"
+
+int
+amd_probe(int fd)
+{
+ char vendor[13];
+ int error;
+ cpuctl_cpuid_args_t idargs = {
+ .level = 0,
+ };
+
+ error = ioctl(fd, CPUCTL_CPUID, &idargs);
+ if (error < 0) {
+ WARN(0, "ioctl()");
+ return (1);
+ }
+ ((uint32_t *)vendor)[0] = idargs.data[1];
+ ((uint32_t *)vendor)[1] = idargs.data[3];
+ ((uint32_t *)vendor)[2] = idargs.data[2];
+ vendor[12] = '\0';
+ if (strncmp(vendor, AMD_VENDOR_ID, sizeof(AMD_VENDOR_ID)) != 0)
+ return (1);
+ return (0);
+}
+
+void
+amd_update(const char *dev, const char *path)
+{
+ int fd, devfd;
+ unsigned int i;
+ struct stat st;
+ uint32_t *fw_image;
+ amd_fw_header_t *fw_header;
+ uint32_t sum;
+ uint32_t signature;
+ uint32_t *fw_data;
+ size_t fw_size;
+ cpuctl_cpuid_args_t idargs = {
+ .level = 1, /* Request signature. */
+ };
+ cpuctl_update_args_t args;
+ int error;
+
+ assert(path);
+ assert(dev);
+
+ fd = -1;
+ devfd = -1;
+ fw_image = MAP_FAILED;
+ error = 0;
+ devfd = open(dev, O_RDWR);
+ if (devfd < 0) {
+ WARN(0, "could not open %s for writing", dev);
+ return;
+ }
+ error = ioctl(devfd, CPUCTL_CPUID, &idargs);
+ if (error < 0) {
+ WARN(0, "ioctl()");
+ goto fail;
+ }
+ signature = idargs.data[0];
+ WARNX(2, "found cpu family %#x model %#x "
+ "stepping %#x extfamily %#x extmodel %#x.",
+ (signature >> 8) & 0x0f, (signature >> 4) & 0x0f,
+ (signature >> 0) & 0x0f, (signature >> 20) & 0xff,
+ (signature >> 16) & 0x0f);
+
+ /*
+ * Open the firmware file.
+ */
+ fd = open(path, O_RDONLY, 0);
+ if (fd < 0) {
+ WARN(0, "open(%s)", path);
+ goto fail;
+ }
+ error = fstat(fd, &st);
+ if (error != 0) {
+ WARN(0, "fstat(%s)", path);
+ goto fail;
+ }
+ if (st.st_size < 0 || (unsigned)st.st_size < sizeof(*fw_header)) {
+ WARNX(2, "file too short: %s", path);
+ goto fail;
+ }
+ /*
+ * mmap the whole image.
+ */
+ fw_image = (uint32_t *)mmap(NULL, st.st_size, PROT_READ,
+ MAP_PRIVATE, fd, 0);
+ if (fw_image == MAP_FAILED) {
+ WARN(0, "mmap(%s)", path);
+ goto fail;
+ }
+ fw_header = (amd_fw_header_t *)fw_image;
+ if ((fw_header->magic >> 8) != AMD_MAGIC) {
+ WARNX(2, "%s is not a valid amd firmware: version mismatch",
+ path);
+ goto fail;
+ }
+ fw_data = (uint32_t *)(fw_header + 1);
+ fw_size = (st.st_size - sizeof(*fw_header)) / sizeof(uint32_t);
+
+ /*
+ * Check the primary checksum.
+ */
+ sum = 0;
+ for (i = 0; i < fw_size; i++)
+ sum += fw_data[i];
+ if (sum != fw_header->checksum) {
+ WARNX(2, "%s: update data checksum invalid", path);
+ goto fail;
+ }
+ if (signature == fw_header->signature) {
+ fprintf(stderr, "%s: updating cpu %s... ", path, dev);
+
+ args.data = fw_image;
+ args.size = st.st_size;
+ error = ioctl(fd, CPUCTL_UPDATE, &args);
+ if (error < 0) {
+ fprintf(stderr, "failed.\n");
+ warn("ioctl()");
+ goto fail;
+ }
+ fprintf(stderr, "done.\n");
+ }
+
+fail:
+ if (fd >= 0)
+ close(fd);
+ if (devfd >= 0)
+ close(devfd);
+ if (fw_image != MAP_FAILED)
+ if(munmap(fw_image, st.st_size) != 0)
+ warn("munmap(%s)", path);
+ return;
+}
diff --git a/usr.sbin/cpucontrol/amd.h b/usr.sbin/cpucontrol/amd.h
new file mode 100644
index 0000000..cf109c2
--- /dev/null
+++ b/usr.sbin/cpucontrol/amd.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2006, 2008 Stanislav Sedov <stas@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef AMD_H
+#define AMD_H
+
+/*
+ * Prototypes.
+ */
+ucode_probe_t amd_probe;
+ucode_update_t amd_update;
+
+typedef struct amd_fw_header {
+ uint32_t date; /* Update creation date. */
+ uint32_t xz0[2];
+ uint32_t checksum; /* ucode checksum. */
+ uint32_t xz1[2];
+ uint32_t signature; /* Low byte of cpuid(0). */
+ uint32_t magic; /* 0x0Xaaaaaa */
+ uint32_t xz2[8];
+} amd_fw_header_t;
+
+#define AMD_MAGIC 0xaaaaaa
+
+#endif /* !AMD_H */
diff --git a/usr.sbin/cpucontrol/cpucontrol.8 b/usr.sbin/cpucontrol/cpucontrol.8
new file mode 100644
index 0000000..cc753e0
--- /dev/null
+++ b/usr.sbin/cpucontrol/cpucontrol.8
@@ -0,0 +1,116 @@
+.\" Copyright (c) 2006, 2008 Stanislav Sedov <stas@FreeBSD.org>.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 4, 2008
+.Dt CPUCONTROL 8
+.Os
+.Sh NAME
+.Nm cpucontrol
+.Nd control utility for the
+.Xr cpuctl 4
+device.
+.Sh SYNOPSIS
+.Nm
+.Op Fl vh
+.Fl m Ar msr Ns Op = Ns Ar value
+.Bk
+.Ar device
+.Ek
+.Nm
+.Op Fl vh
+.Fl i Ar level
+.Bk
+.Ar device
+.Ek
+.Nm
+.Op Fl vh
+.Op Fl d Ar datadir
+.Fl u
+.Bk
+.Ar device
+.Ek
+.Sh DESCRIPTION
+The
+.Nm
+utility can be used to read and write an arbitrary machine-specific
+CPU registers via
+.Xr cpuctl 4
+controlled special device and apply the CPU firmware updates.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d Ar datadir
+Where to look for microcode images. The option can be specified multiple times.
+.It Fl m Ar msr Ns Op = Ns Ar value
+Read/write the specified MSR. Both the MSR and the value should be given as a hex number.
+.It Fl i Ar level
+Retrieve CPUID info. Level should be given as a hex number.
+.It Fl u
+Apply CPU firmware updates. The
+.Nm
+utility will walk through the configured data directories
+and will apply all firmware patches available for this CPU.
+.It Fl v
+Increase the verbosity level.
+.It Fl h
+Show help message.
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh EXAMPLES
+The command
+.Pp
+.Dq Li "cpucontrol -m 0x10 /dev/cpuctl0"
+.Pp
+will read the contents of TSC MSR from CPU 0.
+.Pp
+To set the CPU 0 TSC MSR register value to 0x1 issue
+.Pp
+.Dq Li "cpucontrol -m 0x10=0x1 /dev/cpuctl0"
+.Pp
+.Pp
+The command
+.Pp
+.Dq Li "cpucontrol -i 0x1 /dev/cpuctl1"
+.Pp
+will retrieve the CPUID level 0x1 from CPU 1.
+.Pp
+To perform firmware updated on CPU 0 from images located at
+.Pa /usr/local/share/cpuctl/
+use the following command:
+.Pp
+.Dq Li "cpucontrol -d /usr/local/share/cpuctl/ -u /dev/cpuctl0"
+.Sh SEE ALSO
+.Xr cpuctl 4
+.Sh BUGS
+Yes, probably, report if any.
+.Sh AUTHORS
+The
+.Nm
+utility and this manual page was written by
+.An Stanislav Sedov Aq stas@FreeBSD.org .
diff --git a/usr.sbin/cpucontrol/cpucontrol.c b/usr.sbin/cpucontrol/cpucontrol.c
new file mode 100644
index 0000000..4736e23
--- /dev/null
+++ b/usr.sbin/cpucontrol/cpucontrol.c
@@ -0,0 +1,362 @@
+/*-
+ * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This utility provides userland access to the cpuctl(4) pseudo-device
+ * features.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+#include <sysexits.h>
+#include <dirent.h>
+
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/cpuctl.h>
+
+#include "cpucontrol.h"
+#include "amd.h"
+#include "intel.h"
+
+int verbosity_level = 0;
+
+#define DEFAULT_DATADIR "/usr/local/share/cpucontrol"
+
+#define FLAG_I 0x01
+#define FLAG_M 0x02
+#define FLAG_U 0x04
+
+#define HIGH(val) (uint32_t)(((val) >> 32) & 0xffffffff)
+#define LOW(val) (uint32_t)((val) & 0xffffffff)
+
+/*
+ * Macros for freeing SLISTs, probably must be in /sys/queue.h
+ */
+#define SLIST_FREE(head, field, freef) do { \
+ typeof(SLIST_FIRST(head)) __elm0; \
+ typeof(SLIST_FIRST(head)) __elm; \
+ SLIST_FOREACH_SAFE(__elm, (head), field, __elm0) \
+ (void)(freef)(__elm); \
+} while(0);
+
+struct datadir {
+ const char *path;
+ SLIST_ENTRY(datadir) next;
+};
+static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(&datadirs);
+
+struct ucode_handler {
+ ucode_probe_t *probe;
+ ucode_update_t *update;
+} handlers[] = {
+ { intel_probe, intel_update },
+ { amd_probe, amd_update },
+};
+#define NHANDLERS (sizeof(handlers) / sizeof(*handlers))
+
+static void usage(void);
+static int isdir(const char *path);
+static int do_cpuid(const char *cmdarg, const char *dev);
+static int do_msr(const char *cmdarg, const char *dev);
+static int do_update(const char *dev);
+static void datadir_add(const char *path);
+
+static void __dead2
+usage()
+{
+ const char *name;
+
+ name = getprogname();
+ if (name == NULL)
+ name = "cpuctl";
+ fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | "
+ "-i level | -u] device\n", name);
+ exit(EX_USAGE);
+}
+
+static int
+isdir(const char *path)
+{
+ int error;
+ struct stat st;
+
+ error = stat(path, &st);
+ if (error < 0) {
+ WARN(0, "stat(%s)", path);
+ return (error);
+ }
+ return (st.st_mode & S_IFDIR);
+}
+
+static int
+do_cpuid(const char *cmdarg, const char *dev)
+{
+ unsigned int level;
+ cpuctl_cpuid_args_t args;
+ int fd, error;
+ char *endptr;
+
+ assert(cmdarg != NULL);
+ assert(dev != NULL);
+
+ level = strtoul(cmdarg, &endptr, 16);
+ if (*cmdarg == '\0' || *endptr != '\0') {
+ WARNX(0, "incorrect operand: %s", cmdarg);
+ usage();
+ /* NOTREACHED */
+ }
+
+ /*
+ * Fill ioctl argument structure.
+ */
+ args.level = level;
+ fd = open(dev, O_RDONLY);
+ if (fd < 0) {
+ WARNX(0, "error opening %s for reading", dev);
+ return (1);
+ }
+ error = ioctl(fd, CPUCTL_CPUID, &args);
+ if (error < 0) {
+ WARNX(0, "ioctl(%s, CPUCTL_CPUID)", dev);
+ close(fd);
+ return (error);
+ }
+ fprintf(stdout, "cpuid level 0x%x: 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
+ level, args.data[0], args.data[1], args.data[2], args.data[3]);
+ close(fd);
+ return (0);
+}
+
+static int
+do_msr(const char *cmdarg, const char *dev)
+{
+ unsigned int msr;
+ cpuctl_msr_args_t args;
+ int fd, error;
+ int wr = 0;
+ char *p;
+ char *endptr;
+
+ assert(cmdarg != NULL);
+ assert(dev != NULL);
+
+ p = strchr(cmdarg, '=');
+ if (p != NULL) {
+ wr = 1;
+ *p++ = '\0';
+ args.data = strtoull(p, &endptr, 16);
+ if (*p == '\0' || *endptr != '\0') {
+ WARNX(0, "incorrect MSR value: %s", p);
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ msr = strtoul(cmdarg, &endptr, 16);
+ if (*cmdarg == '\0' || *endptr != '\0') {
+ WARNX(0, "incorrect MSR register: %s", cmdarg);
+ usage();
+ /* NOTREACHED */
+ }
+
+ /*
+ * Fill ioctl argument structure.
+ */
+ args.msr = msr;
+ fd = open(dev, wr == 0 ? O_RDONLY : O_WRONLY);
+ if (fd < 0) {
+ WARNX(0, "error opening %s for %s", dev,
+ wr == 0 ? "reading" : "writing");
+ return (1);
+ }
+ error = ioctl(fd, wr == 0 ? CPUCTL_RDMSR : CPUCTL_WRMSR, &args);
+ if (error < 0) {
+ WARNX(0, "ioctl(%s, %s)", dev,
+ wr == 0 ? "CPUCTL_RDMSR" : "CPUCTL_WRMSR");
+ close(fd);
+ return (1);
+ }
+ if (wr == 0)
+ fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr,
+ HIGH(args.data), LOW(args.data));
+ close(fd);
+ return (0);
+}
+
+static int
+do_update(const char *dev)
+{
+ int fd;
+ unsigned int i;
+ int error;
+ struct ucode_handler *handler;
+ struct datadir *dir;
+ DIR *dirfd;
+ struct dirent *direntry;
+ char buf[MAXPATHLEN];
+
+ fd = open(dev, O_RDONLY);
+ if (fd < 0) {
+ WARNX(0, "error opening %s for reading", dev);
+ return (1);
+ }
+
+ /*
+ * Find the appropriate handler for device.
+ */
+ for (i = 0; i < NHANDLERS; i++)
+ if (handlers[i].probe(fd) == 0)
+ break;
+ if (i < NHANDLERS)
+ handler = &handlers[i];
+ else {
+ WARNX(0, "cannot find the appropriate handler for device");
+ close(fd);
+ return (1);
+ }
+ close(fd);
+
+ /*
+ * Process every image in specified data directories.
+ */
+ SLIST_FOREACH(dir, &datadirs, next) {
+ dirfd = opendir(dir->path);
+ if (dirfd == NULL) {
+ WARNX(1, "skipping directory %s: not accessible", dir->path);
+ continue;
+ }
+ while ((direntry = readdir(dirfd)) != NULL) {
+ if (direntry->d_namlen == 0)
+ continue;
+ error = snprintf(buf, sizeof(buf), "%s/%s", dir->path,
+ direntry->d_name);
+ if ((unsigned)error >= sizeof(buf))
+ WARNX(0, "skipping %s, buffer too short",
+ direntry->d_name);
+ if (isdir(buf) != 0) {
+ WARNX(2, "skipping %s: is a directory", buf);
+ continue;
+ }
+ handler->update(dev, buf);
+ }
+ error = closedir(dirfd);
+ if (error != 0)
+ WARN(0, "closedir(%s)", dir->path);
+ }
+ return (0);
+}
+
+/*
+ * Add new data directory to the search list.
+ */
+static void
+datadir_add(const char *path)
+{
+ struct datadir *newdir;
+
+ newdir = (struct datadir *)malloc(sizeof(*newdir));
+ if (newdir == NULL)
+ err(EX_OSERR, "cannot allocate memory");
+ newdir->path = path;
+ SLIST_INSERT_HEAD(&datadirs, newdir, next);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c, flags;
+ const char *cmdarg;
+ const char *dev;
+ int error;
+
+ flags = 0;
+ error = 0;
+ cmdarg = ""; /* To keep gcc3 happy. */
+
+ /*
+ * Add all default data dirs to the list first.
+ */
+ datadir_add(DEFAULT_DATADIR);
+ while ((c = getopt(argc, argv, "d:hi:m:uv")) != -1) {
+ switch (c) {
+ case 'd':
+ datadir_add(optarg);
+ break;
+ case 'i':
+ flags |= FLAG_I;
+ cmdarg = optarg;
+ break;
+ case 'm':
+ flags |= FLAG_M;
+ cmdarg = optarg;
+ break;
+ case 'u':
+ flags |= FLAG_U;
+ break;
+ case 'v':
+ verbosity_level++;
+ break;
+ case 'h':
+ /* FALLTHROUGH */
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 1) {
+ usage();
+ /* NOTREACHED */
+ }
+ dev = argv[0];
+ c = flags & (FLAG_I | FLAG_M | FLAG_U);
+ switch (c) {
+ case FLAG_I:
+ error = do_cpuid(cmdarg, dev);
+ break;
+ case FLAG_M:
+ error = do_msr(cmdarg, dev);
+ break;
+ case FLAG_U:
+ error = do_update(dev);
+ break;
+ default:
+ usage(); /* Only one command can be selected. */
+ }
+ SLIST_FREE(&datadirs, next, free);
+ return (error);
+}
diff --git a/usr.sbin/cpucontrol/cpucontrol.h b/usr.sbin/cpucontrol/cpucontrol.h
new file mode 100644
index 0000000..63d3995
--- /dev/null
+++ b/usr.sbin/cpucontrol/cpucontrol.h
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef CPUCONTROL_H
+#define CPUCONTROL_H
+
+typedef int ucode_probe_t(int fd);
+typedef void ucode_update_t(const char *dev, const char *image);
+
+extern int verbosity_level;
+
+#ifdef DEBUG
+# define WARNX(level, ...) \
+ if ((level) <= verbosity_level) { \
+ fprintf(stderr, "%s:%d ", __FILE__, __LINE__); \
+ warnx(__VA_ARGS__); \
+ }
+# define WARN(level, ...) \
+ if ((level) <= verbosity_level) { \
+ fprintf(stderr, "%s:%d ", __FILE__, __LINE__); \
+ warn(__VA_ARGS__); \
+ }
+#else
+# define WARNX(level, ...) \
+ if ((level) <= verbosity_level) \
+ warnx(__VA_ARGS__);
+# define WARN(level, ...) \
+ if ((level) <= verbosity_level) \
+ warn(__VA_ARGS__);
+#endif
+
+#endif /* !CPUCONTROL_H */
diff --git a/usr.sbin/cpucontrol/intel.c b/usr.sbin/cpucontrol/intel.c
new file mode 100644
index 0000000..18c6e5e
--- /dev/null
+++ b/usr.sbin/cpucontrol/intel.c
@@ -0,0 +1,285 @@
+/*-
+ * Copyright (c) 2006, 2008 Stanislav Sedov <stas@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/ioccom.h>
+#include <sys/cpuctl.h>
+
+#include <machine/cpufunc.h>
+#include <machine/specialreg.h>
+
+#include "cpucontrol.h"
+#include "intel.h"
+
+#define DEFAULT_UCODE_SIZE 2000 /* Size of update data if not specified. */
+
+int
+intel_probe(int fd)
+{
+ char vendor[13];
+ int error;
+ cpuctl_cpuid_args_t idargs = {
+ .level = 0,
+ };
+
+ error = ioctl(fd, CPUCTL_CPUID, &idargs);
+ if (error < 0) {
+ WARN(0, "ioctl()");
+ return (1);
+ }
+ ((uint32_t *)vendor)[0] = idargs.data[1];
+ ((uint32_t *)vendor)[1] = idargs.data[3];
+ ((uint32_t *)vendor)[2] = idargs.data[2];
+ vendor[12] = '\0';
+ if (strncmp(vendor, INTEL_VENDOR_ID, sizeof(INTEL_VENDOR_ID)) != 0)
+ return (1);
+ return (0);
+}
+
+void
+intel_update(const char *dev, const char *path)
+{
+ int fd, devfd;
+ struct stat st;
+ uint32_t *fw_image;
+ int have_ext_table;
+ uint32_t sum;
+ unsigned int i;
+ size_t payload_size;
+ intel_fw_header_t *fw_header;
+ intel_cpu_signature_t *ext_table;
+ intel_ext_header_t *ext_header;
+ uint32_t signature, flags;
+ int32_t revision;
+ ssize_t ext_size;
+ size_t ext_table_size;
+ void *fw_data;
+ size_t data_size, total_size;
+ cpuctl_msr_args_t msrargs = {
+ .msr = MSR_IA32_PLATFORM_ID,
+ };
+ cpuctl_cpuid_args_t idargs = {
+ .level = 1, /* Signature. */
+ };
+ cpuctl_update_args_t args;
+ int error;
+
+ assert(path);
+ assert(dev);
+
+ fd = -1;
+ devfd = -1;
+ fw_image = MAP_FAILED;
+ ext_table = NULL;
+ ext_header = NULL;
+ devfd = open(dev, O_RDWR);
+ if (devfd < 0) {
+ WARN(0, "could not open %s for writing", dev);
+ return;
+ }
+ error = ioctl(devfd, CPUCTL_CPUID, &idargs);
+ if (error < 0) {
+ WARN(0, "ioctl(%s)", dev);
+ goto fail;
+ }
+ signature = idargs.data[0];
+ error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
+ if (error < 0) {
+ WARN(0, "ioctl(%s)", dev);
+ goto fail;
+ }
+
+ /*
+ * MSR_IA32_PLATFORM_ID contains flag in BCD in bits 52-50.
+ */
+ flags = 1 << ((msrargs.data >> 50) & 7);
+ msrargs.msr = MSR_BIOS_SIGN;
+ error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
+ if (error < 0) {
+ WARN(0, "ioctl(%s)", dev);
+ goto fail;
+ }
+ revision = msrargs.data >> 32; /* Revision in the high dword. */
+ WARNX(2, "found cpu type %#x family %#x model %#x stepping %#x.",
+ (signature >> 12) & 0x03, (signature >> 8) & 0x0f,
+ (signature >> 4) & 0x0f, (signature >> 0) & 0x0f);
+ /*
+ * Open firmware image.
+ */
+ fd = open(path, O_RDONLY, 0);
+ if (fd < 0) {
+ WARN(0, "open(%s)", path);
+ return;
+ }
+ error = fstat(fd, &st);
+ if (error != 0) {
+ WARN(0, "fstat(%s)", path);
+ goto fail;
+ }
+ if (st.st_size < 0 || (unsigned)st.st_size < sizeof(*fw_header)) {
+ WARNX(2, "file too short: %s", path);
+ goto fail;
+ }
+
+ /*
+ * mmap the whole image.
+ */
+ fw_image = (uint32_t *)mmap(NULL, st.st_size, PROT_READ,
+ MAP_PRIVATE, fd, 0);
+ if (fw_image == MAP_FAILED) {
+ WARN(0, "mmap(%s)", path);
+ goto fail;
+ }
+ fw_header = (intel_fw_header_t *)fw_image;
+ if (fw_header->header_version != INTEL_HEADER_VERSION ||
+ fw_header->loader_revision != INTEL_LOADER_REVISION) {
+ WARNX(2, "%s is not a valid intel firmware: version mismatch",
+ path);
+ goto fail;
+ }
+ /*
+ * According to spec, if data_size == 0, then size of ucode = 2000.
+ */
+ if (fw_header->data_size == 0)
+ data_size = DEFAULT_UCODE_SIZE;
+ else
+ data_size = fw_header->data_size;
+ if (fw_header->total_size == 0)
+ total_size = data_size + sizeof(*fw_header);
+ else
+ total_size = fw_header->total_size;
+ if (total_size > (unsigned)st.st_size || st.st_size < 0) {
+ WARNX(2, "file too short: %s", path);
+ goto fail;
+ }
+ payload_size = data_size + sizeof(*fw_header);
+
+ /*
+ * Check the primary checksum.
+ */
+ sum = 0;
+ for (i = 0; i < (payload_size / sizeof(uint32_t)); i++)
+ sum += *((uint32_t *)fw_image + i);
+ if (sum != 0) {
+ WARNX(2, "%s: update data checksum invalid", path);
+ goto fail;
+ }
+
+ /*
+ * Check if there is an extended signature table.
+ */
+ ext_size = total_size - payload_size;
+ have_ext_table = 0;
+
+ if (ext_size > (signed)sizeof(*ext_header)) {
+ ext_header =
+ (intel_ext_header_t *)((char *)fw_image + payload_size);
+ ext_table = (intel_cpu_signature_t *)(ext_header + 1);
+
+ /*
+ * Check the extended table size.
+ */
+ ext_table_size = sizeof(*ext_header) +
+ ext_header->sig_count * sizeof(*ext_table);
+ if (ext_table_size + payload_size > total_size) {
+ WARNX(2, "%s: broken extended signature table", path);
+ goto no_table;
+ }
+
+ /*
+ * Check the extended table signature.
+ */
+ sum = 0;
+ for (i = 0; i < (ext_table_size / sizeof(uint32_t)); i++)
+ sum += *((uint32_t *)ext_header + i);
+ if (sum != 0) {
+ WARNX(2, "%s: extended signature table checksum invalid",
+ path);
+ goto no_table;
+ }
+ have_ext_table = 1;
+ }
+
+no_table:
+ fw_data = fw_header + 1; /* Pointer to the update data. */
+
+ /*
+ * Check if the given image is ok for this cpu.
+ */
+ if (signature == fw_header->cpu_signature &&
+ (flags & fw_header->cpu_flags) != 0)
+ goto matched;
+ else if (have_ext_table != 0) {
+ for (i = 0; i < ext_header->sig_count; i++) {
+ uint32_t sig = ext_table[i].cpu_signature;
+ if (signature == sig &&
+ (flags & ext_table[i].cpu_flags) != 0)
+ goto matched;
+ }
+ } else
+ goto fail;
+
+matched:
+ if (revision >= fw_header->revision) {
+ WARNX(1, "skipping %s of rev %#x: up to date",
+ path, fw_header->revision);
+ return;
+ }
+ fprintf(stderr, "%s: updating cpu %s from rev %#x to rev %#x... ",
+ path, dev, revision, fw_header->revision);
+ args.data = fw_data;
+ args.size = data_size;
+ error = ioctl(devfd, CPUCTL_UPDATE, &args);
+ if (error < 0) {
+ fprintf(stderr, "failed.\n");
+ WARN(0, "ioctl()");
+ goto fail;
+ }
+ fprintf(stderr, "done.\n");
+
+fail:
+ if (fw_image != MAP_FAILED)
+ if (munmap(fw_image, st.st_size) != 0)
+ warn("munmap(%s)", path);
+ if (devfd >= 0)
+ close(devfd);
+ if (fd >= 0)
+ close(fd);
+ return;
+}
diff --git a/usr.sbin/cpucontrol/intel.h b/usr.sbin/cpucontrol/intel.h
new file mode 100644
index 0000000..0303e69
--- /dev/null
+++ b/usr.sbin/cpucontrol/intel.h
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 2006, 2008 Stanislav Sedov <stas@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef INTEL_H
+#define INTEL_H
+
+/*
+ * Prototypes.
+ */
+ucode_probe_t intel_probe;
+ucode_update_t intel_update;
+
+typedef struct intel_fw_header {
+ uint32_t header_version; /* Version of the header. */
+ int32_t revision; /* Unique version number. */
+ uint32_t date; /* Date of creation in BCD. */
+ uint32_t cpu_signature; /* Extended family, extended
+ model, type, family, model
+ and stepping. */
+ uint32_t checksum; /* Sum of all DWORDS should
+ be 0. */
+ uint32_t loader_revision; /* Version of the loader
+ required to load update. */
+ uint32_t cpu_flags; /* Platform IDs encoded in
+ the lower 8 bits. */
+ uint32_t data_size;
+ uint32_t total_size;
+ uint8_t reserved[12];
+} intel_fw_header_t;
+
+typedef struct intel_cpu_signature {
+ uint32_t cpu_signature;
+ uint32_t cpu_flags;
+ uint32_t checksum;
+} intel_cpu_signature_t;
+
+typedef struct intel_ext_header {
+ uint32_t sig_count;
+ uint32_t checksum;
+ uint8_t reserved[12];
+} intel_ext_header_t;
+
+#define INTEL_HEADER_VERSION 0x00000001
+#define INTEL_LOADER_REVISION 0x00000001
+
+#endif /* !INTEL_H */
OpenPOWER on IntegriCloud