1 /* $OpenBSD: cacheinfo.c,v 1.15 2024/06/13 02:19:20 guenther Exp $ */
2
3 /*
4 * Copyright (c) 2022 Jonathan Gray <jsg@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20 #include <sys/systm.h>
21
22 #include <machine/cpu.h>
23 #include <machine/specialreg.h>
24
25 #define MAX_CACHE_LEAF 10
26
27 #ifdef MULTIPROCESSOR
28 uint32_t prev_cache[MAX_CACHE_LEAF][3];
29 # define prev_e5_ecx prev_cache[0][0]
30 # define prev_e5_edx prev_cache[0][1]
31 # define prev_e6_ecx prev_cache[0][2]
32 # define PREV_SET(x,y) (x) = (y)
33 # define PREV_SAME(x,y) ((x) == (y))
34 #else
35 # define PREV_SET(x,y) (void)0
36 # define PREV_SAME(x,y) 0
37 #endif
38
39 void
amd64_print_l1_cacheinfo(struct cpu_info * ci)40 amd64_print_l1_cacheinfo(struct cpu_info *ci)
41 {
42 u_int ways, linesize, totalsize;
43 u_int dummy, ecx, edx;
44
45 if (ci->ci_pnfeatset < 0x80000005)
46 return;
47
48 CPUID(0x80000005, dummy, dummy, ecx, edx);
49
50 if (!CPU_IS_PRIMARY(ci) && PREV_SAME(ecx, prev_e5_ecx) &&
51 PREV_SAME(edx, prev_e5_edx))
52 return;
53 PREV_SET(prev_e5_ecx, ecx);
54 PREV_SET(prev_e5_edx, edx);
55 if (ecx == 0)
56 return;
57
58 /* L1D */
59 linesize = ecx & 0xff;
60 ways = (ecx >> 16) & 0xff;
61 totalsize = (ecx >> 24) & 0xff; /* KB */
62
63 printf("%s: ", ci->ci_dev->dv_xname);
64
65 printf("%dKB ", totalsize);
66 printf("%db/line ", linesize);
67
68 switch (ways) {
69 case 0x00:
70 /* reserved */
71 break;
72 case 0x01:
73 printf("direct-mapped");
74 break;
75 case 0xff:
76 printf("fully associative");
77 break;
78 default:
79 printf("%d-way", ways);
80 break;
81 }
82 printf(" D-cache, ");
83
84 /* L1C */
85 linesize = edx & 0xff;
86 ways = (edx >> 16) & 0xff;
87 totalsize = (edx >> 24) & 0xff; /* KB */
88
89 printf("%dKB ", totalsize);
90 printf("%db/line ", linesize);
91
92 switch (ways) {
93 case 0x00:
94 /* reserved */
95 break;
96 case 0x01:
97 printf("direct-mapped");
98 break;
99 case 0xff:
100 printf("fully associative");
101 break;
102 default:
103 printf("%d-way", ways);
104 break;
105 }
106 printf(" I-cache\n");
107 }
108
109 void
amd64_print_l2_cacheinfo(struct cpu_info * ci)110 amd64_print_l2_cacheinfo(struct cpu_info *ci)
111 {
112 u_int ways, linesize, totalsize;
113 u_int dummy, ecx;
114
115 if (ci->ci_pnfeatset < 0x80000006)
116 return;
117
118 CPUID(0x80000006, dummy, dummy, ecx, dummy);
119
120 if (!CPU_IS_PRIMARY(ci) && PREV_SAME(ecx, prev_e6_ecx))
121 return;
122 PREV_SET(prev_e6_ecx, ecx);
123 if (ecx == 0)
124 return;
125
126 printf("%s: ", ci->ci_dev->dv_xname);
127
128 linesize = ecx & 0xff;
129 ways = (ecx >> 12) & 0x0f;
130 totalsize = ((ecx >> 16) & 0xffff); /* KB */
131
132 if (totalsize < 1024)
133 printf("%dKB ", totalsize);
134 else
135 printf("%dMB ", totalsize >> 10);
136 printf("%db/line ", linesize);
137
138 switch (ways) {
139 case 0x00:
140 printf("disabled");
141 break;
142 case 0x01:
143 printf("direct-mapped");
144 break;
145 case 0x02:
146 case 0x04:
147 printf("%d-way", ways);
148 break;
149 case 0x06:
150 printf("8-way");
151 break;
152 case 0x03:
153 case 0x05:
154 case 0x09:
155 /* reserved */
156 break;
157 case 0x07:
158 /* see cpuid 4 sub-leaf 2 */
159 break;
160 case 0x08:
161 printf("16-way");
162 break;
163 case 0x0a:
164 printf("32-way");
165 break;
166 case 0x0c:
167 printf("64-way");
168 break;
169 case 0x0d:
170 printf("96-way");
171 break;
172 case 0x0e:
173 printf("128-way");
174 break;
175 case 0x0f:
176 printf("fully associative");
177 }
178
179 printf(" L2 cache\n");
180 }
181
182 static inline int
intel_print_one_cache(struct cpu_info * ci,int leaf,u_int eax,u_int ebx,u_int ecx)183 intel_print_one_cache(struct cpu_info *ci, int leaf, u_int eax, u_int ebx,
184 u_int ecx)
185 {
186 u_int ways, partitions, linesize, sets, totalsize;
187 int type, level;
188
189 type = eax & 0x1f;
190 if (type == 0)
191 return 1;
192 level = (eax >> 5) & 7;
193
194 ways = (ebx >> 22) + 1;
195 linesize = (ebx & 0xfff) + 1;
196 partitions = ((ebx >> 12) & 0x3ff) + 1;
197 sets = ecx + 1;
198
199 totalsize = ways * linesize * partitions * sets;
200
201 if (leaf == 0)
202 printf("%s: ", ci->ci_dev->dv_xname);
203 else
204 printf(", ");
205
206 if (totalsize < 1024*1024)
207 printf("%dKB ", totalsize >> 10);
208 else
209 printf("%dMB ", totalsize >> 20);
210 printf("%db/line %d-way ", linesize, ways);
211
212 if (level == 1) {
213 if (type == 1)
214 printf("D");
215 else if (type == 2)
216 printf("I");
217 else if (type == 3)
218 printf("U");
219 printf("-cache");
220 } else {
221 printf("L%d cache", level);
222 }
223 return 0;
224 }
225
226 void
intel_print_cacheinfo(struct cpu_info * ci,u_int fn)227 intel_print_cacheinfo(struct cpu_info *ci, u_int fn)
228 {
229 int leaf;
230 u_int eax, ebx, ecx, dummy;
231
232 leaf = 0;
233 #ifdef MULTIPROCESSOR
234 if (! CPU_IS_PRIMARY(ci)) {
235 int i;
236 /* find the first level that differs, if any */
237 for (; leaf < MAX_CACHE_LEAF; leaf++) {
238 CPUID_LEAF(fn, leaf, eax, ebx, ecx, dummy);
239 if (PREV_SAME(prev_cache[leaf][0], eax) &&
240 PREV_SAME(prev_cache[leaf][1], ebx) &&
241 PREV_SAME(prev_cache[leaf][2], ecx)) {
242 /* last level? */
243 if ((eax & 0x1f) == 0)
244 break;
245 continue;
246 }
247 /* print lower levels that were the same */
248 for (i = 0; i < leaf; i++)
249 intel_print_one_cache(ci, i, prev_cache[i][0],
250 prev_cache[i][1], prev_cache[i][2]);
251 /* print this (differing) level and higher levels */
252 goto printit;
253 }
254 /* same as previous */
255 return;
256 }
257 #endif
258
259 for (; leaf < MAX_CACHE_LEAF; leaf++) {
260 CPUID_LEAF(fn, leaf, eax, ebx, ecx, dummy);
261 #ifdef MULTIPROCESSOR
262 printit:
263 #endif
264 PREV_SET(prev_cache[leaf][0], eax);
265 PREV_SET(prev_cache[leaf][1], ebx);
266 PREV_SET(prev_cache[leaf][2], ecx);
267 if (intel_print_one_cache(ci, leaf, eax, ebx, ecx))
268 break;
269 }
270 printf("\n");
271 }
272
273 void
x86_print_cacheinfo(struct cpu_info * ci)274 x86_print_cacheinfo(struct cpu_info *ci)
275 {
276 uint64_t msr;
277
278 if (ci->ci_vendor == CPUV_INTEL &&
279 rdmsr_safe(MSR_MISC_ENABLE, &msr) == 0 &&
280 (msr & MISC_ENABLE_LIMIT_CPUID_MAXVAL) == 0) {
281 intel_print_cacheinfo(ci, 4);
282 return;
283 }
284
285 if (ci->ci_vendor == CPUV_AMD &&
286 (ci->ci_efeature_ecx & CPUIDECX_TOPEXT)) {
287 intel_print_cacheinfo(ci, 0x8000001d);
288 return;
289 }
290
291 /* 0x80000005 / 0x80000006 */
292 amd64_print_l1_cacheinfo(ci);
293 amd64_print_l2_cacheinfo(ci);
294 }
295