summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--share/man/man4/cpuctl.453
-rw-r--r--sys/dev/cpuctl/cpuctl.c43
-rw-r--r--sys/sys/cpuctl.h9
-rw-r--r--usr.sbin/cpucontrol/cpucontrol.89
-rw-r--r--usr.sbin/cpucontrol/cpucontrol.c59
5 files changed, 149 insertions, 24 deletions
diff --git a/share/man/man4/cpuctl.4 b/share/man/man4/cpuctl.4
index ed80f8a..3cc0138 100644
--- a/share/man/man4/cpuctl.4
+++ b/share/man/man4/cpuctl.4
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd June 30, 2009
+.Dd June 20, 2014
.Dt CPUCTL 4
.Os
.Sh NAME
@@ -65,7 +65,7 @@ All of the supported operations are invoked using the
.Xr ioctl 2
system call.
Currently, the following ioctls are defined:
-.Bl -tag -width CPUCTL_UPDATE
+.Bl -tag -width CPUCTL_CPUID_COUNT
.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.
@@ -87,20 +87,59 @@ Set/clear MSR bits according to the mask given in the
field.
.It Dv CPUCTL_CPUID Fa cpuctl_cpuid_args_t *args
Retrieve CPUID information.
-Arguments are supplied in
-the following struct:
+Arguments are supplied in the following structure:
.Bd -literal
typedef struct {
- int level; /* CPUID level */
+ int level; /* CPUID level */
uint32_t data[4];
} cpuctl_cpuid_args_t;
.Ed
-.Pp
+It is equivalent to the
+.Dv CPUCTL_CPUID_COUNT
+request with
+.Va level_type
+set to 0.
+.It Dv CPUCTL_CPUID_COUNT Fa cpuctl_cpuid_count_args_t *args
+Retrieve CPUID information.
+Arguments are supplied in the following structure:
+.Bd -literal
+typedef struct {
+ int level; /* CPUID level */
+ int level_type; /* CPUID level type */
+ uint32_t data[4];
+} cpuctl_cpuid_count_args_t;
+.Ed
The
.Va level
-field indicates the CPUID level to retrieve information for, while the
+field indicates the CPUID level to retrieve,
+it is loaded into the
+.Va %eax
+register before the CPUID instruction is executed,
+The
+.Va level_type
+field indicates the CPUID level type to retrieve,
+it is loaded into the
+.Va %ecx
+register.
+.Pp
+The
.Va data
field is used to store the received CPUID data.
+That is,
+.Va data[0]
+contains the value of
+.Va %eax
+register after the CPUID instruction is executed,
+.Va data[1]
+is for
+.Va %ebx ,
+.Va data[2]
+for
+.Va %ecx ,
+and
+.Va data[3]
+for
+.Va %edx .
.It Dv CPUCTL_UPDATE cpuctl_update_args_t *args
Update CPU firmware (microcode).
The structure is defined in
diff --git a/sys/dev/cpuctl/cpuctl.c b/sys/dev/cpuctl/cpuctl.c
index 236d687..96ceb0d 100644
--- a/sys/dev/cpuctl/cpuctl.c
+++ b/sys/dev/cpuctl/cpuctl.c
@@ -67,7 +67,9 @@ static d_ioctl_t cpuctl_ioctl;
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,
+static void cpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data,
+ struct thread *td);
+static void cpuctl_do_cpuid_count(int cpu, cpuctl_cpuid_count_args_t *data,
struct thread *td);
static int cpuctl_do_update(int cpu, cpuctl_update_args_t *data,
struct thread *td);
@@ -169,7 +171,8 @@ cpuctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
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);
+ cpuctl_do_cpuid(cpu, (cpuctl_cpuid_args_t *)data, td);
+ ret = 0;
break;
case CPUCTL_UPDATE:
ret = priv_check(td, PRIV_CPUCTL_UPDATE);
@@ -177,6 +180,11 @@ cpuctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
goto fail;
ret = cpuctl_do_update(cpu, (cpuctl_update_args_t *)data, td);
break;
+ case CPUCTL_CPUID_COUNT:
+ cpuctl_do_cpuid_count(cpu, (cpuctl_cpuid_count_args_t *)data,
+ td);
+ ret = 0;
+ break;
default:
ret = EINVAL;
break;
@@ -188,8 +196,9 @@ fail:
/*
* Actually perform cpuid operation.
*/
-static int
-cpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data, struct thread *td)
+static void
+cpuctl_do_cpuid_count(int cpu, cpuctl_cpuid_count_args_t *data,
+ struct thread *td)
{
int is_bound = 0;
int oldcpu;
@@ -199,14 +208,25 @@ cpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data, struct thread *td)
/* 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);
+ DPRINTF("[cpuctl,%d]: retrieving cpuid lev %#0x type %#0x for %d cpu\n",
+ __LINE__, data->level, data->level_type, cpu);
oldcpu = td->td_oncpu;
is_bound = cpu_sched_is_bound(td);
set_cpu(cpu, td);
- cpuid_count(data->level, 0, data->data);
+ cpuid_count(data->level, data->level_type, data->data);
restore_cpu(oldcpu, is_bound, td);
- return (0);
+}
+
+static void
+cpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data, struct thread *td)
+{
+ cpuctl_cpuid_count_args_t cdata;
+
+ cdata.level = data->level;
+ /* Override the level type. */
+ cdata.level_type = 0;
+ cpuctl_do_cpuid_count(cpu, &cdata, td);
+ bcopy(cdata.data, data->data, sizeof(data->data)); /* Ignore error */
}
/*
@@ -271,12 +291,7 @@ cpuctl_do_update(int cpu, cpuctl_update_args_t *data, struct thread *td)
("[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);
- }
+ cpuctl_do_cpuid(cpu, &args, td);
((uint32_t *)vendor)[0] = args.data[1];
((uint32_t *)vendor)[1] = args.data[3];
((uint32_t *)vendor)[2] = args.data[2];
diff --git a/sys/sys/cpuctl.h b/sys/sys/cpuctl.h
index 4bddb34..30af524 100644
--- a/sys/sys/cpuctl.h
+++ b/sys/sys/cpuctl.h
@@ -35,11 +35,17 @@ typedef struct {
} cpuctl_msr_args_t;
typedef struct {
- int level; /* CPUID level */
+ int level; /* CPUID level */
uint32_t data[4];
} cpuctl_cpuid_args_t;
typedef struct {
+ int level; /* CPUID level */
+ int level_type; /* CPUID level type */
+ uint32_t data[4];
+} cpuctl_cpuid_count_args_t;
+
+typedef struct {
void *data;
size_t size;
} cpuctl_update_args_t;
@@ -50,5 +56,6 @@ typedef struct {
#define CPUCTL_UPDATE _IOWR('c', 4, cpuctl_update_args_t)
#define CPUCTL_MSRSBIT _IOWR('c', 5, cpuctl_msr_args_t)
#define CPUCTL_MSRCBIT _IOWR('c', 6, cpuctl_msr_args_t)
+#define CPUCTL_CPUID_COUNT _IOWR('c', 7, cpuctl_cpuid_count_args_t)
#endif /* _CPUCTL_H_ */
diff --git a/usr.sbin/cpucontrol/cpucontrol.8 b/usr.sbin/cpucontrol/cpucontrol.8
index 0998b20..cb1a29e 100644
--- a/usr.sbin/cpucontrol/cpucontrol.8
+++ b/usr.sbin/cpucontrol/cpucontrol.8
@@ -65,6 +65,12 @@ device
.Ek
.Nm
.Op Fl vh
+.Fl i Ar level,level_type
+.Bk
+.Ar device
+.Ek
+.Nm
+.Op Fl vh
.Op Fl d Ar datadir
.Fl u
.Bk
@@ -114,6 +120,9 @@ 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
diff --git a/usr.sbin/cpucontrol/cpucontrol.c b/usr.sbin/cpucontrol/cpucontrol.c
index c0d5a31..69fdf3a 100644
--- a/usr.sbin/cpucontrol/cpucontrol.c
+++ b/usr.sbin/cpucontrol/cpucontrol.c
@@ -99,6 +99,7 @@ static struct ucode_handler {
static void usage(void);
static int isdir(const char *path);
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);
@@ -112,7 +113,7 @@ usage(void)
if (name == NULL)
name = "cpuctl";
fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | "
- "-i level | -u] device\n", name);
+ "-i level | -i level,level_type | -u] device\n", name);
exit(EX_USAGE);
}
@@ -170,6 +171,57 @@ do_cpuid(const char *cmdarg, const char *dev)
}
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;
@@ -414,7 +466,10 @@ main(int argc, char *argv[])
c = flags & (FLAG_I | FLAG_M | FLAG_U);
switch (c) {
case FLAG_I:
- error = do_cpuid(cmdarg, dev);
+ 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);
OpenPOWER on IntegriCloud