【问题标题】:Broken CPUID brand string?损坏的 CPUID 品牌字符串?
【发布时间】:2016-07-31 05:40:42
【问题描述】:

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

注意:

  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

【问题讨论】:

  • 愚蠢的问题。你的程序有完全权限吗?
  • 你是指 C++ 还是 C#?
  • @MichaelPetch C++。 Console 是 EFI 控制台的命名空间。(Console::Output 是控制台输出)不是 C# 中的 Console 类。
  • 啊,好吧,只是确保
  • 尝试使用 __cpuid 内在函数而不是汇编函数:msdn.microsoft.com/en-us/library/hskdteyh.aspx

标签: c++ assembly x86-64 masm uefi


【解决方案1】:

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


CPUID 破坏了 RAXRBXRCXRDX 的内容,但你这样做了这在你的代码中:

cpuid
mov [ rdx ], eax

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

根据Windows 64-bit Calling Convention,这些是调用者需要保留的易失性寄存器:

寄存器 RAX、RCX、RDX、R8、R9、R10、R11 被认为是易失性的,并且必须在函数调用时被视为已销毁(除非通过分析可以证明其他方面的安全性,例如整个程序优化)。

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

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

我们可以使用R10(一个易失性寄存器)来临时存储RDX。我们可以重用 R10 来更新 pEdx 处的值,而不是在代码中使用 RSI。如果我们不使用它,我们就不需要保留 RSICPUID 确实会破坏 RBX,而 RBX 是非易失性的,所以我们需要保留它。 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

【讨论】:

  • 我还建议更改函数签名,只将一个指针传递给结构!这样,两端的代码就更简单了。 (但仍然比使用像 __cpuid() 这样的内置编译器复杂得多)。
  • @PeterCordes :我故意不改变他的代码结构。我正在确定导致他的代码无法正常运行的主要问题。更好的解决方案在我的答案的第一行。使用 __cpuid 编译器内在函数,不需要汇编程序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-28
  • 1970-01-01
  • 1970-01-01
  • 2013-10-19
  • 2017-03-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多