1 | /* $NetBSD: procfs_machdep.c,v 1.13 2016/08/08 09:39:06 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.13 2016/08/08 09:39:06 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 | |
59 | /* |
60 | * The feature table. The order is the same as Linux's |
61 | * x86/include/asm/cpufeatures.h. |
62 | */ |
63 | static const char * const x86_features[][32] = { |
64 | { /* (0) Common: 0x0000001 edx */ |
65 | "fpu" , "vme" , "de" , "pse" , "tsc" , "msr" , "pae" , "mce" , |
66 | "cx8" , "apic" , NULL, "sep" , "mtrr" , "pge" , "mca" , "cmov" , |
67 | "pat" , "pse36" , "pn" , "clflush" , NULL, "dts" , "acpi" , "mmx" , |
68 | "fxsr" , "sse" , "sse2" , "ss" , "ht" , "tm" , "ia64" , "pbe" }, |
69 | |
70 | { /* (1) AMD-defined: 0x80000001 edx */ |
71 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
72 | NULL, NULL, NULL, "syscall" , NULL, NULL, NULL, NULL, |
73 | NULL, NULL, NULL, "mp" , "nx" , NULL, "mmxext" , NULL, |
74 | NULL, "fxsr_opt" , "pdpe1gb" , "rdtscp" , NULL, "lm" , "3dnowext" ,"3dnow" }, |
75 | |
76 | { /* (2) Transmeta-defined */ |
77 | "recovery" , "longrun" , NULL, "lrti" , NULL, NULL, NULL, NULL, |
78 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
79 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
80 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, |
81 | |
82 | { /* (3) Linux mapping */ |
83 | "cxmmx" , NULL, "cyrix_arr" , "centaur_mcr" , NULL, |
84 | "constant_tsc" , NULL, NULL, |
85 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
86 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
87 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, |
88 | |
89 | { /* (4) Intel-defined: 0x00000001 ecx */ |
90 | "pni" , "pclmulqdq" , "dtes64" , "monitor" , "ds_cpl" , "vmx" , "smx" , "est" , |
91 | "tm2" , "ssse3" , "cid" , "sdbg" , "fma" , "cx16" , "xtpr" , "pdcm" , |
92 | NULL, "pcid" , "dca" , "sse4_1" , "sse4_2" , "x2apic" , "movbe" , "popcnt" , |
93 | "tsc_deadline_timer" , "aes" , "xsave" , NULL, |
94 | "avx" , "f16c" , "rdrand" , "hypervisor" }, |
95 | |
96 | { /* (5) VIA/Cyrix/Centaur-defined */ |
97 | NULL, NULL, "rng" , "rng_en" , NULL, NULL, "ace" , "ace_en" , |
98 | "ace2" , "ace2_en" , "phe" , "phe_en" , "pmm" , "pmm_en" , NULL, NULL, |
99 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
100 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, |
101 | |
102 | { /* (6) AMD defined 80000001 ecx */ |
103 | "lahf_lm" , "cmp_legacy" , "svm" , "extapic" , |
104 | "cr8_legacy" , "abm" , "sse4a" , "misalignsse" , |
105 | "3dnowprefetch" , "osvw" , "ibs" , "xop" , "skinit" , "wdt" , NULL, "lwp" , |
106 | "fma4" , "tce" , NULL, "nodeid_msr" , |
107 | NULL, "tbm" , "topoext" , "perfctr_core" , |
108 | "perfctr_nb" , NULL, "bpext" , "ptsc" , |
109 | "perfctr_l2" , "mwaitx" , NULL, NULL}, |
110 | |
111 | { /* (7) Linux mapping */ |
112 | NULL, NULL, "cpb" , "ebp" , NULL, "pln" , "pts" , "dtherm" , |
113 | "hw_pstate" , "proc_feedback" , NULL, NULL, |
114 | NULL, NULL, NULL, "intel_pt" , |
115 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
116 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, |
117 | |
118 | { /* (8) Linux mapping */ |
119 | "tpr_shadow" , "vnmi" , "flexpriority" , "ept" , |
120 | "vpid" , "npt" , "lbrv" , "svm_lock" , |
121 | "nrip_save" , "tsc_scale" , "vmcb_clean" , "flushbyasid" , |
122 | "decodeassists" , "pausefilter" , "pfthreshold" , "vmmcall" , |
123 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
124 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, |
125 | |
126 | { /* (9) Intel-defined: 00000007 ebx */ |
127 | "fsgsbase" , "tsc_adjust" , NULL, "bmi1" , "hle" , "avx2" , NULL, "smep" , |
128 | "bmi2" , "erms" , "invpcid" , "rtm" , "cqm" , NULL, "mpx" , NULL, |
129 | "avx512f" , "avx512dq" , "rdseed" , "adx" , |
130 | "smap" , NULL, "pcommit" , "clflushopt" , |
131 | "clwb" , NULL, "avx512pf" , "avx512er" , |
132 | "avx512cd" , "sha_ni" , "avx512bw" , "avx512vl" }, |
133 | |
134 | { /* (10) 0000000d eax */ |
135 | "xsaveopt" , "xsavec" , "xgetbv1" , "xsaves" , NULL, NULL, NULL, NULL, |
136 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
137 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
138 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, |
139 | |
140 | { /* (11) 0x0000000f:0 edx */ |
141 | NULL, "cqm_llc" , NULL, NULL, NULL, NULL, NULL, NULL, |
142 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
143 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
144 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, |
145 | |
146 | { /* (12) 0x0000000f:1 edx */ |
147 | "cqm_occup_llc" , NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
148 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
149 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
150 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, |
151 | }; |
152 | |
153 | static int procfs_getonecpu(int, struct cpu_info *, char *, size_t *); |
154 | |
155 | /* |
156 | * Linux-style /proc/cpuinfo. |
157 | * Only used when procfs is mounted with -o linux. |
158 | * |
159 | * In the multiprocessor case, this should be a loop over all CPUs. |
160 | */ |
161 | int |
162 | procfs_getcpuinfstr(char *bf, size_t *len) |
163 | { |
164 | struct cpu_info *ci; |
165 | CPU_INFO_ITERATOR cii; |
166 | size_t i, total, size, used; |
167 | |
168 | i = total = 0; |
169 | used = size = *len; |
170 | |
171 | for (CPU_INFO_FOREACH(cii, ci)) { |
172 | procfs_getonecpu(i++, ci, bf, &used); |
173 | total += used + 1; |
174 | if (used + 1 < size) { |
175 | bf += used; |
176 | *bf++ = '\n'; |
177 | size -= used + 1; |
178 | used = size; |
179 | } else |
180 | used = 0; |
181 | } |
182 | size = *len; |
183 | *len = total; |
184 | return size < *len ? -1 : 0; |
185 | } |
186 | |
187 | static int |
188 | procfs_getonefeatreg(uint32_t reg, const char * const *table, char *p, |
189 | size_t *left) |
190 | { |
191 | size_t l; |
192 | |
193 | for (size_t i = 0; i < 32; i++) { |
194 | if ((reg & (1 << i)) && table[i]) { |
195 | l = snprintf(p, *left, "%s " , table[i]); |
196 | if (l < *left) { |
197 | *left -= l; |
198 | p += l; |
199 | } else |
200 | break; |
201 | } |
202 | } |
203 | |
204 | return 0; /* XXX */ |
205 | } |
206 | |
207 | /* |
208 | * Print feature bits. The code assume that unused entry of x86_features[] |
209 | * is zero-cleared. |
210 | * |
211 | * XXX This function will be rewritten when all of linux entries are |
212 | * decoded. |
213 | */ |
214 | static int |
215 | procfs_getonecpufeatures(struct cpu_info *ci, char *p, size_t *left) |
216 | { |
217 | size_t last = *left; |
218 | size_t diff; |
219 | |
220 | procfs_getonefeatreg(ci->ci_feat_val[0], x86_features[0], p, left); |
221 | diff = last - *left; |
222 | |
223 | procfs_getonefeatreg(ci->ci_feat_val[2], x86_features[1], p + diff, |
224 | left); |
225 | diff = last - *left; |
226 | |
227 | /* x86_features[2] is for Transmeta */ |
228 | /* x86_features[3] is Linux defined mapping */ |
229 | |
230 | procfs_getonefeatreg(ci->ci_feat_val[1], x86_features[4], p + diff, |
231 | left); |
232 | diff = last - *left; |
233 | |
234 | procfs_getonefeatreg(ci->ci_feat_val[4], x86_features[5], p + diff, |
235 | left); |
236 | diff = last - *left; |
237 | |
238 | procfs_getonefeatreg(ci->ci_feat_val[3], x86_features[6], p + diff, |
239 | left); |
240 | diff = last - *left; |
241 | |
242 | /* x86_features[7] is Linux defined mapping */ |
243 | /* x86_features[8] is Linux defined mapping */ |
244 | |
245 | procfs_getonefeatreg(ci->ci_feat_val[5], x86_features[9], p + diff, |
246 | left); |
247 | diff = last - *left; |
248 | |
249 | /* (10) 0000000d eax */ |
250 | /* (11) 0x0000000f(ecx=0) edx */ |
251 | /* (12) 0x0000000f(ecx=1) edx */ |
252 | |
253 | return 0; /* XXX */ |
254 | } |
255 | |
256 | static int |
257 | procfs_getonecpu(int xcpu, struct cpu_info *ci, char *bf, size_t *len) |
258 | { |
259 | size_t left, l, size; |
260 | char featurebuf[1024], *p; |
261 | |
262 | p = featurebuf; |
263 | left = sizeof(featurebuf); |
264 | size = *len; |
265 | procfs_getonecpufeatures(ci, p, &left); |
266 | |
267 | p = bf; |
268 | left = *len; |
269 | size = 0; |
270 | l = snprintf(p, left, |
271 | "processor\t: %d\n" |
272 | "vendor_id\t: %s\n" |
273 | "cpu family\t: %d\n" |
274 | "model\t\t: %d\n" |
275 | "model name\t: %s\n" |
276 | "stepping\t: " , |
277 | xcpu, |
278 | (char *)ci->ci_vendor, |
279 | CPUID_TO_FAMILY(ci->ci_signature), |
280 | CPUID_TO_MODEL(ci->ci_signature), |
281 | cpu_brand_string |
282 | ); |
283 | size += l; |
284 | if (l < left) { |
285 | left -= l; |
286 | p += l; |
287 | } else |
288 | left = 0; |
289 | |
290 | if (cpuid_level >= 0) |
291 | l = snprintf(p, left, "%d\n" , |
292 | CPUID_TO_STEPPING(ci->ci_signature)); |
293 | else |
294 | l = snprintf(p, left, "unknown\n" ); |
295 | |
296 | size += l; |
297 | if (l < left) { |
298 | left -= l; |
299 | p += l; |
300 | } else |
301 | left = 0; |
302 | |
303 | if (ci->ci_data.cpu_cc_freq != 0) { |
304 | uint64_t freq, fraq; |
305 | |
306 | freq = (ci->ci_data.cpu_cc_freq + 4999) / 1000000; |
307 | fraq = ((ci->ci_data.cpu_cc_freq + 4999) / 10000) % 100; |
308 | l = snprintf(p, left, "cpu MHz\t\t: %" PRIu64 ".%02" PRIu64 |
309 | "\n" , freq, fraq); |
310 | } else |
311 | l = snprintf(p, left, "cpu MHz\t\t: unknown\n" ); |
312 | |
313 | size += l; |
314 | if (l < left) { |
315 | left -= l; |
316 | p += l; |
317 | } else |
318 | left = 0; |
319 | |
320 | l = snprintf(p, left, |
321 | "fdiv_bug\t: %s\n" |
322 | "fpu\t\t: %s\n" |
323 | "fpu_exception\t: yes\n" |
324 | "cpuid level\t: %d\n" |
325 | "wp\t\t: %s\n" |
326 | "flags\t\t: %s\n" , |
327 | i386_fpu_fdivbug ? "yes" : "no" , /* an old pentium */ |
328 | i386_fpu_present ? "yes" : "no" , /* not a 486SX */ |
329 | cpuid_level, |
330 | (rcr0() & CR0_WP) ? "yes" : "no" , |
331 | featurebuf |
332 | ); |
333 | size += l; |
334 | |
335 | left = *len; |
336 | *len = size; |
337 | return left < *len ? -1 : 0; |
338 | } |
339 | |
340 | #if defined(__HAVE_PROCFS_MACHDEP) && !defined(__x86_64__) |
341 | |
342 | void |
343 | procfs_machdep_allocvp(struct vnode *vp) |
344 | { |
345 | struct pfsnode *pfs = vp->v_data; |
346 | |
347 | switch (pfs->pfs_type) { |
348 | case Pmachdep_xmmregs: |
349 | /* /proc/N/xmmregs = -rw------- */ |
350 | pfs->pfs_mode = S_IRUSR|S_IWUSR; |
351 | vp->v_type = VREG; |
352 | break; |
353 | default: |
354 | KASSERT(false); |
355 | } |
356 | } |
357 | |
358 | int |
359 | procfs_machdep_rw(struct lwp *curl, struct lwp *l, struct pfsnode *pfs, |
360 | struct uio *uio) |
361 | { |
362 | |
363 | switch (pfs->pfs_type) { |
364 | case Pmachdep_xmmregs: |
365 | return (procfs_machdep_doxmmregs(curl, l, pfs, uio)); |
366 | default: |
367 | KASSERT(false); |
368 | } |
369 | return EINVAL; |
370 | } |
371 | |
372 | int |
373 | procfs_machdep_getattr(struct vnode *vp, struct vattr *vap, struct proc *procp) |
374 | { |
375 | struct pfsnode *pfs = VTOPFS(vp); |
376 | |
377 | switch (pfs->pfs_type) { |
378 | case Pmachdep_xmmregs: |
379 | vap->va_bytes = vap->va_size = sizeof(struct xmmregs); |
380 | break; |
381 | default: |
382 | KASSERT(false); |
383 | } |
384 | return 0; |
385 | } |
386 | |
387 | int |
388 | procfs_machdep_doxmmregs(struct lwp *curl, struct lwp *l, |
389 | struct pfsnode *pfs, struct uio *uio) |
390 | { |
391 | |
392 | return process_machdep_doxmmregs(curl, l, uio); |
393 | } |
394 | |
395 | int |
396 | procfs_machdep_validxmmregs(struct lwp *l, struct mount *mp) |
397 | { |
398 | |
399 | return process_machdep_validxmmregs(l->l_proc); |
400 | } |
401 | |
402 | #endif |
403 | |