这一节主要主要是APIC。
所需要的文件在Github:https://github.com/yongkangluo/Ubuntu20.04OS/tree/main/Files/Lec7-ExternalInterrupt
历史方法:PIC(Programmable Interrupt Controller)
Intel 8259:
APIC(Advanced Programmable Interrupt Controller)
APIC是为了多核渠道而规划的。它由两个部分组成IOAPIC和LAPIC,其间IOAPIC一般坐落南桥中用于处理桥上的设备所发生的各种中止,LAPIC则是每个CPU都会有一个。IOAPIC经过APICBUS(现在都是经过FSB/QPI)将中止信息分派给每颗CPU的LAPIC。CPU上的LAPIC可以智能的决议是否承受体系总线上传递过来的中止信息,并且它还可以处理Local端中止的pending、nesting、masking,以及IOAPIC于Local CPU的交互处理。
1. LVT与寄存器
LVT (Local Vector Table)是一组APIC内置寄存器,定义APIC的本地中断至CPU中断向量号的映射。
2. 初始化AIPC
- 禁用中断
- 禁用8259PIC
- 硬启用LAPIC
- 初始化LAPIC
- 设置中断优先级
- 初始化LVT
- 软启用LAPIC
- 初始化I/O APIC
- 映射IRQ至中断
- 打开中断
硬启用Local APIC
movl $0x1B, %ecx
rdmsr
orl $0x800, %eax
wdmsr
中断优先级
只执行 中断号大于 16 * Process-Priority Class + Process-Priority Sub Class 的中断。
初始化LVT
伪中断 :告诉CPU这个中断时误触发的,是噪声。
软启动LVT APIC
初始化I/O APIC
Local APIC 的所有寄存器都是映射在内存 0xFFE00000。
使用ACPI来获取 I/O APIC 的地址。
ACPI(Advanced Configuration and Power Management Interface)
过程是: 通过字头签名找到(Root Description Pointer RSDP),通过RSDP找到RSDT表,找到MADT表,找到I/OAPIC Structure的寄存器地址。
Code Time
init_apic()
{
//关闭外部中断
cpu_disable_interrupt();
assert_msg(cpu_has_apic(), "No APIC detected!");
//关闭PIC
pic_disable();
//全局启用APIC
asm volatile (
"movl %0, %%ecx\n"
"rdmsr\n"
"orl %1, %%eax\n"
"wrmsr\n"
::"i"(IA32_APIC_BASE_MSR), "i"(IA32_APIC_ENABLE)
: "eax", "ecx", "edx"
);
uint32_t apic_id = apic_read_reg(APIC_IDR) >> 24;
uint32_t apic_ver = apic_read_reg(APIC_VER);
kprintf(KINFO "ID: %x, Version: %x, Max LVT: %u\n",
apic_id,
apic_ver & 0xff,
(apic_ver >> 16) & 0xff);
//设置LVT表
apic_setup_lvts();
//设置等级
apic_write_reg(APIC_TPR, APIC_PRIORITY(2, 0));
uint32_t spiv = apic_read_reg(APIC_SPIVR);
spiv = (spiv & ~0xff) | APIC_SPIV_APIC_ENABLE | APIC_SPIV_IV;
apic_write_reg(APIC_SPIVR, spiv);
apic_setup_timer();
}
具体的代码都在github上。