Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/cpucontrol/cpucontrol.c
107110 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2008-2011 Stanislav Sedov <[email protected]>.
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
*/
27
28
/*
29
* This utility provides userland access to the cpuctl(4) pseudo-device
30
* features.
31
*/
32
33
#include <sys/cdefs.h>
34
#include <assert.h>
35
#include <err.h>
36
#include <errno.h>
37
#include <dirent.h>
38
#include <fcntl.h>
39
#include <inttypes.h>
40
#include <paths.h>
41
#include <stdint.h>
42
#include <stdio.h>
43
#include <stdlib.h>
44
#include <string.h>
45
#include <unistd.h>
46
#include <sysexits.h>
47
48
#include <sys/queue.h>
49
#include <sys/param.h>
50
#include <sys/types.h>
51
#include <sys/mman.h>
52
#include <sys/stat.h>
53
#include <sys/ioctl.h>
54
#include <sys/cpuctl.h>
55
56
#include "cpucontrol.h"
57
#include "amd.h"
58
#include "intel.h"
59
#include "via.h"
60
61
int verbosity_level = 0;
62
63
#define DEFAULT_DATADIR _PATH_LOCALBASE "/share/cpucontrol"
64
65
#define FLAG_I 0x01
66
#define FLAG_M 0x02
67
#define FLAG_U 0x04
68
#define FLAG_N 0x08
69
#define FLAG_E 0x10
70
71
#define OP_INVAL 0x00
72
#define OP_READ 0x01
73
#define OP_WRITE 0x02
74
#define OP_OR 0x04
75
#define OP_AND 0x08
76
77
#define HIGH(val) (uint32_t)(((val) >> 32) & 0xffffffff)
78
#define LOW(val) (uint32_t)((val) & 0xffffffff)
79
80
struct datadir {
81
const char *path;
82
SLIST_ENTRY(datadir) next;
83
};
84
static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(datadirs);
85
86
static struct ucode_handler {
87
ucode_probe_t *probe;
88
ucode_update_t *update;
89
} handlers[] = {
90
{ intel_probe, intel_update },
91
{ amd10h_probe, amd10h_update },
92
{ amd_probe, amd_update },
93
{ via_probe, via_update },
94
};
95
#define NHANDLERS (sizeof(handlers) / sizeof(*handlers))
96
97
static void usage(void);
98
static int do_cpuid(const char *cmdarg, const char *dev);
99
static int do_cpuid_count(const char *cmdarg, const char *dev);
100
static int do_msr(const char *cmdarg, const char *dev);
101
static int do_update(const char *dev);
102
static void datadir_add(const char *path);
103
104
static void __dead2
105
usage(void)
106
{
107
const char *name;
108
109
name = getprogname();
110
if (name == NULL)
111
name = "cpuctl";
112
fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | "
113
"-i level | -i level,level_type | -e | -u] device\n", name);
114
exit(EX_USAGE);
115
}
116
117
static uint32_t
118
strtouint32(const char *str, char **endptr, int base)
119
{
120
uintmax_t val;
121
122
errno = 0;
123
val = strtoumax(str, endptr, base);
124
if (*str == '\0' || errno == ERANGE || val > UINT32_MAX) {
125
WARNX(0, "invalid operand: %s", str);
126
exit(EX_USAGE);
127
/* NOTREACHED */
128
}
129
return ((uint32_t)val);
130
}
131
132
static int
133
do_cpuid(const char *cmdarg, const char *dev)
134
{
135
unsigned int level;
136
cpuctl_cpuid_args_t args;
137
int fd, error;
138
char *endptr;
139
140
assert(cmdarg != NULL);
141
assert(dev != NULL);
142
143
level = strtouint32(cmdarg, &endptr, 16);
144
if (*cmdarg == '\0' || *endptr != '\0') {
145
WARNX(0, "incorrect operand: %s", cmdarg);
146
usage();
147
/* NOTREACHED */
148
}
149
150
/*
151
* Fill ioctl argument structure.
152
*/
153
args.level = level;
154
fd = open(dev, O_RDONLY);
155
if (fd < 0) {
156
WARN(0, "error opening %s for reading", dev);
157
return (1);
158
}
159
error = ioctl(fd, CPUCTL_CPUID, &args);
160
if (error < 0) {
161
WARN(0, "ioctl(%s, CPUCTL_CPUID)", dev);
162
close(fd);
163
return (error);
164
}
165
fprintf(stdout, "cpuid level 0x%x: 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
166
level, args.data[0], args.data[1], args.data[2], args.data[3]);
167
close(fd);
168
return (0);
169
}
170
171
static int
172
do_cpuid_count(const char *cmdarg, const char *dev)
173
{
174
char *cmdarg1, *endptr, *endptr1;
175
unsigned int level, level_type;
176
cpuctl_cpuid_count_args_t args;
177
int fd, error;
178
179
assert(cmdarg != NULL);
180
assert(dev != NULL);
181
182
level = strtouint32(cmdarg, &endptr, 16);
183
if (*cmdarg == '\0' || *endptr == '\0') {
184
WARNX(0, "incorrect or missing operand: %s", cmdarg);
185
usage();
186
/* NOTREACHED */
187
}
188
/* Locate the comma... */
189
cmdarg1 = strstr(endptr, ",");
190
/* ... and skip past it */
191
cmdarg1 += 1;
192
level_type = strtouint32(cmdarg1, &endptr1, 16);
193
if (*cmdarg1 == '\0' || *endptr1 != '\0') {
194
WARNX(0, "incorrect or missing operand: %s", cmdarg);
195
usage();
196
/* NOTREACHED */
197
}
198
199
/*
200
* Fill ioctl argument structure.
201
*/
202
args.level = level;
203
args.level_type = level_type;
204
fd = open(dev, O_RDONLY);
205
if (fd < 0) {
206
WARN(0, "error opening %s for reading", dev);
207
return (1);
208
}
209
error = ioctl(fd, CPUCTL_CPUID_COUNT, &args);
210
if (error < 0) {
211
WARN(0, "ioctl(%s, CPUCTL_CPUID_COUNT)", dev);
212
close(fd);
213
return (error);
214
}
215
fprintf(stdout, "cpuid level 0x%x, level_type 0x%x: 0x%.8x 0x%.8x "
216
"0x%.8x 0x%.8x\n", level, level_type, args.data[0], args.data[1],
217
args.data[2], args.data[3]);
218
close(fd);
219
return (0);
220
}
221
222
static int
223
do_msr(const char *cmdarg, const char *dev)
224
{
225
unsigned int msr;
226
cpuctl_msr_args_t args;
227
size_t len;
228
uint64_t data = 0;
229
unsigned long command;
230
int do_invert = 0, op;
231
int fd, error;
232
const char *command_name;
233
char *endptr;
234
char *p;
235
236
assert(cmdarg != NULL);
237
assert(dev != NULL);
238
len = strlen(cmdarg);
239
if (len == 0) {
240
WARNX(0, "MSR register expected");
241
usage();
242
/* NOTREACHED */
243
}
244
245
/*
246
* Parse command string.
247
*/
248
msr = strtouint32(cmdarg, &endptr, 16);
249
switch (*endptr) {
250
case '\0':
251
op = OP_READ;
252
break;
253
case '=':
254
op = OP_WRITE;
255
break;
256
case '&':
257
op = OP_AND;
258
endptr++;
259
break;
260
case '|':
261
op = OP_OR;
262
endptr++;
263
break;
264
default:
265
op = OP_INVAL;
266
}
267
if (op != OP_READ) { /* Complex operation. */
268
if (*endptr != '=')
269
op = OP_INVAL;
270
else {
271
p = ++endptr;
272
if (*p == '~') {
273
do_invert = 1;
274
p++;
275
}
276
data = strtoull(p, &endptr, 16);
277
if (*p == '\0' || *endptr != '\0') {
278
WARNX(0, "argument required: %s", cmdarg);
279
usage();
280
/* NOTREACHED */
281
}
282
}
283
}
284
if (op == OP_INVAL) {
285
WARNX(0, "invalid operator: %s", cmdarg);
286
usage();
287
/* NOTREACHED */
288
}
289
290
/*
291
* Fill ioctl argument structure.
292
*/
293
args.msr = msr;
294
if ((do_invert != 0) ^ (op == OP_AND))
295
args.data = ~data;
296
else
297
args.data = data;
298
switch (op) {
299
case OP_READ:
300
command = CPUCTL_RDMSR;
301
command_name = "RDMSR";
302
break;
303
case OP_WRITE:
304
command = CPUCTL_WRMSR;
305
command_name = "WRMSR";
306
break;
307
case OP_OR:
308
command = CPUCTL_MSRSBIT;
309
command_name = "MSRSBIT";
310
break;
311
case OP_AND:
312
command = CPUCTL_MSRCBIT;
313
command_name = "MSRCBIT";
314
break;
315
default:
316
abort();
317
}
318
fd = open(dev, op == OP_READ ? O_RDONLY : O_WRONLY);
319
if (fd < 0) {
320
WARN(0, "error opening %s for %s", dev,
321
op == OP_READ ? "reading" : "writing");
322
return (1);
323
}
324
error = ioctl(fd, command, &args);
325
if (error < 0) {
326
WARN(0, "ioctl(%s, CPUCTL_%s (%#x))", dev, command_name, msr);
327
close(fd);
328
return (1);
329
}
330
if (op == OP_READ)
331
fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr,
332
HIGH(args.data), LOW(args.data));
333
close(fd);
334
return (0);
335
}
336
337
static int
338
do_eval_cpu_features(const char *dev)
339
{
340
int fd, error;
341
342
assert(dev != NULL);
343
344
fd = open(dev, O_RDWR);
345
if (fd < 0) {
346
WARN(0, "error opening %s for writing", dev);
347
return (1);
348
}
349
error = ioctl(fd, CPUCTL_EVAL_CPU_FEATURES, NULL);
350
if (error < 0)
351
WARN(0, "ioctl(%s, CPUCTL_EVAL_CPU_FEATURES)", dev);
352
close(fd);
353
return (error);
354
}
355
356
static int
357
try_a_fw_image(const char *dev_path, int devfd, int fwdfd, const char *dpath,
358
const char *fname, struct ucode_handler *handler)
359
{
360
struct ucode_update_params parm;
361
struct stat st;
362
char *fw_path;
363
void *fw_map;
364
int fwfd, rc;
365
366
rc = 0;
367
fw_path = NULL;
368
fw_map = MAP_FAILED;
369
fwfd = openat(fwdfd, fname, O_RDONLY);
370
if (fwfd < 0) {
371
WARN(0, "openat(%s, %s)", dpath, fname);
372
goto out;
373
}
374
375
rc = asprintf(&fw_path, "%s/%s", dpath, fname);
376
if (rc == -1) {
377
WARNX(0, "out of memory");
378
rc = ENOMEM;
379
goto out;
380
}
381
382
rc = fstat(fwfd, &st);
383
if (rc != 0) {
384
WARN(0, "fstat(%s)", fw_path);
385
rc = 0;
386
goto out;
387
}
388
if (!S_ISREG(st.st_mode))
389
goto out;
390
if (st.st_size <= 0) {
391
WARN(0, "%s: empty", fw_path);
392
goto out;
393
}
394
395
fw_map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fwfd, 0);
396
if (fw_map == MAP_FAILED) {
397
WARN(0, "mmap(%s)", fw_path);
398
goto out;
399
}
400
401
402
memset(&parm, 0, sizeof(parm));
403
parm.devfd = devfd;
404
parm.fwimage = fw_map;
405
parm.fwsize = st.st_size;
406
parm.dev_path = dev_path;
407
parm.fw_path = fw_path;
408
409
handler->update(&parm);
410
411
out:
412
if (fw_map != MAP_FAILED)
413
munmap(fw_map, st.st_size);
414
free(fw_path);
415
if (fwfd >= 0)
416
close(fwfd);
417
return (rc);
418
}
419
420
static int
421
do_update(const char *dev)
422
{
423
int fd, fwdfd;
424
unsigned int i;
425
int error;
426
struct ucode_handler *handler;
427
struct datadir *dir;
428
DIR *dirp;
429
struct dirent *direntry;
430
431
fd = open(dev, O_RDONLY);
432
if (fd < 0) {
433
WARN(0, "error opening %s for reading", dev);
434
return (1);
435
}
436
437
/*
438
* Find the appropriate handler for CPU.
439
*/
440
for (i = 0; i < NHANDLERS; i++)
441
if (handlers[i].probe(fd) == 0)
442
break;
443
if (i < NHANDLERS)
444
handler = &handlers[i];
445
else {
446
WARNX(0, "cannot find the appropriate handler for %s", dev);
447
close(fd);
448
return (1);
449
}
450
close(fd);
451
452
fd = open(dev, O_RDWR);
453
if (fd < 0) {
454
WARN(0, "error opening %s for writing", dev);
455
return (1);
456
}
457
458
/*
459
* Process every image in specified data directories.
460
*/
461
SLIST_FOREACH(dir, &datadirs, next) {
462
fwdfd = open(dir->path, O_RDONLY);
463
if (fwdfd < 0) {
464
WARN(1, "skipping directory %s: not accessible", dir->path);
465
continue;
466
}
467
dirp = fdopendir(fwdfd);
468
if (dirp == NULL) {
469
WARNX(0, "out of memory");
470
close(fwdfd);
471
close(fd);
472
return (1);
473
}
474
475
while ((direntry = readdir(dirp)) != NULL) {
476
if (direntry->d_namlen == 0)
477
continue;
478
if (direntry->d_type == DT_DIR)
479
continue;
480
481
error = try_a_fw_image(dev, fd, fwdfd, dir->path,
482
direntry->d_name, handler);
483
if (error != 0) {
484
closedir(dirp);
485
close(fd);
486
return (1);
487
}
488
}
489
error = closedir(dirp);
490
if (error != 0)
491
WARN(0, "closedir(%s)", dir->path);
492
}
493
close(fd);
494
return (0);
495
}
496
497
/*
498
* Add new data directory to the search list.
499
*/
500
static void
501
datadir_add(const char *path)
502
{
503
struct datadir *newdir;
504
505
newdir = (struct datadir *)malloc(sizeof(*newdir));
506
if (newdir == NULL)
507
err(EX_OSERR, "cannot allocate memory");
508
newdir->path = path;
509
SLIST_INSERT_HEAD(&datadirs, newdir, next);
510
}
511
512
int
513
main(int argc, char *argv[])
514
{
515
struct datadir *elm;
516
int c, flags;
517
const char *cmdarg;
518
const char *dev;
519
int error;
520
521
flags = 0;
522
error = 0;
523
cmdarg = ""; /* To keep gcc3 happy. */
524
525
while ((c = getopt(argc, argv, "d:ehi:m:nuv")) != -1) {
526
switch (c) {
527
case 'd':
528
datadir_add(optarg);
529
break;
530
case 'e':
531
flags |= FLAG_E;
532
break;
533
case 'i':
534
flags |= FLAG_I;
535
cmdarg = optarg;
536
break;
537
case 'm':
538
flags |= FLAG_M;
539
cmdarg = optarg;
540
break;
541
case 'n':
542
flags |= FLAG_N;
543
break;
544
case 'u':
545
flags |= FLAG_U;
546
break;
547
case 'v':
548
verbosity_level++;
549
break;
550
case 'h':
551
/* FALLTHROUGH */
552
default:
553
usage();
554
/* NOTREACHED */
555
}
556
}
557
argc -= optind;
558
argv += optind;
559
if (argc < 1) {
560
usage();
561
/* NOTREACHED */
562
}
563
if ((flags & FLAG_N) == 0)
564
datadir_add(DEFAULT_DATADIR);
565
dev = argv[0];
566
c = flags & (FLAG_E | FLAG_I | FLAG_M | FLAG_U);
567
switch (c) {
568
case FLAG_I:
569
if (strstr(cmdarg, ",") != NULL)
570
error = do_cpuid_count(cmdarg, dev);
571
else
572
error = do_cpuid(cmdarg, dev);
573
break;
574
case FLAG_M:
575
error = do_msr(cmdarg, dev);
576
break;
577
case FLAG_U:
578
error = do_update(dev);
579
break;
580
case FLAG_E:
581
error = do_eval_cpu_features(dev);
582
break;
583
default:
584
usage(); /* Only one command can be selected. */
585
}
586
while ((elm = SLIST_FIRST(&datadirs)) != NULL) {
587
SLIST_REMOVE_HEAD(&datadirs, next);
588
free(elm);
589
}
590
return (error == 0 ? 0 : 1);
591
}
592
593