; CPUINFO - the caches: from the leaf 4 of Intel or its copy 0x8000001D of ; AMD, else from 0x80000005 and 0x80000006. ; ==================================================================== struct CACHE size dd ? ; KB ways dd ? ; -1: fully associative shared dd ? ; threads, 0: unknown line dd ? ; bytes ends LEAF4_CODE = 2 ; leaf 4: the type of a code cache LEAF4_MAX = 16 ; the subleaves read at most L1D = 0 ; the caches in that order, L2 and L3 at their level L1I = 1 L2 = 2 L3 = 3 struct DESCRIPTOR ; of the leaf 2 id db ? cache db ? ; L1D... size dw ? ; KB ways db ? line db ? ends struct CPU_CACHES CPU_IDS ; CPU_IDS, then the caches CPUID does not tell l1 db ? ; KB of data and of code each l1_ways db ? l1_line db ? l2 dw ? ; KB on the chip, 0: on the board l2_ways db ? ends struct CPU_L2 CPU_IDS ; CPU_IDS, then the size of L2 l2 dw ? ; KB ends ; ==================================================================== ; The caches, from the first way that finds them. cache_init: mov ebp, 4 ; Intel and Zhaoxin: the leaf 4 cmp [vendor_id], INTEL je .leaves cmp [vendor_id], ZHAOXIN je .leaves mov ebp, 0x8000001D ; AMD: its copy, with TOPOEXT mov eax, HAS_TOPOEXT bt [cpu_regs], eax jnc .old ; each way only if the ones before found nothing: the leaves, the ; descriptors of Intel, 0x80000005 and 0x80000006, the known CPUs .leaves: call cache_leaves cmp [cache_l1d.size], 0 jne .done cmp [vendor_id], INTEL jne .old call cache_descriptors cmp [cache_l1d.size], 0 jne .done .old: call cache_old cmp [cache_l1d.size], 0 jne .done call cache_known .done: ret ; ==================================================================== ; ebp = the leaf, a subleaf a cache; the type 0 ends them. cache_leaves: movi esi, 0 .next: mov eax, ebp mov ecx, esi call cpuid_leaf mov edx, eax and edx, 0x1F jz .done ; its index in caches: the level, L1 by the type: data or code mov edi, eax shr edi, 5 and edi, 7 cmp edi, 1 jne .level movi edi, L1D cmp edx, LEAF4_CODE jne .level movi edi, L1I .level: cmp edi, L3 ja .skip imul edi, sizeof.CACHE add edi, caches ; the threads sharing it, the line, the ways and the size: ; sets * line * partitions * ways shr eax, 14 and eax, 0xFFF inc eax mov [edi + CACHE.shared], eax mov eax, ebx and eax, 0xFFF inc eax mov [edi + CACHE.line], eax inc ecx ; the sets: ECX + 1 imul ecx, eax mov eax, ebx shr eax, 12 and eax, 0x3FF inc eax imul ecx, eax shr ebx, 22 inc ebx mov [edi + CACHE.ways], ebx imul ecx, ebx shr ecx, 10 mov [edi + CACHE.size], ecx ; Intel tells the IDs of the threads sharing it, with the bits of ; the threads of a core even without them: the cores by smt_shift, ; at least one, times the threads of a core; AMD the threads mov eax, [edi + CACHE.shared] mov ecx, [smt_shift] shr eax, cl test eax, eax ; shr by 0 leaves the flags jnz .threads inc eax .threads: imul eax, [core_threads] mov [edi + CACHE.shared], eax .skip: inc esi cmp esi, LEAF4_MAX jb .next .done: ret ; ==================================================================== ; The caches in 0x80000005 and 0x80000006. cache_old: ; L1 and L2 of each core: these CPUs have a thread a core movi eax, 1 mov [cache_l1d.shared], eax mov [cache_l1i.shared], eax mov [cache_l2.shared], eax ; L1 data in ECX, L1 code in EDX mov eax, 0x80000005 call cpuid_leaf0 mov edi, cache_l1d mov eax, ecx call .l1 mov edi, cache_l1i mov eax, edx call .l1 ; L3 in EDX: 512 KB in bits 31..18 mov eax, 0x80000006 call cpuid_leaf0 mov edi, cache_l3 mov eax, edx mov ebx, edx shr ebx, 18 shl ebx, 9 call .l23 ; L2 in ECX of the same leaf, .l23 keeps it; in the layout of L1 on ; IDT and VIA C3 before model 9 mov edi, cache_l2 mov eax, ecx cmp [vendor_id], CENTAUR jne .l2 cmp [family], 6 jb .l1 ; its ret ends cache_old ja .l2 cmp [model], 9 jb .l1 .l2: mov ebx, ecx ; KB in bits 31..16 shr ebx, 16 call .l23 ; its errata: the first row of l2_errata for the CPU mov esi, l2_errata mov ebx, sizeof.CPU_L2 call cpu_find jc .done movzx eax, word [esi + CPU_L2.l2] mov [cache_l2.size], eax .done: ret ; eax = KB in bits 31..24, ways in 23..16, line in 7..0 .l1: movzx ebx, al mov [edi + CACHE.line], ebx shr eax, 16 movsx ebx, al ; 0xFF: fully associative mov [edi + CACHE.ways], ebx movzx eax, ah mov [edi + CACHE.size], eax ret ; eax = the code of the ways in bits 15..12, line in 7..0; ebx = KB; ; changes eax, ebx .l23: mov [edi + CACHE.size], ebx movzx ebx, al mov [edi + CACHE.line], ebx shr eax, 12 and eax, 0xF movsx eax, word [.ways + eax * 2] mov [edi + CACHE.ways], eax ret ; the ways of the codes, -1: fully associative; words: a signed byte ; cannot hold 128 .ways dw 0, 1, 2, 3, 4, 6, 8, 0, 16, 0, 32, 48, 64, 96, 128, -1 ; ==================================================================== ; Intel before the leaf 4: the descriptors in the bytes of the leaf 2; its ; low byte is the count of the calls, 1 on all of them, not a descriptor; ; the registers with bit 31 have none. cache_descriptors: mov eax, 2 call cpuid_leaf0 mov al, 0 mov [leaf2], eax mov [leaf2 + 4], ebx mov [leaf2 + 8], ecx mov [leaf2 + 12], edx mov esi, leaf2 .register: test dword [esi], 1 shl 31 jz .valid mov dword [esi], 0 .valid: add esi, 4 cmp esi, leaf2 + 16 jb .register mov esi, leaf2 .byte: lodsb call .find cmp esi, leaf2 + 16 jb .byte ret ; al = a descriptor: its cache from descriptors, if it is a cache .find: mov ebx, descriptors .entry: cmp al, [ebx + DESCRIPTOR.id] je .found add ebx, sizeof.DESCRIPTOR cmp ebx, descriptors_end jb .entry ret .found: movzx edi, byte [ebx + DESCRIPTOR.cache] imul edi, sizeof.CACHE add edi, caches movzx eax, word [ebx + DESCRIPTOR.size] mov [edi + CACHE.size], eax movzx eax, byte [ebx + DESCRIPTOR.ways] mov [edi + CACHE.ways], eax movzx eax, byte [ebx + DESCRIPTOR.line] mov [edi + CACHE.line], eax ret ; ==================================================================== ; The CPUs CPUID tells nothing of the caches about: the first row of ; known_caches for the CPU. cache_known: mov esi, known_caches mov ebx, sizeof.CPU_CACHES call cpu_find jc .done ; L1 data and code alike, L2 if it is on the chip movzx eax, byte [esi + CPU_CACHES.l1] mov [cache_l1d.size], eax mov [cache_l1i.size], eax movzx eax, byte [esi + CPU_CACHES.l1_ways] mov [cache_l1d.ways], eax mov [cache_l1i.ways], eax movzx eax, byte [esi + CPU_CACHES.l1_line] mov [cache_l1d.line], eax mov [cache_l1i.line], eax movzx eax, word [esi + CPU_CACHES.l2] mov [cache_l2.size], eax movzx eax, byte [esi + CPU_CACHES.l2_ways] mov [cache_l2.ways], eax .done: ret ; ==================================================================== ; The cache descriptors of the leaf 2, Intel SDM vol. 2A, table 3-12; the ; trace caches of P4 (70h..73h) as L1 code in K-uops. descriptors: DESCRIPTOR 06h, L1I, 8, 4, 32 DESCRIPTOR 08h, L1I, 16, 4, 32 DESCRIPTOR 09h, L1I, 32, 4, 64 DESCRIPTOR 0Ah, L1D, 8, 2, 32 DESCRIPTOR 0Ch, L1D, 16, 4, 32 DESCRIPTOR 0Dh, L1D, 16, 4, 64 DESCRIPTOR 0Eh, L1D, 24, 6, 64 DESCRIPTOR 1Dh, L2, 128, 2, 64 DESCRIPTOR 21h, L2, 256, 8, 64 DESCRIPTOR 22h, L3, 512, 4, 64 DESCRIPTOR 23h, L3, 1024, 8, 64 DESCRIPTOR 24h, L2, 1024, 16, 64 DESCRIPTOR 25h, L3, 2048, 8, 64 DESCRIPTOR 29h, L3, 4096, 8, 64 DESCRIPTOR 2Ch, L1D, 32, 8, 64 DESCRIPTOR 30h, L1I, 32, 8, 64 DESCRIPTOR 39h, L2, 128, 4, 64 DESCRIPTOR 3Ah, L2, 192, 6, 64 DESCRIPTOR 3Bh, L2, 128, 2, 64 DESCRIPTOR 3Ch, L2, 256, 4, 64 DESCRIPTOR 3Dh, L2, 384, 6, 64 DESCRIPTOR 3Eh, L2, 512, 4, 64 DESCRIPTOR 41h, L2, 128, 4, 32 DESCRIPTOR 42h, L2, 256, 4, 32 DESCRIPTOR 43h, L2, 512, 4, 32 DESCRIPTOR 44h, L2, 1024, 4, 32 DESCRIPTOR 45h, L2, 2048, 4, 32 DESCRIPTOR 46h, L3, 4096, 4, 64 DESCRIPTOR 47h, L3, 8192, 8, 64 DESCRIPTOR 48h, L2, 3072, 12, 64 DESCRIPTOR 49h, L2, 4096, 16, 64 DESCRIPTOR 4Ah, L3, 6144, 12, 64 DESCRIPTOR 4Bh, L3, 8192, 16, 64 DESCRIPTOR 4Ch, L3, 12288, 12, 64 DESCRIPTOR 4Dh, L3, 16384, 16, 64 DESCRIPTOR 4Eh, L2, 6144, 24, 64 DESCRIPTOR 60h, L1D, 16, 8, 64 DESCRIPTOR 66h, L1D, 8, 4, 64 DESCRIPTOR 67h, L1D, 16, 4, 64 DESCRIPTOR 68h, L1D, 32, 4, 64 DESCRIPTOR 70h, L1I, 12, 8, 0 DESCRIPTOR 71h, L1I, 16, 8, 0 DESCRIPTOR 72h, L1I, 32, 8, 0 DESCRIPTOR 73h, L1I, 64, 8, 0 DESCRIPTOR 78h, L2, 1024, 4, 64 DESCRIPTOR 79h, L2, 128, 8, 64 DESCRIPTOR 7Ah, L2, 256, 8, 64 DESCRIPTOR 7Bh, L2, 512, 8, 64 DESCRIPTOR 7Ch, L2, 1024, 8, 64 DESCRIPTOR 7Dh, L2, 2048, 8, 64 DESCRIPTOR 7Fh, L2, 512, 2, 64 DESCRIPTOR 80h, L2, 512, 8, 64 DESCRIPTOR 81h, L2, 128, 8, 32 DESCRIPTOR 82h, L2, 256, 8, 32 DESCRIPTOR 83h, L2, 512, 8, 32 DESCRIPTOR 84h, L2, 1024, 8, 32 DESCRIPTOR 85h, L2, 2048, 8, 32 DESCRIPTOR 86h, L2, 512, 4, 64 DESCRIPTOR 87h, L2, 1024, 8, 64 DESCRIPTOR 0D0h, L3, 512, 4, 64 DESCRIPTOR 0D1h, L3, 1024, 4, 64 DESCRIPTOR 0D2h, L3, 2048, 4, 64 DESCRIPTOR 0D6h, L3, 1024, 8, 64 DESCRIPTOR 0D7h, L3, 2048, 8, 64 DESCRIPTOR 0D8h, L3, 4096, 8, 64 DESCRIPTOR 0DCh, L3, 1536, 12, 64 DESCRIPTOR 0DDh, L3, 3072, 12, 64 DESCRIPTOR 0DEh, L3, 6144, 12, 64 DESCRIPTOR 0E2h, L3, 2048, 16, 64 DESCRIPTOR 0E3h, L3, 4096, 16, 64 DESCRIPTOR 0E4h, L3, 8192, 16, 64 DESCRIPTOR 0EAh, L3, 12288, 24, 64 DESCRIPTOR 0EBh, L3, 18432, 24, 64 DESCRIPTOR 0ECh, L3, 24576, 24, 64 descriptors_end: ; The CPUs without the caches in CPUID: the first row for the CPU counts. ; The L2 of Pentium and Cyrix is on the board. known_caches: ; vendors family model stepping L1 KB, ways, line, L2 KB, ways CPU_CACHES BY_INTEL, 5, 5, 4, 4, 0, 0xF, 16, 4, 32, 0, 0 ; Pentium MMX CPU_CACHES BY_INTEL, 5, 5, 8, 8, 0, 0xF, 16, 4, 32, 0, 0 ; Pentium MMX mobile CPU_CACHES BY_INTEL, 5, 5, 0, 0xFF, 0, 0xF, 8, 2, 32, 0, 0 ; Pentium CPU_CACHES BY_CYRIX, 5, 5, 0, 0xFF, 0, 0xF, 8, 4, 16, 0, 0 ; 6x86 CPU_CACHES BY_CYRIX, 6, 6, 0, 0xFF, 0, 0xF, 32, 4, 32, 0, 0 ; 6x86MX, MII CPU_CACHES BY_VORTEX, 5, 5, 0, 0xFF, 0, 0xF, 16, 4, 0, 256, 4 ; Vortex86 dw 0 ; the end ; The CPUs whose L2 is not as CPUID tells, as in Linux: AMD erratum T13 ; (amd_size_cache), VIA C3 Nehemiah 65 KB (cpu_detect_cache_sizes). l2_errata: ; vendors family model stepping L2 KB CPU_L2 BY_AMD, 6, 6, 3, 3, 0, 0, 64 ; Duron A0 CPU_L2 BY_AMD, 6, 6, 4, 4, 0, 1, 256 ; Thunderbird A1, A2 CPU_L2 BY_CENTAUR, 6, 6, 9, 9, 1, 1, 64 ; C3 Nehemiah dw 0 ; the end uglobal align 4 leaf2 rd 4 caches: cache_l1d CACHE cache_l1i CACHE cache_l2 CACHE cache_l3 CACHE endg