1 /*
2 * Routines to indentify caches on Intel CPU.
3 *
4 * Changes:
5 * Venkatesh Pallipadi : Adding cache identification through cpuid(4)
6 * Ashok Raj <ashok.raj@intel.com>: Work with CPU hotplug infrastructure.
7 * Andi Kleen / Andreas Herrmann : CPUID4 emulation on AMD.
8 */
9
10 #include <xen/init.h>
11 #include <xen/lib.h>
12 #include <xen/errno.h>
13 #include <asm/processor.h>
14
15 #define LVL_1_INST 1
16 #define LVL_1_DATA 2
17 #define LVL_2 3
18 #define LVL_3 4
19 #define LVL_TRACE 5
20
21 struct _cache_table
22 {
23 unsigned char descriptor;
24 char cache_type;
25 short size;
26 };
27
28 /* all the cache descriptor types we care about (no TLB or trace cache entries) */
29 static const struct _cache_table cache_table[] =
30 {
31 { 0x06, LVL_1_INST, 8 }, /* 4-way set assoc, 32 byte line size */
32 { 0x08, LVL_1_INST, 16 }, /* 4-way set assoc, 32 byte line size */
33 { 0x0a, LVL_1_DATA, 8 }, /* 2 way set assoc, 32 byte line size */
34 { 0x0c, LVL_1_DATA, 16 }, /* 4-way set assoc, 32 byte line size */
35 { 0x22, LVL_3, 512 }, /* 4-way set assoc, sectored cache, 64 byte line size */
36 { 0x23, LVL_3, 1024 }, /* 8-way set assoc, sectored cache, 64 byte line size */
37 { 0x25, LVL_3, 2048 }, /* 8-way set assoc, sectored cache, 64 byte line size */
38 { 0x29, LVL_3, 4096 }, /* 8-way set assoc, sectored cache, 64 byte line size */
39 { 0x2c, LVL_1_DATA, 32 }, /* 8-way set assoc, 64 byte line size */
40 { 0x30, LVL_1_INST, 32 }, /* 8-way set assoc, 64 byte line size */
41 { 0x39, LVL_2, 128 }, /* 4-way set assoc, sectored cache, 64 byte line size */
42 { 0x3a, LVL_2, 192 }, /* 6-way set assoc, sectored cache, 64 byte line size */
43 { 0x3b, LVL_2, 128 }, /* 2-way set assoc, sectored cache, 64 byte line size */
44 { 0x3c, LVL_2, 256 }, /* 4-way set assoc, sectored cache, 64 byte line size */
45 { 0x3d, LVL_2, 384 }, /* 6-way set assoc, sectored cache, 64 byte line size */
46 { 0x3e, LVL_2, 512 }, /* 4-way set assoc, sectored cache, 64 byte line size */
47 { 0x41, LVL_2, 128 }, /* 4-way set assoc, 32 byte line size */
48 { 0x42, LVL_2, 256 }, /* 4-way set assoc, 32 byte line size */
49 { 0x43, LVL_2, 512 }, /* 4-way set assoc, 32 byte line size */
50 { 0x44, LVL_2, 1024 }, /* 4-way set assoc, 32 byte line size */
51 { 0x45, LVL_2, 2048 }, /* 4-way set assoc, 32 byte line size */
52 { 0x46, LVL_3, 4096 }, /* 4-way set assoc, 64 byte line size */
53 { 0x47, LVL_3, 8192 }, /* 8-way set assoc, 64 byte line size */
54 { 0x49, LVL_3, 4096 }, /* 16-way set assoc, 64 byte line size */
55 { 0x4a, LVL_3, 6144 }, /* 12-way set assoc, 64 byte line size */
56 { 0x4b, LVL_3, 8192 }, /* 16-way set assoc, 64 byte line size */
57 { 0x4c, LVL_3, 12288 }, /* 12-way set assoc, 64 byte line size */
58 { 0x4d, LVL_3, 16384 }, /* 16-way set assoc, 64 byte line size */
59 { 0x60, LVL_1_DATA, 16 }, /* 8-way set assoc, sectored cache, 64 byte line size */
60 { 0x66, LVL_1_DATA, 8 }, /* 4-way set assoc, sectored cache, 64 byte line size */
61 { 0x67, LVL_1_DATA, 16 }, /* 4-way set assoc, sectored cache, 64 byte line size */
62 { 0x68, LVL_1_DATA, 32 }, /* 4-way set assoc, sectored cache, 64 byte line size */
63 { 0x70, LVL_TRACE, 12 }, /* 8-way set assoc */
64 { 0x71, LVL_TRACE, 16 }, /* 8-way set assoc */
65 { 0x72, LVL_TRACE, 32 }, /* 8-way set assoc */
66 { 0x73, LVL_TRACE, 64 }, /* 8-way set assoc */
67 { 0x78, LVL_2, 1024 }, /* 4-way set assoc, 64 byte line size */
68 { 0x79, LVL_2, 128 }, /* 8-way set assoc, sectored cache, 64 byte line size */
69 { 0x7a, LVL_2, 256 }, /* 8-way set assoc, sectored cache, 64 byte line size */
70 { 0x7b, LVL_2, 512 }, /* 8-way set assoc, sectored cache, 64 byte line size */
71 { 0x7c, LVL_2, 1024 }, /* 8-way set assoc, sectored cache, 64 byte line size */
72 { 0x7d, LVL_2, 2048 }, /* 8-way set assoc, 64 byte line size */
73 { 0x7f, LVL_2, 512 }, /* 2-way set assoc, 64 byte line size */
74 { 0x82, LVL_2, 256 }, /* 8-way set assoc, 32 byte line size */
75 { 0x83, LVL_2, 512 }, /* 8-way set assoc, 32 byte line size */
76 { 0x84, LVL_2, 1024 }, /* 8-way set assoc, 32 byte line size */
77 { 0x85, LVL_2, 2048 }, /* 8-way set assoc, 32 byte line size */
78 { 0x86, LVL_2, 512 }, /* 4-way set assoc, 64 byte line size */
79 { 0x87, LVL_2, 1024 }, /* 8-way set assoc, 64 byte line size */
80 { 0x00, 0, 0}
81 };
82
83 unsigned short num_cache_leaves;
84
cpuid4_cache_lookup(int index,struct cpuid4_info * this_leaf)85 int cpuid4_cache_lookup(int index, struct cpuid4_info *this_leaf)
86 {
87 union _cpuid4_leaf_eax eax;
88 union _cpuid4_leaf_ebx ebx;
89 union _cpuid4_leaf_ecx ecx;
90 unsigned edx;
91
92 cpuid_count(4, index, &eax.full, &ebx.full, &ecx.full, &edx);
93 if (eax.split.type == CACHE_TYPE_NULL)
94 return -EIO; /* better error ? */
95
96 this_leaf->eax = eax;
97 this_leaf->ebx = ebx;
98 this_leaf->ecx = ecx;
99 this_leaf->size = (ecx.split.number_of_sets + 1) *
100 (ebx.split.coherency_line_size + 1) *
101 (ebx.split.physical_line_partition + 1) *
102 (ebx.split.ways_of_associativity + 1);
103 return 0;
104 }
105
find_num_cache_leaves(void)106 static int find_num_cache_leaves(void)
107 {
108 unsigned int eax, ebx, ecx, edx;
109 union _cpuid4_leaf_eax cache_eax;
110 int i = -1;
111
112 do {
113 ++i;
114 /* Do cpuid(4) loop to find out num_cache_leaves */
115 cpuid_count(4, i, &eax, &ebx, &ecx, &edx);
116 cache_eax.full = eax;
117 } while (cache_eax.split.type != CACHE_TYPE_NULL);
118 return i;
119 }
120
init_intel_cacheinfo(struct cpuinfo_x86 * c)121 unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c)
122 {
123 unsigned int trace = 0, l1i = 0, l1d = 0, l2 = 0, l3 = 0; /* Cache sizes */
124 unsigned int new_l1d = 0, new_l1i = 0; /* Cache sizes from cpuid(4) */
125 unsigned int new_l2 = 0, new_l3 = 0, i; /* Cache sizes from cpuid(4) */
126 unsigned int l2_id = 0, l3_id = 0, num_threads_sharing, index_msb;
127
128 if (c->cpuid_level > 3) {
129 static int is_initialized;
130
131 if (is_initialized == 0) {
132 /* Init num_cache_leaves from boot CPU */
133 num_cache_leaves = find_num_cache_leaves();
134 is_initialized++;
135 }
136
137 /*
138 * Whenever possible use cpuid(4), deterministic cache
139 * parameters cpuid leaf to find the cache details
140 */
141 for (i = 0; i < num_cache_leaves; i++) {
142 struct cpuid4_info this_leaf;
143
144 int retval;
145
146 retval = cpuid4_cache_lookup(i, &this_leaf);
147 if (retval >= 0) {
148 switch(this_leaf.eax.split.level) {
149 case 1:
150 if (this_leaf.eax.split.type ==
151 CACHE_TYPE_DATA)
152 new_l1d = this_leaf.size/1024;
153 else if (this_leaf.eax.split.type ==
154 CACHE_TYPE_INST)
155 new_l1i = this_leaf.size/1024;
156 break;
157 case 2:
158 new_l2 = this_leaf.size/1024;
159 num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing;
160 index_msb = get_count_order(num_threads_sharing);
161 l2_id = c->apicid >> index_msb;
162 break;
163 case 3:
164 new_l3 = this_leaf.size/1024;
165 num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing;
166 index_msb = get_count_order(num_threads_sharing);
167 l3_id = c->apicid >> index_msb;
168 break;
169 default:
170 break;
171 }
172 }
173 }
174 }
175 /*
176 * Don't use cpuid2 if cpuid4 is supported. For P4, we use cpuid2 for
177 * trace cache
178 */
179 if ((num_cache_leaves == 0 || c->x86 == 15) && c->cpuid_level > 1) {
180 /* supports eax=2 call */
181 int i, j, n;
182 int regs[4];
183 unsigned char *dp = (unsigned char *)regs;
184 int only_trace = 0;
185
186 if (num_cache_leaves != 0 && c->x86 == 15)
187 only_trace = 1;
188
189 /* Number of times to iterate */
190 n = cpuid_eax(2) & 0xFF;
191
192 for ( i = 0 ; i < n ; i++ ) {
193 cpuid(2, ®s[0], ®s[1], ®s[2], ®s[3]);
194
195 /* If bit 31 is set, this is an unknown format */
196 for ( j = 0 ; j < 3 ; j++ ) {
197 if ( regs[j] < 0 ) regs[j] = 0;
198 }
199
200 /* Byte 0 is level count, not a descriptor */
201 for ( j = 1 ; j < 16 ; j++ ) {
202 unsigned char des = dp[j];
203 unsigned char k = 0;
204
205 /* look up this descriptor in the table */
206 while (cache_table[k].descriptor != 0)
207 {
208 if (cache_table[k].descriptor == des) {
209 if (only_trace && cache_table[k].cache_type != LVL_TRACE)
210 break;
211 switch (cache_table[k].cache_type) {
212 case LVL_1_INST:
213 l1i += cache_table[k].size;
214 break;
215 case LVL_1_DATA:
216 l1d += cache_table[k].size;
217 break;
218 case LVL_2:
219 l2 += cache_table[k].size;
220 break;
221 case LVL_3:
222 l3 += cache_table[k].size;
223 break;
224 case LVL_TRACE:
225 trace += cache_table[k].size;
226 break;
227 }
228
229 break;
230 }
231
232 k++;
233 }
234 }
235 }
236 }
237
238 if (new_l1d)
239 l1d = new_l1d;
240
241 if (new_l1i)
242 l1i = new_l1i;
243
244 if (new_l2) {
245 l2 = new_l2;
246 }
247
248 if (new_l3) {
249 l3 = new_l3;
250 }
251
252 if (opt_cpu_info) {
253 if (trace)
254 printk("CPU: Trace cache: %dK uops", trace);
255 else if ( l1i )
256 printk("CPU: L1 I cache: %dK", l1i);
257
258 if (l1d)
259 printk(", L1 D cache: %dK\n", l1d);
260 else
261 printk("\n");
262
263 if (l2)
264 printk("CPU: L2 cache: %dK\n", l2);
265
266 if (l3)
267 printk("CPU: L3 cache: %dK\n", l3);
268 }
269
270 c->x86_cache_size = l3 ? l3 : (l2 ? l2 : (l1i+l1d));
271
272 return l2;
273 }
274