CPUID
指令是一條x86架構中的的擴充指令(此處的擴充指相對i80386),其操作碼輔助記憶碼縮寫於「CPU辨識」(CPU Identification),其作用是返回特定的CPU資訊,使得軟件可以在執行時檢測CPU的硬件特性,以便於辨識並決定執行哪些代碼。首批支援CPUID指令的處理器是1993年發佈的奔騰(i586)和486SL。[1]
通過使用CPUID
,軟件可以確定處理器的類型和特性支援(例如MMX/SSE)。CPUID
操作碼為0Fh、A2h。在呼叫CPUID前EAX暫存器內的值作為其輸入參數,Intel64指令集手冊中稱其為「葉」(Leaf),AMD64指令集手冊中稱「功能碼」(Function Number),執行CPUID後,返回值將被寫入EAX,EBX,ECX和EDX暫存器內。此外,在使用一些特殊的輸入參數時,必須同時於ECX內寫入第二個輸入參數,否則無法得到有意義的返回值。第二個參數也被稱為「子葉」(Sub Leaf)或「子功能碼」(Sub Function)。
歷史
在CPUID
指令被引入前,若要在執行時確定CPU的型號,需要編寫晦澀的匯編語言代碼以判斷某些指令被執行後處理器的行為。
1990年以前,對於如何判斷CPU的型號沒有官方說明與實現方法,但軟硬件工程師們總結了一套利用PUSHF/POPF指令檢測CPU行為以判斷其架構的方法。此方法在廣泛應用的同時引起了巨大爭議,反對者認為其並非萬無一失,在某些需要高可靠性的軟件平台上若處理器型號判斷錯誤,則有可能造成巨大損失,但英特爾在1990年發佈了《i486微處理器程式設計師參考手冊》(i486 Microprocessor Programmer's Reference Manual Intel Corp. 1990),其中在手冊的第22.10節中使用了上述方法判斷CPU代數,並且英特爾聲稱「代碼序列已經過 Intel 驗證,可以檢測 CPUID、數學協處理器功能,並進行相應的初始化。任何其他方法都可能在未來的處理器中產生不可預測的結果」。此後該方法被應用於大多數作業系統內核中。
以下所介紹之方法為英特爾手冊中的官方演算法。
該演算法的核心是利用PUSHF/POPF指令將標誌暫存器中的特定位設為1或0,並觀察處理器隨後的行為,每一代處理器的具體行為均有所不同。
8086/8088系列處理器在清除標誌暫存器的12-15位後,無論向其中寫入何值,處理器將始終把它們設為1,其檢測方式如下
pushf ; 将标志寄存器内容压栈
pop ax ; 将标志寄存器内容送入AX
mov cx, ax ; 将标志寄存器内容送入CX备份
and ax, 0fffh ; 将AX的12-15位置零
push ax ; 将标志寄存器的新值压栈
popf ; 送入FLAG
pushf ; 获取弹栈后标志寄存器的内容
pop ax ; 存入AX
and ax, 0f000h ; 如果12-15位被处理器置1则为8086/8088
cmp ax, 0f000h
je end_cpu_type_8688 ; 跳转至对应代码
對於80286系列處理器,在真實模式下這些位始終被清零,在保護模式下,其用於I/O特權級別和巢狀任務(NT),其檢測方法如下,代碼接上述代碼執行
or cx, 0f000h ; 将12-15位置1
push cx ; 压栈
popf ; 将栈内容弹入标志寄存器
pushf ; 获取标志寄存器并存入AX
pop ax
and ax, 0f000h ; 若12-15位位0则为286
jz end_cpu_type_286
檢測386/486處理器的原理與上述代碼相同,但檢測的位有所區別,486處理器中引入了一個AC標誌位,並將把它置1,其對於386而言是無效的,故386處理器將其置零,其實現如下
pushfd;EFLAGS压栈
pop eax ;获取原始 EFLAGS
mov ecx, eax ;保存原来的 EFLAGS
xor eax, 40000h ; 翻转 EFLAGS 中的 AC 位
push eax ;将新的 EFLAGS 压栈
popfd 中;替换当前的 EFLAGS 值
pushfd;获取新的 EFLAGS
pop eax ;将新的 EFLAGS 存储在 EAX
xor eax, ecx 中;无法反转AC则为386
jz end_cpu_type_386 ;如果 80386 处理器
此外,80386處理器的EDX暫存器在處理器被硬件復位後將被初始化為處理器的修訂版本號。
奔騰處理器支援使用CPUID指令,因此其標誌位中設置了一個被稱為ID的特殊標誌,若該標誌存在,則對應的處理器支援CPUID,檢測實現如下
mov eax, ecx ; 获取原始 EFLAGS
xor eax, 200000h ;翻转 EFLAGS 中的 ID 位
push eax ;将新的 EFLAGS 值保存在堆栈
popfd 中;替换当前的 EFLAGS 值
pushfd ;获取新的 EFLAGS
pop eax ;将新的 EFLAGS 存储在 EAX
xor eax, ecx 中;可切换则为奔腾或486SL
jne end_cpu_type_586
該演算法無法檢測80186處理器(雖然其並未被廣泛使用),80186/88處理器包80286中包含的大部分新指令和異常,包括PUSHA/POPA、PUSH、SHL和無效操作碼異常。80186/88中唯一沒有實現的是專門用於保護模式的指令和異常。未能檢測到此處理器可能會禁止使用某些可以利用這些新指令和異常的軟件。
同時,由於PUSHF/POPF是Ring0指令,故該演算法只能在真實模式中執行,保護模式作業系統中執行的應用程式無法使用這個方法,除非處理器以及作業系統支援虛擬模式擴充(VME)。
以上演算法僅供參考與學習,在使用非英特爾處理器,完全軟件虛擬化(Full emulation)的虛擬機器或其他平台上,該演算法很有可能無法正確判斷處理器支援的指令集。考慮到奔騰已經是近30年前的處理器,除非有必要在古老的電腦上檢測CPU,否則不應該再使用這種演算法,而是直接呼叫CPUID指令。
對於非x86架構的CPU,仍然需要精心設計的代碼判斷其差別(例如使用MOVE的特權要求判斷摩托羅拉68000和68010),但大多數架構都要求具體實現者提供相應的暫存器以提供區分和判斷處理器(例如ARM64的ID_AA64暫存器)。
呼叫CPUID
CPUID
指令的操作碼是 0F A2
。CPUID
沒有顯式的運算元,其隱式使用EAX或EAX與ECX中的值作為參數,以確定要取得的資訊。在英特爾指令集手冊中,CPUID
的輸入參數(EAX),被稱為「葉」(Leaf),AMD稱其為「功能碼」(Function Number),執行CPUID後,返回值將被寫入EAX,EBX,ECX和EDX暫存器內。此外,在使用一些特殊的輸入參數時,必須同時於ECX內寫入第二個輸入參數,否則無法得到有意義的返回值。第二個參數也被稱為「子葉」(Sub Leaf)或「子功能碼」(Sub Function)。
注意,若且唯若MSR暫存器IA32_MISC_ENABLE.BOOT_NT4 的第22位為0時,EAX大於3的輸入才是有意義的。若且唯若該位被設為1時,Windows NT 4.0 SP6以前的作業系統才能正常啟動。
截止到2022年,對於一些常見的處理器架構,最大的有效的基礎輸入是0x20(GoldenCove),0x10(Zen4);最大有效的擴充輸入是0x80000008(GoldenCove),0x80000028(Zen4)。
輸入參數碼0時,CPUID返回對當前處理器有意義的(即被當前架構所實現的)最大輸入參數以及ASCII編碼的製造商ID(Manufacturer ID)。
最大輸入參數被寫入EAX暫存器中,對於英特爾和AMD處理器,已知的返回值如下表所示:
架構 | 基礎輸入 | 擴充輸入 |
---|---|---|
80486及以前 | 不支援CPUID指令 | |
486和P5 | 0x01 | 不支援 |
P6 | 0x02 | 不支援 |
P6(奔騰III) | 0x03 | 不支援 |
Netbrust | 0x02 | 0x8000 0004 |
Netbrust(64位元) | 0x05 | 0x8000 0008 |
Core | 0x0A | 0x8000 0008 |
Core(Penryn) | 0x0D | 0x8000 0008 |
Bonnell | 0x0A | 0x8000 0008 |
Nehalem | 0x0B | 0x8000 0008 |
Sandy Bridge | 0x0D | 0x8000 0008 |
Skylake | 0x16 | 0x8000 0008 |
SunnyCove | 0x1B | 0x8000 0008 |
SunnyCove(Alder Lake) | 0x20 | 0x8000 0008 |
SunnyCove(Sapphire Rapids) | 0x8000 0008 |
架構 | 基礎輸入 | 擴充輸入 |
---|---|---|
K5以前 | 不支援CPUID指令 | |
K5 | 0x1 | 不支援 |
K5(Krypton) | 0x1 | 0x8000 0005 |
K6 | 0x1 | 0x8000 0006 |
K7 | 0x1 | 0x8000 0007 |
K8 | 0x1 | 0x8000 0018 |
K10 | 0x5 | 0x8000 001B |
K10(Llanro) | 0x6 | 0x8000 001B |
Bulldozer | 0xD | 0x8000 001E |
Zen | 0xD | 0x8000 001F |
Zen2 | 0x10 | 0x8000 0020 |
Zen3 | 0x10 | 0x8000 0023 |
Zen4 | 0x10 | 0x8000 0028 |
製造商ID含有12個字元,以EBX-EDX-ECX的順序拼接,例如在英特爾處理器上將返回「GenuineIntel」,具體的返回值如下表所示:
暫存器 | 16進制值 | ASCII |
---|---|---|
EBX | 756E6547 | Genu |
ECX | 6C65746E | ntel |
EDX | 49656E69 | ineI |
已知的製造商以及其ID如下:
"AMDisbetter!"
– AMD K5的早期工程樣品"AuthenticAMD"
– AMD"CentaurHauls"
– IDT/半人馬座(Centaur)/兆芯"CyrixInstead"
– Cyrix/意法半導體/IBM"GenuineIntel"
– 英特爾"TransmetaCPU"
– 全美達"GenuineTMx86"
– 全美達"Geode by NSC"
– 國家半導體"NexGenDriven"
– NexGen"RiseRiseRise"
– Rise"SiS SiS SiS "
– 矽統"UMC UMC UMC "
– 聯華電子(台聯電)"VIA VIA VIA "
– 威盛"Vortex86 SoC"
– DM&P Vortex86" Shanghai "
– 兆芯"HygonGenuine"
– 海光"Genuine RDC"
– RDC"E2K MACHINE"
– MCST Elbrus
已知的軟核x86處理器ID:
"MiSTer AO486"
– ao486"GenuineIntel"
– v586
已知的x86虛擬機器ID:
"bhyve bhyve "
– bhyve" KVMKVMKVM "
– KVM"TCGTCGTCGTCG"
– QEMU"Microsoft Hv"
– Hyper-V"MicrosoftXTA"
– 微軟x86到ARM轉譯器" lrpepyh vr"
– Parallels"VMwareVMware"
– VMware"XenVMMXenVMM"
– Xen HVM"ACRNACRNACRN"
– Project ACRN" QNXQVMBSQG "
– QNX"GenuineIntel"
– 蘋果 羅塞塔2 x86到ARM轉譯器"VirtualApple"
– 蘋果 羅塞塔2 x86到ARM轉譯器
使用GNU格式匯編語言取得製造商ID和最高輸入的例子:
.data
s0: .asciz "CPUID: %x\n"
s1: .asciz "Largest basic function number implemented: %i\n"
s2: .asciz "Vendor ID: %.12s\n"
.text
.align 32
.globl main
main:
pushq %rbp
movq %rsp,%rbp
subq $16,%rsp
movl $1,%eax
cpuid
leaq s0(%rip),%rdi
movl %eax,%esi
xorl %eax,%eax
call printf
pushq %rbx
xorl %eax,%eax
cpuid
movl %ebx,8(%rsp)
movl %edx,12(%rsp)
movl %ecx,16(%rsp)
popq %rbx
leaq s1(%rip),%rdi
movl %eax,%esi
xorl %eax,%eax
call printf
leaq s2(%rip),%rdi
movq %rsp,%rsi
xorl %eax,%eax
call printf
movq %rbp,%rsp
popq %rbp
// ret
movl $1,%eax
int $0x80
該參數在EAX中返回處理器的版本資訊,這些資訊也被稱為「簽章」(signature),一般而言,對於使用同一工藝製造的同一架構的處理器,其簽章是相同的,故該資訊可被用於判斷架構和工藝。其包含的內容以及意義如下:
EAX | |||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
保留=0 | 擴充系列號(Extended Family ID) | 擴充型號(Extended Model) | 保留=0 | 處理器類型(Type) | 系列號(Family) | 型號(Model) | 步進(Stepping) |
關於版本資訊的注意事項:
- 若系列號部分為0xF,則實際的系列號為EAX中系列號欄位和擴充系列號欄位的和,反之實際系列號與EAX中欄位相同。
- 若系列號部分為0xF或6,則實際的型號為EAX中擴充型號欄位左移四位後與型號欄位的和,反之實際型號與EAX中欄位相同。
上述內容用偽代碼表示:
IF Family_ID ≠ 0FH
THEN DisplayFamily = Family_ID;
ELSE DisplayFamily = Extended_Family_ID + Family_ID;
END;
IF (Family_ID = 06H or Family_ID = 0FH)
THEN DisplayModel = (Extended_Model_ID « 4) + Model_ID;
ELSE DisplayModel = Model_ID;
END;
步進表示對處理器的修正或使用不同工藝製造,其具體意義應當參考處理器廠商所發佈的手冊。
其中,處理器類型(Type)的編碼如下:
類型 | 二進制編碼 |
---|---|
預設,一般OEM處理器 | 00 |
Overdrive處理器 | 01 |
雙處理器 | 10 |
保留 | 11 |
EBX中返回處理器的附加資訊,其內容被分為以下幾個欄位:
欄位 | EBX | 說明 |
---|---|---|
7:0 | 處理器名稱索引號(Brand Index) | |
15:8 | CLFLUSH指令清除快取的大小 | 僅當CPU支援CLFLUSH指令時
(CPUID.01.EDX.CLFSH [bit 19]= 1)有效 |
23:16 | 對於AMD處理器,若HTT標誌(位於CPUID Fn0000_0001_EDX)為1,則該欄位為該處理器的邏輯核心(即線程)數,反之,該欄位無意義且為0。
對於Intel處理器,若與上述相同位置的HT標誌有效,則該欄位代表該架構處理器中可定址的最大APIC ID數量,(注意:這個值不是邏輯處理器數),在奔騰4HT及以前的處理器中,該欄位代表每個物理核心對應的邏輯處理器數量。 |
無論是何種處理器,該欄位只在HT標誌有效時有意義。 |
31:24 | 初始Local APIC ID | 該值已被x2APIC ID取代 |
ECX和EDX返回處理器實現的功能,一個位元代表一個功能,該位為1是表示處理器支援該功能,反之則不支援,各位對應功能或指令如下:
位 | EDX | ECX | ||
---|---|---|---|---|
簡稱 | 描述 | 簡稱 | 描述 | |
0 | fpu | 整合FPU(Onboard x87 FPU) | sse3 | 串流SIMD擴充3(SSE3或Prescott新指令(PNI)) |
1 | vme | 虛擬8086模式擴充(Virtual 8086 mode extensions) | pclmulqdq | PCLMULQDQ指令 |
2 | de | 除錯擴充(Debugging extensions CR4 bit 3) | dtes64 | 64位元Debug store (edx bit 21) |
3 | pse | 頁尺寸擴充(Page Size Extension) | monitor | MONITOR和MWAIT指令 |
4 | tsc | 時鐘周期計數器(Time Stamp Counter) | ds-cpl | CPL qualified debug store |
5 | msr | 特殊模組暫存器(Model-Specific Registers) | vmx | 虛擬機器擴充(Virtual Machine eXtensions,VMX) |
6 | pae | 實體位址擴充(Physical Address Extension) | smx | 安全模式擴充(Safer Mode Extensions) |
7 | mce | 機器檢查異常(Machine Check Exception) | est | 增強SpeedStep(Enhanced Intel SpeedStep,EIST) |
8 | cx8 | CMPXCHG8指令 | tm2 | 溫度感測器2(Thermal Monitor 2) |
9 | apic | 整合進階可程式化中斷控制器(Advanced Programmable Interrupt Controller) | ssse3 | 串流SIMD擴充3補充指令集(Supplemental SSE3,SSSE3) |
10 | 保留 | cnxt-id | 一級快取上下文ID(L1 Context ID) | |
11 | sep | SYSENTER和SYSEXIT指令 | sdbg | Silicon Debug interface |
12 | mtrr | 主記憶體類型範圍暫存器(Memory Type Range Registers) | fma | 3運算元融合乘加(Fused multiply-add 3,FMA3) |
13 | pge | 全域分頁控制位(Page Global Enable bit in CR4) | cx16 | CMPXCHG16B指令 |
14 | mca | 機器檢查架構(Machine check architecture) | xtpr | Can disable sending task priority messages |
15 | cmov | 條件傳送指令(CMOV) | pdcm | Perfmon & debug capability |
16 | pat | 頁屬性表(Page Attribute Table) | 保留 | |
17 | pse-36 | 36位元頁尺寸擴充(36-bit page size extension) | pcid | Process context identifiers (CR4 bit 17) |
18 | psn | 處理器序列號(Processor Serial Number) | dca | Direct cache access for DMA writes[2][3] |
19 | clfsh | CLFLUSH指令 | sse4.1 | 串流SIMD擴充4.1(SSE4.1) |
20 | 保留 | sse4.2 | 串流SIMD擴充4.2(SSE4.2) | |
21 | ds | 除錯儲存(Debug store) | x2apic | x2APIC |
22 | acpi | 整合ACPI溫控暫存器(Onboard thermal control MSRs for ACPI) | movbe | MOVBE指令 |
23 | mmx | 多媒體擴充(MMX) | popcnt | POPCNT指令 |
24 | fxsr | FXSAVE,FXRESTOR 指令 | tsc-deadline | APIC implements one-shot operation using a TSC deadline value |
25 | sse | 串流SIMD擴充(SSE或Katmai新指令) | aes | AES指令集 |
26 | sse2 | 串流SIMD擴充2(SSE2) | xsave | XSAVE, XRESTOR, XSETBV, XGETBV指令 |
27 | ss | 自窺探快取(self-snoop cache) | osxsave | 作業系統啟用XSAVE |
28 | htt | 超線程(Hyper-threading) | avx | 進階向量擴充(Advanced Vector Extensions,AVX) |
29 | tm | 自控溫溫度感測器(Thermal monitor automatically limits temperature) | f16c | F16C指令集 |
30 | ia64 | 基於IA64架構的處理器 | rdrnd | 整合亂數發生器on-chip random number generator) |
31 | pbe | 掛起中斷(Pending Break Enable) | hypervisor | 當前為虛擬機器環境 |
對於英特爾處理器,EAX=2將返回處理器快取,TLB和預取器資訊,這些資訊被編碼為數個1位元組資訊返回於EAX,EBX,ECX和EDX四個暫存器中,編碼規則如下:
EAX暫存器返回值的最低位(LSB)一定是1,它不代表任何資訊,軟件讀取時應當將其忽略。
每個暫存器返回值的最高位(MSB)代表該暫存器中的值是否有效,若MSB為0,則該暫存器包含有意義的資訊,反之則代表該暫存器為保留值且無意義。
若某一暫存器內的返回值有意義,則其應當被分割為4個等長部分,每部分長度為8為,即一位元組,每部分代表了一個快取/TLB或預取器的資訊,具體編碼見英特爾所發行的官方手冊Intel 64 and IA-32 Architectures Software Developer Manuals (頁面存檔備份,存於互聯網檔案館) 卷2A中關於CPUID指令的表3-12。(截至2023.2,該表位於Vol2A,3-245到3-248頁)。
代表快取/TLB/預取的編碼位元組沒有特定的順序,不存在「某個暫存器的某個位元組對應於哪一級別的快取」。
使用EAX=2是取得英特爾處理器快取資訊的傳統方法,但由於快取類型迅速增多,使用該方法已經無法高效的取得資訊,故對於較新的(一般認為是Core及以後)的英特爾處理器,返回值的編碼中可能包含類似0xFF或0的空描述符,對於此種情況,應當使用更新的方法(EAX=4)取得快取資訊。
對於AMD處理器,EAX=2是保留項。四個暫存器都將返回0。
以3作為輸入參數時,CPUID將在ECX和EDX暫存器中返回處理器序列號。
該參數僅在使用核心代號為Katmai和Coppermine的奔騰III處理器上可用,處理器序列號是一個獨一無二的96位長的二進制數(實際只使用了64位元),每個處理器僅對應一個序列號,通過序列號可用追蹤該處理器從生產到銷售使用的全部流程,英特爾自稱引入該功能的原因是「為了提高電子商務安全性,例如只能在某台電腦上使用某張銀行卡」,但其在中美歐等多個國家和地區引起了強烈的私隱和國家安全問題爭議,民間認為該功能侵犯了用戶私隱權,居心不良者可用該功能追蹤他人的電腦,且一旦英特爾的序列號資料庫泄露,將帶來難以估量的損失,而多國的國家安全機關認為,敵對國家的情報部門可以使用該功能輕而易舉的記錄並分析電腦活動,迫於市場壓力,英特爾從核心代號Tulatin的奔騰III處理器開始禁用了這一功能,且在奔騰4以及Core處理器等後續產品上再未使用過該功能。
對於AMD處理器,EAX=3是保留項。四個暫存器都將返回0。
以4作為EAX輸入參數時,CPUID依據ECX暫存器中輸入的第二個參數(也稱為子葉)返回處理器快取資訊。
EAX返回快取類型資訊,值分為以下幾個欄位
EAX | ||
---|---|---|
位 | 內容 | 備註 |
4:0 | 快取類型 | 00:空
01:數據快取 10:指令快取 11:數據和指令快取 |
7:5 | 快取級別 | 從1開始 |
8 | 自初始化快取標誌 | 為1則支援自初始化,反之需要軟件初始化 |
9 | 全相聯快取標誌 | 同上 |
13:10 | 保留=0 | |
25:14 | 最大共用此快取的線程數 | 1.指邏輯處理器,需考慮超線程
2.為最大值,不一定代表真實值 3.用於計算時將該欄位值+1 |
31:26 | 最大可定址的核心ID | 同上 |
EBX,ECX返回快取尺寸資訊,返回值各個欄位如下:
EBX | ||
---|---|---|
位 | 內容 | 備註 |
11:0 | 快取線尺寸(Line Size,L) | 1.以位元組為單位
2.真實值為該欄位+1 |
21:12 | 物理快取分區數(Physical Line partitions,P) | 真實值為該欄位+1 |
31:22 | 快取組行數(Ways of associativity,W) | 同上 |
ECX返回組相聯快取的的組數(Sets,S)。
英特爾並未在其發佈的官方手冊中定義子葉與快取的絕對對應關係,也未說明最大支援的子葉,上述關係表格只是軟硬件工程師的總結,但截止到2023年的SunnyCove架構,所有的英特爾處理器都遵守這一對應關係。
在面對一個全新的英特爾架構時,應當假定上述對應關係與最大子葉數不存在,並通過該指令判斷,其中對應關係由上述EAX中的返回值相應欄位確定,最大子葉值由以下方法判斷:在EAX輸入4,ECX中的初始值為0並執行CPUID指令,每次執行後將ECX內值加1,EAX仍為4,直到四個暫存器的返回值全部為0,則使返回值不為0的最大ECX輸入就是最大支援的子葉。
對於AMD處理器,EAX=3是保留項。四個暫存器都將返回0。
英特爾SDM手冊中未直接給出讀取快取尺寸的方法,但可以通過如下公式計算:
其中C為以位元組為單位的快取大小,W,P,L,S為讀取該快取時返回值的相應欄位。
x86外的特定CPU辨識資訊
一些非x86的CPU架構也提供了有關處理器能力的某種形式的結構化資訊,通常作為一組特殊暫存器:
參見
參考資料
Wikiwand in your browser!
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.