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