4. Local APIC ID

  • 1. local APIC ID寄存器
  • 2. APIC ID在multi-threading处理器下
    • 2.1. 检测处理器是否支持multi-threading
    • 2.2. multi-threading下的APIC ID
    • 2.3. xAPIC ID
      • 2.3.1. SMT
      • 2.3.2. initial xAPIC ID值的查询
    • 2.4. x2APIC ID
      • 2.4.1. 测试是否支持CPUID 0B leaf
      • 2.4.2. x2APIC ID值的查询
      • 2.4.3. 枚举x2APIC ID的level数
    • 2.5. Intel Hyper-threading技术的处理器
    • 2.6. 支持multi-core和Hyper-threading技术的处理器
    • 2.7. 在multi-core和Hyper-threading技术下的x2APIC ID
  • 3. multi-threading技术的使用
    • 3.1. 收集处理器的Package/Core/SMT ID
    • 3.2. 提取Package/Core/SMT ID值
      • 3.2.1. SMT_MASK_WIDTH与CORE_MASK_WIDTH
      • 3.2.2. SMT_SELECT_MASK与CORE_SELECT_MASK
      • 3.2.3. 32位x2APIC ID的排列规则
    • 3.3. 分解提取3-level结构的x2APIC ID
      • 3.3.1. 枚举MASK_WIDTH值
      • 3.3.2. 计算SMT_SELECT_MASK值
      • 3.3.3. 得到SMT_ID值
      • 3.3.4. 计算CORE_SELECT_MASK值
      • 3.3.5. 得到Core_ID值
      • 3.3.6. 计算PACKAGE_SELECT_MASK值
      • 3.3.7. 得到Package_ID值
    • 3.4. 分解提取8位的xAPIC ID
  • 4. multi-threading处理器编程
    • 4.1. 枚举所有处理器的APIC ID
    • 4.2. BSP与AP处理器
    • 4.3. 枚举所有processor
    • 4.4. 提供start-up代码

无论是在Intel还是AMD平台上,local APIC ID都非常重要。在处理器power-up或reset后,处理器和bus硬件上赋予每个local APIC唯一的initial APIC ID值,这个唯一的APIC ID值基于system bus上的拓扑结构构建,可以使用下面的方法查询这个值。

① 在处理器支持CPUID 0B leaf的情况下,使用CPUID.0B:EDX[31:0]得到32位的APIC ID值

不支持CPUID 0B leaf时,使用CPUID.01:EBX[31:24]得到8位的APIC ID值**。

然后,在处理器power-up或者reset期间,这个initial值被写入到local APIC ID寄存器里(在支持CPUID 0B leaf不支持x2APIC模式时,只保存32位APIC ID值的低8位)。

APIC ID实际上也是每个logical processorsystem bus(或者Pentium和P6处理器使用的APIC bus)上的唯一编号。这个编号可以被使用在处理器间进行通信系统软件可以使用这个编号动态调度任务给某些处理器执行

1. local APIC ID寄存器

local APIC ID寄存器保存着APIC ID值,地址在偏移量20H位置上,如下所示。

config

在P6和Pentium处理器使用的APIC版本里,APIC ID值是4位。Pentium 4以后的处理器上的xAPIC版本APIC ID值是8位x2APIC版本上使用32位的APIC ID值(实际上,在支持CPUID 0B leaf的处理器APIC ID都使用32位的x2APIC ID值!!!)。

在本节后续的内容主要围绕着8位的xAPIC ID32位的x2APIC ID值进行探讨。

2. APIC ID在multi-threading处理器下

Intel的处理器实现了两种技术:

  • 从后期的Pentium 4 处理器开始加入的Hyper-Threading(超线程)技术, 使用逻辑core技术,两个logical processor共用一个physical package(物理处理器)。
  • Core duo处理器开始加入的multi-core(多处理器核心)技术, 使用的是物理core技术,一个physical package上有两个物理core单元

multi-threading技术不单指Hyper-Threading(超线程),也包括了multi-core(多处理器核心)技术。

  • 现在Intel的处理器上的multi-threading实现了Hyper-Threadingmulti-core相结合
  • AMDmulti-threading技术以multi-core形式实现。

两种技术都有的话, 在有cluster的MP系统中, 会有多个cluster, 每个cluster会有多个physical package(物理处理器), 一个physical package会有多个物理processor core单元(2个, multi-core技术), 每个物理core单元内有多个logical processor(逻辑, Hyper-Threading技术, 2个), logical processor就是指SMT单元, 用来执行单元的线程, 拥有自己的资源(处理器的stat信息)和自己的local APIC等.

2.1. 检测处理器是否支持multi-threading

对于处理器是否支持multi-threading技术,软件可以使用CPUID.01:EDX[28]位进行检测,为1时表示支持,如以下代码所示。

代码清单18-9(lib\cpuid.asm):

1
2
3
4
5
6
7
8
9
10
;-----------------------------------------------------
; support_multi_threading():查询是否支持多线程技术
;-----------------------------------------------------
support_multi_threading:
      mov eax,1
      cpuid
      bt edx,28     ; HTT 位
      setc al
      movzx eax,al
      ret

上面这个support_multi_threading()函数用来检测处理器是否支持multi-threading技术,可以使用在Intel和AMD平台上。然而,这里无法得知是支持Hyper-threading还是multi-core技术

2.2. multi-threading下的APIC ID

multi-threading技术下,initial APIC ID通常分成3-level或4-level的拓扑结构,因此一个完整的APIC ID将分为3或4个sub-field(子域),某些处理器的32位x2APIC ID还可以超过4-level,这些具体的sub-field信息需要通过CPUID的相关leaf进行查询。

2.3. xAPIC ID

MP系统里包含cluster时,8位的initial APIC ID被分成4-level结构,最高层为Cluster ID,下面是一个使用cluster bridge连接各个cluster的示意图,每个cluster内有数个physical package

config

没有Cluster的MP系统里,initial xAPIC ID被分成3-level结构

config

如上所示,8位的initital xAPIC ID结构3个子域分别如下。

Package ID:APIC ID最上层是Package ID,在一个MP(multi-processor)系统里,system bus会有多个physical package,每个physical package就是一个物理处理器。APIC ID里的package ID就是用来区分cluster内(!!!)(假如system bus上有cluster)或者system bus上的物理处理器的。

Core ID:Package ID下一层是Core ID,用来区分physical package内的processor core,在一个单核的处理器上,Core ID的值为0

SMT ID:Core ID下一层是SMT ID,每个SMT ID代表一个logical processor,SMT ID用来区分processor core内的logical processor(!!!)。

这些Package ID、Core ID及SMT ID子域的宽度依赖于处理器的实现,每个处理器在power-up时,bus硬件会赋一个initial APIC ID给local APIC,这个initial APIC ID值可以从CPUID指令里查询得到。软件可以使用CPUID的01 leaf和04 leaf查询和枚举initial APIC ID的子域。然而值得注意的是,这个initial APIC ID值可能与通过local APIC ID寄存器查出来的ID值不一样。在某些处理器(!!!)上,local APIC ID寄存器是可写的,BIOS或OS可能会写给local APIC ID寄存器不一样的ID值(当然,改写的可能性极少)。

2.3.1. SMT

SMT(Simultaneous Multi-Threading)直译为同步的多线程,就是指Intel的Hyper-Threading技术,在processor core内实现两个共享物理core执行单元的线程。这些线程单元拥有自已的处理器资源(处理器的state信息!!!),包括有自己的local APIC。logical processor就是指SMT单元

2.3.2. initial xAPIC ID值的查询

显然,system bus 硬件产生的8位的initial xAPIC ID会被保存到local APIC ID寄存器里,软件可以使用CPUID.01:EBX[31:24]查询得到initial xAPIC ID值。

代码清单18-10(lib\apic.asm):

1
2
3
4
5
6
7
8
9
;-------------------------------------
; get_apic_id():得到 initial apic id
;-------------------------------------
get_apic_id:
       mov eax,1
       cpuid
       mov eax,ebx
       shr eax,24
       ret

这个get_apic_id()函数用来获得当前运行代码的处理器的8位的initial xAPIC ID值,在CPUID指令的01 leaf返回的EBX寄存器[31:24]位里。

思考一下,这个initial xAPIC ID值,只会对当前运行的线程(也就是当前logical processor!!!)执行后所返回的。

2.4. x2APIC ID

在处理器支持CPUID 0B leaf或者支持x2APIC模式的情况下,软件可以查询到一个32位的x2APIC ID值,结构如下。

config

x2APIC ID值可能会超过4个子域,依赖于处理器的实现,有多少个子域每个子域的宽度必须使用CPUID指令的0B leaf进行查询和枚举。然而在没有cluster的情况下(cluster为0),处理器还是使用3-level的结构,也就是32位的x2APIC ID依然使用了3-level结构

值得注意的是,CPUID指令的0B leaf不依赖于local APIC的x2APIC模式

不支持x2APIC模式的处理器,并不代表不支持CPUID 0B leaf,因此,在不支持x2APIC模式的处理器上,如果支持CPUID的0B leaf,软件依然可以使用CPUID 0B leaf来查询32位的x2APIC ID值。

CPUID 0B leaf允许处理器查询超过8位的xAPIC ID值(在xAPIC模式下),使用CPUID 01 leaf查询得到的8位xAPIC ID值(由CPUID.01:EBX[31:24]得到)等于使用CPUID 0B leaf查询得到的32位x2APIC ID值的低8位(即CPUID.01:EBX[31:24]=CPUID.0B:EDX[7:0])。

只有在处理器支持x2APIC模式情况下,才可能使用完整的32位x2APIC ID值,操纵超过256个logical处理器

2.4.1. 测试是否支持CPUID 0B leaf

CPUID指令的0B leaf用来查询和枚举处理器扩展的拓扑结构,在近期的处理器上才实现了0B leaf。

代码清单18-11(lib\apic.asm):

1
2
3
4
5
; 测试是否支持 leaf 11
       mov eax,0
       cpuid
       cmp eax,11
       jb extrac_x2apic_id_done    ; 不支持 0B leaf

上面这段代码用来测试是否支持0B leaf,关于测试CPUID指令支持的最大leaf(叶号),请参考4.3节所述。

2.4.2. x2APIC ID值的查询

当处理器支持CPUID 0B leaf时,软件就可以使用CPUID 0B leaf来查询x2APIC ID值,代码如下所示。

代码清单18-12(lib\apic.asm):

1
2
3
4
5
6
7
8
;---------------------------------------
; get_x2apic_id():得到 x2APIC ID
;---------------------------------------
get_x2apic_id:
       mov eax,11
       cpuid
       mov eax,edx   ; 返回 x2APIC ID
       ret

上面的get_x2apic_id()函数用来查询x2APIC ID值,CPUID.0B:EDX[31:0]返回的是当前运行线程(当前运行的logical processor!!!)下的32位x2APIC ID值

2.4.3. 枚举x2APIC ID的level数

前面提及,x2APIC ID的子域(level数)可以使用CPUID 0B leaf来枚举。EAX寄存器输入main-leaf号(即0BH),ECX寄存器输入sub-leaf号枚举,最终的结果值表示在physical package内有多少level。

在第1次发起CPUID 0B leaf查询时,使用EAX=0BH(main-leaf),ECX=0(subleaf),每次递增ECX,直到返回的ECX[15:8]=0为止,它的算法描述如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#define SMT  1
#define CORE  2
#define INVALID 0
int get_x2apic_id_level()
{
   sub_leaf=0;    /* sub-leaf 号 */
   do
   {
       eax=0Bh;    /* EAX=main-leaf */
       ecx=sub_leaf;  /* ECX=sub-leaf */
       cpuid();    /* 执行 CPUID 指令枚举 */
       sub_leaf++;    /* 递增 sub-leaf */
   } while (ecx.level_type != INVALID); /* 返回的 ECX[15:8] 值不等于0时,重复迭代*/
   return ecx.level;      /* 返回的 ECX[7:0] 值就是 level 数 */
}