Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/cpuctl/cpuctl.c
39507 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2006-2008 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 AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*
28
*/
29
30
#include <sys/param.h>
31
#include <sys/systm.h>
32
#include <sys/conf.h>
33
#include <sys/fcntl.h>
34
#include <sys/ioccom.h>
35
#include <sys/malloc.h>
36
#include <sys/module.h>
37
#include <sys/mutex.h>
38
#include <sys/priv.h>
39
#include <sys/proc.h>
40
#include <sys/queue.h>
41
#include <sys/sched.h>
42
#include <sys/kernel.h>
43
#include <sys/sysctl.h>
44
#include <sys/uio.h>
45
#include <sys/pcpu.h>
46
#include <sys/smp.h>
47
#include <sys/pmckern.h>
48
#include <sys/cpuctl.h>
49
50
#include <vm/vm.h>
51
#include <vm/vm_param.h>
52
#include <vm/pmap.h>
53
54
#include <machine/cpufunc.h>
55
#include <machine/md_var.h>
56
#include <machine/specialreg.h>
57
#include <x86/ucode.h>
58
59
static d_open_t cpuctl_open;
60
static d_ioctl_t cpuctl_ioctl;
61
62
#define CPUCTL_VERSION 1
63
64
#ifdef CPUCTL_DEBUG
65
# define DPRINTF(format,...) printf(format, __VA_ARGS__);
66
#else
67
# define DPRINTF(...)
68
#endif
69
70
#define UCODE_SIZE_MAX (4 * 1024 * 1024)
71
72
static int cpuctl_do_msr(int cpu, cpuctl_msr_args_t *data, u_long cmd,
73
struct thread *td);
74
static int cpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data,
75
struct thread *td);
76
static int cpuctl_do_cpuid_count(int cpu, cpuctl_cpuid_count_args_t *data,
77
struct thread *td);
78
static int cpuctl_do_eval_cpu_features(int cpu, struct thread *td);
79
static int cpuctl_do_update(int cpu, cpuctl_update_args_t *data,
80
struct thread *td);
81
static int update_intel(int cpu, cpuctl_update_args_t *args,
82
struct thread *td);
83
static int update_amd(int cpu, cpuctl_update_args_t *args, struct thread *td);
84
static int update_via(int cpu, cpuctl_update_args_t *args,
85
struct thread *td);
86
87
static struct cdev **cpuctl_devs;
88
static MALLOC_DEFINE(M_CPUCTL, "cpuctl", "CPUCTL buffer");
89
90
static struct cdevsw cpuctl_cdevsw = {
91
.d_version = D_VERSION,
92
.d_open = cpuctl_open,
93
.d_ioctl = cpuctl_ioctl,
94
.d_name = "cpuctl",
95
};
96
97
/*
98
* This function checks if specified cpu enabled or not.
99
*/
100
static int
101
cpu_enabled(int cpu)
102
{
103
104
return (pmc_cpu_is_disabled(cpu) == 0);
105
}
106
107
/*
108
* Check if the current thread is bound to a specific cpu.
109
*/
110
static int
111
cpu_sched_is_bound(struct thread *td)
112
{
113
int ret;
114
115
thread_lock(td);
116
ret = sched_is_bound(td);
117
thread_unlock(td);
118
return (ret);
119
}
120
121
/*
122
* Switch to target cpu to run.
123
*/
124
static void
125
set_cpu(int cpu, struct thread *td)
126
{
127
128
KASSERT(cpu >= 0 && cpu <= mp_maxid && cpu_enabled(cpu),
129
("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu));
130
thread_lock(td);
131
sched_bind(td, cpu);
132
thread_unlock(td);
133
KASSERT(td->td_oncpu == cpu,
134
("[cpuctl,%d]: cannot bind to target cpu %d on cpu %d", __LINE__,
135
cpu, td->td_oncpu));
136
}
137
138
static void
139
restore_cpu(int oldcpu, int is_bound, struct thread *td)
140
{
141
142
KASSERT(oldcpu >= 0 && oldcpu <= mp_maxid && cpu_enabled(oldcpu),
143
("[cpuctl,%d]: bad cpu number %d", __LINE__, oldcpu));
144
thread_lock(td);
145
if (is_bound == 0)
146
sched_unbind(td);
147
else
148
sched_bind(td, oldcpu);
149
thread_unlock(td);
150
}
151
152
int
153
cpuctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
154
int flags, struct thread *td)
155
{
156
int cpu, ret;
157
158
cpu = dev2unit(dev);
159
if (cpu > mp_maxid || !cpu_enabled(cpu)) {
160
DPRINTF("[cpuctl,%d]: bad cpu number %d\n", __LINE__, cpu);
161
return (ENXIO);
162
}
163
/* Require write flag for "write" requests. */
164
if ((cmd == CPUCTL_MSRCBIT || cmd == CPUCTL_MSRSBIT ||
165
cmd == CPUCTL_UPDATE || cmd == CPUCTL_WRMSR ||
166
cmd == CPUCTL_EVAL_CPU_FEATURES) &&
167
(flags & FWRITE) == 0)
168
return (EPERM);
169
switch (cmd) {
170
case CPUCTL_RDMSR:
171
ret = cpuctl_do_msr(cpu, (cpuctl_msr_args_t *)data, cmd, td);
172
break;
173
case CPUCTL_MSRSBIT:
174
case CPUCTL_MSRCBIT:
175
case CPUCTL_WRMSR:
176
ret = priv_check(td, PRIV_CPUCTL_WRMSR);
177
if (ret != 0)
178
goto fail;
179
ret = cpuctl_do_msr(cpu, (cpuctl_msr_args_t *)data, cmd, td);
180
break;
181
case CPUCTL_CPUID:
182
ret = cpuctl_do_cpuid(cpu, (cpuctl_cpuid_args_t *)data, td);
183
break;
184
case CPUCTL_UPDATE:
185
ret = priv_check(td, PRIV_CPUCTL_UPDATE);
186
if (ret != 0)
187
goto fail;
188
ret = cpuctl_do_update(cpu, (cpuctl_update_args_t *)data, td);
189
break;
190
case CPUCTL_CPUID_COUNT:
191
ret = cpuctl_do_cpuid_count(cpu,
192
(cpuctl_cpuid_count_args_t *)data, td);
193
break;
194
case CPUCTL_EVAL_CPU_FEATURES:
195
ret = cpuctl_do_eval_cpu_features(cpu, td);
196
break;
197
default:
198
ret = EINVAL;
199
break;
200
}
201
fail:
202
return (ret);
203
}
204
205
/*
206
* Actually perform cpuid operation.
207
*/
208
static int
209
cpuctl_do_cpuid_count(int cpu, cpuctl_cpuid_count_args_t *data,
210
struct thread *td)
211
{
212
int is_bound = 0;
213
int oldcpu;
214
215
KASSERT(cpu >= 0 && cpu <= mp_maxid,
216
("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu));
217
218
/* Explicitly clear cpuid data to avoid returning stale info. */
219
bzero(data->data, sizeof(data->data));
220
DPRINTF("[cpuctl,%d]: retrieving cpuid lev %#0x type %#0x for %d cpu\n",
221
__LINE__, data->level, data->level_type, cpu);
222
#ifdef __i386__
223
if (cpu_id == 0)
224
return (ENODEV);
225
#endif
226
oldcpu = td->td_oncpu;
227
is_bound = cpu_sched_is_bound(td);
228
set_cpu(cpu, td);
229
cpuid_count(data->level, data->level_type, data->data);
230
restore_cpu(oldcpu, is_bound, td);
231
return (0);
232
}
233
234
static int
235
cpuctl_do_cpuid(int cpu, cpuctl_cpuid_args_t *data, struct thread *td)
236
{
237
cpuctl_cpuid_count_args_t cdata;
238
int error;
239
240
cdata.level = data->level;
241
/* Override the level type. */
242
cdata.level_type = 0;
243
error = cpuctl_do_cpuid_count(cpu, &cdata, td);
244
bcopy(cdata.data, data->data, sizeof(data->data)); /* Ignore error */
245
return (error);
246
}
247
248
/*
249
* Actually perform MSR operations.
250
*/
251
static int
252
cpuctl_do_msr(int cpu, cpuctl_msr_args_t *data, u_long cmd, struct thread *td)
253
{
254
uint64_t reg;
255
int is_bound = 0;
256
int oldcpu;
257
int ret;
258
259
KASSERT(cpu >= 0 && cpu <= mp_maxid,
260
("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu));
261
262
/*
263
* Explicitly clear cpuid data to avoid returning stale
264
* info
265
*/
266
DPRINTF("[cpuctl,%d]: operating on MSR %#0x for %d cpu\n", __LINE__,
267
data->msr, cpu);
268
#ifdef __i386__
269
if ((cpu_feature & CPUID_MSR) == 0)
270
return (ENODEV);
271
#endif
272
oldcpu = td->td_oncpu;
273
is_bound = cpu_sched_is_bound(td);
274
set_cpu(cpu, td);
275
if (cmd == CPUCTL_RDMSR) {
276
data->data = 0;
277
ret = rdmsr_safe(data->msr, &data->data);
278
} else if (cmd == CPUCTL_WRMSR) {
279
ret = wrmsr_safe(data->msr, data->data);
280
} else if (cmd == CPUCTL_MSRSBIT) {
281
critical_enter();
282
ret = rdmsr_safe(data->msr, &reg);
283
if (ret == 0)
284
ret = wrmsr_safe(data->msr, reg | data->data);
285
critical_exit();
286
} else if (cmd == CPUCTL_MSRCBIT) {
287
critical_enter();
288
ret = rdmsr_safe(data->msr, &reg);
289
if (ret == 0)
290
ret = wrmsr_safe(data->msr, reg & ~data->data);
291
critical_exit();
292
} else
293
panic("[cpuctl,%d]: unknown operation requested: %lu",
294
__LINE__, cmd);
295
restore_cpu(oldcpu, is_bound, td);
296
return (ret);
297
}
298
299
/*
300
* Actually perform microcode update.
301
*/
302
static int
303
cpuctl_do_update(int cpu, cpuctl_update_args_t *data, struct thread *td)
304
{
305
cpuctl_cpuid_args_t args = {
306
.level = 0,
307
};
308
char vendor[13];
309
int ret;
310
311
KASSERT(cpu >= 0 && cpu <= mp_maxid,
312
("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu));
313
DPRINTF("[cpuctl,%d]: XXX %d", __LINE__, cpu);
314
315
ret = cpuctl_do_cpuid(cpu, &args, td);
316
if (ret != 0)
317
return (ret);
318
((uint32_t *)vendor)[0] = args.data[1];
319
((uint32_t *)vendor)[1] = args.data[3];
320
((uint32_t *)vendor)[2] = args.data[2];
321
vendor[12] = '\0';
322
if (strncmp(vendor, INTEL_VENDOR_ID, sizeof(INTEL_VENDOR_ID)) == 0)
323
ret = update_intel(cpu, data, td);
324
else if(strncmp(vendor, AMD_VENDOR_ID, sizeof(AMD_VENDOR_ID)) == 0)
325
ret = update_amd(cpu, data, td);
326
else if(strncmp(vendor, CENTAUR_VENDOR_ID, sizeof(CENTAUR_VENDOR_ID))
327
== 0)
328
ret = update_via(cpu, data, td);
329
else
330
ret = ENXIO;
331
return (ret);
332
}
333
334
struct ucode_update_data {
335
void *ptr;
336
int cpu;
337
int ret;
338
};
339
340
static void
341
ucode_intel_load_rv(void *arg)
342
{
343
struct ucode_update_data *d;
344
345
d = arg;
346
if (PCPU_GET(cpuid) == d->cpu)
347
d->ret = ucode_intel_load(d->ptr, SAFE, NULL, NULL);
348
}
349
350
static int
351
update_intel(int cpu, cpuctl_update_args_t *args, struct thread *td)
352
{
353
struct ucode_update_data d;
354
void *ptr;
355
int is_bound, oldcpu, ret;
356
357
if (args->size == 0 || args->data == NULL) {
358
DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__);
359
return (EINVAL);
360
}
361
if (args->size > UCODE_SIZE_MAX) {
362
DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__);
363
return (EINVAL);
364
}
365
366
/*
367
* 16 byte alignment required. Rely on the fact that
368
* malloc(9) always returns the pointer aligned at least on
369
* the size of the allocation.
370
*/
371
ptr = malloc(args->size + 16, M_CPUCTL, M_WAITOK);
372
if (copyin(args->data, ptr, args->size) != 0) {
373
DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed",
374
__LINE__, args->data, ptr, args->size);
375
ret = EFAULT;
376
goto out;
377
}
378
oldcpu = td->td_oncpu;
379
is_bound = cpu_sched_is_bound(td);
380
set_cpu(cpu, td);
381
d.ptr = ptr;
382
d.cpu = cpu;
383
smp_rendezvous(NULL, ucode_intel_load_rv, NULL, &d);
384
restore_cpu(oldcpu, is_bound, td);
385
ret = d.ret;
386
387
/*
388
* Replace any existing update. This ensures that the new update
389
* will be reloaded automatically during ACPI resume.
390
*/
391
if (ret == 0)
392
ptr = ucode_update(ptr);
393
394
out:
395
free(ptr, M_CPUCTL);
396
return (ret);
397
}
398
399
/*
400
* NB: MSR 0xc0010020, MSR_K8_UCODE_UPDATE, is not documented by AMD.
401
* Coreboot, illumos and Linux source code was used to understand
402
* its workings.
403
*/
404
static void
405
amd_ucode_wrmsr(void *arg)
406
{
407
struct ucode_update_data *d = arg;
408
uint32_t tmp[4];
409
410
if (PCPU_GET(cpuid) == d->cpu)
411
d->ret = wrmsr_safe(MSR_K8_UCODE_UPDATE, (uintptr_t)d->ptr);
412
do_cpuid(0, tmp);
413
}
414
415
static int
416
update_amd(int cpu, cpuctl_update_args_t *args, struct thread *td)
417
{
418
struct ucode_update_data d = { .cpu = cpu };
419
420
if (args->size == 0 || args->data == NULL) {
421
DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__);
422
return (EINVAL);
423
}
424
if (args->size > UCODE_SIZE_MAX) {
425
DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__);
426
return (EINVAL);
427
}
428
429
/*
430
* 16 byte alignment required. Rely on the fact that
431
* malloc(9) always returns the pointer aligned at least on
432
* the size of the allocation.
433
*/
434
d.ptr = malloc(args->size + 16, M_CPUCTL, M_ZERO | M_WAITOK);
435
if (copyin(args->data, d.ptr, args->size) != 0) {
436
DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed",
437
__LINE__, args->data, ptr, args->size);
438
d.ret = EFAULT;
439
goto fail;
440
}
441
smp_rendezvous(NULL, amd_ucode_wrmsr, NULL, &d);
442
fail:
443
free(d.ptr, M_CPUCTL);
444
return (d.ret);
445
}
446
447
static int
448
update_via(int cpu, cpuctl_update_args_t *args, struct thread *td)
449
{
450
void *ptr;
451
uint64_t rev0, rev1, res;
452
uint32_t tmp[4];
453
int is_bound;
454
int oldcpu;
455
int ret;
456
457
if (args->size == 0 || args->data == NULL) {
458
DPRINTF("[cpuctl,%d]: zero-sized firmware image", __LINE__);
459
return (EINVAL);
460
}
461
if (args->size > UCODE_SIZE_MAX) {
462
DPRINTF("[cpuctl,%d]: firmware image too large", __LINE__);
463
return (EINVAL);
464
}
465
466
/*
467
* 4 byte alignment required.
468
*/
469
ptr = malloc(args->size, M_CPUCTL, M_WAITOK);
470
if (copyin(args->data, ptr, args->size) != 0) {
471
DPRINTF("[cpuctl,%d]: copyin %p->%p of %zd bytes failed",
472
__LINE__, args->data, ptr, args->size);
473
ret = EFAULT;
474
goto fail;
475
}
476
oldcpu = td->td_oncpu;
477
is_bound = cpu_sched_is_bound(td);
478
set_cpu(cpu, td);
479
critical_enter();
480
rdmsr_safe(MSR_BIOS_SIGN, &rev0); /* Get current microcode revision. */
481
482
/*
483
* Perform update.
484
*/
485
wrmsr_safe(MSR_BIOS_UPDT_TRIG, (uintptr_t)(ptr));
486
do_cpuid(1, tmp);
487
488
/*
489
* Result are in low byte of MSR FCR5:
490
* 0x00: No update has been attempted since RESET.
491
* 0x01: The last attempted update was successful.
492
* 0x02: The last attempted update was unsuccessful due to a bad
493
* environment. No update was loaded and any preexisting
494
* patches are still active.
495
* 0x03: The last attempted update was not applicable to this processor.
496
* No update was loaded and any preexisting patches are still
497
* active.
498
* 0x04: The last attempted update was not successful due to an invalid
499
* update data block. No update was loaded and any preexisting
500
* patches are still active
501
*/
502
rdmsr_safe(0x1205, &res);
503
res &= 0xff;
504
critical_exit();
505
rdmsr_safe(MSR_BIOS_SIGN, &rev1); /* Get new microcode revision. */
506
restore_cpu(oldcpu, is_bound, td);
507
508
DPRINTF("[cpu,%d]: rev0=%x rev1=%x res=%x\n", __LINE__,
509
(unsigned)(rev0 >> 32), (unsigned)(rev1 >> 32), (unsigned)res);
510
511
if (res != 0x01)
512
ret = EINVAL;
513
else
514
ret = 0;
515
fail:
516
free(ptr, M_CPUCTL);
517
return (ret);
518
}
519
520
static int
521
cpuctl_do_eval_cpu_features(int cpu, struct thread *td)
522
{
523
int is_bound = 0;
524
int oldcpu;
525
526
KASSERT(cpu >= 0 && cpu <= mp_maxid,
527
("[cpuctl,%d]: bad cpu number %d", __LINE__, cpu));
528
529
#ifdef __i386__
530
if (cpu_id == 0)
531
return (ENODEV);
532
#endif
533
oldcpu = td->td_oncpu;
534
is_bound = cpu_sched_is_bound(td);
535
set_cpu(cpu, td);
536
identify_cpu1();
537
identify_cpu2();
538
restore_cpu(oldcpu, is_bound, td);
539
hw_ibrs_recalculate(true);
540
hw_ssb_recalculate(true);
541
#ifdef __amd64__
542
amd64_syscall_ret_flush_l1d_recalc();
543
pmap_allow_2m_x_ept_recalculate();
544
#endif
545
hw_mds_recalculate();
546
x86_taa_recalculate();
547
x86_rngds_mitg_recalculate(true);
548
zenbleed_check_and_apply(true);
549
printcpuinfo();
550
return (0);
551
}
552
553
int
554
cpuctl_open(struct cdev *dev, int flags, int fmt __unused, struct thread *td)
555
{
556
int ret = 0;
557
int cpu;
558
559
cpu = dev2unit(dev);
560
if (cpu > mp_maxid || !cpu_enabled(cpu)) {
561
DPRINTF("[cpuctl,%d]: incorrect cpu number %d\n", __LINE__,
562
cpu);
563
return (ENXIO);
564
}
565
if (flags & FWRITE)
566
ret = securelevel_gt(td->td_ucred, 0);
567
return (ret);
568
}
569
570
static int
571
cpuctl_modevent(module_t mod __unused, int type, void *data __unused)
572
{
573
int cpu;
574
575
switch(type) {
576
case MOD_LOAD:
577
if (bootverbose)
578
printf("cpuctl: access to MSR registers/cpuid info.\n");
579
cpuctl_devs = malloc(sizeof(*cpuctl_devs) * (mp_maxid + 1), M_CPUCTL,
580
M_WAITOK | M_ZERO);
581
CPU_FOREACH(cpu)
582
if (cpu_enabled(cpu))
583
cpuctl_devs[cpu] = make_dev(&cpuctl_cdevsw, cpu,
584
UID_ROOT, GID_KMEM, 0640, "cpuctl%d", cpu);
585
break;
586
case MOD_UNLOAD:
587
CPU_FOREACH(cpu) {
588
if (cpuctl_devs[cpu] != NULL)
589
destroy_dev(cpuctl_devs[cpu]);
590
}
591
free(cpuctl_devs, M_CPUCTL);
592
break;
593
case MOD_SHUTDOWN:
594
break;
595
default:
596
return (EOPNOTSUPP);
597
}
598
return (0);
599
}
600
601
DEV_MODULE(cpuctl, cpuctl_modevent, NULL);
602
MODULE_VERSION(cpuctl, CPUCTL_VERSION);
603
604