1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/smp.h> |
3 | #include <linux/timex.h> |
4 | #include <linux/string.h> |
5 | #include <linux/seq_file.h> |
6 | #include <linux/cpufreq.h> |
7 | #include <asm/prctl.h> |
8 | #include <linux/proc_fs.h> |
9 | |
10 | #include "cpu.h" |
11 | |
12 | #ifdef CONFIG_X86_VMX_FEATURE_NAMES |
13 | extern const char * const x86_vmx_flags[NVMXINTS*32]; |
14 | #endif |
15 | |
16 | /* |
17 | * Get CPU information for use by the procfs. |
18 | */ |
19 | static void show_cpuinfo_core(struct seq_file *m, struct cpuinfo_x86 *c, |
20 | unsigned int cpu) |
21 | { |
22 | #ifdef CONFIG_SMP |
23 | seq_printf(m, fmt: "physical id\t: %d\n" , c->topo.pkg_id); |
24 | seq_printf(m, fmt: "siblings\t: %d\n" , |
25 | cpumask_weight(topology_core_cpumask(cpu))); |
26 | seq_printf(m, fmt: "core id\t\t: %d\n" , c->topo.core_id); |
27 | seq_printf(m, fmt: "cpu cores\t: %d\n" , c->booted_cores); |
28 | seq_printf(m, fmt: "apicid\t\t: %d\n" , c->topo.apicid); |
29 | seq_printf(m, fmt: "initial apicid\t: %d\n" , c->topo.initial_apicid); |
30 | #endif |
31 | } |
32 | |
33 | #ifdef CONFIG_X86_32 |
34 | static void show_cpuinfo_misc(struct seq_file *m, struct cpuinfo_x86 *c) |
35 | { |
36 | seq_printf(m, |
37 | "fdiv_bug\t: %s\n" |
38 | "f00f_bug\t: %s\n" |
39 | "coma_bug\t: %s\n" |
40 | "fpu\t\t: %s\n" |
41 | "fpu_exception\t: %s\n" |
42 | "cpuid level\t: %d\n" |
43 | "wp\t\t: yes\n" , |
44 | boot_cpu_has_bug(X86_BUG_FDIV) ? "yes" : "no" , |
45 | boot_cpu_has_bug(X86_BUG_F00F) ? "yes" : "no" , |
46 | boot_cpu_has_bug(X86_BUG_COMA) ? "yes" : "no" , |
47 | boot_cpu_has(X86_FEATURE_FPU) ? "yes" : "no" , |
48 | boot_cpu_has(X86_FEATURE_FPU) ? "yes" : "no" , |
49 | c->cpuid_level); |
50 | } |
51 | #else |
52 | static void show_cpuinfo_misc(struct seq_file *m, struct cpuinfo_x86 *c) |
53 | { |
54 | seq_printf(m, |
55 | fmt: "fpu\t\t: yes\n" |
56 | "fpu_exception\t: yes\n" |
57 | "cpuid level\t: %d\n" |
58 | "wp\t\t: yes\n" , |
59 | c->cpuid_level); |
60 | } |
61 | #endif |
62 | |
63 | static int show_cpuinfo(struct seq_file *m, void *v) |
64 | { |
65 | struct cpuinfo_x86 *c = v; |
66 | unsigned int cpu; |
67 | int i; |
68 | |
69 | cpu = c->cpu_index; |
70 | seq_printf(m, fmt: "processor\t: %u\n" |
71 | "vendor_id\t: %s\n" |
72 | "cpu family\t: %d\n" |
73 | "model\t\t: %u\n" |
74 | "model name\t: %s\n" , |
75 | cpu, |
76 | c->x86_vendor_id[0] ? c->x86_vendor_id : "unknown" , |
77 | c->x86, |
78 | c->x86_model, |
79 | c->x86_model_id[0] ? c->x86_model_id : "unknown" ); |
80 | |
81 | if (c->x86_stepping || c->cpuid_level >= 0) |
82 | seq_printf(m, fmt: "stepping\t: %d\n" , c->x86_stepping); |
83 | else |
84 | seq_puts(m, s: "stepping\t: unknown\n" ); |
85 | if (c->microcode) |
86 | seq_printf(m, fmt: "microcode\t: 0x%x\n" , c->microcode); |
87 | |
88 | if (cpu_has(c, X86_FEATURE_TSC)) { |
89 | unsigned int freq = arch_freq_get_on_cpu(cpu); |
90 | |
91 | seq_printf(m, fmt: "cpu MHz\t\t: %u.%03u\n" , freq / 1000, (freq % 1000)); |
92 | } |
93 | |
94 | /* Cache size */ |
95 | if (c->x86_cache_size) |
96 | seq_printf(m, fmt: "cache size\t: %u KB\n" , c->x86_cache_size); |
97 | |
98 | show_cpuinfo_core(m, c, cpu); |
99 | show_cpuinfo_misc(m, c); |
100 | |
101 | seq_puts(m, s: "flags\t\t:" ); |
102 | for (i = 0; i < 32*NCAPINTS; i++) |
103 | if (cpu_has(c, i) && x86_cap_flags[i] != NULL) |
104 | seq_printf(m, fmt: " %s" , x86_cap_flags[i]); |
105 | |
106 | #ifdef CONFIG_X86_VMX_FEATURE_NAMES |
107 | if (cpu_has(c, X86_FEATURE_VMX) && c->vmx_capability[0]) { |
108 | seq_puts(m, s: "\nvmx flags\t:" ); |
109 | for (i = 0; i < 32*NVMXINTS; i++) { |
110 | if (test_bit(i, (unsigned long *)c->vmx_capability) && |
111 | x86_vmx_flags[i] != NULL) |
112 | seq_printf(m, fmt: " %s" , x86_vmx_flags[i]); |
113 | } |
114 | } |
115 | #endif |
116 | |
117 | seq_puts(m, s: "\nbugs\t\t:" ); |
118 | for (i = 0; i < 32*NBUGINTS; i++) { |
119 | unsigned int bug_bit = 32*NCAPINTS + i; |
120 | |
121 | if (cpu_has_bug(c, bug_bit) && x86_bug_flags[i]) |
122 | seq_printf(m, fmt: " %s" , x86_bug_flags[i]); |
123 | } |
124 | |
125 | seq_printf(m, fmt: "\nbogomips\t: %lu.%02lu\n" , |
126 | c->loops_per_jiffy/(500000/HZ), |
127 | (c->loops_per_jiffy/(5000/HZ)) % 100); |
128 | |
129 | #ifdef CONFIG_X86_64 |
130 | if (c->x86_tlbsize > 0) |
131 | seq_printf(m, fmt: "TLB size\t: %d 4K pages\n" , c->x86_tlbsize); |
132 | #endif |
133 | seq_printf(m, fmt: "clflush size\t: %u\n" , c->x86_clflush_size); |
134 | seq_printf(m, fmt: "cache_alignment\t: %d\n" , c->x86_cache_alignment); |
135 | seq_printf(m, fmt: "address sizes\t: %u bits physical, %u bits virtual\n" , |
136 | c->x86_phys_bits, c->x86_virt_bits); |
137 | |
138 | seq_puts(m, s: "power management:" ); |
139 | for (i = 0; i < 32; i++) { |
140 | if (c->x86_power & (1 << i)) { |
141 | if (i < ARRAY_SIZE(x86_power_flags) && |
142 | x86_power_flags[i]) |
143 | seq_printf(m, fmt: "%s%s" , |
144 | x86_power_flags[i][0] ? " " : "" , |
145 | x86_power_flags[i]); |
146 | else |
147 | seq_printf(m, fmt: " [%d]" , i); |
148 | } |
149 | } |
150 | |
151 | seq_puts(m, s: "\n\n" ); |
152 | |
153 | return 0; |
154 | } |
155 | |
156 | static void *c_start(struct seq_file *m, loff_t *pos) |
157 | { |
158 | *pos = cpumask_next(n: *pos - 1, cpu_online_mask); |
159 | if ((*pos) < nr_cpu_ids) |
160 | return &cpu_data(*pos); |
161 | return NULL; |
162 | } |
163 | |
164 | static void *c_next(struct seq_file *m, void *v, loff_t *pos) |
165 | { |
166 | (*pos)++; |
167 | return c_start(m, pos); |
168 | } |
169 | |
170 | static void c_stop(struct seq_file *m, void *v) |
171 | { |
172 | } |
173 | |
174 | const struct seq_operations cpuinfo_op = { |
175 | .start = c_start, |
176 | .next = c_next, |
177 | .stop = c_stop, |
178 | .show = show_cpuinfo, |
179 | }; |
180 | |
181 | #ifdef CONFIG_X86_USER_SHADOW_STACK |
182 | static void dump_x86_features(struct seq_file *m, unsigned long features) |
183 | { |
184 | if (features & ARCH_SHSTK_SHSTK) |
185 | seq_puts(m, s: "shstk " ); |
186 | if (features & ARCH_SHSTK_WRSS) |
187 | seq_puts(m, s: "wrss " ); |
188 | } |
189 | |
190 | void arch_proc_pid_thread_features(struct seq_file *m, struct task_struct *task) |
191 | { |
192 | seq_puts(m, s: "x86_Thread_features:\t" ); |
193 | dump_x86_features(m, features: task->thread.features); |
194 | seq_putc(m, c: '\n'); |
195 | |
196 | seq_puts(m, s: "x86_Thread_features_locked:\t" ); |
197 | dump_x86_features(m, features: task->thread.features_locked); |
198 | seq_putc(m, c: '\n'); |
199 | } |
200 | #endif /* CONFIG_X86_USER_SHADOW_STACK */ |
201 | |