CPUID品牌字符串坏了吗?

Gip*_*umi 3 c++ assembly x86-64 masm uefi

我正在使用CPUID指令在我的操作系统中打印有关CPU的一些信息.

阅读和打印供应商字符串(GenuineIntel)运作良好,但阅读品牌字符串给我一点奇怪的字符串.

ok cpu-info <= Run command
CPU Vendor name: GenuineIntel <= Vendor string is good
CPU Brand: D:  l(R)  Core(TMD:   CPU       MD:  <= What..?
ok
Run Code Online (Sandbox Code Playgroud)

供应商字符串应该是:

Intel(R) Core(TM) i5 CPU       M 540
Run Code Online (Sandbox Code Playgroud)

但我得到的是:

 D:  l(R)  Core(TMD:   CPU       MD:
Run Code Online (Sandbox Code Playgroud)

C++代码:

            char vendorString[13] = { 0, };
            Dword eax, ebx, ecx, edx;
            ACpuid(0, &eax, &ebx, &ecx, &edx);
            *((Dword*)vendorString) = ebx;
            *((Dword*)vendorString + 1) = edx;
            *((Dword*)vendorString + 2) = ecx;
            Console::Output.Write(L"CPU vendor name: ");
            for (int i = 0; i < 13; i++) {
                Console::Output.Write((wchar_t)(vendorString[i]));
            }
            Console::Output.WriteLine();

            char brandString[48] = { 0, };
            ACpuid(0x80000002, &eax, &ebx, &ecx, &edx);
            *((Dword*)brandString) = eax;
            *((Dword*)brandString + 1) = ebx;
            *((Dword*)brandString + 2) = ecx;
            *((Dword*)brandString + 3) = edx;
            ACpuid(0x80000003, &eax, &ebx, &ecx, &edx);
            *((Dword*)brandString + 4) = eax;
            *((Dword*)brandString + 5) = ebx;
            *((Dword*)brandString + 6) = ecx;
            *((Dword*)brandString + 7) = edx;
            ACpuid(0x80000004, &eax, &ebx, &ecx, &edx);
            *((Dword*)brandString + 8) = eax;
            *((Dword*)brandString + 9) = ebx;
            *((Dword*)brandString + 10) = ecx;
            *((Dword*)brandString + 11) = edx;
            Console::Output.Write(L"CPU brand: ");
            for (int i = 0; i < 48; i++) {
                Console::Output.Write((wchar_t) brandString[i]);
            }
            Console::Output.WriteLine();
Run Code Online (Sandbox Code Playgroud)

注意:

  1. 该程序是UEFI应用程序.权限没问题.

  2. Console是EFI控制台的包装类.不是C#的东西.

  3. Dword =无符号32位整数

汇编代码(MASM):

;Cpuid command
;ACpuid(Type, pEax, pEbx, pEcx, pEdx)
ACpuid Proc
    ;Type => Rcx
    ;pEax => Rdx
    ;pEbx => R8
    ;pEcx => R9
    ;pEdx => [ rbp + 48 ] ?
    push rbp
    mov rbp, rsp
    push rax
    push rsi

    mov rax, rcx
    cpuid
    mov [ rdx ], eax
    mov [ r8 ], ebx
    mov [ r9 ], ecx
    mov rsi, [ rbp + 48 ]
    mov [ rsi ], rdx

    pop rsi
    pop rax
    pop rbp
    ret
ACpuid Endp
Run Code Online (Sandbox Code Playgroud)

Mic*_*tch 5

我同意Ross Ridge你应该使用编译器内在的__cpuid.至于为什么你的代码可能不能正常工作 - 有一些错误会导致问题.


CPUID会破坏 RAX, RBX, RCX RDX的内容,但您在代码中执行此操作:

cpuid
mov [ rdx ], eax
Run Code Online (Sandbox Code Playgroud)

mov [ rdx ], eax执行时已经破坏了RDX,导致RDX中的指针无效.在使用该指令之前,您需要将RDX移动到另一个寄存器CPUID.

根据Windows 64位调用约定,这些是调用者需要保留的易失性寄存器:

寄存器RAX,RCX,RDX,R8,R9,R10,R11被认为是易失性的,必须考虑在函数调用中被破坏(除非通过分析(例如整个程序优化)进行安全性证明).

这些是需要由被调用者保存的非易失性:

寄存器RBX,RBP,RDI,RSI,RSP,R12,R13,R14和R15被认为是非易失性的,必须由使用它们的函数保存和恢复.

我们可以使用R10(易失性寄存器)临时存储RDX.我们可以重用R10来更新值,而不是在代码中使用RSI.如果我们不使用它,我们将不需要保留RSI.CPUID确实会破坏RBX,而RBX是非易失性的,所以我们需要保留它.RAX是不稳定的,所以我们不需要保留它.pEdx


在你的代码中你有这一行:

mov [ rsi ], rdx
Run Code Online (Sandbox Code Playgroud)

RSIpEdx调用者提供的存储器地址(),用于将值存储在EDX中.您拥有的代码会将8字节寄存器RDX的内容移动到期望4字节DWORD的内存位置.这可能会使调用者中的数据丢失.这应该是:

mov [ rsi ], edx
Run Code Online (Sandbox Code Playgroud)

考虑到上述所有因素,我们可以通过ACpuid这种方式编写例程:

option casemap:none
.code

;Cpuid command
;ACpuid(Type, pEax, pEbx, pEcx, pEdx)
ACpuid Proc
    ;Type => Rcx
    ;pEax => Rdx
    ;pEbx => R8
    ;pEcx => R9
    ;pEdx => [ rbp + 48 ] ?
    push rbp
    mov rbp, rsp
    push rbx         ; Preserve RBX (destroyed by CPUID)

    mov r10, rdx     ; Save RDX before CPUID
    mov rax, rcx
    cpuid
    mov [ r10 ], eax
    mov [ r8 ], ebx
    mov [ r9 ], ecx
    mov r10, [ rbp + 48 ]
    mov [ r10 ], edx ; Last parameter is pointer to 32-bit DWORD, 
                     ;  Move EDX to the memory location, not RDX

    pop rbx
    pop rbp
    ret
ACpuid Endp

end
Run Code Online (Sandbox Code Playgroud)