x86/x64体系探索及编程
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第4章 处理器的身份

我需要了解处理器的型号、厂商、有什么具体的特性等这些关于处理器的参数,应该怎么办?x86/x64处理器上提供了CPUID指令用来查看和识别身份。

4.1 测试是否支持CPUID指令

eflags寄存器的bit 21是ID(Processor Feature Identification)标志位,在286处理器中flags是16位的,在386处理器中eflags扩展为32位,bit 21是reserved。CPUID指令从Intel 486处理器上开始加入,因此除非你在古老的机器上运行,否则没必要检测处理器是否支持CPUID指令。

实验4-1:测试是否支持CPUID指令

test_CPUID()在源文件lib\lib16.asm里,在topic04\ex4-1\目录有实验代码:

代码清单4-1(lib\lib16.asm):

;---------------------------------------------------
; test_CPUID():测试是否支持 CPUID 指令
; output:
;                1 - support, 0 - no support
;---------------------------------------------------
__test_CPUID:
      pushfd                                            ; save eflags DWORD size
      mov eax,dword [esp]                          ; get old eflags
      xor dword [esp],0x200000                   ; xor the eflags.ID bit
      popfd                                             ; set eflags register
      pushfd                                            ; save eflags again
      pop ebx                                          ; get new eflags
      cmp eax,ebx                                    ; test eflags.ID has been modify
      setnz al                                         ; OK! support CPUID instruction
      movzx eax,al
      ret

修改eflags寄存器的bit 21标志位,如果能成功修改,就表示支持CPUID指令。

4.2 CPUID指令的术语及表达

Intel64手册里对CPUID指令广泛使用了如下一些表达形式和术语。

leaf(叶)

功能号使用leaf术语,例如:CPUID的01号功能,你可以称其为CPUID的01 leaf(叶)。

sub-leaf(子叶)

对于一些较复杂的信息查询,往往需要一个辅助的子号。EAX寄存器输入的是main leaf(主叶号),ECX寄存器提供的是sub-leaf(子叶号)。

mov eax,0Bh    ; main leaf
mov ecx,0     ; sub-leaf
cpuid

如上所示,0B号功能就是main leaf(主叶),ECX提供的0号就是sub-leaf(子叶)。

CPUID指令的描述形式

当软件需要判断某项功能处理器是否支持时,使用CPUID指令进行查询,在Intel手册中使用了下面的描述形式。

上面是判断处理器是否支持PAE(Physical Address Extensions)功能时的描述,当CPUID.01H:EDX[6]的值为1时,表示支持PAE功能。

4.3 基本信息与扩展信息

从CPUID指令获得的信息有两大类:basic(基本)和extended(扩展),要获得信息必须要先提供相应的leaf(功能号),每一类信息都有最大的功能号限制。

某些功能号下面还有许多sub-leaf(子叶),也就是信息的子集。使用CPUID指令之前在eax寄存器提供要查询的某个信息相应的功能号。

mov eax,0                 ; 功能号 0(main leaf)
cpuid                       ; 查询 0 号信息

返回的相应信息放在eax、ebx、ecx,以及edx寄存器中。这些信息是32位的,因此在64位模式下,rax、rbx、rcx,以及rdx寄存器的高32位被清0。

查询最大leaf(功能)号

CPUID指令被使用来查询处理器所支持的特性,因此CPUID所支持的leaf数量是与处理器相关的。很多时候,在使用某个leaf查询之前,必须判断处理器是否支持该leaf。

典型地,在使用0BH号功能时,应先查询处理器是否支持0BH号功能。

基本最大功能号

使用CPUID的00H leaf来查询,最大的基本功能号返回在EAX寄存器里。

mov eax,0                 ; 功能号 0(main leaf)
cpuid                       ; 查询 0 号信息
  cmp eax,0BH    ; 判断是否支持 0B leaf
jb no_support    ; 假如不支持...

扩展最大功能号

同样,最大的扩展功能号也可以查询。

mov eax,80000000          ; 功能号 80000000(main leaf)
cpuid                        ; 查询最大扩展功能号
  cmp eax,80000001H  ; 判断是否支持 80000001 leaf
jb no_support    ; 假如不支持...

输入80000000H功能号,从返回的EAX寄存器里可以得到最大的扩展功能号。

功能号0也返回处理器厂商名,在Intel的机器上返回的是:ebx寄存器是“Genu”,ecx寄存器是“ntel”,edx寄存器是“ineI”,组合起来是“GenuineIntel”,在AMD的机器上是“AuthenticAMD”。

实验4-2:获得basic和extended功能号

实验的源码在\topic04\ex4-2\setup.asm文件里。

代码清单4-2(topic04\ex4-2\setup.asm):

      call test_CPUID test ax,ax
      jz no_support
;; 获得最大 basic 功能号
      mov eax,0
      cpuid
      mov esi,eax
      mov di,value_address
      call get_dword_hex_string
      mov si,basic_message
      call puts
      mov si,value_address
      call puts
      call println
;; 获得最大 extended 功能号
      mov eax,0x80000000
      cpuid
      mov esi,eax
      mov di,value_address
      call get_dword_hex_string
      mov si,extend_message
      call puts
      mov si,value_address
      call puts
      call println
      jmp $
no_support:
      mov si,[message_table + eax * 2]
      call puts
      jmp $
support_message            db 'support CPUID instruction',13,10,0
no_support_message        db 'no support CPUID instruction',13,10,0
message_table              dw no_support_message,support_message,0
basic_message                db 'maximun basic function:0x',0
extend_message               db 'maximun extended function:0x',0
value_address                dd 0,0,0

在Bochs里运行的结果如下所示。

Bochs是模拟Intel系列的CPU,在笔者的AMD真实机器上最大basic功能号是0x00000005,最大extended功能号是0x8000001A,在另一台Intel i5机器上分别是0Bh和80000008h:

Intel的处理器上目前的最大basic功能号是0DH,最大extended功能号是80000008H(依赖于每个机器的实现)。Intel手册中指示Core i7处理器(Westmere架构)最大的basic功能号是0BH,最大的extended功能号是80000008H。

如果在eax中输入的功能号超过了最大的功能号,那么将返回basic最大功能号的信息。

当eax=0Eh时,返回的信息和eax=0Dh一致,当eax=80000009h时返回的信息也和eax=0Dh一致。

实验4-3:分别使用0Dh和0Eh,以及80000008h和80000009h来运行获得信息

由于在目前的Intel上0Dh是最大basic功能号,80000008h是最大的扩展功能号,因此本实验的目的是验证Intel所说的话。

代码清单4-3(topic04\ex4-3\setup.asm):

;; 现在得到最大功能号 0DH 的信息
      mov si,msg1
      call puts
      mov eax,0Dh
      cpuid
      mov [eax_value],eax
      mov [ebx_value],ebx
      mov [ecx_value],ecx
      mov [edx_value],edx
      call print_register_value                    ; 打印寄存器的值
; 测试输入功能号为 eax=0Eh
      mov si,msg2
      call puts
      mov eax,0Eh
      cpuid
      mov [eax_value],eax
      mov [ebx_value],ebx
      mov [ecx_value],ecx
      mov [edx_value],edx
      call print_register_value                    ; 打印寄存器的值
;; 现在得到 extended 最大功能号 80000008h 的信息
      mov si,msg3
      call puts
      mov eax,80000008h
      cpuid
      mov [eax_value],eax
      mov [ebx_value],ebx
      mov [ecx_value],ecx
      mov [edx_value],edx
      call print_register_value                    ; 打印寄存器的值
;; 现在测试 extended 最大功能号 80000009 的信息
      mov si,msg4
      call puts
      mov eax,80000009h
      cpuid
      mov [eax_value],eax
      mov [ebx_value],ebx
      mov [ecx_value],ecx
      mov [edx_value],edx
      call print_register_value                   ; 打印寄存器的值

下面是在Bochs中运行的结果。

可以看出,当eax分别等于0Dh、0Eh和80000009h时,所返回的信息是一样的。在笔者的AMD真实机器上运行上面的例子,得不到输出结果,目前在AMD的机器上最大的basic功能号是06H,而extended功能号则达到了8000001Bh。

请注意:当输入的功能号<=最大功能号时,如果CPU并不支持该功能号,则所有寄存器返回0值,eax=ebx=ecx=edx=0。

由于在Bochs上所支持的最大basic功能号是0Dh,如果以0Ch去访问CPUID指令(0Ch功能号CPU不支持),返回的寄存器将全是0值(eax<=maxinum number)。

当输入功能号>最大功能号时,访问CPUID,返回的将是最大功能号的信息,也就是上面做的实验所证明的。

4.4 处理器的型号(family,model与stepping)

由CPUID.01H:EAX可返回处理器的家族、模型及步进信息。

EAX[11:8]得到family编码,典型地,P6家族处理器的family编码是06H,Pentium4家族的family编码是0FH。EAX[27:20]得到扩展的family值。EAX[7:4]得到处理器在家族中的model。EAX[19:16]得到扩展的model值。

在这些处理器信息里,最重要的是family与model。由于x86处理器的庞大和混乱,在Intel文档的相关描述中,使用了family和model的配合来描述处理器的特性,例如下面的这个描述。

DisplayFamily值的计算方法如下。

if (Family == 0FH)
{
        DisplayFamily=ExtendedFamily + Family;
}
else
        DisplayFamily=Family;

可见,只有在Pentium4以后的处理器才支持ExtendedFamily编号。DisplayModel值的计算方法如下。

if (Family == 06H || Family == 0FH)
{
        DisplayModel=ExtendedModel << 4 + Model;
}
else
        DisplayModel=Model;

现在我们可以做个实验。

实验4-4:获得处理器的DisplayFamily与DisplayModel

实验的完整源码在topic04\ex4-4\setup.asm文件里,调用了lib\lib16.asm文件里面的get_DisplayFamily_DisplayModel()过程来获得DisplayFamily和DisplayModel值,下面是运行结果。

在笔者实验的机器上的处理器DispalyFamily是06H,DisplayModel是25H,根据Intel的说明这个Model是属于32nm制程的Core i3/i5/i7 Mobile处理器(属于Westmere微架构)。

它的ExtendedModel值是0010B,Model值是0101B,组合起来是:25H。那么这个处理器模型将被描述为:06_25H。这种DisplayFamily_DisplayModel的模型相当重要,在Intel手册的描述中使用这种方式来区别处理器的模型。

在Intel的机器上,06H家族和0FH家族是两大派系,0FH家族典型地是指Pentium4处理器系列。而06H家族很庞大,从早期的Pentium Pro、PentiumII到今天的Westmere/SandyBridge微架构的i7/i5/i3处理器都属于06H家族。在如此庞大的06H家族,要靠它的DisplayModel来识别每一代架构的处理器。05H家族则属于Pentium处理器。

4.5 最大的物理地址和线性地址

在CPUID.80000008H叶里,我们可以获取处理器所支持的最大物理地址和线性地址的宽度。

上面的实验中80000008h功能号返回的eax值为0x00003028,低byte是物理地址宽度,值为28h就是40位,接着是线性地址宽度。当CPU支持long mode时,这个值是30h,否则就是20h,这表明long mode下最高支持48位的线性地址,线性地址的高16位被用做sign位,在32位的机器上这个值是20h(32位)。

MAXPHYADDR值

在Intel中广泛使用MAXPHYADDR这个术语来表示最大的物理地址。80000008H leaf返回的EAX[7:0]被称为MAXPHYADDR。

当处理器不支持80000008H leaf(功能)时,如果检测到CPUID.01H:EDX[6]=1(即:PAE=1)则支持最高36位物理地址。如果此时PAE也并不支持,那么MAXPHYADDR就是32位。

4.6 处理器扩展状态信息

实际上0Dh功能号是获得处理器对Processor Extended State(处理器扩展状态)的支持度,这在AVX指令编程里非常重要。在最新的Sandy Bridge架构的处理器中,支持度只使用了3位。

Processor Extended State(处理器扩展状态)是什么?

0Dh功能号是一个功能集,在前面的例子中:

mov eax,0Dh                  ; 0Dh 功能号
mov ecx,0                    ; main leaf(主叶)功能
cpuid                          ; 得到 0Dh 的 main leaf功能

这个main leaf功能就可以获得CPU目前对处理器状态信息的支持度,eax和edx寄存器返回processor extended state的enable/disable位图。

返回的edx和eax寄存器组成一个64位的Processor Extended State功能表,高32位在edx寄存器,低32位在eax寄存器。在处理器的规划中,每1位对应一个扩展状态功能enable位。为1时支持该状态,为0时不支持。

这个Processor Extended State值将影响到XCR0(Extended Control Register)的值。当State值的某位为0时,那么XCR0的相应位为保留位(此位不能被XSETBV指令设置)。

目前的x86处理器中(包括Intel和AMD)仅使用了低3位(Bit 0~Bit2)。Bit 0对应X87 FPU状态,Bit 1对应SSE状态,这两位一般都能返回1,表示CPU支持X87 FPU和SSE功能。Bit 2对应YMM状态。

YMM状态在Intel的Sandy Bridge架构和AMD的bulldozer架构中得到了支持,也就是说CPU支持AVX指令集。因此,在这种处理器以上才返回YMM状态位为1值。

图中的阴影部分为reserved(保留位),未来的处理器可能会使用这些位来支持更多的处理器状态信息。保留位的值全部为0。

看看前面在Bochs里运行的结果。

得到的结果表明:CPU支持x87 FPU和SSE状态而不支持YMM状态,也就是说不支持AVX指令。值得注意的是,由于Bochs模拟实现了CPUID 0DH号功能,因此可以使用0DH leaf进行查询。实际上,在不支持AVX指令的处理器上,并不支持CPUID 0DH leaf。

4.6.1 探测Processor Extended State子叶

由于eax返回的是处理器对Processor Extended State的enable位,在未来的处理器中可能加入更多的支持,因此在使用前应该探测处理器到底支持了哪些Prcessor Extended State。

      mov eax,0Dh                  ; 0Dh 功能号
      mov ecx,0                    ; main leaf(主叶)功能
      cpuid                          ; 得到 0Dh 的 main leaf功能,在edx:eax返回对
      ; processor extended state的支持位
      mov [extended_state],eax             ; 保存 enable 位
      mov [extended_size],ebx               ; 保存整个exended state的size值
      mov dword [detect_value],2           ; 从Bit2 开始探测
detect:
      mov eax,0Dh              ; 0Dh 功能号
      mov ecx,[detect_value]                ; sub-leaves 子叶号
      bt dword [extended_state],ecx       ; Bit n == 1 ?
      jnc next_detect                          ;  该位不支持,继续探测
      cpuid                                       ; 探测
      mov ecx,[detect_value]
      mov [state + ecx * 4],eax           ; 保存子叶 size
      mov [state + ecx * 4 + 4],ebx      ; 保存子叶 offset
next_detect:
      inc dword [detect_value]                ; 继续探测下一个子叶
      cmp dword [detect_value],62           ; 小于等于 62 就继续
      jle detect
next:
      ... ...

假如CPU支持AVX指令,那么CPUID(eax=0Dh,ecx=0h):EAX[YMM]为1,也就是eax的Bit 2为1,因此从第2位开始探测,假如Bit 3是支持的,继续探测Bit 3,以此类推……

记录下来:在上面的64位Processor Extended State值中每1位代表一个子叶,从Bit 2(YMM状态)开始。

在Processor Extended State中X87 FPU state是必须要支持的,它必定为1,而 SSE state也就是XMM寄存器状态,也是支持的,这时Bit 1位是保留。所以从Bit 2开始探测直至Bit 62位,Bit 63位是保留未用。

4.6.2 Processor Extended State子叶所需内存size

在main leaf(eax=0Dh,ecx=0h)里,ebx和ecx寄存器都返回一个内存size。

ebx寄存器返回的是在XCR0(Extended Control Register)里设置了开启某个位去支持处理器状态所需要的内存size。

XCR0的结构与上面CPUID(EAX=0Dh,ECX=0H) 返回的64位Processor Extended State值结构是一样的。XCR0可以设置SSE状态为disable(关闭),这样CPU将不会保存SSE(即:XMM寄存器)的值。

Processor Extended State与XCR0的关系

XCR0是一个功能开关,用来开启或关闭某些state的保存。而CPUID(eax=0Dh,ecx=00h)返回的Processor Extended State值将决定处理器保存哪些state信息的能力。

OS可以通过XSETBV指令对XCR0进行相应的设置,如果某位被disable掉,这时候,XCR0与CPU支持的64位Processor Extended State值会不一致。因此,ebx寄存器返回的是这个XCR0设置的保存状态所需要的内存size。

ecx寄存器返回的是CPU所支持的所有状态(即:在main leaf里返回eax的位图),保存这些状态所需要的总内存size,因此,ebx不一定等于ecx(取决于XCR0是否开启相应的位)。

如果CPUID(eax=0Dh,ecx=0h)返回的eax寄存器为07H值,那么说明它支持AVX指令,这样的话就可以接着使用0Dh的2号子叶去查看相关的信息。

在AMD处理器上支持LWP指令,Bit 62位是LWP state位,所以在AMD里就可以查看62号子叶的信息。

下面是查看子叶号为62的相关信息(如果CPU支持)。

mov eax,0Dh                  ; 0Dh 功能号
mov ecx,62                   ; AMD机器上的62号子叶功能
cpuid

4.6.3 Processor Extended State的保存

Processor Extended State通过使用XSAVE指令来保存处理器的状态,在内存中处理器的state映像看起来如下。

实际情况还要更复杂些,SSE和YMM状态的保存,还取决于XSAVE指令提供的64位MASK码和XCR0上面所述的相应位的enable(开启),如果XCR0.YMM=0表明不保存YMM状态,那么图中的YMM state区域是0值(不被更新)。

mov edx,0
mov eax,3                         ; 不保存 YMM state
xsave [state_image]              ; 根据 64位的 Mask值和XCR0位进行保存

同样,在edx:eax这个64位的mask码值中,如果YMM state mask位为0,YMM state区域也不被更新,必须XCR0和MASK值同时被置1(AND与关系)区域才被更新。

回到上面的例子,由于不支持AVX指令,所以eax返回0x03,表明仅支持X87 FPU和SSE状态。而ebx和ecx寄存器的返回值是0x240,这个值就等于512+64=576(0x240),X87 FPU需要512字节,还要加上一个header部分。这个header的首64位(8 bytes)是一个XSTATE_BV结构,当XSAVE保存状态时,用来记录部分状态被保存了。若保存X87 FPU和SSE状态,就设置Bit 0和Bit 1为1,反之将清0。

4.6.4 Processor Extended State的恢复

使用XRSTOR指令可以从内存的state映像中恢复原来保存的state信息。

header结构是64字节,首8字节是一个64位的MASK值,用来记录在state映像中哪些区域是被保存过的(被更新过),相应的位为1时表示image中的相应的区域被更新,如:YMM state区域中被保存过,那么XSTATE_BV[2]=1(Bit 2对应于YMM state位)。

XSTATE_BV的位在执行XSAVE指令保存state时被CPU设置。当执行xrstor指令时,CPU会根据XSTATE_BV中哪一位被置位,而做相应的恢复。

4.7 处理器的特性

EAX=01h号功能将获得处理器的基本信息,它是CPUID中一个重要的功能号,eax寄存器返回处理器的Model、Family、Stepping等信息。在ebx寄存器中:ebx[15:8]返回CLFLUSH line size,这个值乘8就可以得到Cache line的size,ebx[23:16]返回逻辑处理器最大可寻址的数目。

实验4-5:查看CPU的cache line size和Maximum logic processor

实验的完整源码在\topic04\ex4-5\setup.asm文件里,下面是在VMware里的运行结果。

在VMware中笔者设置processor core的数目为4个,上面显示的logic processor是4个,ebx[15:8]的值是0x08,所以它的cache line size是64(0x40)字节。

在ecx和edx寄存器中返回CPU支持的种类繁多的特性,下面分别是ecx和edx寄存器返回值所对应的功能。

这些特性是处理器硬件物理上所支持的功能,利用CPUID指令可以检测处理器所支持的特性。

可是CPUID.EAX=01H在ECX和EDX返回的某些特性是由软件设置的。还有部分特性受到MSR(Model Specific Register)的设置影响。

例如:ECX寄存器的bit 27是OSXSAVE标志位,这个标志位并不是硬件特性,是由软件设置而来的,在OS里为了开启AVX指令使用环境,OS最终会设置CR4的Bit 18位CR4.OSXSAVE=1,处理器会根据这个位来置CPUID(EAX=01h):ECX[27]标志,也就是上面的ECX[OSXSAVE]标志位。软件使用AVX指令前会读取这个标志位,确认OS已经准备好AVX指令的执行环境。

检测CPU是否支持AVX指令的是Bit 28 AVX标志位,而Bit 26位是XSAVE标志位,它用于检测CPU是否支持XSAVE/XRSTOR、XSETBV/XGETBV指令和XCR0。

4.8 处理器的Cache与TLB信息

CPUID.EAX=02H用来获得关于CPU的Cache与TLB信息。

mov eax,02H              ; 02号功能
cpuid

eax寄存器的最低字节(byte 0)是需要执行CPUID.EAX=02H的次数。

上面这个最低字节的值为01,表明只需要执行一次CPUID.EAX=02H就能获得完整的信息。

寄存器的MSB(Bit31)位是个标志位,为0指示有效,为1指示无效。

eax、ebx、ecx和edx寄存器中的每个字节指示一个信息(除EAX寄存器的byte 0外)。

如果是FF字节表示CPUID.EAX=02H不返回cache及TLB信息,需要执行CPUID.EAX=04H进行查询。

实验4-6:使用CPUID.EAX=02H查看cache及TLB信息

实验的源码在\topic04\ex4-6\setup.asm中,下面是在VMware中的运行结果。

上面的信息是在VMware下打印出来的,从这个结果,我们可以看出来以下信息。

处理器的Cache和TLB情况也可以从CPUID.EAX=04H里获得,04H功能号是一个集,以ECX输入一个子叶号,枚举出处理器Cache的所有信息。

首先以ECX=0H子叶开始查询,在eax寄存器返回,我们所关心的是以下几个位域。

EAX[4:0]返回cache的类型,分别是:1=Data cache,2=Instruction cache,3=Unified cache,0值是终止标志,表示已经没有子叶可以枚举了,其他值是保留的。

EAX[7:5]返回cache的level,EAX[31:26]域里也可以查询得到处理器的core数,这个查询结果值需要加上1才是真正的值。

接下来EBX寄存器的返回信息是:EBX[11:00]是line size,EBX[21:12]是physical line partition,EBX[31:22]返回cache的way数。最后ECX寄存器返回32位的cache set数量。

实验4-7:使用CPUID.EAX=04H/ECX=n来查看cache及TLB信息

下面是部分代码片断,完整的源码在topic04\ex4-7\setup.asm里。

代码清单4-4:

@@1:
mov ecx,[sub_leaves]                          ; 子叶号
mov esi,ecx
mov di,value_address
call get_hex_string
mov si,msg
call puts
mov si,value_address
call puts
mov si,msg2
call puts
mov eax,04                                       ; 探测 ECX=n
cpuid
mov [eax_value],eax                           ; 保存各个寄存器的值
mov [ebx_value],ebx
mov [ecx_value],ecx
mov [edx_value],edx
inc dword [sub_leaves]                        ; 下一个子叶号
call print_cache_info                         ; 打印信息
test eax,eax
jnz @@1

从子叶ECX=0H开始进行探测,直至EAX[4:0]为0(表示没有下一个子叶),每一个子叶得到的cache size的计算方法如下。

cache size=line size×line partition×way×set

也就是等于:(EBX[11:00]+1)*(EBX[21:12]+1)*(EBX[31:22]+1)*(ECX+1)。

每一项加上1,是因为返回的信息加上1才是完整的值。这个计算的过程在print_cache_info()过程里,读者可以在topic04\ex4-7\setup.asm源码里看到。下面是在VMware中的运行结果。

对比一下上面的运行结果和使用CPUID.EAX=02H查询到的结果,看是否一致。上面的结果是:

1级Data-cache的size是32KB(值为0x8000),8-way 64 set结构。1级Instruction-cache的size同样是32KB(值为0x8000),4-way 128 set结构。2级cache的size是256KB(值为0x40000),8-way 512 set结构。3级cache的size是3MB(值为0x300000),属于12-way 4096 set结构。

对比结果cache size和cache结构是一样的,在这个例子里没有完整打印详细的信息,读者朋友可以自行加入更完整的显示。这个例子探测到子叶号ECX=03H为止,ECX=04H已经不支持了。

让人感到沮丧的是:在AMD的机器上并不支持EAX=02H到EAX=04H的功能号!

是的,这确实让人反感,在AMD的机器上basic功能号支持EAX=01H、EAX=05H、EAX=06H以及EAX=0DH,这4个功能号。可是AMD比Intel支持更多的extended功能号,根据AMD的CPUID specification指示,最多达到了8000001EH,而Intel只支持到80000008H。

在AMD机器上可以使用80000005H、80000006H,以及8000001DH扩展功能号查询cache和TLB的相关信息。

4.9 MONITOR/MWAIT信息

在CPUID.EAX=01H功能里返回的ECX[3]可以查询处理器是否支持MONITOR与MWAIT指令,MONITOR/MWAIT指令用来监控某个线性地址范围内没发生store操作进入一个被优化的状态。

下面是一个典型的MONITOR/MWAIT指令使用序列。

@Loop:
      bt dword [WorkQueue],0                   ; 测试 WorkQueue == 1 ?
      jnc no_work
      ... ...                                            ; 执行其他调度
      jmp @Loop
;;; 当 WorkQueue == 0 时:
no_work:
      mov eax,WorkQueue                              ; 监控地址
      mov ecx,0                                        ;  0 extensions
      mov edx,0                                        ;  0 hints
      MONITOR                                            ;  进入监控
      bt dword [WorkQueue],0                   ;  测试 WorkQueue
      jc @Loop                                           ; WorkdQueue == 1 时重新循环
      sti                                                 ; 允许被中断事件退出优化的状态
      mov eax,0                                        ; 0 hints
      mov ecx,0                                        ; 0 extensions
      MWAIT                                              ;  wait
      jmp @Loop

monitor/mwait指令的使用情形有些与pause指令类似,在Idle Loop(空闲的循环)中用来提高处理器的性能和减少能耗。pause常用于OS的自旋锁,monitor/mwait对某个地址范围做监控,这个地址范围内没发生store操作就进入优化的状态,由mwait等待某些事件而退出,因地址store操作而退出或者因某些中断类的事件发生。

执行CPUID.05H在EAX[15:00]里返回Smallest monitor-line size,在EBX[15:00]里返回Largest monitor-line size。在MSR IA32_MONITOR_FILTER_LINE_SIZE里可以对这些值进行设置,从而影响CPUID.05H leaf的读取结果。

4.10 处理器的long mode

extended功能号80000001H是一个很重要的CPUID leaf,里面有最为重要的Long Mode标志位,用来查询处理器是否支持long mode,在AMD的机器上80000001H具有很多特性,而Intel的机器上许多为保留位。

最重要的位是EDX[29]返回long mode标志,EDX[11]是SYSCALL/SYSRET指令支持标志位,EDX[26]是1G-page支持位。

mov eax,80000000H
cpuid
cmp eax,80000001H                      ; < 80000001h ?
jb no_support
mov eax,80000001H
cpuid
bt edx,29                                ; 测试是否支持 long mode
jnc no_support
... ...

上面是典型的测试处理器是否支持long mode的代码。