diff options
-rw-r--r-- | sys/amd64/amd64/machdep.c | 2 | ||||
-rw-r--r-- | sys/dev/cpuctl/cpuctl.c | 30 | ||||
-rw-r--r-- | sys/sys/cpuctl.h | 1 | ||||
-rw-r--r-- | sys/x86/include/x86_var.h | 3 | ||||
-rw-r--r-- | sys/x86/x86/identcpu.c | 46 | ||||
-rw-r--r-- | usr.sbin/cpucontrol/cpucontrol.8 | 33 | ||||
-rw-r--r-- | usr.sbin/cpucontrol/cpucontrol.c | 60 |
7 files changed, 128 insertions, 47 deletions
diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c index 3ed29e0..8e508c3 100644 --- a/sys/amd64/amd64/machdep.c +++ b/sys/amd64/amd64/machdep.c @@ -1559,7 +1559,7 @@ hammer_time(u_int64_t modulep, u_int64_t physfree) kmdp = init_ops.parse_preload_data(modulep); - identify_cpu(); + identify_cpu1(); identify_hypervisor(); /* Init basic tunables, hz etc */ diff --git a/sys/dev/cpuctl/cpuctl.c b/sys/dev/cpuctl/cpuctl.c index 5351d8ed..6a854e4 100644 --- a/sys/dev/cpuctl/cpuctl.c +++ b/sys/dev/cpuctl/cpuctl.c @@ -71,6 +71,7 @@ static int cpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data, struct thread *td); static int cpuctl_do_cpuid_count(int cpu, cpuctl_cpuid_count_args_t *data, struct thread *td); +static int cpuctl_do_eval_cpu_features(int cpu, 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, @@ -157,7 +158,8 @@ cpuctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, } /* Require write flag for "write" requests. */ if ((cmd == CPUCTL_MSRCBIT || cmd == CPUCTL_MSRSBIT || - cmd == CPUCTL_UPDATE || cmd == CPUCTL_WRMSR) && + cmd == CPUCTL_UPDATE || cmd == CPUCTL_WRMSR || + cmd == CPUCTL_EVAL_CPU_FEATURES) && (flags & FWRITE) == 0) return (EPERM); switch (cmd) { @@ -185,6 +187,9 @@ cpuctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, ret = cpuctl_do_cpuid_count(cpu, (cpuctl_cpuid_count_args_t *)data, td); break; + case CPUCTL_EVAL_CPU_FEATURES: + ret = cpuctl_do_eval_cpu_features(cpu, td); + break; default: ret = EINVAL; break; @@ -502,6 +507,29 @@ fail: return (ret); } +static int +cpuctl_do_eval_cpu_features(int cpu, struct thread *td) +{ + int is_bound = 0; + int oldcpu; + + KASSERT(cpu >= 0 && cpu <= mp_maxid, + ("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu)); + +#ifdef __i386__ + if (cpu_id == 0) + return (ENODEV); +#endif + oldcpu = td->td_oncpu; + is_bound = cpu_sched_is_bound(td); + set_cpu(cpu, td); + identify_cpu1(); + identify_cpu2(); + restore_cpu(oldcpu, is_bound, td); + return (0); +} + + int cpuctl_open(struct cdev *dev, int flags, int fmt __unused, struct thread *td) { diff --git a/sys/sys/cpuctl.h b/sys/sys/cpuctl.h index 30af524..65556ec 100644 --- a/sys/sys/cpuctl.h +++ b/sys/sys/cpuctl.h @@ -57,5 +57,6 @@ typedef struct { #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) +#define CPUCTL_EVAL_CPU_FEATURES _IO('c', 8) #endif /* _CPUCTL_H_ */ diff --git a/sys/x86/include/x86_var.h b/sys/x86/include/x86_var.h index 7705a1c..ffdf2f0 100644 --- a/sys/x86/include/x86_var.h +++ b/sys/x86/include/x86_var.h @@ -116,7 +116,8 @@ void cpu_setregs(void); void dump_add_page(vm_paddr_t); void dump_drop_page(vm_paddr_t); void finishidentcpu(void); -void identify_cpu(void); +void identify_cpu1(void); +void identify_cpu2(void); void identify_hypervisor(void); void initializecpu(void); void initializecpucache(void); diff --git a/sys/x86/x86/identcpu.c b/sys/x86/x86/identcpu.c index c0cc2a6..ab7214c 100644 --- a/sys/x86/x86/identcpu.c +++ b/sys/x86/x86/identcpu.c @@ -1370,9 +1370,8 @@ fix_cpuid(void) return (false); } -#ifdef __amd64__ void -identify_cpu(void) +identify_cpu1(void) { u_int regs[4]; @@ -1389,7 +1388,29 @@ identify_cpu(void) cpu_feature = regs[3]; cpu_feature2 = regs[2]; } -#endif + +void +identify_cpu2(void) +{ + u_int regs[4], cpu_stdext_disable; + + if (cpu_high >= 7) { + cpuid_count(7, 0, regs); + cpu_stdext_feature = regs[1]; + + /* + * Some hypervisors failed to filter out unsupported + * extended features. Allow to disable the + * extensions, activation of which requires setting a + * bit in CR4, and which VM monitors do not support. + */ + cpu_stdext_disable = 0; + TUNABLE_INT_FETCH("hw.cpu_stdext_disable", &cpu_stdext_disable); + cpu_stdext_feature &= ~cpu_stdext_disable; + + cpu_stdext_feature2 = regs[2]; + } +} /* * Final stage of CPU identification. @@ -1397,7 +1418,7 @@ identify_cpu(void) void finishidentcpu(void) { - u_int regs[4], cpu_stdext_disable; + u_int regs[4]; #ifdef __i386__ u_char ccr3; #endif @@ -1416,22 +1437,7 @@ finishidentcpu(void) cpu_mon_max_size = regs[1] & CPUID5_MON_MAX_SIZE; } - if (cpu_high >= 7) { - cpuid_count(7, 0, regs); - cpu_stdext_feature = regs[1]; - - /* - * Some hypervisors failed to filter out unsupported - * extended features. Allow to disable the - * extensions, activation of which requires setting a - * bit in CR4, and which VM monitors do not support. - */ - cpu_stdext_disable = 0; - TUNABLE_INT_FETCH("hw.cpu_stdext_disable", &cpu_stdext_disable); - cpu_stdext_feature &= ~cpu_stdext_disable; - - cpu_stdext_feature2 = regs[2]; - } + identify_cpu2(); #ifdef __i386__ if (cpu_high > 0 && diff --git a/usr.sbin/cpucontrol/cpucontrol.8 b/usr.sbin/cpucontrol/cpucontrol.8 index 2a1a5f1..4e54bb5 100644 --- a/usr.sbin/cpucontrol/cpucontrol.8 +++ b/usr.sbin/cpucontrol/cpucontrol.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd September 30, 2017 +.Dd January 5, 2018 .Dt CPUCONTROL 8 .Os .Sh NAME @@ -34,46 +34,51 @@ device .Sh SYNOPSIS .Nm +.Bk .Op Fl v .Fl m Ar msr -.Bk .Ar device .Ek +.Bk .Nm .Op Fl v .Fl m Ar msr Ns = Ns Ar value -.Bk .Ar device .Ek +.Bk .Nm .Op Fl v .Fl m Ar msr Ns &= Ns Ar mask -.Bk .Ar device .Ek +.Bk .Nm .Op Fl v .Fl m Ar msr Ns |= Ns Ar mask -.Bk .Ar device .Ek +.Bk .Nm .Op Fl v .Fl i Ar level -.Bk .Ar device .Ek +.Bk .Nm .Op Fl v .Fl i Ar level,level_type -.Bk .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 @@ -136,6 +141,20 @@ 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 diff --git a/usr.sbin/cpucontrol/cpucontrol.c b/usr.sbin/cpucontrol/cpucontrol.c index 6760b83..554395c 100644 --- a/usr.sbin/cpucontrol/cpucontrol.c +++ b/usr.sbin/cpucontrol/cpucontrol.c @@ -61,6 +61,7 @@ int verbosity_level = 0; #define FLAG_M 0x02 #define FLAG_U 0x04 #define FLAG_N 0x08 +#define FLAG_E 0x10 #define OP_INVAL 0x00 #define OP_READ 0x01 @@ -115,7 +116,7 @@ usage(void) if (name == NULL) name = "cpuctl"; fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | " - "-i level | -i level,level_type | -u] device\n", name); + "-i level | -i level,level_type | -e | -u] device\n", name); exit(EX_USAGE); } @@ -339,6 +340,25 @@ do_msr(const char *cmdarg, const char *dev) } 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 do_update(const char *dev) { int fd; @@ -428,11 +448,14 @@ main(int argc, char *argv[]) error = 0; cmdarg = ""; /* To keep gcc3 happy. */ - while ((c = getopt(argc, argv, "d:hi:m:nuv")) != -1) { + 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; @@ -466,22 +489,25 @@ main(int argc, char *argv[]) if ((flags & FLAG_N) == 0) datadir_add(DEFAULT_DATADIR); dev = argv[0]; - c = flags & (FLAG_I | FLAG_M | FLAG_U); + 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; - default: - usage(); /* Only one command can be selected. */ + 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. */ } SLIST_FREE(&datadirs, next, free); return (error == 0 ? 0 : 1); |