我正在使用 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

供应商字符串应该是:
Intel(R) Core(TM) i5 CPU       M 540

但我得到的是:
 D:  l(R)  Core(TMD:   CPU       MD:

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();

笔记:
  • 这个程序是 UEFI 应用程序。权限没有问题。
  • Console 是 EFI 控制台的包装类。不是 C# 的东西。
  • 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
    

    最佳答案

    我同意 Ross Ridge 的观点,你应该使用编译器内在的 __cpuid 。至于为什么您的代码可能无法按原样工作 - 有一些错误会导致问题。

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

    cpuid
    mov [ rdx ], eax
    

    执行 mov [ rdx ], eax 时 RDX 已被销毁,导致 RDX 中的指针无效。在使用 CPUID 指令之前,您需要将 RDX 移动到另一个寄存器。

    根据 Windows 64-bit Calling Convention 这些是需要由 调用者 保留的 volatile 寄存器:



    这些是需要由 被调用方 保留的非 volatile :



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

    在您的代码中,您有这一行:
    mov [ rsi ], rdx
    

    RSI 是调用者提供的内存地址 (pEdx),用于存储 EDX 中的值。您拥有的代码会将 8 字节寄存器 RDX 的内容移动到需要 4 字节 DWORD 的内存位置。这可能会破坏调用者中的数据。这真的应该是:
    mov [ rsi ], edx
    

    考虑到上述所有内容,我们可以这样编写 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
    

    关于c++ - CPUID 品牌字符串损坏?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36525340/

    10-13 05:02