; CPUINFO - what CPUID tells about the processor: the vendor, the name, ; the signature, the features and the cores. ; ==================================================================== ; the registers of the features in cpu_regs, the bits of the flags in them F_1D = 0 * 32 ; leaf 1 EDX F_1C = 1 * 32 ; leaf 1 ECX F_7B = 2 * 32 ; leaf 7 EBX F_7C = 3 * 32 F_7D = 4 * 32 F_X1D = 5 * 32 ; leaf 0x80000001 EDX F_X1C = 6 * 32 CPU_REGS = F_X1C / 32 + 1 ; the registers in cpu_regs ; the features the code tests, bits in cpu_regs HAS_MSR = F_1D + 5 HAS_HTT = F_1D + 28 ; the threads of the package in leaf 1 EBX HAS_HYBRID = F_7D + 15 ; cores of two kinds HAS_TOPOEXT = F_X1C + 22 ; AMD: the leaves 0x8000001D, 0x8000001E ; the bits of the leaves outside cpu_regs LEAF6_DTS = 1 shl 0 ; leaf 6 EAX: the digital sensor PM_FID = 1 shl 1 ; leaf 0x80000007 EDX: FID control PM_VID = 1 shl 2 ; VID control EXT_LEAVES = 0x80000000 ; the first extended leaf EFLAGS_ID = 1 shl 21 ; can be changed: the CPU has CPUID VENDOR_CHARS = 12 ; of the vendor string of CPUID BRAND_CHARS = 48 ; of its brand string assert BRAND_CHARS = 3 * 16 ; in 0x80000002..0x80000004 ; the vendors by the last 4 letters of their CPUID strings INTEL = 1 ; GenuineIntel AMD = 2 ; AuthenticAMD HYGON = 3 ; HygonGenuine CENTAUR = 4 ; CentaurHauls: IDT, VIA ZHAOXIN = 5 ; " Shanghai " CYRIX = 6 ; CyrixInstead TRANSMETA = 7 ; GenuineTMx86 VORTEX = 8 ; Vortex86 SoC VENDORS = 8 ; the vendors of a CPU_IDS, one bit each BY_INTEL = 1 shl INTEL BY_AMD = 1 shl AMD BY_HYGON = 1 shl HYGON BY_CENTAUR = 1 shl CENTAUR BY_ZHAOXIN = 1 shl ZHAOXIN BY_CYRIX = 1 shl CYRIX BY_TRANSMETA = 1 shl TRANSMETA BY_VORTEX = 1 shl VORTEX assert VENDORS < 16 ; a bit each in CPU_IDS.vendors struct CPU_IDS ; the CPUs a row of a table is for vendors dw ? ; BY_INTEL..., 0: the end of the table family db ? ; the first and the last family family_last db ? model db ? ; the first and the last model, to 0xFF model_last db ? stepping db ? ; the first and the last stepping, to 0xF stepping_last db ? ends struct CPU_HANDLER CPU_IDS ; CPU_IDS, then the procedure for its CPUs handler dd ? ends struct VENDOR key dd ? ; the last 4 letters of its CPUID string color dd ? ; of its logo name dd ? ; -> its name in lower case, as in the names of its CPUs ends ; the vendors in the order of vendor_id; for 0, unknown, an empty name, ; found at once vendors: ; key color name VENDOR 0, 0, vendor_none VENDOR "ntel", 0x0068B5, vendor_intel VENDOR "cAMD", 0xED1C24, vendor_amd VENDOR "uine", 0xC8102E, vendor_hygon VENDOR "auls", 0x1B3F8B, vendor_via VENDOR "ai ", 0x0B4EA2, vendor_zhaoxin VENDOR "tead", 0x333333, vendor_cyrix VENDOR "Mx86", 0x00704A, vendor_transmeta VENDOR " SoC", 0x0057A8, vendor_vortex assert ($ - vendors) / sizeof.VENDOR = 1 + VENDORS vendor_none db 0 vendor_intel db "intel", 0 vendor_amd db "amd", 0 vendor_hygon db "hygon", 0 vendor_via db "via", 0 vendor_zhaoxin db "zhaoxin", 0 vendor_cyrix db "cyrix", 0 vendor_transmeta db "transmeta", 0 vendor_vortex db "vortex", 0 ; ==================================================================== ; CPUID if the ID flag can be changed; without it one core and thread, ; the vendor empty, the rest unknown. cpu_init: pushfd pop eax mov ecx, eax xor eax, EFLAGS_ID push eax popfd pushfd pop eax cmp eax, ecx je .done call read_vendor call read_brand call read_signature call read_topology .done: ret ; ==================================================================== ; The vendor and the last leaves. read_vendor: movi eax, 0 cpuid mov [max_leaf], eax mov dword [vendor], ebx mov dword [vendor + 4], edx mov dword [vendor + 8], ecx ; its number by the last 4 letters, 0: unknown mov eax, VENDORS .key: imul edx, eax, sizeof.VENDOR cmp ecx, [vendors + edx + VENDOR.key] je .known dec eax jnz .key .known: mov [vendor_id], eax ; " Shanghai " without its spaces mov esi, vendor call str_squeeze mov eax, EXT_LEAVES cpuid mov [max_ext], eax ret ; The name of the model without the runs of spaces, if there is one. read_brand: mov edi, brand mov ebp, 0x80000002 .leaf: mov eax, ebp call cpuid_leaf0 stosd mov eax, ebx stosd mov eax, ecx stosd mov eax, edx stosd inc ebp cmp ebp, 0x80000004 jbe .leaf mov esi, brand call str_squeeze cmp [brand], 0 je .done mov [brand_ptr], brand .done: ret ; esi -> a text: in place, without spaces at its start and end, its runs ; of spaces as one; changes eax, edx, esi, edi str_squeeze: mov edi, esi mov edx, esi ; the start .char: lodsb cmp al, " " jne .keep ; a space: one before the next word, none at the start or the end cmp edi, edx je .char cmp byte [esi], " " je .char cmp byte [esi], 0 je .char .keep: stosb test al, al jnz .char ret ; The signature, the family, the model and the stepping, the features ; and the logical processors if HTT tells them. read_signature: mov eax, 1 call cpuid_leaf0 mov [signature], eax mov [cpu_regs + F_1D / 8], edx mov [cpu_regs + F_1C / 8], ecx movzx ecx, bl mov [brand_id], ecx shr ebx, 16 movzx ebx, bl ; the logical processors mov ecx, HAS_HTT bt [cpu_regs], ecx jnc .id test ebx, ebx jz .id mov [threads], ebx ; the extended family is added from 0xF, the extended model is the ; high digit from 6 .id: mov ecx, eax and ecx, 0xF mov [stepping], ecx mov ecx, eax shr ecx, 8 and ecx, 0xF mov edx, eax shr edx, 4 and edx, 0xF cmp ecx, 6 jb .family mov ebx, eax shr ebx, 12 and ebx, 0xF0 or edx, ebx cmp ecx, 0xF jne .family shr eax, 20 movzx eax, al add ecx, eax .family: mov [family], ecx mov [model], edx ; the other features mov eax, 7 call cpuid_leaf0 mov [cpu_regs + F_7B / 8], ebx mov [cpu_regs + F_7C / 8], ecx mov [cpu_regs + F_7D / 8], edx mov eax, 0x80000001 call cpuid_leaf0 mov [cpu_regs + F_X1C / 8], ecx ; the extended MMX of Cyrix is in bit 24, of the others in bit 22 cmp [vendor_id], CYRIX jne .extended bt edx, 24 jnc .extended or edx, 1 shl 22 .extended: mov [cpu_regs + F_X1D / 8], edx ret ; The threads of the package and the cores: the threads over the threads ; of a core. read_topology: cmp [vendor_id], INTEL je .intel cmp [vendor_id], ZHAOXIN ; as Intel, Linux zhaoxin.c jne .amd .intel: ; Intel: the threads of a core and of the package in the leaf 0xB, ; and the bits of the thread in the APIC ID for cache_leaves mov eax, 0xB call cpuid_leaf0 movzx ebp, bx test ebp, ebp jz .leaf4 and eax, 0x1F mov [smt_shift], eax mov [core_threads], ebp mov eax, 0xB mov ecx, 1 call cpuid_leaf movzx eax, bx test eax, eax jz .leaf4 mov [threads], eax mov ebp, [core_threads] jmp .divide ; else the cores of the package in the leaf 4, not more than threads .leaf4: mov eax, 4 call cpuid_leaf0 shr eax, 26 inc eax cmp eax, [threads] jbe .cores mov eax, [threads] .cores: mov [cores], eax ret ; AMD and the others: the threads in 0x80000008, of a core in ; 0x8000001E from Zen; 15h has the cores of a module there .amd: mov eax, 0x80000008 call cpuid_leaf0 movzx ecx, cl jecxz .amd_core inc ecx mov [threads], ecx .amd_core: mov ebp, 1 cmp [family], 0x17 jb .divide mov eax, HAS_TOPOEXT bt [cpu_regs], eax jnc .divide mov eax, 0x8000001E call cpuid_leaf0 movzx ebp, bh inc ebp ; ebp = the threads of a core .divide: mov eax, [threads] movi edx, 0 div ebp mov [cores], eax ; hybrid Intel, from Alder Lake: its cores have 1 or 2 threads, ; their number is unknown then (0) mov eax, HAS_HYBRID bt [cpu_regs], eax jnc .done mov [cores], 0 .done: ret ; ==================================================================== ; eax = leaf; out: eax..edx of its subleaf 0 cpuid_leaf0: movi ecx, 0 ; eax = leaf, ecx = subleaf; out: eax..edx, zeros past the last leaf cpuid_leaf: mov ebx, [max_leaf] cmp eax, EXT_LEAVES jb .check mov ebx, [max_ext] .check: cmp eax, ebx ja .none cpuid ret .none: movi eax, 0 movi ebx, 0 movi ecx, 0 movi edx, 0 ret ; esi -> a CPU_IDS: CF = 0 if the CPU is one of its CPUs, else 1; ; changes eax, ecx cpu_match: movzx eax, word [esi + CPU_IDS.vendors] mov ecx, [vendor_id] bt eax, ecx jnc .other mov ecx, [family] movzx eax, byte [esi + CPU_IDS.family] cmp ecx, eax jb .other movzx eax, byte [esi + CPU_IDS.family_last] cmp ecx, eax ja .other mov ecx, [model] movzx eax, byte [esi + CPU_IDS.model] cmp ecx, eax jb .other movzx eax, byte [esi + CPU_IDS.model_last] cmp ecx, eax ja .other mov ecx, [stepping] movzx eax, byte [esi + CPU_IDS.stepping] cmp ecx, eax jb .other movzx eax, byte [esi + CPU_IDS.stepping_last] cmp ecx, eax ja .other clc ret .other: stc ret ; esi -> a table of rows of ebx bytes, each a CPU_IDS and its data, ended ; by a 0 word: esi -> the first row for the CPU, CF = 1 if none; changes ; eax, ecx cpu_find: cmp word [esi + CPU_IDS.vendors], 0 je .none call cpu_match jnc .found add esi, ebx jmp cpu_find .found: clc ret .none: stc ret ; esi -> a table of CPU_HANDLER rows: the procedure of the first row for ; the CPU, if there is one; changes all the registers cpu_handle: mov ebx, sizeof.CPU_HANDLER call cpu_find jc .done call [esi + CPU_HANDLER.handler] .done: ret ; ==================================================================== ; esi -> a text in lower case: edx -> it in the name of the CPU, matched ; in upper or lower case, edi past it; both at the end of the name if it ; is not there; changes eax, ecx brand_find: mov edx, [brand_ptr] .start: mov ecx, esi mov edi, edx cmp byte [edx], 0 je .done .letter: cmp byte [ecx], 0 je .done mov al, [edi] or al, 0x20 ; in lower case cmp al, [ecx] jne .next inc ecx inc edi jmp .letter .next: inc edx jmp .start .done: ret ; esi -> a table of a value byte and a text in lower case each, ended by ; a 0 value: eax = the value of the first text in the name of the CPU, 0 ; if none; changes ecx, edx, esi, edi, ebp brand_value: movzx ebp, byte [esi] inc esi test ebp, ebp jz .done call brand_find cmp byte [edx], 0 ; found: not at the end of the name jne .done call str_skip ; esi past the text jmp brand_value .done: mov eax, ebp ret ; ==================================================================== align 4 cores dd 1 threads dd 1 core_threads dd 1 ; Intel: the threads of a core brand_ptr dd vendor ; the name, the vendor if none uglobal align 4 max_leaf dd ? max_ext dd ? signature dd ? family dd ? model dd ? stepping dd ? brand_id dd ? ; leaf 1 EBX[7:0] vendor_id dd ? ; INTEL..., 0: unknown smt_shift dd ? ; Intel: the bits of the thread in the APIC ID, else 0 ; a string of bits, F_1D...: bt with a number counts it modulo 32, so the ; bit goes in a register cpu_regs rd CPU_REGS vendor rb VENDOR_CHARS + 1 ; the zero after it from uglobal brand rb BRAND_CHARS + 1 endg