汇编学习笔记(26) - APIC

Local APIC

Local APIC 是在CPU内部的,每个逻辑处理核心都配有一的对应的local APIC。
Local APIC 能产生、发送和接受中断,CPU之间的通讯IPI也是通过Local APIC来实现的。
Local APIC 使用一组寄存器来控制,之前的APIC 是将寄存器映射到内存来使用,新的x2APIC模式则是将寄存器映射到MSR空间中了,适应RDMSR和WRMSR指令进行读写了。
Local APIC 有地址编号,这个编号同时也是CPU核心的编号。
在 xAPIC 模式下,APIC寄存器是映射到一块连续的4K内存中的,由 IA32_APIC_BASE (0x1B)这个MSR寄存器提供控制。
在x2APIC模式下,所有寄存器全被映射到了MSR空间中,地址从802H 到 83FH。

IA32_APIC_BASE提供了一些基本的控制

 

 

 


第8位: 是否是主核心BSP标志位
第10位:是否启用x2APIC模式
第11位:APIC的global启用,虽然是global但是只是正对本核心
第12开始:是xAPIC模式的时候的APIC_BASE的地址,具体是几位取决于地址线的宽度,这是物理地址

APIC寄存器参考x86/x64体系探索及编程 page. 606
以下介绍几个重要的寄存器

APIC ID寄存器(APIC_BASE+ 20H)
  APIC 是根据 Cluster、Package、Core、SMT(逻辑运算单元)多级来组织的,每一级占一定的位数,所以实际上每个逻辑运算单元的编号就不连续了
  xAPIC 中是没有Cluster这个层级的并且APIC ID只有8位有效,是数据的最高8位,而x2APIC则是32位都有效的。CLuster应该是对应的多CPU多主板
  具体是几级,每一级别的位宽都需要通过CPUID指令来获取。

  

 

 

 

APIC 版本寄存器(APIC_BASE+30H)

 

  version:指示Local APIC使用外部8249DX芯片还是内部APIC
  Max LVT Entry: 则是LVT寄存器的数量,这个值需要+1才是真实数量
  24位:SVR寄存器的Bit12位是否支持(bit12 被置位将抑制EOI命令被发送到IO APIC)

LVT Entry寄存器组
  一般是7个LVT 寄存器,用于对7种中断的配置,根据不同的中断有不同位的解释也不太同,但是他们都提供了一个中断向量值用于指示当本中断对应的IDT中断向量。
  

  Timer mode: 值用于Timer 用于设置时间计数的模式
    00: 一次性计数
    01: 周期计数
    10: tsc指定计数

  mask: 屏蔽位,当置位的时候屏蔽本中断

  Trigger mode:仅用于LINT0和LINT1寄存器,设置他们的触发模式
    0: edge触发
    1: level触发

  Remote IRR flag: 仅用于LINT0和LINT1寄存器,只有在delivery mode = fixed 并且 Trigger mode = level时有效
    1: 表示Local APIC 已经接受并处理 本中断
    0: 表示接受到EOI命令

  Interrupt input pin polary: 仅用于LINT0和LINT1寄存器,设置Trigger mode = level时到触发条件
    0: high-level,低电平转高电平触发
    1: low-level,高电平转低电平触发

  Delivery status:指定交付的状态
    0: IDLE 当前没有待提交给CPU的终端,可能是没中中断,也可能是中断已经提交给CPU,CPU还在处理。
    1: Send Pending 当前有中断要需要提交给CPU

  Delivery mode:配置中断的交付模式
    000: Fixed,允许vector里提供自定义的vector值
    010: SMI,vector的值必须为0
    100: NMI,vector被忽略,强制认为值是 2
    111: ExtInt,使用外部中断器发送过来的vector值
    101: INIT,vectort的值必须是0

  vector: 指定本中断的在IDT的中断向量,如果不是交付模式配置,则0-15是无效内容

ICR(APIC_LOCA + 300H / 310H)
  因为ICR寄存器是64位的,所以分为高低两部分占两个地址。
  这个寄存器是用来发送核心间通讯指令 IPI的
  

 

 

 

  Destination Field: 配合 Destination ShortHand 来指定发送的目标

  Destination ShortHand:
    00: No ShortHand,用户指定发送的目标
    01: Self ,发送给自己
    10: All Includeing Self, 发送给所有核心包括自己
    11: All Exclude Self,发送给出了自己以外的所有核心

  Trigger Mode: 同LVT的解释,设置他们的触发模式
    0: Edge
    1: Level

  Level:
    0: De-assert
    1: Assert

  Delivery Status: 同LVT的解释,指定交付的状态
    0: IDLE,当前没有待提交给CPU的终端,可能是没中中断,也可能是中断已经提交给CPU,CPU还在处理。
    1: end Pending, 当前有中断要需要提交给CPU

  Destination Mode: 当使用 No ShortHand 的时候,指示如何解释 Destination Field 指定的目标
    0: Physical, 物理ID,就是APIC ID
    1: Logincal,逻辑ID,这个逻辑ID可以在LDR和DFR寄存器中由软件指定

  Delivery Mode: 配置中断的交付模式
    000: Fixed,允许vector使用自定义的值
    001: Lowest Priority,目标核心运行中断的时候将运行在较低的特权级,允许vector使用自定义的值
    010: SMI, vector的值必须为0
    011: Reserved
    100: NMI NMI,vector被忽略,强制认为值是 2
    101: INIT,初始化核心,初始化完的核心将运行在实模式
    110: Start up,启动核心(SPIPI),当处理完INIT中断之后,核心处于Wait-For-SIPI状态,使用这个模式将指示核心从 Vector * 100H的物理内存地址执行Start-up代码,进行核心的启动。
    111: Reserved

  Vector: 指定目标核心接收到本IPI中断的时候执行IDT中的哪个中断,当IPI是SIPI即 Delivery Mode = Start up 的时候 Vector指定的是要执行的代码的物理地址,实际地址需要vector * 100H(这是由于在Start up执行之前,核心刚初始化完成,还是实模式,并没有IDT,所以直接执行指定位置的代码),如果不是交付模式配置,则0-15是无效内容

优先级
  优先级是根据vector的大小来决定的,vector的越大优先级越高,同时vector被分为2级,前四位制定优先级的成为中断优先等级,在同一等级之下在根据后四位的值来确定优先级。
  同时还又一个TPR寄存器用来屏蔽中断,之后vector大于这个值的中断才能被相应,当然实际能不能相应还要看ISR寄存器也就是当前正在相应的中断的等级。
  在64位模式下CR8 = TPR寄存器,写CR8寄存器会将值同步到TPR寄存器中

  IPI的工作机制
    IPI 是核心之间的通讯机制,也是采用中断的形式的
    1. 首先使用通过 Destination ShortHand 来指定确定目标的方式,除了No ShortHand以外,目标都是固定的
    2. 如果采用了 No ShortHand 模式,则是要使用Destination Field的值确定目标核心
    3. 然后根据 Destination Mode 来解释 Destination Field的值
      Physical: 说明Field的值就是 APIC ID
      Logincal:Destinal Field 就是一个MASK值
        在LDR寄存器的23-31位可以给这个核心定义个逻辑ID,当使用Logical模式的时候 就用Destianal Field中的MASK和LDR中的逻辑ID进行匹配,如果匹配成功就表明这是核心是目标核心
        匹配的规则则由DFR寄存器的 28-31位给出
        1111B: 与匹配,MASK和LDR进行AND匹配,结果为TRUE表示匹配,这表示可以多匹配
        0000B:等于匹配:MASK = ID 匹配

  多核心的初始化工作
    当CPU启动的时候会优先执行CPU内部的代码,他的主要作用是枚举出所有的核心并给这些核心分配APIC ID号,同时会推举一个核心作为主核心成为BSP,并将BSP核心的IA32_APIC_BASE寄存器的第八位置1 ,其余核心称为AP,并清理AP处理器IA32_APIC_BASE的第8位,然后与BSP处理器开始执行BIOS的代码。
    当我们结果BSP处理的控制权以后,一般要执行以下步骤
    1. 初始化BSP寄存器使其进入保护模式
    2. 通过ICR寄存器 发送 Delivery Mod = INIT,Destination ShortHand = 11(All Exclude Self) 的INIT广播指令
    3. 延迟至少10MS 后 发送两次 Delivery Mod = Start Up,Destination ShortHand = 11(All Exclude Self)(SIPI)指令,这两个指令要间隔10US发送,内容是一样的,其中vector 需要指定在一个用于AP初始化时运行的代码地址,这段代码需要在BSP初始化的时候就配置好
    4. 接下来就是AP之行初始化代码,AP初始化的主要工作就是也将之际进入保护模式,这里有两种策略,可以所有核心共用页表,IDT,GDT等,这样AP的初始化就是简单的给个寄存器赋值,当然也可以各核心设置各自的数据,这样就要求各AP进行完整的初始化,
      注意: 由于消息时广播的,所以核心的执行顺序是不一定,这里要处理号数据的同步问题,可以使用带LOCK的汇编之行来进行同步操作
    5. BSP等待所有AP初始化完成之后CPU的初始化就完成了。

  中断的处理
    APIC的LVT寄存器能产生中断,其中LVT LINT0 能接受8259控制器的中断请求,而LVT LINT1能接受NMI中断请求。
    所以中断有本地中断和来自system bus上的中断两种。根据这两种来源的不同有不同的处理流程
    

  Fixed 模式下的流程:

    IRR,ISR,TMR(tigger Mode Register) 都是255位了,所以可以记录所有255个中断的状态
    1. 当中断发生的时候会设置IRR寄存器的相应位
    2. 判断MASK屏蔽位
    3. 判断优先级
    4. 清理IRR位置ISR位提交CPU运行
    5. 发送EOI清理ISR相应位。

  其他模式流程
    其他模式是不判断优先级也不设置IRR和ISR寄存器的,也不用发送EOI命令
    本地:判断MSAK标志
    SYSTEM BUS:啥都不判断直接执行
  注意:他们都能被标志寄存器的IF标志位屏蔽


  LVT TIME / LVT ERROR page.663


  LVT LINT0 / LINT1
    这两个寄存器连接了8259和NMI口,实际上是只链接了BSP芯片,所以只有BSP芯片才能收到这两个中断。
    触发模式只有在使用fixed模式时能设置0:edge 1:level,其余模式默认使用edge模式触发。
    Remote IRR 也只有Fixed模式并且触发模式时:level时可用,用来指示 中断服务是否运行中,ISR时是否提交。
    一般lint0 使用 ExtINT模式。LINT1使用NMI模式。

I/O APIC

  8259已经不适合多核的处理器了,所以I/O APIC 取代了8259,I/O APIC能发送指定的中断到指定的核心上(通过核心的LOCAL APIC)。

  直接访问的寄存器
    寄存器映射到了内存地址空间上
    访问方式通过向index寄存器写入要访问的地址,然后通过data寄存器获取值,和IO端口的操作差不多
    

    其中的xx是可变的地址,这个xx记录在OIC寄存器中
    

    OIC寄存器的地址又位于RCBA寄存器的31FEh位置,而RCBA(root complex base address)寄存器又位于PCI BUS的device 31 设备的0f0偏移处。(关于PCI设备看 下一章节)

  间接访问的寄存器
    间接访问的寄存器就是通过上面直接访问的寄存器的 index 和 data访问的寄存器,一共三组
    

    1. ID寄存器,用于唯一标识一个IO APIC,IO APIC可以存在多个?,有一张叫MADT的配置表,这里面有详细的关系IO APIC的信息,包括多少个IO APIC和对应的直接访问寄存器的地址,参见文末的文献

    

    2. version寄存器,主要确定了硬件的连接方式

    

 

 

    3. 24个 中断转发寄存器,interrupt redirection table 寄存器
    

    他是local APIC中的LVT和ICR寄存器的结合,他首先和LVT寄存器一样配置了一些中断触发时的配置信息,然后每个中断转发寄存器都和一条IRQ线相连,当对应的IRQ线触发中断的时候,就会向ICR寄存器一样,将对应的信息发送到对应的核心处,其中IRQ0 是连接着8259芯片的,所以irq0可以使用ExtInt模式,直接使用8259提交的Vector。


  中断过程
    IO APIC对中断的仲裁 是有Local APIC来完成的,IO APIC将中断提交到Local APIC后他的工作就结束了。所以IO APIC的捉妖责任就是当IRQ先触发中断,IO APIC根据对应的interrupt redirection table 的配置向指定的LOCAL APIC提交中断,之后就全是LOCAL APIC的工作了。


其他文献
https://wiki.osdev.org/IOAPIC
https://wiki.osdev.org/MADT
https://blog.csdn.net/gaojy19881225/article/details/80018761
http://blog.chinaunix.net/uid-20499746-id-786.html

 

posted @ 2021-03-13 19:36  蹦蹦骑士  阅读(2655)  评论(0编辑  收藏  举报