#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: procfs_machdep.c,v 1.49 2024/10/06 15:36:05 msaitoh Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#include <miscfs/procfs/procfs.h>
#include <machine/cpu.h>
#include <machine/reg.h>
#include <machine/specialreg.h>
#include <x86/cputypes.h>
#include <x86/cpuvar.h>
static const char * const x86_features[][32] = {
{
"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
"cx8", "apic", NULL, "sep", "mtrr", "pge", "mca", "cmov",
"pat", "pse36", "pn", "clflush", NULL, "dts", "acpi", "mmx",
"fxsr", "sse", "sse2", "ss", "ht", "tm", "ia64", "pbe"},
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, "syscall", NULL, NULL, NULL, NULL,
NULL, NULL, NULL, "mp", "nx", NULL, "mmxext", NULL,
NULL, "fxsr_opt", "pdpe1gb", "rdtscp", NULL, "lm", "3dnowext","3dnow"},
{
"recovery", "longrun", NULL, "lrti", NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{
"cxmmx", "k6_mtrr", "cyrix_arr", "centaur_mcr", NULL, NULL, NULL, NULL,
"constant_tsc", "up", "art", "arch_perfmon",
"pebs", "bts", NULL, NULL,
"rep_good", NULL, NULL, "acc_power",
"nopl", NULL, "xtopology", "tsc_reliable",
"nonstop_tsc", "cpuid", "extd_apicid", "amd_dcm",
"aperfmperf", "rapl", "nonstop_tsc_s3", "tsc_known_freq"},
{
"pni", "pclmulqdq", "dtes64", "monitor", "ds_cpl", "vmx", "smx", "est",
"tm2", "ssse3", "cid", "sdbg", "fma", "cx16", "xtpr", "pdcm",
NULL, "pcid", "dca", "sse4_1", "sse4_2", "x2apic", "movbe", "popcnt",
"tsc_deadline_timer", "aes", "xsave", NULL,
"avx", "f16c", "rdrand", "hypervisor"},
{
NULL, NULL, "rng", "rng_en", NULL, NULL, "ace", "ace_en",
"ace2", "ace2_en", "phe", "phe_en", "pmm", "pmm_en", NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{
"lahf_lm", "cmp_legacy", "svm", "extapic",
"cr8_legacy", "abm", "sse4a", "misalignsse",
"3dnowprefetch", "osvw", "ibs", "xop", "skinit", "wdt", NULL, "lwp",
"fma4", "tce", NULL, "nodeid_msr",
NULL, "tbm", "topoext", "perfctr_core",
"perfctr_nb", NULL, "bpext", "ptsc",
"perfctr_llc", "mwaitx", NULL, NULL},
{
"ring3mwait", "cpuid_fault", "cpb", "epb",
"cat_l3", "cat_l2", "cdp_l3", "invpcid_single",
"hw_pstate", "proc_feedback", NULL, "pti",
NULL, NULL, "intel_ppin", "cdp_l2",
NULL, "ssbd", "mba", NULL, "perfmon_v2", NULL, NULL, NULL,
NULL, "ibrs", "ibpb", "stibp", NULL, NULL, "ibrs_enhanced", NULL},
{
"tpr_shadow", "flexpriority", "ept", "vpid", NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, "vmmcall",
NULL, "ept_ad", NULL, NULL, NULL, NULL, "tdx_guest", NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{
"fsgsbase", "tsc_adjust", "sgx", "bmi1", "hle", "avx2", NULL, "smep",
"bmi2", "erms", "invpcid", "rtm", "cqm", NULL, "mpx", "rdt_a",
"avx512f", "avx512dq", "rdseed", "adx",
"smap", "avx512ifma", NULL, "clflushopt",
"clwb", "intel_pt", "avx512pf", "avx512er",
"avx512cd", "sha_ni", "avx512bw", "avx512vl"},
{
"xsaveopt", "xsavec", "xgetbv1", "xsaves", NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{
"cqm_llc", "cqm_occup_llc", "cqm_mbm_total", "cqm_mbm_local",
NULL, NULL, "split_lock_detect", NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{
NULL, NULL, NULL, NULL,
"avx_vnni", "avx512_bf16", NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, "lam", NULL, NULL, NULL, NULL, NULL},
{
"clzero", "irperf", "xsaveerptr", NULL, "rdpru", NULL, NULL, NULL,
NULL, "wbnoinvd", NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, "ppin",
NULL, "virt_ssbd", NULL, "cppc", NULL, NULL, NULL, "brs"},
{
"dtherm", "ida", "arat", NULL, "pln", NULL, "pts", "hwp",
"hwp_notify", "hwp_act_window", "hwp_epp","hwp_pkg_req",
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, "hfi", NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{
"npt", "lbrv", "svm_lock", "nrip_save",
"tsc_scale", "vmcb_clean", "flushbyasid", "decodeassists",
NULL, NULL, "pausefilter", NULL, "pfthreshold", "avic", NULL,
"v_vmsave_vmload",
"vgif", NULL, "x2avic", NULL, "v_spec_ctrl", NULL, NULL, NULL,
NULL, "vnmi", NULL, NULL, NULL, NULL, NULL, NULL},
{
NULL, "avx512vbmi", "umip", "pku",
"ospke", "waitpkg", "avx512_vbmi2", NULL,
"gfni", "vaes", "vpclmulqdq", "avx512_vnni",
"avx512_bitalg", "tme", "avx512_vpopcntdq", NULL,
"la57", NULL, NULL, NULL, NULL, NULL, "rdpid", NULL,
NULL, "cldemote", NULL, "movdiri",
"movdir64b", "enqcmd", "sgx_lc", NULL},
{
"overflow_recov", "succor", NULL, "smca", NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{
NULL, NULL, "avx512_4vnniw", "avx512_4fmaps", "fsrm", NULL, NULL, NULL,
"vp2intersect", NULL, "md_clear", NULL, NULL, NULL, "serialize", NULL,
"tsxldtrk", NULL, "pconfig", "arch_lbr",
"ibt", NULL, "amx_bf16", "avx512_fp16",
"amx_tile", "amx_int8", NULL, NULL,
"flush_l1d", "arch_capabilities", NULL, NULL},
{
"sme", "sev", NULL, "sev_es", NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, "svsm", NULL, NULL, NULL},
};
static int procfs_getonecpu(int, struct cpu_info *, char *, size_t *);
int
procfs_getcpuinfstr(char *bf, size_t *len)
{
struct cpu_info *ci;
CPU_INFO_ITERATOR cii;
size_t i, total, size, used;
i = total = 0;
used = size = *len;
for (CPU_INFO_FOREACH(cii, ci)) {
procfs_getonecpu(i++, ci, bf, &used);
total += used + 1;
if (used + 1 <= size) {
bf += used;
*bf++ = '\n';
size -= used + 1;
used = size;
} else
used = 0;
}
size = *len;
*len = total;
return size < *len ? -1 : 0;
}
static int
procfs_getonefeatreg(uint32_t reg, const char * const *table, char *p,
size_t *left)
{
size_t l;
for (size_t i = 0; i < 32; i++) {
if ((reg & (1U << i)) && table[i]) {
l = snprintf(p, *left, "%s ", table[i]);
if (l < *left) {
*left -= l;
p += l;
} else
break;
}
}
return 0;
}
static int
procfs_getonecpufeatures(struct cpu_info *ci, char *p, size_t *left)
{
size_t last = *left;
size_t diff;
u_int descs[4];
procfs_getonefeatreg(ci->ci_feat_val[0], x86_features[0], p, left);
diff = last - *left;
procfs_getonefeatreg(ci->ci_feat_val[2], x86_features[1], p + diff,
left);
diff = last - *left;
procfs_getonefeatreg(ci->ci_feat_val[1], x86_features[4], p + diff,
left);
diff = last - *left;
procfs_getonefeatreg(ci->ci_feat_val[4], x86_features[5], p + diff,
left);
diff = last - *left;
procfs_getonefeatreg(ci->ci_feat_val[3], x86_features[6], p + diff,
left);
diff = last - *left;
procfs_getonefeatreg(ci->ci_feat_val[5], x86_features[9], p + diff,
left);
diff = last - *left;
if (ci->ci_max_cpuid >= 0x0d) {
x86_cpuid2(0x0d, 1, descs);
procfs_getonefeatreg(descs[0], x86_features[10], p + diff,
left);
diff = last - *left;
}
if (ci->ci_max_cpuid >= 0x07) {
x86_cpuid2(0x07, 1, descs);
procfs_getonefeatreg(descs[0], x86_features[12], p + diff,
left);
diff = last - *left;
}
if ((cpu_vendor == CPUVENDOR_AMD)
&& (ci->ci_max_ext_cpuid >= 0x80000008)) {
x86_cpuid(0x80000008, descs);
procfs_getonefeatreg(descs[1], x86_features[13], p + diff,
left);
diff = last - *left;
}
if (ci->ci_max_cpuid >= 0x06) {
x86_cpuid(0x06, descs);
procfs_getonefeatreg(descs[0], x86_features[14], p + diff,
left);
diff = last - *left;
}
if ((cpu_vendor == CPUVENDOR_AMD)
&& (ci->ci_max_ext_cpuid >= 0x8000000a)) {
x86_cpuid(0x8000000a, descs);
procfs_getonefeatreg(descs[3], x86_features[15], p + diff,
left);
diff = last - *left;
}
procfs_getonefeatreg(ci->ci_feat_val[6], x86_features[16], p + diff,
left);
diff = last - *left;
if ((cpu_vendor == CPUVENDOR_AMD)
&& (ci->ci_max_ext_cpuid >= 0x80000007)) {
x86_cpuid(0x80000007, descs);
procfs_getonefeatreg(descs[1], x86_features[17], p + diff,
left);
diff = last - *left;
}
if ((cpu_vendor == CPUVENDOR_INTEL)
&& (ci->ci_max_cpuid >= 0x00000007)) {
x86_cpuid(0x00000007, descs);
procfs_getonefeatreg(descs[3], x86_features[18], p + diff,
left);
diff = last - *left;
}
if ((cpu_vendor == CPUVENDOR_AMD)
&& (ci->ci_max_ext_cpuid >= 0x80000019)) {
x86_cpuid(0x8000001f, descs);
procfs_getonefeatreg(descs[0], x86_features[19], p + diff,
left);
diff = last - *left;
}
return 0;
}
static int
procfs_getonecpu(int xcpu, struct cpu_info *ci, char *bf, size_t *len)
{
size_t left, l, size;
char featurebuf[1024], *p;
p = featurebuf;
left = sizeof(featurebuf);
size = *len;
procfs_getonecpufeatures(ci, p, &left);
p = bf;
left = *len;
size = 0;
l = snprintf(p, left,
"processor\t: %d\n"
"vendor_id\t: %s\n"
"cpu family\t: %d\n"
"model\t\t: %d\n"
"model name\t: %s\n"
"stepping\t: ",
xcpu,
(char *)ci->ci_vendor,
CPUID_TO_FAMILY(ci->ci_signature),
CPUID_TO_MODEL(ci->ci_signature),
cpu_brand_string
);
size += l;
if (l < left) {
left -= l;
p += l;
} else
left = 0;
if (cpuid_level >= 0)
l = snprintf(p, left, "%d\n",
CPUID_TO_STEPPING(ci->ci_signature));
else
l = snprintf(p, left, "unknown\n");
size += l;
if (l < left) {
left -= l;
p += l;
} else
left = 0;
if (ci->ci_data.cpu_cc_freq != 0) {
uint64_t freq, fraq;
freq = (ci->ci_data.cpu_cc_freq + 4999) / 1000000;
fraq = ((ci->ci_data.cpu_cc_freq + 4999) / 10000) % 100;
l = snprintf(p, left, "cpu MHz\t\t: %" PRIu64 ".%02" PRIu64
"\n", freq, fraq);
} else
l = snprintf(p, left, "cpu MHz\t\t: unknown\n");
size += l;
if (l < left) {
left -= l;
p += l;
} else
left = 0;
l = snprintf(p, left,
"apicid\t\t: %lu\n"
"initial apicid\t: %u\n",
ci->ci_cpuid,
ci->ci_initapicid
);
size += l;
if (l < left) {
left -= l;
p += l;
} else
left = 0;
l = snprintf(p, left,
#ifdef __i386__
"fdiv_bug\t: %s\n"
#endif
"fpu\t\t: yes\n"
"fpu_exception\t: yes\n"
"cpuid level\t: %d\n"
"wp\t\t: %s\n"
"flags\t\t: %s\n"
"clflush size\t: %d\n",
#ifdef __i386__
i386_fpu_fdivbug ? "yes" : "no",
#endif
ci->ci_max_cpuid,
(rcr0() & CR0_WP) ? "yes" : "no",
featurebuf,
ci->ci_cflush_lsize
);
size += l;
left = *len;
*len = size;
return left < *len ? -1 : 0;
}
#if defined(__HAVE_PROCFS_MACHDEP) && !defined(__x86_64__)
void
procfs_machdep_allocvp(struct vnode *vp)
{
struct pfsnode *pfs = vp->v_data;
switch (pfs->pfs_type) {
case Pmachdep_xmmregs:
pfs->pfs_mode = S_IRUSR|S_IWUSR;
vp->v_type = VREG;
break;
default:
KASSERT(false);
}
}
int
procfs_machdep_rw(struct lwp *curl, struct lwp *l, struct pfsnode *pfs,
struct uio *uio)
{
switch (pfs->pfs_type) {
case Pmachdep_xmmregs:
return (procfs_machdep_doxmmregs(curl, l, pfs, uio));
default:
KASSERT(false);
}
return EINVAL;
}
int
procfs_machdep_getattr(struct vnode *vp, struct vattr *vap, struct proc *procp)
{
struct pfsnode *pfs = VTOPFS(vp);
switch (pfs->pfs_type) {
case Pmachdep_xmmregs:
vap->va_bytes = vap->va_size = sizeof(struct xmmregs);
break;
default:
KASSERT(false);
}
return 0;
}
int
procfs_machdep_doxmmregs(struct lwp *curl, struct lwp *l,
struct pfsnode *pfs, struct uio *uio)
{
return process_machdep_doxmmregs(curl, l, uio);
}
int
procfs_machdep_validxmmregs(struct lwp *l, struct mount *mp)
{
return process_machdep_validxmmregs(l->l_proc);
}
#endif