1/*
2 * Copyright (c) 2015 ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <cpuid_internal.h>
11
12#include <dev/cpuid_amd_dev.h>
13/*
14 * ===============================================================================
15 * helpers
16 * ===============================================================================
17 */
18
19static uint16_t lookup_cache_assoc(uint8_t key)
20{
21    switch (key) {
22        case cpuid_amd_cache_assoc_disabled:
23            return 0;
24        case cpuid_amd_cache_assoc_direct:
25            return 1;
26        case cpuid_amd_cache_assoc_2way:
27            return 2;
28        case cpuid_amd_cache_assoc_4way:
29            return 4;
30        case cpuid_amd_cache_assoc_8way:
31            return 8;
32        case cpuid_amd_cache_assoc_16way:
33            return 16;
34        case cpuid_amd_cache_assoc_32way:
35            return 32;
36        case cpuid_amd_cache_assoc_48way:
37            return 48;
38        case cpuid_amd_cache_assoc_64way:
39            return 64;
40        case cpuid_amd_cache_assoc_96way:
41            return 96;
42        case cpuid_amd_cache_assoc_128way :
43            return 128;
44        case cpuid_amd_cache_assoc_fully :
45            return 0xff;
46    }
47    return 0;
48}
49
50/*
51 * ===============================================================================
52 * basic processor information
53 * ===============================================================================
54 */
55
56static errval_t proc_name(char *buf, size_t len)
57{
58    // check if this operation is supported
59    if (cpuid_g_max_input_extended < 4) {
60        return CPUID_ERR_UNSUPPORTED_FUNCTION;
61    }
62
63    size_t written;
64
65    struct cpuid_regs reg = CPUID_REGS_INITIAL(0x80000002, 0);
66    cpuid_exec(&reg);
67    written = snprintf(buf, len, "%s", (char *)&reg.eax);
68    len -= written;
69    buf += written;
70
71    reg.eax = 0x80000003;
72    reg.ecx = 0x0;
73    cpuid_exec(&reg);
74    written = snprintf(buf, len, "%s", (char *)&reg.eax);
75    len -= written;
76    buf += written;
77
78    reg.eax = 0x80000004;
79    reg.ecx = 0x0;
80    cpuid_exec(&reg);
81    written = snprintf(buf, len, "%s", (char *)&reg.eax);
82    len -= written;
83    buf += written;
84
85    return SYS_ERR_OK;
86}
87
88static errval_t proc_family(struct cpuid_proc_family *family)
89{
90    if (cpuid_g_max_input_basic < 1) {
91        return CPUID_ERR_UNSUPPORTED_FUNCTION;
92    }
93
94    struct cpuid_regs reg = CPUID_REGS_INITIAL(1, 0);
95    cpuid_exec(&reg);
96
97    cpuid_amd_family_t f = (cpuid_amd_family_t)&reg.eax;
98
99    family->stepping = cpuid_amd_family_stepping_extract(f);
100    family->family = cpuid_amd_family_family_extract(f);
101    family->model = cpuid_amd_family_model_extract(f);
102    // amd has no proc type
103    family->type = 0x0;
104
105    if (family->family == 0x0f) {
106        uint16_t model = cpuid_amd_family_extmodel_extract(f);
107        family->model += (model << 4);
108    }
109
110    /* if family is zero we have to consider the extended family id */
111    if (family->family != 0x0f) {
112        family->family += cpuid_amd_family_extfamily_extract(f);
113    }
114
115    return SYS_ERR_OK;
116}
117
118static uint32_t proc_max_input_basic(void)
119{
120    struct cpuid_regs reg  = CPUID_REGS_INITIAL(0, 0);
121    cpuid_exec(&reg);
122
123    return reg.eax;
124}
125
126static uint32_t proc_max_input_extended(void)
127{
128    struct cpuid_regs reg  = CPUID_REGS_INITIAL(0x80000000, 0);
129    cpuid_exec(&reg);
130
131    return reg.eax;
132}
133
134static errval_t frequency_info(struct cpuid_freqinfo *fi)
135{
136    return CPUID_ERR_UNSUPPORTED_FUNCTION;
137}
138
139/*
140 * ===============================================================================
141 * cache topology information
142 * ===============================================================================
143 */
144
145static errval_t cache_info_alternate(struct cpuid_cacheinfo *ci, uint32_t idx)
146{
147    if (CPUID_EXTENDED_INPUT_MASK(cpuid_g_max_input_extended) < 0x6) {
148        CPUID_PRINTF("amd_cache_info not supported. max fnct= %x\n",
149                             cpuid_g_max_input_extended);
150        return CPUID_ERR_UNSUPPORTED_FUNCTION;
151    }
152
153    if (idx >= 4) {
154        return CPUID_ERR_INVALID_INDEX;
155    }
156
157
158    uint32_t fn = 0x80000006;
159    if (idx < 2) {
160        fn = 0x80000005;
161    }
162    struct cpuid_regs reg  = CPUID_REGS_INITIAL(fn, 0);
163    cpuid_exec(&reg);
164
165    switch(idx) {
166        case 0:;
167            cpuid_amd_l1_dcache_t l1d = (cpuid_amd_l1_dcache_t)&reg.ecx;
168            ci->associativity = cpuid_amd_l1_dcache_assoc_extract(l1d);
169            ci->size = cpuid_amd_l1_dcache_size_extract(l1d) * 1024;
170            ci->linesize = cpuid_amd_l1_dcache_linesize_extract(l1d);
171            ci->level = 1;
172            ci->shared = 1;
173            ci->type = CPUID_CACHE_TYPE_DATA;
174            break;
175        case 1:;
176            cpuid_amd_l1_icache_t l1i = (cpuid_amd_l1_icache_t)&reg.edx;
177            ci->associativity = cpuid_amd_l1_icache_assoc_extract(l1i);
178            ci->size = cpuid_amd_l1_icache_size_extract(l1i) * 1024;
179            ci->linesize = cpuid_amd_l1_icache_linesize_extract(l1i);
180            ci->level = 1;
181            /* TODO: get the number of cores sharing that cache */
182            ci->shared = 1;
183            ci->type = CPUID_CACHE_TYPE_INSTR;
184            break;
185        case 2:;
186            cpuid_amd_l2_cache_t l2 = (cpuid_amd_l2_cache_t)&reg.ecx;
187            ci->associativity = lookup_cache_assoc(cpuid_amd_l2_cache_assoc_extract(l2));
188            assert(cpuid_amd_l2_cache_assoc_extract(l2));
189            assert(ci->associativity);
190            ci->size = cpuid_amd_l2_cache_size_extract(l2) * 1024;
191            ci->linesize = cpuid_amd_l2_cache_linesize_extract(l2);
192            ci->level = 2;
193            /* TODO: get the number of cores sharing that cache */
194            ci->shared = 1;
195            ci->type = CPUID_CACHE_TYPE_UNIFIED;
196            break;
197        case 3:;
198            /*
199             * This provides the processor���s third level cache characteristics
200             * shared by all cores
201             */
202            cpuid_amd_l3_cache_t l3 = (cpuid_amd_l3_cache_t)&reg.edx;
203            ci->associativity = lookup_cache_assoc(cpuid_amd_l3_cache_assoc_extract(l3));
204            ci->size = cpuid_amd_l3_cache_size_extract(l3) * 512 * 1024;
205            ci->linesize = cpuid_amd_l3_cache_linesize_extract(l3);
206            ci->level = 3;
207            /* TODO: get the number of cores sharing that cache */
208            ci->shared = 1;
209            ci->type = CPUID_CACHE_TYPE_UNIFIED;
210            break;
211        default :
212            return CPUID_ERR_INVALID_INDEX;
213    }
214    if (ci->size == 0 || ci->associativity == 0 || ci->linesize == 0) {
215        /* no size, associativity indicates invalid / disabled cache */
216        return CPUID_ERR_INVALID_INDEX;
217    }
218
219    ci->inclusive = 1;
220    ci->sets = (ci->size / ci->linesize) / ci->associativity;
221    /* TODO: get the number of cores sharing that cache */
222    ci->shared = 1;
223    ci->name = cpuid_cache_names[ci->level][ci->type];
224
225    return SYS_ERR_OK;
226}
227
228static errval_t cache_info(struct cpuid_cacheinfo *ci, uint32_t idx)
229{
230    if (CPUID_EXTENDED_INPUT_MASK(cpuid_g_max_input_extended) < 0x1d) {
231        return cache_info_alternate(ci, idx);
232    }
233
234    struct cpuid_regs reg = CPUID_REGS_INITIAL(0x8000001d, idx);
235    cpuid_exec(&reg);
236
237    cpuid_amd_cache_info_eax_t ci_a = (cpuid_amd_cache_info_eax_t) &reg.eax;
238    cpuid_amd_cache_info_ebx_t ci_b = (cpuid_amd_cache_info_ebx_t) &reg.ebx;
239
240    ci->level = cpuid_amd_cache_info_eax_level_extract(ci_a);
241
242    switch (cpuid_amd_cache_info_eax_ctype_extract(ci_a)) {
243        case cpuid_amd_cache_type_data :
244            /* data cache */
245            ci->type = CPUID_CACHE_TYPE_DATA;
246            break;
247        case cpuid_amd_cache_type_instr :
248            /* instruction cache */
249            ci->type = CPUID_CACHE_TYPE_INSTR;
250            break;
251        case cpuid_amd_cache_type_unified :
252            ci->type = CPUID_CACHE_TYPE_UNIFIED;
253            /* unified cache */
254            break;
255        default:
256            /* no more cache */
257            ci->type = CPUID_CACHE_TYPE_INVALID;
258            return CPUID_ERR_INVALID_INDEX;
259            break;
260    }
261
262    ci->name = cpuid_cache_names[ci->level][ci->type];
263    ci->linesize = cpuid_amd_cache_info_ebx_cachelinesize_extract(ci_b)+1;
264
265    /* the the number of sets */
266    ci->sets = reg.ecx + 1;
267
268    if (cpuid_amd_cache_info_eax_fullyassoc_extract(ci_a)) {
269        ci->size = (size_t)ci->linesize * ci->sets;
270        ci->associativity = 0xff;
271    } else {
272        ci->associativity = cpuid_amd_cache_info_ebx_assoc_extract(ci_b)+1;
273        ci->size = (size_t)ci->linesize * ci->sets * ci->associativity;
274    }
275
276    ci->shared = cpuid_amd_cache_info_eax_num_sharing_extract(ci_a) +1;
277
278    cpuid_amd_cache_info_edx_t ci_d = (cpuid_amd_cache_info_edx_t) &reg.edx;
279    ci->inclusive = cpuid_amd_cache_info_edx_inclusive_extract(ci_d);
280
281    return SYS_ERR_OK;
282}
283
284static uint16_t cache_line_size(void)
285{
286    if (cpuid_g_max_input_basic < 1) {
287        return CPUID_ERR_UNSUPPORTED_FUNCTION;
288    }
289
290    struct cpuid_regs reg = CPUID_REGS_INITIAL(1, 0);
291    cpuid_exec(&reg);
292
293    cpuid_amd_miscinfo_t mi = (cpuid_amd_miscinfo_t)&reg.ebx;
294
295    return cpuid_amd_miscinfo_cflush_sz_extract(mi) * 8;
296}
297
298/*
299 * ===============================================================================
300 * TLB information
301 * ===============================================================================
302 */
303
304static errval_t tlb_info(struct cpuid_tlbinfo *ti, uint32_t idx)
305{
306    if (CPUID_EXTENDED_INPUT_MASK(cpuid_g_max_input_extended) < 0x5) {
307        return CPUID_ERR_UNSUPPORTED_FUNCTION;
308    }
309    struct cpuid_regs reg = CPUID_REGS_INITIAL(0x80000005, 0);
310    ti->pagesize = 0;
311    if (idx < 4) {
312        /* level 1 tlb 4k/2m pages */
313        ti->level = 1;
314        reg.eax = 0x80000005;
315        cpuid_exec(&reg);
316        cpuid_amd_tlb_l1_t tlb;
317        if (idx < 2) {
318            tlb = (cpuid_amd_tlb_l1_t)&reg.eax;
319            ti->pagesize = LARGE_PAGE_SIZE;
320        } else {
321            tlb = (cpuid_amd_tlb_l1_t)&reg.ebx;
322            ti->pagesize = BASE_PAGE_SIZE;
323        }
324        if (idx & 0x1) {
325            ti->type = CPUID_CACHE_TYPE_DATA;
326            ti->associativity = cpuid_amd_tlb_l1_dtlb_assoc_extract(tlb);
327            ti->entries = cpuid_amd_tlb_l1_dtlb_sz_extract(tlb);
328        } else {
329            ti->type = CPUID_CACHE_TYPE_INSTR;
330            ti->associativity = cpuid_amd_tlb_l1_itlb_assoc_extract(tlb);
331            ti->entries = cpuid_amd_tlb_l1_itlb_sz_extract(tlb);
332        }
333        return SYS_ERR_OK;
334    } else if (idx < 8) {
335        /* level 2 tlb 4k/2m pages */
336        if (CPUID_EXTENDED_INPUT_MASK(cpuid_g_max_input_extended) < 0x6) {
337            return CPUID_ERR_UNSUPPORTED_FUNCTION;
338        }
339        idx -= 4;
340        ti->level = 2;
341        ti->pagesize = (idx < 2) ? LARGE_PAGE_SIZE : BASE_PAGE_SIZE;
342        reg.eax = 0x80000006;
343
344    } else if (idx < 12){
345        /* huge pages */
346        if (CPUID_EXTENDED_INPUT_MASK(cpuid_g_max_input_extended) < 0x19) {
347            return CPUID_ERR_UNSUPPORTED_FUNCTION;
348        }
349        idx -= 8;
350        if (idx < 2) {
351            ti->level = 1;
352        } else {
353            ti->level = 2;
354        }
355        ti->pagesize = HUGE_PAGE_SIZE;
356        reg.eax = 0x80000019;
357    } else {
358        return CPUID_ERR_INVALID_INDEX;
359    }
360
361    cpuid_exec(&reg);
362    cpuid_amd_tlb_l1_t tlb2;
363    if (idx < 2) {
364        tlb2 = (cpuid_amd_tlb_l1_t)&reg.eax;
365    } else {
366        tlb2 = (cpuid_amd_tlb_l1_t)&reg.ebx;
367    }
368
369    if (idx & 0x1) {
370        ti->type = CPUID_CACHE_TYPE_DATA;
371        ti->associativity = cpuid_amd_tlb_l2_dtlb_assoc_extract(tlb2);
372        ti->entries = cpuid_amd_tlb_l2_dtlb_sz_extract(tlb2);
373    } else {
374        ti->type = CPUID_CACHE_TYPE_INSTR;
375        ti->associativity = cpuid_amd_tlb_l2_itlb_assoc_extract(tlb2);
376        ti->entries = cpuid_amd_tlb_l2_itlb_sz_extract(tlb2);
377    }
378    ti->associativity = lookup_cache_assoc(ti->associativity);
379
380    return SYS_ERR_OK;
381}
382
383/*
384 * ===============================================================================
385 * thread and topology information
386 * ===============================================================================
387 */
388
389static errval_t thread_info(struct cpuid_threadinfo *ti)
390{
391    if (cpuid_g_max_input_basic < 1) {
392        return CPUID_ERR_UNSUPPORTED_FUNCTION;
393    }
394
395    struct cpuid_regs reg  = CPUID_REGS_INITIAL(0x01, 0);
396    cpuid_exec(&reg);
397
398    cpuid_amd_miscinfo_t mi = (cpuid_amd_miscinfo_t)&reg.ebx;
399
400    uint8_t local_apic_id = cpuid_amd_miscinfo_init_apicid_extract(mi);
401    uint8_t logical_processors = cpuid_amd_miscinfo_max_log_proc_extract(mi);
402
403    if (!((reg.edx >> 28) & 0x1)) {
404        /* TODO: then the following is not valid */
405        ti->core = 0;
406        ti->hyperthread = 0;
407        ti->package = local_apic_id;
408        return SYS_ERR_OK;
409    }
410
411    if (CPUID_EXTENDED_INPUT_MASK(cpuid_g_max_input_extended) < 8) {
412        return CPUID_ERR_UNSUPPORTED_FUNCTION;
413    }
414
415    reg.eax = 0x80000008;
416    reg.ecx = 0x0;
417    cpuid_exec(&reg);
418
419    cpuid_amd_apicid_t ac = (cpuid_amd_apicid_t)&reg.ecx;
420
421    uint8_t mnc;
422    uint8_t ApicIdCoreIdSize = cpuid_amd_apicid_apic_sz_extract(ac);
423    uint8_t nc = cpuid_amd_apicid_ncores_extract(ac);
424    if (ApicIdCoreIdSize == 0) {
425        // Used by legacy dual-core/single-core processors
426        mnc = nc + 1;
427    } else {
428        // use ApicIdCoreIdSize[3:0] field
429        mnc = 1 << ApicIdCoreIdSize;
430    }
431
432    //XXX: not sure about these calculations.
433    //        uint8_t hyperthreads = logical_processors / nc;
434    uint8_t ht = logical_processors / mnc;
435    uint8_t ht_shift = cpuid_bits_needed(ht - 1);
436    uint8_t ht_mask = (1 << ht_shift) - 1;
437    uint8_t core_shift = cpuid_bits_needed(mnc - 1);
438    uint8_t core_mask = (1 << core_shift) - 1;
439
440    ti->core = (local_apic_id >> (ht_shift)) & core_mask;
441    ti->package = local_apic_id >> (ht_shift + core_shift);
442    ti->hyperthread = local_apic_id & ht_mask;
443
444    return SYS_ERR_OK;
445}
446
447static errval_t topology_info(struct cpuid_topologyinfo *topo, uint8_t idx)
448{
449    return 1;
450    return SYS_ERR_OK;
451}
452
453static errval_t feature_info(struct cpuid_featureinfo *fi)
454{
455    USER_PANIC("NYI");
456    return SYS_ERR_OK;
457}
458
459static errval_t address_space_info(struct cpuid_adressspaceinfo *ai)
460{
461    if (CPUID_EXTENDED_INPUT_MASK(cpuid_g_max_input_extended) < 0x8) {
462        return CPUID_ERR_UNSUPPORTED_FUNCTION;
463    }
464
465    struct cpuid_regs reg  = CPUID_REGS_INITIAL(0x80000008, 0);
466    cpuid_exec(&reg);
467
468    cpuid_amd_addrspace_t as = (cpuid_amd_addrspace_t)&reg.eax;
469
470    ai->physical = cpuid_amd_addrspace_physical_extract(as);
471    ai->virtual = cpuid_amd_addrspace_linear_extract(as);
472    ai->guest_physical = cpuid_amd_addrspace_guest_extract(as);
473    if (ai->guest_physical == 0) {
474        ai->guest_physical = ai->physical;
475    }
476
477    return SYS_ERR_OK;
478}
479
480/*
481 * ===============================================================================
482 * backend initialization
483 * ===============================================================================
484 */
485
486/**
487 * \brief fills the vendor specific handler functions
488 *
489 * \param fn_tab  function pointer table to be filled
490 */
491void cpuid_amd_set_handlers(struct cpuid_functions *fn_tab)
492{
493    fn_tab->proc_name = proc_name;
494    fn_tab->proc_family = proc_family;
495    fn_tab->proc_max_input_basic = proc_max_input_basic;
496    fn_tab->proc_max_input_extended = proc_max_input_extended;
497    fn_tab->frequency_info = frequency_info;
498
499    fn_tab->cache_info = cache_info;
500    fn_tab->cache_line_size = cache_line_size;
501
502    fn_tab->tlb_info = tlb_info;
503
504    fn_tab->thread_info = thread_info;
505    fn_tab->topology_info = topology_info;
506
507    fn_tab->feature_info = feature_info;
508
509    fn_tab->address_space_info = address_space_info;
510}
511