xref: /netbsd-src/sys/arch/x86/x86/procfs_machdep.c (revision dbcbc999cfbee5f0bc32c256f051976c1b76f226)
1 /*	$NetBSD: procfs_machdep.c,v 1.49 2024/10/06 15:36:05 msaitoh Exp $ */
2 
3 /*
4  * Copyright (c) 2001 Wasabi Systems, Inc.
5  * All rights reserved.
6  *
7  * Written by Frank van der Linden and Jason R. Thorpe for
8  * Wasabi Systems, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *      This product includes software developed for the NetBSD Project by
21  *      Wasabi Systems, Inc.
22  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
23  *    or promote products derived from this software without specific prior
24  *    written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * NOTE: We simply use the primary CPU's cpuid_level and tsc_freq
41  * here.  Might want to change this later.
42  */
43 
44 #include <sys/cdefs.h>
45 __KERNEL_RCSID(0, "$NetBSD: procfs_machdep.c,v 1.49 2024/10/06 15:36:05 msaitoh Exp $");
46 
47 #include <sys/param.h>
48 #include <sys/systm.h>
49 #include <sys/mount.h>
50 #include <sys/stat.h>
51 #include <sys/vnode.h>
52 
53 #include <miscfs/procfs/procfs.h>
54 
55 #include <machine/cpu.h>
56 #include <machine/reg.h>
57 #include <machine/specialreg.h>
58 #include <x86/cputypes.h>
59 #include <x86/cpuvar.h>
60 
61 /*
62  *  The feature table. The order is the same as Linux's
63  *  x86/include/asm/cpufeatures.h.
64  */
65 static const char * const x86_features[][32] = {
66 	{ /* (0) Common: 0x0000001 edx */
67 	"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
68 	"cx8", "apic", NULL, "sep", "mtrr", "pge", "mca", "cmov",
69 	"pat", "pse36", "pn", "clflush", NULL, "dts", "acpi", "mmx",
70 	"fxsr", "sse", "sse2", "ss", "ht", "tm", "ia64", "pbe"},
71 
72 	{ /* (1) AMD-defined: 0x80000001 edx */
73 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
74 	NULL, NULL, NULL, "syscall", NULL, NULL, NULL, NULL,
75 	NULL, NULL, NULL, "mp", "nx", NULL, "mmxext", NULL,
76 	NULL, "fxsr_opt", "pdpe1gb", "rdtscp", NULL, "lm", "3dnowext","3dnow"},
77 
78 	{ /* (2) Transmeta-defined */
79 	"recovery", "longrun", NULL, "lrti", NULL, NULL, NULL, NULL,
80 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
81 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
82 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
83 
84 	{ /* (3) Linux mapping */
85 	"cxmmx", "k6_mtrr", "cyrix_arr", "centaur_mcr", NULL, NULL, NULL, NULL,
86 	"constant_tsc", "up", "art", "arch_perfmon",
87 	"pebs", "bts", NULL, NULL,
88 	"rep_good", NULL, NULL, "acc_power",
89 	"nopl", NULL, "xtopology", "tsc_reliable",
90 	"nonstop_tsc", "cpuid", "extd_apicid", "amd_dcm",
91 	"aperfmperf", "rapl", "nonstop_tsc_s3", "tsc_known_freq"},
92 
93 	{ /* (4) Intel-defined: 0x00000001 ecx */
94 	"pni", "pclmulqdq", "dtes64", "monitor", "ds_cpl", "vmx", "smx", "est",
95 	"tm2", "ssse3", "cid", "sdbg", "fma", "cx16", "xtpr", "pdcm",
96 	NULL, "pcid", "dca", "sse4_1", "sse4_2", "x2apic", "movbe", "popcnt",
97 	"tsc_deadline_timer", "aes", "xsave", NULL,
98 	"avx", "f16c", "rdrand", "hypervisor"},
99 
100 	{ /* (5) VIA/Cyrix/Centaur-defined */
101 	NULL, NULL, "rng", "rng_en", NULL, NULL, "ace", "ace_en",
102 	"ace2", "ace2_en", "phe", "phe_en", "pmm", "pmm_en", NULL, NULL,
103 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
104 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
105 
106 	{ /* (6) AMD defined 80000001 ecx */
107 	"lahf_lm", "cmp_legacy", "svm", "extapic",
108 	"cr8_legacy", "abm", "sse4a", "misalignsse",
109 	"3dnowprefetch", "osvw", "ibs", "xop", "skinit", "wdt", NULL, "lwp",
110 	"fma4", "tce", NULL, "nodeid_msr",
111 	NULL, "tbm", "topoext", "perfctr_core",
112 	"perfctr_nb", NULL, "bpext", "ptsc",
113 	"perfctr_llc", "mwaitx", NULL, NULL},
114 
115 	{ /* (7) Linux mapping */
116 	"ring3mwait", "cpuid_fault", "cpb", "epb",
117 	"cat_l3", "cat_l2", "cdp_l3", "invpcid_single",
118 	"hw_pstate", "proc_feedback", NULL, "pti",
119 	NULL, NULL, "intel_ppin", "cdp_l2",
120 	NULL, "ssbd", "mba", NULL, "perfmon_v2", NULL, NULL, NULL,
121 	NULL, "ibrs", "ibpb", "stibp", NULL, NULL, "ibrs_enhanced", NULL},
122 
123 	{ /* (8) Linux mapping */
124 	"tpr_shadow", "flexpriority", "ept", "vpid", NULL, NULL, NULL, NULL,
125 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, "vmmcall",
126 	NULL, "ept_ad", NULL, NULL, NULL, NULL, "tdx_guest", NULL,
127 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
128 
129 	{ /* (9) Intel-defined: 00000007 ebx */
130 	"fsgsbase", "tsc_adjust", "sgx", "bmi1", "hle", "avx2", NULL, "smep",
131 	"bmi2", "erms", "invpcid", "rtm", "cqm", NULL, "mpx", "rdt_a",
132 	"avx512f", "avx512dq", "rdseed", "adx",
133 	"smap", "avx512ifma", NULL, "clflushopt",
134 	"clwb", "intel_pt", "avx512pf", "avx512er",
135 	"avx512cd", "sha_ni", "avx512bw", "avx512vl"},
136 
137 	{ /* (10) 0x0000000d:1 eax */
138 	"xsaveopt", "xsavec", "xgetbv1", "xsaves", NULL, NULL, NULL, NULL,
139 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
140 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
141 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
142 
143 	{ /* (11) Linux mapping */
144 	"cqm_llc", "cqm_occup_llc", "cqm_mbm_total", "cqm_mbm_local",
145 	NULL, NULL, "split_lock_detect", NULL,
146 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
147 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
148 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
149 
150 	{ /* (12) Intel-defined 0x00000007:1 eax */
151 	NULL, NULL, NULL, NULL,
152 	"avx_vnni", "avx512_bf16", NULL, NULL,
153 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
154 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
155 	NULL, NULL, "lam", NULL, NULL, NULL, NULL, NULL},
156 
157 	{ /* (13) AMD 0x80000008 ebx */
158 	"clzero", "irperf", "xsaveerptr", NULL, "rdpru", NULL, NULL, NULL,
159 	NULL, "wbnoinvd", NULL, NULL, NULL, NULL, NULL, NULL,
160 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, "ppin",
161 	NULL, "virt_ssbd", NULL, "cppc", NULL, NULL, NULL, "brs"},
162 
163 	{ /* (14) 0x00000006 eax */
164 	"dtherm", "ida", "arat", NULL, "pln", NULL, "pts", "hwp",
165 	"hwp_notify", "hwp_act_window", "hwp_epp","hwp_pkg_req",
166 	NULL, NULL, NULL, NULL,
167 	NULL, NULL, NULL, "hfi", NULL, NULL, NULL, NULL,
168 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
169 
170 	{ /* (15) AMD 0x8000000a edx */
171 	"npt", "lbrv", "svm_lock", "nrip_save",
172 	"tsc_scale", "vmcb_clean", "flushbyasid", "decodeassists",
173 	NULL, NULL, "pausefilter", NULL, "pfthreshold", "avic", NULL,
174 	"v_vmsave_vmload",
175 	"vgif", NULL, "x2avic", NULL, "v_spec_ctrl", NULL, NULL, NULL,
176 	NULL, "vnmi", NULL, NULL, NULL, NULL, NULL, NULL},
177 
178 	{ /* (16) 0x00000007:0 ecx */
179 	NULL, "avx512vbmi", "umip", "pku",
180 	"ospke", "waitpkg", "avx512_vbmi2", NULL,
181 	"gfni", "vaes", "vpclmulqdq", "avx512_vnni",
182 	"avx512_bitalg", "tme", "avx512_vpopcntdq", NULL,
183 	"la57", NULL, NULL, NULL, NULL, NULL, "rdpid", NULL,
184 	NULL, "cldemote", NULL, "movdiri",
185 	"movdir64b", "enqcmd", "sgx_lc", NULL},
186 
187 	{ /* (17) AMD 0x80000007 ebx */
188 	"overflow_recov", "succor", NULL, "smca", NULL, NULL, NULL, NULL,
189 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
190 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
191 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
192 
193 	{ /* (18) Intel 0x00000007 edx */
194 	NULL, NULL, "avx512_4vnniw", "avx512_4fmaps", "fsrm", NULL, NULL, NULL,
195 	"vp2intersect", NULL, "md_clear", NULL, NULL, NULL, "serialize", NULL,
196 	"tsxldtrk", NULL, "pconfig", "arch_lbr",
197 	"ibt", NULL, "amx_bf16", "avx512_fp16",
198 	"amx_tile", "amx_int8", NULL, NULL,
199 	"flush_l1d", "arch_capabilities", NULL, NULL},
200 
201 	{ /* (19) AMD 0x8000001f eax */
202 	"sme", "sev", NULL, "sev_es", NULL, NULL, NULL, NULL,
203 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
204 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
205 	NULL, NULL, NULL, NULL, "svsm", NULL, NULL, NULL},
206 };
207 
208 static int	procfs_getonecpu(int, struct cpu_info *, char *, size_t *);
209 
210 /*
211  * Linux-style /proc/cpuinfo.
212  * Only used when procfs is mounted with -o linux.
213  *
214  * In the multiprocessor case, this should be a loop over all CPUs.
215  */
216 int
217 procfs_getcpuinfstr(char *bf, size_t *len)
218 {
219 	struct cpu_info *ci;
220 	CPU_INFO_ITERATOR cii;
221 	size_t i, total, size, used;
222 
223 	i = total = 0;
224 	used = size = *len;
225 
226 	for (CPU_INFO_FOREACH(cii, ci)) {
227 		procfs_getonecpu(i++, ci, bf, &used);
228 		total += used + 1;
229 		if (used + 1 <= size) {
230 			bf += used;
231 			*bf++ = '\n';
232 			size -= used + 1;
233 			used = size;
234 		} else
235 			used = 0;
236 	}
237 	size = *len;
238 	*len = total;
239 	return size < *len ? -1 : 0;
240 }
241 
242 static int
243 procfs_getonefeatreg(uint32_t reg, const char * const *table, char *p,
244     size_t *left)
245 {
246 	size_t l;
247 
248 	for (size_t i = 0; i < 32; i++) {
249 		if ((reg & (1U << i)) && table[i]) {
250 			l = snprintf(p, *left, "%s ", table[i]);
251 			if (l < *left) {
252 				*left -= l;
253 				p += l;
254 			} else
255 				break;
256 		}
257 	}
258 
259 	return 0; /* XXX */
260 }
261 
262 /*
263  * Print feature bits. The code assume that unused entry of x86_features[]
264  * is zero-cleared.
265  *
266  * XXX This function will be rewritten when all of linux entries are
267  * decoded.
268  */
269 static int
270 procfs_getonecpufeatures(struct cpu_info *ci, char *p, size_t *left)
271 {
272 	size_t last = *left;
273 	size_t diff;
274 	u_int descs[4];
275 
276 	procfs_getonefeatreg(ci->ci_feat_val[0], x86_features[0], p, left);
277 	diff = last - *left;
278 
279 	procfs_getonefeatreg(ci->ci_feat_val[2], x86_features[1], p + diff,
280 	    left);
281 	diff = last - *left;
282 
283 	/* x86_features[2] is for Transmeta */
284 	/* x86_features[3] is Linux defined mapping */
285 
286 	procfs_getonefeatreg(ci->ci_feat_val[1], x86_features[4], p + diff,
287 	    left);
288 	diff = last - *left;
289 
290 	procfs_getonefeatreg(ci->ci_feat_val[4], x86_features[5], p + diff,
291 	    left);
292 	diff = last - *left;
293 
294 	procfs_getonefeatreg(ci->ci_feat_val[3], x86_features[6], p + diff,
295 	    left);
296 	diff = last - *left;
297 
298 	/* x86_features[7] is Linux defined mapping */
299 	/* x86_features[8] is Linux defined mapping */
300 
301 	procfs_getonefeatreg(ci->ci_feat_val[5], x86_features[9], p + diff,
302 	    left);
303 	diff = last - *left;
304 
305 	if (ci->ci_max_cpuid >= 0x0d) {
306 		x86_cpuid2(0x0d, 1, descs);
307 		procfs_getonefeatreg(descs[0], x86_features[10], p + diff,
308 		    left);
309 		diff = last - *left;
310 	}
311 
312 	/* x86_features[11] is Linux defined mapping */
313 
314 	if (ci->ci_max_cpuid >= 0x07) {
315 		x86_cpuid2(0x07, 1, descs);
316 		procfs_getonefeatreg(descs[0], x86_features[12], p + diff,
317 		    left);
318 		diff = last - *left;
319 	}
320 
321 	if ((cpu_vendor == CPUVENDOR_AMD)
322 	    && (ci->ci_max_ext_cpuid >= 0x80000008)) {
323 		x86_cpuid(0x80000008, descs);
324 		procfs_getonefeatreg(descs[1], x86_features[13], p + diff,
325 		    left);
326 		diff = last - *left;
327 	}
328 
329 	if (ci->ci_max_cpuid >= 0x06) {
330 		x86_cpuid(0x06, descs);
331 		procfs_getonefeatreg(descs[0], x86_features[14], p + diff,
332 		    left);
333 		diff = last - *left;
334 	}
335 
336 	if ((cpu_vendor == CPUVENDOR_AMD)
337 	    && (ci->ci_max_ext_cpuid >= 0x8000000a)) {
338 		x86_cpuid(0x8000000a, descs);
339 		procfs_getonefeatreg(descs[3], x86_features[15], p + diff,
340 		    left);
341 		diff = last - *left;
342 	}
343 
344 	procfs_getonefeatreg(ci->ci_feat_val[6], x86_features[16], p + diff,
345 	    left);
346 	diff = last - *left;
347 
348 	if ((cpu_vendor == CPUVENDOR_AMD)
349 	    && (ci->ci_max_ext_cpuid >= 0x80000007)) {
350 		x86_cpuid(0x80000007, descs);
351 		procfs_getonefeatreg(descs[1], x86_features[17], p + diff,
352 		    left);
353 		diff = last - *left;
354 	}
355 
356 	if ((cpu_vendor == CPUVENDOR_INTEL)
357 	    && (ci->ci_max_cpuid >= 0x00000007)) {
358 		x86_cpuid(0x00000007, descs);
359 		procfs_getonefeatreg(descs[3], x86_features[18], p + diff,
360 		    left);
361 		diff = last - *left;
362 	}
363 
364 	if ((cpu_vendor == CPUVENDOR_AMD)
365 	    && (ci->ci_max_ext_cpuid >= 0x80000019)) {
366 		x86_cpuid(0x8000001f, descs);
367 		procfs_getonefeatreg(descs[0], x86_features[19], p + diff,
368 		    left);
369 		diff = last - *left;
370 	}
371 
372 	return 0; /* XXX */
373 }
374 
375 static int
376 procfs_getonecpu(int xcpu, struct cpu_info *ci, char *bf, size_t *len)
377 {
378 	size_t left, l, size;
379 	char featurebuf[1024], *p;
380 
381 	p = featurebuf;
382 	left = sizeof(featurebuf);
383 	size = *len;
384 	procfs_getonecpufeatures(ci, p, &left);
385 
386 	p = bf;
387 	left = *len;
388 	size = 0;
389 	l = snprintf(p, left,
390 	    "processor\t: %d\n"
391 	    "vendor_id\t: %s\n"
392 	    "cpu family\t: %d\n"
393 	    "model\t\t: %d\n"
394 	    "model name\t: %s\n"
395 	    "stepping\t: ",
396 	    xcpu,
397 	    (char *)ci->ci_vendor,
398 	    CPUID_TO_FAMILY(ci->ci_signature),
399 	    CPUID_TO_MODEL(ci->ci_signature),
400 	    cpu_brand_string
401 	);
402 	size += l;
403 	if (l < left) {
404 		left -= l;
405 		p += l;
406 	} else
407 		left = 0;
408 
409 	if (cpuid_level >= 0)
410 		l = snprintf(p, left, "%d\n",
411 		    CPUID_TO_STEPPING(ci->ci_signature));
412 	else
413 		l = snprintf(p, left, "unknown\n");
414 
415 	size += l;
416 	if (l < left) {
417 		left -= l;
418 		p += l;
419 	} else
420 		left = 0;
421 
422 	if (ci->ci_data.cpu_cc_freq != 0) {
423 		uint64_t freq, fraq;
424 
425 		freq = (ci->ci_data.cpu_cc_freq + 4999) / 1000000;
426 		fraq = ((ci->ci_data.cpu_cc_freq + 4999) / 10000) % 100;
427 		l = snprintf(p, left, "cpu MHz\t\t: %" PRIu64 ".%02" PRIu64
428 		    "\n", freq, fraq);
429 	} else
430 		l = snprintf(p, left, "cpu MHz\t\t: unknown\n");
431 
432 	size += l;
433 	if (l < left) {
434 		left -= l;
435 		p += l;
436 	} else
437 		left = 0;
438 
439 	l = snprintf(p, left,
440 	    "apicid\t\t: %lu\n"
441 	    "initial apicid\t: %u\n",
442 	    ci->ci_cpuid,
443 	    ci->ci_initapicid
444 	);
445 	size += l;
446 	if (l < left) {
447 		left -= l;
448 		p += l;
449 	} else
450 		left = 0;
451 
452 	l = snprintf(p, left,
453 #ifdef __i386__
454 	    "fdiv_bug\t: %s\n"
455 #endif
456 	    "fpu\t\t: yes\n"
457 	    "fpu_exception\t: yes\n"
458 	    "cpuid level\t: %d\n"
459 	    "wp\t\t: %s\n"
460 	    "flags\t\t: %s\n"
461 	    "clflush size\t: %d\n",
462 #ifdef __i386__
463 	    i386_fpu_fdivbug ? "yes" : "no",	/* an old pentium */
464 #endif
465 	    ci->ci_max_cpuid,
466 	    (rcr0() & CR0_WP) ? "yes" : "no",
467 	    featurebuf,
468 	    ci->ci_cflush_lsize
469 	);
470 	size += l;
471 
472 	left = *len;
473 	*len = size;
474 	return left < *len ? -1 : 0;
475 }
476 
477 #if defined(__HAVE_PROCFS_MACHDEP) && !defined(__x86_64__)
478 
479 void
480 procfs_machdep_allocvp(struct vnode *vp)
481 {
482 	struct pfsnode *pfs = vp->v_data;
483 
484 	switch (pfs->pfs_type) {
485 	case Pmachdep_xmmregs:
486 		/* /proc/N/xmmregs = -rw------- */
487 		pfs->pfs_mode = S_IRUSR|S_IWUSR;
488 		vp->v_type = VREG;
489 		break;
490 	default:
491 		KASSERT(false);
492 	}
493 }
494 
495 int
496 procfs_machdep_rw(struct lwp *curl, struct lwp *l, struct pfsnode *pfs,
497     struct uio *uio)
498 {
499 
500 	switch (pfs->pfs_type) {
501 	case Pmachdep_xmmregs:
502 		return (procfs_machdep_doxmmregs(curl, l, pfs, uio));
503 	default:
504 		KASSERT(false);
505 	}
506 	return EINVAL;
507 }
508 
509 int
510 procfs_machdep_getattr(struct vnode *vp, struct vattr *vap, struct proc *procp)
511 {
512 	struct pfsnode *pfs = VTOPFS(vp);
513 
514 	switch (pfs->pfs_type) {
515 	case Pmachdep_xmmregs:
516 		vap->va_bytes = vap->va_size = sizeof(struct xmmregs);
517 		break;
518 	default:
519 		KASSERT(false);
520 	}
521 	return 0;
522 }
523 
524 int
525 procfs_machdep_doxmmregs(struct lwp *curl, struct lwp *l,
526     struct pfsnode *pfs, struct uio *uio)
527 {
528 
529 	return process_machdep_doxmmregs(curl, l, uio);
530 }
531 
532 int
533 procfs_machdep_validxmmregs(struct lwp *l, struct mount *mp)
534 {
535 
536 	return process_machdep_validxmmregs(l->l_proc);
537 }
538 
539 #endif
540