损坏的 CPUID 品牌字符串?
Broken CPUID brand string?
我正在使用 CPUID 指令在我的 OS 中打印一些关于 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 控制台的包装器 class。不是 C# 东西。
双字 = 无符号 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
RDX 在执行 mov [ rdx ], eax
时已被销毁,导致 RDX 中的指针无效。在使用 CPUID
指令之前,您需要将 RDX 移动到另一个寄存器。
根据 Windows 64-bit Calling Convention 这些是需要由 调用者保存的易失性寄存器:
The registers RAX, RCX, RDX, R8, R9, R10, R11 are considered volatile and must be considered destroyed on function calls (unless otherwise safety-provable by analysis such as whole program optimization).
这些是 callee 需要保存的非易失性的:
The registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15 are considered nonvolatile and must be saved and restored by a function that uses them.
我们可以使用R10(一个易失性寄存器)来临时存储RDX。我们可以重用 R10 来更新 pEdx
处的值,而不是在代码中使用 RSI。如果我们不使用它,我们将不需要保留 RSI。 CPUID确实会破坏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 指令在我的 OS 中打印一些关于 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 控制台的包装器 class。不是 C# 东西。
双字 = 无符号 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
RDX 在执行 mov [ rdx ], eax
时已被销毁,导致 RDX 中的指针无效。在使用 CPUID
指令之前,您需要将 RDX 移动到另一个寄存器。
根据 Windows 64-bit Calling Convention 这些是需要由 调用者保存的易失性寄存器:
The registers RAX, RCX, RDX, R8, R9, R10, R11 are considered volatile and must be considered destroyed on function calls (unless otherwise safety-provable by analysis such as whole program optimization).
这些是 callee 需要保存的非易失性的:
The registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15 are considered nonvolatile and must be saved and restored by a function that uses them.
我们可以使用R10(一个易失性寄存器)来临时存储RDX。我们可以重用 R10 来更新 pEdx
处的值,而不是在代码中使用 RSI。如果我们不使用它,我们将不需要保留 RSI。 CPUID确实会破坏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