aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/cpucontrol
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/cpucontrol')
-rw-r--r--usr.sbin/cpucontrol/Makefile11
-rw-r--r--usr.sbin/cpucontrol/Makefile.depend15
-rw-r--r--usr.sbin/cpucontrol/amd.c153
-rw-r--r--usr.sbin/cpucontrol/amd.h51
-rw-r--r--usr.sbin/cpucontrol/amd10h.c158
-rw-r--r--usr.sbin/cpucontrol/cpucontrol.8204
-rw-r--r--usr.sbin/cpucontrol/cpucontrol.c574
-rw-r--r--usr.sbin/cpucontrol/cpucontrol.h66
-rw-r--r--usr.sbin/cpucontrol/intel.c268
-rw-r--r--usr.sbin/cpucontrol/intel.h70
-rw-r--r--usr.sbin/cpucontrol/via.c193
-rw-r--r--usr.sbin/cpucontrol/via.h63
12 files changed, 1826 insertions, 0 deletions
diff --git a/usr.sbin/cpucontrol/Makefile b/usr.sbin/cpucontrol/Makefile
new file mode 100644
index 000000000000..875c1eec4121
--- /dev/null
+++ b/usr.sbin/cpucontrol/Makefile
@@ -0,0 +1,11 @@
+PROG= cpucontrol
+MAN= cpucontrol.8
+SRCS= cpucontrol.c intel.c amd.c amd10h.c via.c ucode_subr.c
+
+.PATH: ${SRCTOP}/sys/x86/x86
+
+NO_WCAST_ALIGN=
+
+CFLAGS+= -I${.CURDIR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/cpucontrol/Makefile.depend b/usr.sbin/cpucontrol/Makefile.depend
new file mode 100644
index 000000000000..6ef78fac5cbf
--- /dev/null
+++ b/usr.sbin/cpucontrol/Makefile.depend
@@ -0,0 +1,15 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/usr.sbin/cpucontrol/amd.c b/usr.sbin/cpucontrol/amd.c
new file mode 100644
index 000000000000..21a3389ced2f
--- /dev/null
+++ b/usr.sbin/cpucontrol/amd.c
@@ -0,0 +1,153 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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>
+#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 struct ucode_update_params *params)
+{
+ int devfd;
+ unsigned int i;
+ const char *dev, *path;
+ const uint32_t *fw_image;
+ const amd_fw_header_t *fw_header;
+ uint32_t sum;
+ uint32_t signature;
+ const uint32_t *fw_data;
+ size_t fw_size;
+ cpuctl_cpuid_args_t idargs = {
+ .level = 1, /* Request signature. */
+ };
+ cpuctl_update_args_t args;
+ int error;
+
+ dev = params->dev_path;
+ path = params->fw_path;
+ devfd = params->devfd;
+ fw_image = params->fwimage;
+
+ assert(path);
+ assert(dev);
+
+ 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.
+ */
+ if (params->fwsize < sizeof(*fw_header)) {
+ WARNX(2, "file too short: %s", path);
+ goto fail;
+ }
+ fw_header = (const 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 = (const uint32_t *)(fw_header + 1);
+ fw_size = (params->fwsize - 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 = __DECONST(void *, fw_image);
+ args.size = params->fwsize;
+ error = ioctl(devfd, CPUCTL_UPDATE, &args);
+ if (error < 0) {
+ fprintf(stderr, "failed.\n");
+ warn("ioctl()");
+ goto fail;
+ }
+ fprintf(stderr, "done.\n");
+ }
+
+fail:
+ return;
+}
diff --git a/usr.sbin/cpucontrol/amd.h b/usr.sbin/cpucontrol/amd.h
new file mode 100644
index 000000000000..8e4efe60dca6
--- /dev/null
+++ b/usr.sbin/cpucontrol/amd.h
@@ -0,0 +1,51 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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.
+ */
+
+#ifndef AMD_H
+#define AMD_H
+
+/*
+ * Prototypes.
+ */
+ucode_probe_t amd_probe;
+ucode_update_t amd_update;
+ucode_probe_t amd10h_probe;
+ucode_update_t amd10h_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/amd10h.c b/usr.sbin/cpucontrol/amd10h.c
new file mode 100644
index 000000000000..9fc861fe5914
--- /dev/null
+++ b/usr.sbin/cpucontrol/amd10h.c
@@ -0,0 +1,158 @@
+/*-
+ * Copyright (c) 2012 Andriy Gapon <avg@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/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 <x86/ucode.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+
+#include "cpucontrol.h"
+#include "amd.h"
+
+int
+amd10h_probe(int fd)
+{
+ char vendor[13];
+ cpuctl_cpuid_args_t idargs;
+ uint32_t family;
+ uint32_t signature;
+ int error;
+
+ 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);
+
+ idargs.level = 1;
+ error = ioctl(fd, CPUCTL_CPUID, &idargs);
+ if (error < 0) {
+ WARN(0, "ioctl()");
+ return (1);
+ }
+ signature = idargs.data[0];
+ family = ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff);
+ if (family < 0x10)
+ return (1);
+ return (0);
+}
+
+void
+amd10h_update(const struct ucode_update_params *params)
+{
+ cpuctl_cpuid_args_t idargs;
+ cpuctl_msr_args_t msrargs;
+ cpuctl_update_args_t args;
+ const uint8_t *fw_image;
+ const char *dev, *path;
+ const void *selected_fw;
+ size_t fw_size;
+ size_t selected_size;
+ uint32_t revision;
+ uint32_t new_rev, old_rev;
+ uint32_t signature;
+ int devfd;
+ int error;
+
+ dev = params->dev_path;
+ path = params->fw_path;
+ devfd = params->devfd;
+ fw_image = params->fwimage;
+ fw_size = params->fwsize;
+
+ assert(path);
+ assert(dev);
+
+ idargs.level = 1;
+ error = ioctl(devfd, CPUCTL_CPUID, &idargs);
+ if (error < 0) {
+ WARN(0, "ioctl()");
+ goto done;
+ }
+ signature = idargs.data[0];
+
+ msrargs.msr = MSR_BIOS_SIGN;
+ error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
+ if (error < 0) {
+ WARN(0, "ioctl(%s)", dev);
+ goto done;
+ }
+ old_rev = revision = (uint32_t)msrargs.data;
+
+ selected_fw = ucode_amd_find(path, signature, &revision, fw_image,
+ fw_size, &selected_size);
+
+ if (selected_fw != NULL) {
+ WARNX(1, "selected ucode size is %zu", selected_size);
+ fprintf(stderr,
+ "%s: updating cpu %s from rev %#x to rev %#x... ",
+ path, dev, old_rev, revision);
+
+ args.data = __DECONST(void *, selected_fw);
+ args.size = selected_size;
+ error = ioctl(devfd, CPUCTL_UPDATE, &args);
+ if (error < 0) {
+ fprintf(stderr, "failed.\n");
+ warn("ioctl()");
+ goto done;
+ }
+ fprintf(stderr, "done.\n");
+ }
+
+ msrargs.msr = MSR_BIOS_SIGN;
+ error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
+ if (error < 0) {
+ WARN(0, "ioctl(%s)", dev);
+ goto done;
+ }
+ new_rev = (uint32_t)msrargs.data;
+ if (new_rev != revision)
+ WARNX(0, "revision after update %#x", new_rev);
+
+done:
+ return;
+}
diff --git a/usr.sbin/cpucontrol/cpucontrol.8 b/usr.sbin/cpucontrol/cpucontrol.8
new file mode 100644
index 000000000000..6f99224bd3a3
--- /dev/null
+++ b/usr.sbin/cpucontrol/cpucontrol.8
@@ -0,0 +1,204 @@
+.\" 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.
+.\"
+.Dd January 5, 2018
+.Dt CPUCONTROL 8
+.Os
+.Sh NAME
+.Nm cpucontrol
+.Nd control utility for the
+.Xr cpuctl 4
+device
+.Sh SYNOPSIS
+.Bk
+.Nm
+.Op Fl v
+.Fl m Ar msr
+.Ar device
+.Ek
+.Bk
+.Nm
+.Op Fl v
+.Fl m Ar msr Ns = Ns Ar value
+.Ar device
+.Ek
+.Bk
+.Nm
+.Op Fl v
+.Fl m Ar msr Ns &= Ns Ar mask
+.Ar device
+.Ek
+.Bk
+.Nm
+.Op Fl v
+.Fl m Ar msr Ns |= Ns Ar mask
+.Ar device
+.Ek
+.Bk
+.Nm
+.Op Fl v
+.Fl i Ar level
+.Ar device
+.Ek
+.Bk
+.Nm
+.Op Fl v
+.Fl i Ar level,level_type
+.Ar device
+.Ek
+.Bk
+.Nm
+.Op Fl vn
+.Op Fl d Ar datadir
+.Fl u
+.Ar device
+.Ek
+.Bk
+.Nm
+.Fl e
+.Ar device
+.Ek
+.Sh DESCRIPTION
+The
+.Nm
+utility can be used to read and write arbitrary machine-specific
+CPU registers via the
+.Xr cpuctl 4
+special device.
+It can also be used to apply CPU firmware updates.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d Ar datadir
+Directory paths where to look for microcode images.
+The option can be specified multiple times.
+The paths are added in order of the options appearance on the command
+line, default directories are appended after the user-supplied paths.
+.It Fl n
+Do not look for the microcode images in the standard directories.
+Currently standard directory to look for the microcode update files is
+.Pa /usr/local/share/cpucontrol .
+.It Fl m Ar msr
+Show value of the specified MSR.
+MSR register number should be given as a hexadecimal number.
+The high word is printed first, then the low word is printed second.
+.It Fl m Ar msr Ns = Ns Ar value
+Store the
+.Ar value
+in the specified MSR register.
+The
+.Ar value
+argument can be prefixed with ~ operator.
+In this case the inverted value of argument will be stored in the register.
+.It Fl m Ar msr Ns &= Ns Ar mask
+Store the result of bitwise AND operation between
+.Ar mask
+and the current MSR value in the MSR register.
+The
+.Ar mask
+argument can be prefixed with ~ operator.
+In this case the inverted value of mask will be used.
+.It Fl m Ar msr Ns |= Ns Ar mask
+Store the result of bitwise OR operation between
+.Ar mask
+and the current MSR value in the MSR register.
+The
+.Ar mask
+argument can be prefixed with ~ operator.
+In this case the inverted value of mask will be used.
+.It Fl i Ar level
+Retrieve CPUID info.
+Level should be given as a hex number.
+.It Fl i Ar level,level_type
+Retrieve CPUID info.
+Level and level_type should be given as hex numbers.
+.It Fl u
+Apply CPU firmware updates.
+The
+.Nm
+utility will walk through the configured data directories
+and apply all firmware updates available for this CPU.
+.It Fl e
+Re-evaluate the kernel flags indicating the present CPU features.
+This command is typically executed after a firmware update was applied
+which changes information reported by the
+.Dv CPUID
+instruction.
+.Pp
+.Bf -symbolic
+Only execute the
+.Fl e
+command after the microcode update was applied to all CPUs in the system.
+The kernel does not operate correctly if the features of processors are
+not identical.
+.Ef
+.It Fl v
+Increase the verbosity level.
+.It Fl h
+Show help message.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.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
+The following command will clear the second bit of TSC register:
+.Pp
+.Dq Li "cpucontrol -m 0x10&=~0x02 /dev/cpuctl0" .
+.Pp
+The following command will set the forth and second bit of TSC register:
+.Pp
+.Dq Li "cpucontrol -m 0x10|=0x0a /dev/cpuctl0" .
+.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 updates on CPU 0 from images located at
+.Pa /usr/local/share/cpuctl
+use the following command:
+.Pp
+.Dq Li "cpucontrol -nd /usr/local/share/cpuctl -u /dev/cpuctl0"
+.Sh SEE ALSO
+.Xr cpuctl 4
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 7.2 .
+.Sh AUTHORS
+The
+.Nm
+utility and this manual page was written by
+.An Stanislav Sedov Aq Mt stas@FreeBSD.org .
diff --git a/usr.sbin/cpucontrol/cpucontrol.c b/usr.sbin/cpucontrol/cpucontrol.c
new file mode 100644
index 000000000000..9cc3968de01d
--- /dev/null
+++ b/usr.sbin/cpucontrol/cpucontrol.c
@@ -0,0 +1,574 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2008-2011 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>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sysexits.h>
+
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/cpuctl.h>
+
+#include "cpucontrol.h"
+#include "amd.h"
+#include "intel.h"
+#include "via.h"
+
+int verbosity_level = 0;
+
+#define DEFAULT_DATADIR _PATH_LOCALBASE "/share/cpucontrol"
+
+#define FLAG_I 0x01
+#define FLAG_M 0x02
+#define FLAG_U 0x04
+#define FLAG_N 0x08
+#define FLAG_E 0x10
+
+#define OP_INVAL 0x00
+#define OP_READ 0x01
+#define OP_WRITE 0x02
+#define OP_OR 0x04
+#define OP_AND 0x08
+
+#define HIGH(val) (uint32_t)(((val) >> 32) & 0xffffffff)
+#define LOW(val) (uint32_t)((val) & 0xffffffff)
+
+struct datadir {
+ const char *path;
+ SLIST_ENTRY(datadir) next;
+};
+static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(datadirs);
+
+static struct ucode_handler {
+ ucode_probe_t *probe;
+ ucode_update_t *update;
+} handlers[] = {
+ { intel_probe, intel_update },
+ { amd10h_probe, amd10h_update },
+ { amd_probe, amd_update },
+ { via_probe, via_update },
+};
+#define NHANDLERS (sizeof(handlers) / sizeof(*handlers))
+
+static void usage(void);
+static int do_cpuid(const char *cmdarg, const char *dev);
+static int do_cpuid_count(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(void)
+{
+ const char *name;
+
+ name = getprogname();
+ if (name == NULL)
+ name = "cpuctl";
+ fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | "
+ "-i level | -i level,level_type | -e | -u] device\n", name);
+ exit(EX_USAGE);
+}
+
+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) {
+ WARN(0, "error opening %s for reading", dev);
+ return (1);
+ }
+ error = ioctl(fd, CPUCTL_CPUID, &args);
+ if (error < 0) {
+ WARN(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_cpuid_count(const char *cmdarg, const char *dev)
+{
+ char *cmdarg1, *endptr, *endptr1;
+ unsigned int level, level_type;
+ cpuctl_cpuid_count_args_t args;
+ int fd, error;
+
+ assert(cmdarg != NULL);
+ assert(dev != NULL);
+
+ level = strtoul(cmdarg, &endptr, 16);
+ if (*cmdarg == '\0' || *endptr == '\0') {
+ WARNX(0, "incorrect or missing operand: %s", cmdarg);
+ usage();
+ /* NOTREACHED */
+ }
+ /* Locate the comma... */
+ cmdarg1 = strstr(endptr, ",");
+ /* ... and skip past it */
+ cmdarg1 += 1;
+ level_type = strtoul(cmdarg1, &endptr1, 16);
+ if (*cmdarg1 == '\0' || *endptr1 != '\0') {
+ WARNX(0, "incorrect or missing operand: %s", cmdarg);
+ usage();
+ /* NOTREACHED */
+ }
+
+ /*
+ * Fill ioctl argument structure.
+ */
+ args.level = level;
+ args.level_type = level_type;
+ fd = open(dev, O_RDONLY);
+ if (fd < 0) {
+ WARN(0, "error opening %s for reading", dev);
+ return (1);
+ }
+ error = ioctl(fd, CPUCTL_CPUID_COUNT, &args);
+ if (error < 0) {
+ WARN(0, "ioctl(%s, CPUCTL_CPUID_COUNT)", dev);
+ close(fd);
+ return (error);
+ }
+ fprintf(stdout, "cpuid level 0x%x, level_type 0x%x: 0x%.8x 0x%.8x "
+ "0x%.8x 0x%.8x\n", level, level_type, 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;
+ size_t len;
+ uint64_t data = 0;
+ unsigned long command;
+ int do_invert = 0, op;
+ int fd, error;
+ const char *command_name;
+ char *endptr;
+ char *p;
+
+ assert(cmdarg != NULL);
+ assert(dev != NULL);
+ len = strlen(cmdarg);
+ if (len == 0) {
+ WARNX(0, "MSR register expected");
+ usage();
+ /* NOTREACHED */
+ }
+
+ /*
+ * Parse command string.
+ */
+ msr = strtoul(cmdarg, &endptr, 16);
+ switch (*endptr) {
+ case '\0':
+ op = OP_READ;
+ break;
+ case '=':
+ op = OP_WRITE;
+ break;
+ case '&':
+ op = OP_AND;
+ endptr++;
+ break;
+ case '|':
+ op = OP_OR;
+ endptr++;
+ break;
+ default:
+ op = OP_INVAL;
+ }
+ if (op != OP_READ) { /* Complex operation. */
+ if (*endptr != '=')
+ op = OP_INVAL;
+ else {
+ p = ++endptr;
+ if (*p == '~') {
+ do_invert = 1;
+ p++;
+ }
+ data = strtoull(p, &endptr, 16);
+ if (*p == '\0' || *endptr != '\0') {
+ WARNX(0, "argument required: %s", cmdarg);
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ }
+ if (op == OP_INVAL) {
+ WARNX(0, "invalid operator: %s", cmdarg);
+ usage();
+ /* NOTREACHED */
+ }
+
+ /*
+ * Fill ioctl argument structure.
+ */
+ args.msr = msr;
+ if ((do_invert != 0) ^ (op == OP_AND))
+ args.data = ~data;
+ else
+ args.data = data;
+ switch (op) {
+ case OP_READ:
+ command = CPUCTL_RDMSR;
+ command_name = "RDMSR";
+ break;
+ case OP_WRITE:
+ command = CPUCTL_WRMSR;
+ command_name = "WRMSR";
+ break;
+ case OP_OR:
+ command = CPUCTL_MSRSBIT;
+ command_name = "MSRSBIT";
+ break;
+ case OP_AND:
+ command = CPUCTL_MSRCBIT;
+ command_name = "MSRCBIT";
+ break;
+ default:
+ abort();
+ }
+ fd = open(dev, op == OP_READ ? O_RDONLY : O_WRONLY);
+ if (fd < 0) {
+ WARN(0, "error opening %s for %s", dev,
+ op == OP_READ ? "reading" : "writing");
+ return (1);
+ }
+ error = ioctl(fd, command, &args);
+ if (error < 0) {
+ WARN(0, "ioctl(%s, CPUCTL_%s (%#x))", dev, command_name, msr);
+ close(fd);
+ return (1);
+ }
+ if (op == OP_READ)
+ fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr,
+ HIGH(args.data), LOW(args.data));
+ close(fd);
+ return (0);
+}
+
+static int
+do_eval_cpu_features(const char *dev)
+{
+ int fd, error;
+
+ assert(dev != NULL);
+
+ fd = open(dev, O_RDWR);
+ if (fd < 0) {
+ WARN(0, "error opening %s for writing", dev);
+ return (1);
+ }
+ error = ioctl(fd, CPUCTL_EVAL_CPU_FEATURES, NULL);
+ if (error < 0)
+ WARN(0, "ioctl(%s, CPUCTL_EVAL_CPU_FEATURES)", dev);
+ close(fd);
+ return (error);
+}
+
+static int
+try_a_fw_image(const char *dev_path, int devfd, int fwdfd, const char *dpath,
+ const char *fname, struct ucode_handler *handler)
+{
+ struct ucode_update_params parm;
+ struct stat st;
+ char *fw_path;
+ void *fw_map;
+ int fwfd, rc;
+
+ rc = 0;
+ fw_path = NULL;
+ fw_map = MAP_FAILED;
+ fwfd = openat(fwdfd, fname, O_RDONLY);
+ if (fwfd < 0) {
+ WARN(0, "openat(%s, %s)", dpath, fname);
+ goto out;
+ }
+
+ rc = asprintf(&fw_path, "%s/%s", dpath, fname);
+ if (rc == -1) {
+ WARNX(0, "out of memory");
+ rc = ENOMEM;
+ goto out;
+ }
+
+ rc = fstat(fwfd, &st);
+ if (rc != 0) {
+ WARN(0, "fstat(%s)", fw_path);
+ rc = 0;
+ goto out;
+ }
+ if (!S_ISREG(st.st_mode))
+ goto out;
+ if (st.st_size <= 0) {
+ WARN(0, "%s: empty", fw_path);
+ goto out;
+ }
+
+ fw_map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fwfd, 0);
+ if (fw_map == MAP_FAILED) {
+ WARN(0, "mmap(%s)", fw_path);
+ goto out;
+ }
+
+
+ memset(&parm, 0, sizeof(parm));
+ parm.devfd = devfd;
+ parm.fwimage = fw_map;
+ parm.fwsize = st.st_size;
+ parm.dev_path = dev_path;
+ parm.fw_path = fw_path;
+
+ handler->update(&parm);
+
+out:
+ if (fw_map != MAP_FAILED)
+ munmap(fw_map, st.st_size);
+ free(fw_path);
+ if (fwfd >= 0)
+ close(fwfd);
+ return (rc);
+}
+
+static int
+do_update(const char *dev)
+{
+ int fd, fwdfd;
+ unsigned int i;
+ int error;
+ struct ucode_handler *handler;
+ struct datadir *dir;
+ DIR *dirp;
+ struct dirent *direntry;
+
+ fd = open(dev, O_RDONLY);
+ if (fd < 0) {
+ WARN(0, "error opening %s for reading", dev);
+ return (1);
+ }
+
+ /*
+ * Find the appropriate handler for CPU.
+ */
+ 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 %s", dev);
+ close(fd);
+ return (1);
+ }
+ close(fd);
+
+ fd = open(dev, O_RDWR);
+ if (fd < 0) {
+ WARN(0, "error opening %s for writing", dev);
+ return (1);
+ }
+
+ /*
+ * Process every image in specified data directories.
+ */
+ SLIST_FOREACH(dir, &datadirs, next) {
+ fwdfd = open(dir->path, O_RDONLY);
+ if (fwdfd < 0) {
+ WARN(1, "skipping directory %s: not accessible", dir->path);
+ continue;
+ }
+ dirp = fdopendir(fwdfd);
+ if (dirp == NULL) {
+ WARNX(0, "out of memory");
+ close(fwdfd);
+ close(fd);
+ return (1);
+ }
+
+ while ((direntry = readdir(dirp)) != NULL) {
+ if (direntry->d_namlen == 0)
+ continue;
+ if (direntry->d_type == DT_DIR)
+ continue;
+
+ error = try_a_fw_image(dev, fd, fwdfd, dir->path,
+ direntry->d_name, handler);
+ if (error != 0) {
+ closedir(dirp);
+ close(fd);
+ return (1);
+ }
+ }
+ error = closedir(dirp);
+ if (error != 0)
+ WARN(0, "closedir(%s)", dir->path);
+ }
+ close(fd);
+ 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[])
+{
+ struct datadir *elm;
+ int c, flags;
+ const char *cmdarg;
+ const char *dev;
+ int error;
+
+ flags = 0;
+ error = 0;
+ cmdarg = ""; /* To keep gcc3 happy. */
+
+ while ((c = getopt(argc, argv, "d:ehi:m:nuv")) != -1) {
+ switch (c) {
+ case 'd':
+ datadir_add(optarg);
+ break;
+ case 'e':
+ flags |= FLAG_E;
+ break;
+ case 'i':
+ flags |= FLAG_I;
+ cmdarg = optarg;
+ break;
+ case 'm':
+ flags |= FLAG_M;
+ cmdarg = optarg;
+ break;
+ case 'n':
+ flags |= FLAG_N;
+ 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 */
+ }
+ if ((flags & FLAG_N) == 0)
+ datadir_add(DEFAULT_DATADIR);
+ dev = argv[0];
+ c = flags & (FLAG_E | FLAG_I | FLAG_M | FLAG_U);
+ switch (c) {
+ case FLAG_I:
+ if (strstr(cmdarg, ",") != NULL)
+ error = do_cpuid_count(cmdarg, dev);
+ else
+ error = do_cpuid(cmdarg, dev);
+ break;
+ case FLAG_M:
+ error = do_msr(cmdarg, dev);
+ break;
+ case FLAG_U:
+ error = do_update(dev);
+ break;
+ case FLAG_E:
+ error = do_eval_cpu_features(dev);
+ break;
+ default:
+ usage(); /* Only one command can be selected. */
+ }
+ while ((elm = SLIST_FIRST(&datadirs)) != NULL) {
+ SLIST_REMOVE_HEAD(&datadirs, next);
+ free(elm);
+ }
+ return (error == 0 ? 0 : 1);
+}
diff --git a/usr.sbin/cpucontrol/cpucontrol.h b/usr.sbin/cpucontrol/cpucontrol.h
new file mode 100644
index 000000000000..d03c30a6bd6d
--- /dev/null
+++ b/usr.sbin/cpucontrol/cpucontrol.h
@@ -0,0 +1,66 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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.
+ */
+
+#ifndef CPUCONTROL_H
+#define CPUCONTROL_H
+
+#include <stddef.h>
+
+typedef int ucode_probe_t(int fd);
+struct ucode_update_params {
+ int devfd; /* RDWR handle to cpucontrol device */
+ const void *fwimage; /* READ mapping of firmware image */
+ size_t fwsize; /* Non-zero size of firmware image */
+ const char *dev_path; /* cpucontrol device path, for logging */
+ const char *fw_path; /* firmware image path, for logging */
+};
+typedef void ucode_update_t(const struct ucode_update_params *params);
+
+extern int verbosity_level;
+
+#ifdef DEBUG
+#include <stdio.h>
+# 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 000000000000..d775d66d33aa
--- /dev/null
+++ b/usr.sbin/cpucontrol/intel.c
@@ -0,0 +1,268 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+#include <errno.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 struct ucode_update_params *params)
+{
+ int devfd;
+ const char *dev, *path;
+ const uint32_t *fw_image;
+ int have_ext_table;
+ uint32_t sum;
+ unsigned int i;
+ size_t payload_size;
+ const intel_fw_header_t *fw_header;
+ const intel_cpu_signature_t *ext_table;
+ const intel_ext_header_t *ext_header;
+ uint32_t sig, signature, flags;
+ int32_t revision;
+ ssize_t ext_size;
+ size_t ext_table_size;
+ const void *fw_data;
+ size_t data_size, total_size;
+ cpuctl_msr_args_t msrargs = {
+ .msr = MSR_BIOS_SIGN,
+ .data = 0,
+ };
+ cpuctl_cpuid_args_t idargs = {
+ .level = 1, /* Signature. */
+ };
+ cpuctl_update_args_t args;
+ int error;
+
+ dev = params->dev_path;
+ path = params->fw_path;
+ devfd = params->devfd;
+ fw_image = params->fwimage;
+
+ assert(path);
+ assert(dev);
+
+ ext_table = NULL;
+ ext_header = NULL;
+
+ error = ioctl(devfd, CPUCTL_WRMSR, &msrargs);
+ if (error < 0) {
+ WARN(0, "ioctl(%s)", dev);
+ goto fail;
+ }
+ error = ioctl(devfd, CPUCTL_CPUID, &idargs);
+ if (error < 0) {
+ WARN(0, "ioctl(%s)", dev);
+ goto fail;
+ }
+ signature = idargs.data[0];
+ msrargs.msr = MSR_IA32_PLATFORM_ID;
+ 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.
+ */
+ if (params->fwsize < sizeof(*fw_header)) {
+ WARNX(2, "file too short: %s", path);
+ goto fail;
+ }
+
+ fw_header = (const 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 > params->fwsize) {
+ 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 += *((const 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 = (const intel_ext_header_t *)
+ ((const char *)fw_image + payload_size);
+ ext_table = (const 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 += *((const 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++) {
+ sig = ext_table[i].cpu_signature;
+ if (signature == sig &&
+ (flags & ext_table[i].cpu_flags) != 0)
+ goto matched;
+ }
+ }
+ goto fail;
+
+matched:
+ if (revision >= fw_header->revision) {
+ WARNX(1, "skipping %s of rev %#x: up to date",
+ path, fw_header->revision);
+ goto fail;
+ }
+ fprintf(stderr, "%s: updating cpu %s from rev %#x to rev %#x... ",
+ path, dev, revision, fw_header->revision);
+ args.data = __DECONST(void *, fw_data);
+ args.size = data_size;
+ error = ioctl(devfd, CPUCTL_UPDATE, &args);
+ if (error < 0) {
+ error = errno;
+ fprintf(stderr, "failed.\n");
+ errno = error;
+ WARN(0, "ioctl()");
+ goto fail;
+ }
+ fprintf(stderr, "done.\n");
+
+fail:
+ return;
+}
diff --git a/usr.sbin/cpucontrol/intel.h b/usr.sbin/cpucontrol/intel.h
new file mode 100644
index 000000000000..7aa58b333de4
--- /dev/null
+++ b/usr.sbin/cpucontrol/intel.h
@@ -0,0 +1,70 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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.
+ */
+
+#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 */
diff --git a/usr.sbin/cpucontrol/via.c b/usr.sbin/cpucontrol/via.c
new file mode 100644
index 000000000000..432399c69e35
--- /dev/null
+++ b/usr.sbin/cpucontrol/via.c
@@ -0,0 +1,193 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2011 Fabien Thomas <fabient@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>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+#include <errno.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 "via.h"
+
+int
+via_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, CENTAUR_VENDOR_ID, sizeof(CENTAUR_VENDOR_ID)) != 0)
+ return (1);
+
+ /* TODO: detect Nano CPU. */
+ return (0);
+}
+
+void
+via_update(const struct ucode_update_params *params)
+{
+ int devfd;
+ const char *dev, *path;
+ const uint32_t *fw_image;
+ uint32_t sum;
+ unsigned int i;
+ size_t payload_size;
+ const via_fw_header_t *fw_header;
+ uint32_t signature;
+ int32_t revision;
+ const 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;
+
+ dev = params->dev_path;
+ path = params->fw_path;
+ devfd = params->devfd;
+ fw_image = params->fwimage;
+
+ assert(path);
+ assert(dev);
+
+ 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.
+ */
+ 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);
+
+ if (params->fwsize < sizeof(*fw_header)) {
+ WARNX(2, "file too short: %s", path);
+ goto fail;
+ }
+
+ fw_header = (const via_fw_header_t *)fw_image;
+ if (fw_header->signature != VIA_HEADER_SIGNATURE ||
+ fw_header->loader_revision != VIA_LOADER_REVISION) {
+ WARNX(2, "%s is not a valid via firmware: version mismatch",
+ path);
+ goto fail;
+ }
+ data_size = fw_header->data_size;
+ total_size = fw_header->total_size;
+ if (total_size > params->fwsize) {
+ 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 += *((const uint32_t *)fw_image + i);
+ if (sum != 0) {
+ WARNX(2, "%s: update data checksum invalid", path);
+ goto fail;
+ }
+
+ 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)
+ goto fail;
+
+ if (fw_header->revision != 0 && revision >= fw_header->revision) {
+ WARNX(1, "skipping %s of rev %#x: up to date",
+ path, fw_header->revision);
+ goto fail;
+ }
+ fprintf(stderr, "%s: updating cpu %s from rev %#x to rev %#x... ",
+ path, dev, revision, fw_header->revision);
+ args.data = __DECONST(void *, fw_data);
+ args.size = data_size;
+ error = ioctl(devfd, CPUCTL_UPDATE, &args);
+ if (error < 0) {
+ error = errno;
+ fprintf(stderr, "failed.\n");
+ errno = error;
+ WARN(0, "ioctl()");
+ goto fail;
+ }
+ fprintf(stderr, "done.\n");
+
+fail:
+ return;
+}
diff --git a/usr.sbin/cpucontrol/via.h b/usr.sbin/cpucontrol/via.h
new file mode 100644
index 000000000000..d2e5c82878b8
--- /dev/null
+++ b/usr.sbin/cpucontrol/via.h
@@ -0,0 +1,63 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2011 Fabien Thomas <fabient@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.
+ */
+
+#ifndef VIA_H
+#define VIA_H
+
+/*
+ * Prototypes.
+ */
+ucode_probe_t via_probe;
+ucode_update_t via_update;
+
+typedef struct via_fw_header {
+ uint32_t signature; /* Signature. */
+ 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 reserverd1; /* Platform IDs encoded in
+ the lower 8 bits. */
+ uint32_t data_size;
+ uint32_t total_size;
+ uint8_t reserved2[12];
+} via_fw_header_t;
+
+typedef struct via_cpu_signature {
+ uint32_t cpu_signature;
+ uint32_t checksum;
+} via_cpu_signature_t;
+
+#define VIA_HEADER_SIGNATURE 0x53415252
+#define VIA_LOADER_REVISION 0x00000001
+
+#endif /* !VIA_H */