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