xref: /openbsd/sys/arch/amd64/amd64/cacheinfo.c (revision 4f0183c8)
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