; CPUINFO - the clocks: the frequency measured by the kernel, the ; multiplier from the MSRs, the bus clock from both. ; ==================================================================== MSR_EBL_CR_POWERON = 0x2A ; Intel P6, P4 Willamette MSR_EBC_FREQ_ID = 0x2C ; Intel P4 MSR_PLATFORM_INFO = 0xCE ; Intel from Nehalem MSR_PERF_STATUS = 0x198 ; Intel Pentium M to Core 2, Atom MSR_K6_PSOR = 0xC0000087 ; AMD K6 MSR_HWCR = 0xC0010015 ; AMD K7 MSR_FIDVID = 0xC0010042 ; AMD K7 mobile, K8 MSR_PSTATE_0 = 0xC0010064 ; AMD from family 10h ; ==================================================================== ; The clock, the multiplier and the bus clock. clock_init: ; the clock measured by the kernel at boot, in tenths of MHz and MHz; ; Hz in edx:eax, the kernels that give only eax leave edx at 0 movi edx, 0 mcall SF_SYSTEM, SSF_GET_CPU_FREQUENCY mov ecx, 100000 div ecx mov [freq10], eax movi edx, 0 mov ecx, 10 div ecx mov [freq_mhz], eax call mult_init ; the bus clock in tenths of MHz: the clock over the multiplier, ; rounded mov ecx, [mult10] jecxz .done mov eax, [freq10] imul eax, 10 mov edx, ecx shr edx, 1 add eax, edx movi edx, 0 div ecx mov [bus10], eax .done: ret ; ==================================================================== ; The multiplier of the base clock in tenths: Intel from Pentium Pro, AMD ; from K6. The tables are those of the old CPUID and GMON. mult_init: mov esi, .cpus call cpu_handle ; also the end of the readers below, their CF kept .done: ret ; the reader of the multiplier: the first row for the CPU .cpus: ; vendors family model stepping reader CPU_HANDLER BY_INTEL, 6, 6, 0, 6, 0, 0xF, .p6 ; Pentium Pro, II CPU_HANDLER BY_INTEL, 6, 6, 7, 8, 0, 0xF, .p3 ; Pentium III CPU_HANDLER BY_INTEL, 6, 6, 9, 9, 0, 0xF, .perf_status ; Pentium M CPU_HANDLER BY_INTEL, 6, 6, 0xA, 0xA, 0, 0xF, .p3 ; Pentium III Xeon CPU_HANDLER BY_INTEL, 6, 6, 0xB, 0xB, 0, 0xF, .tualatin CPU_HANDLER BY_INTEL, 6, 6, 0xC, 0x19, 0, 0xF, .perf_status ; to Core 2 CPU_HANDLER BY_INTEL, 6, 6, 0x1C, 0x1D, 0, 0xF, .perf_status ; Atom Diamondville, Pineview; Xeon 7400 CPU_HANDLER BY_INTEL, 6, 6, 0x26, 0x27, 0, 0xF, .perf_status ; Atom Lincroft, Penwell CPU_HANDLER BY_INTEL, 6, 6, 0x35, 0x36, 0, 0xF, .perf_status ; Atom Cloverview, Cedarview CPU_HANDLER BY_INTEL, 6, 6, 0x1A, 0xFF, 0, 0xF, .nehalem CPU_HANDLER BY_INTEL, 0xF, 0xF, 0, 1, 0, 0xF, .willamette ; P4 CPU_HANDLER BY_INTEL, 0xF, 0xF, 2, 0xFF, 0, 0xF, .northwood ; P4 from Northwood CPU_HANDLER BY_INTEL, 0x12, 0xFF, 0, 0xFF, 0, 0xF, .nehalem ; from Nova Lake, Diamond Rapids CPU_HANDLER BY_AMD + BY_HYGON, 5, 5, 0, 7, 0, 0xF, .k6_old ; K5, K6 CPU_HANDLER BY_AMD + BY_HYGON, 5, 5, 8, 0xFF, 0, 0xF, .k6 ; K6-2, K6-III CPU_HANDLER BY_AMD + BY_HYGON, 6, 6, 0, 5, 0, 0xF, .k7 CPU_HANDLER BY_AMD + BY_HYGON, 6, 6, 6, 0xFF, 0, 0xF, .k7_mobile CPU_HANDLER BY_AMD + BY_HYGON, 0xF, 0xF, 0, 0xFF, 0, 0xF, .k8 CPU_HANDLER BY_AMD + BY_HYGON, 0x10, 0x10, 0, 0xFF, 0, 0xF, .k10 CPU_HANDLER BY_AMD + BY_HYGON, 0x11, 0x11, 0, 0xFF, 0, 0xF, .k11 CPU_HANDLER BY_AMD + BY_HYGON, 0x13, 0x13, 0, 0xFF, 0, 0xF, .k10 CPU_HANDLER BY_AMD + BY_HYGON, 0x15, 0x15, 0, 0xF, 0, 0xF, .k10 ; Bulldozer, Piledriver CPU_HANDLER BY_AMD + BY_HYGON, 0x15, 0x15, 0x10, 0xFF, 0, 0xF, .apu CPU_HANDLER BY_AMD + BY_HYGON, 0x16, 0x16, 0, 0xFF, 0, 0xF, .apu CPU_HANDLER BY_AMD + BY_HYGON, 0x17, 0x19, 0, 0xFF, 0, 0xF, .zen CPU_HANDLER BY_AMD + BY_HYGON, 0x1A, 0xFF, 0, 0xFF, 0, 0xF, .zen5 dw 0 ; the end ; Pentium Pro and II: the code in bits 25..22 of the power-on ; configuration .p6: mov esi, p6_ratios movi edi, 0xF jmp .poweron ; Pentium III: and bit 27 as bit 4 of the code; the Tualatin has ; its own table .p3: mov esi, p6_ratios movi edi, 0x1F jmp .poweron .tualatin: mov esi, tualatin_ratios movi edi, 0x1F ; esi -> the table, edi = the mask of the code; the code: bits ; 25..22 of the MSR, bit 27 as bit 4 (bit 26: the low power mode) .poweron: mov edx, MSR_EBL_CR_POWERON call msr_read jc .done mov ecx, eax shr eax, 22 and eax, 0xF bt ecx, 27 jnc .p6_code or eax, 1 shl 4 .p6_code: and eax, edi jmp .table ; from Nehalem: the base ratio in bits 15..8 .nehalem: mov edx, MSR_PLATFORM_INFO call msr_read jc .done movzx eax, ah jmp .x10 ; Pentium M to Core 2: the maximum in bits 44..40, a half in 46 .perf_status: mov edx, MSR_PERF_STATUS call msr_read jc .done mov eax, edx shr eax, 40 - 32 and eax, 0x1F imul eax, 10 bt edx, 46 - 32 jnc .store add eax, 5 jmp .store ; P4 Willamette: the code in bits 11..8 of the power-on ; configuration .willamette: mov edx, MSR_EBL_CR_POWERON call msr_read jc .done shr eax, 8 and eax, 0xF mov esi, willamette_ratios jmp .table ; the other P4: in bits 31..24 of their own register .northwood: mov edx, MSR_EBC_FREQ_ID call msr_read jc .done shr eax, 24 jmp .x10 ; K6: the code in bits 2..0; 6 is x2 before model 8, in the table ; of the old ones .k6_old: mov esi, k6_old_ratios jmp .psor .k6: mov esi, k6_ratios ; esi -> the table .psor: mov edx, MSR_K6_PSOR call msr_read jc .done and eax, 7 jmp .table ; K7 from model 6: the mobile ones with FID control have the FID ; in bits 4..0 .k7_mobile: mov eax, 0x80000007 call cpuid_leaf0 and edx, PM_FID + PM_VID cmp edx, PM_FID + PM_VID jne .k7 mov edx, MSR_FIDVID call msr_read jc .done and eax, 0x1F mov esi, k7_mobile_ratios jmp .table ; K7: the code in bits 27..24 of HWCR, bit 19 as bit 4 .k7: mov edx, MSR_HWCR call msr_read jc .done mov ecx, eax shr eax, 24 and eax, 0xF bt ecx, 19 jnc .k7_code or eax, 1 shl 4 .k7_code: mov esi, k7_ratios jmp .table ; K8: 4 + FID / 2 .k8: mov edx, MSR_FIDVID call msr_read jc .done and eax, 0x3F imul eax, 5 add eax, 40 jmp .store ; from 10h to 16h: (FID + 16) / 2^DID of 100 MHz, FID in bits 5..0 ; of the P-state 0, DID in 8..6; 11h: (FID + 8); of the 200 MHz bus ; of 10h, 11h and 15h before the APUs: a half of it, 2^(DID + 1); ; 12h and 14h divide in their own ways .k10: movi edi, 16 movi ebp, 1 jmp .fid_did .k11: movi edi, 8 movi ebp, 1 jmp .fid_did .apu: movi edi, 16 movi ebp, 0 ; edi = added to FID, ebp = 1 on the 200 MHz bus: added to DID .fid_did: call .pstate jc .done mov ecx, eax and eax, 0x3F add eax, edi imul eax, 10 shr ecx, 6 and ecx, 7 add ecx, ebp shr eax, cl jmp .store ; Zen: FID * 200 MHz / DFS, FID in bits 7..0, DFS in 13..8 .zen: call .pstate jc .done movzx ecx, ah and ecx, 0x3F jz .done movzx eax, al imul eax, 20 movi edx, 0 div ecx jmp .store ; from Zen 5: FID * 5 MHz, FID in bits 11..0 .zen5: call .pstate jc .done and eax, 0xFFF shr eax, 1 jmp .store ; esi -> a table of the tenths, eax = the code; 0 in it: unknown .table: movzx eax, byte [esi + eax] jmp .store .x10: imul eax, 10 .store: mov [mult10], eax ret ; out: eax = the P-state 0 of AMD, CF = 1 if it cannot be read or ; is not enabled; changes edx .pstate: mov edx, MSR_PSTATE_0 call msr_read jc .done test edx, edx ; bit 63: enabled js .enabled stc ret .enabled: clc ret ; the tenths of the multiplier by the code k6_ratios db 45, 50, 40, 55, 25, 30, 60, 35 k6_old_ratios db 45, 50, 40, 55, 25, 30, 20, 35 k7_ratios db 110, 115, 120, 125, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 105 db 120, 190, 120, 200, 130, 135, 140, 210, 150, 220, 160, 165, 170, 180, 230, 240 k7_mobile_ratios db 110, 115, 120, 125, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 105 db 30, 190, 40, 200, 130, 135, 140, 210, 150, 225, 160, 165, 170, 180, 0, 0 p6_ratios db 50, 30, 40, 20, 55, 35, 45, 25, 35, 70, 80, 60, 20, 75, 15, 65 db 90, 110, 120, 20, 95, 115, 85, 25, 35, 70, 80, 100, 20, 75, 15, 105 tualatin_ratios db 120, 35, 35, 40, 55, 35, 115, 35, 160, 70, 80, 60, 40, 75, 35, 65 db 90, 110, 35, 35, 95, 35, 85, 35, 35, 35, 130, 100, 140, 35, 150, 105 willamette_ratios db 160, 170, 180, 190, 200, 210, 220, 230, 80, 90, 100, 110, 120, 130, 140, 150 ; ==================================================================== ; edx = MSR; out: edx:eax, CF = 1 if the CPU has no such MSR or it reads ; all zeros; the other registers kept. A thread reads it: the kernel kills ; the thread then, not the program. msr_read: push ebx ecx esi ; without RDMSR the kernel would tell of an unknown instruction mov eax, HAS_MSR bt [cpu_regs], eax jnc .none ; a probe that did not end in time would share msr_stack cmp [msr_stuck], 0 jne .none mov [msr_num], edx mov [msr_done], 0 mcall SF_THREAD_CONTROL, SSF_CREATE_THREAD, .thread, msr_stack_top mov [msr_thread], eax ; up to 0.2 s for it, unless it is gone: killed for a missing MSR mov esi, 20 .wait: cmp [msr_done], 0 jne .read mcall SF_SYSTEM, SSF_GET_THREAD_SLOT, [msr_thread] test eax, eax jz .gone mcall SF_SLEEP, 1 dec esi jnz .wait mov [msr_stuck], 1 jmp .none ; killed, or it read the MSR just after the check of msr_done .gone: cmp [msr_done], 0 jne .read .none: stc jmp .done ; all zeros: a virtual machine without it .read: mov eax, [msr_value] mov edx, [msr_value + 4] mov ecx, eax or ecx, edx jz .none clc .done: pop esi ecx ebx ret ; SSF_READ_MSR: edx = the MSR; out: ebx:eax .thread: mcall SF_SYS_MISC, SSF_READ_MSR, , [msr_num] mov [msr_value], eax mov [msr_value + 4], ebx mov [msr_done], 1 mcall SF_TERMINATE_PROCESS ; ==================================================================== uglobal align 4 freq10 dd ? ; the clock in tenths of MHz freq_mhz dd ? mult10 dd ? ; the multiplier in tenths, 0: unknown bus10 dd ? ; the bus clock in tenths of MHz, 0: unknown msr_num dd ? msr_thread dd ? ; its TID msr_done dd ? msr_stuck dd ? ; 1: a probe did not end in time msr_value rd 2 msr_stack rb 256 msr_stack_top: endg