--- /dev/null
+/*
+ * ------------------------------------------------------------------------
+ * File Name: apic.h
+ * Author: Zhao Yanbai
+ * 2026-01-01 16:08:19 Thursday CST
+ * Description: none
+ * ------------------------------------------------------------------------
+ */
+
+#pragma once
+
+// APIC的ID寄存器
+// 在x2apic上,LAPIC_ID是32位
+// 在Pentium 4和Xeon上,LAPIC_ID是8位,位置在[31:24]
+// 早期Pentium和P6家族上,LAPIC_ID是4位,位置在[27:24]
+#define LAPIC_ID 0x20
+
+// APIC的版本寄存器
+// bit[7:0][VERSION] APIC版本
+// - 0x0x Intel 82489DX (外部APIC芯片)
+// - 0x1x Integrated APIC (内部APIC芯片)
+// bit[23:16][MAXLVT] 最大本地向量表 <- 此值+1代表LVT表项数
+// bit[24][EOI] 禁止广播EOI消息标志位
+#define LAPIC_VERSION 0x30
+
+#define LAPIC_TPR 0x80
+#define LAPIC_APR 0x90
+#define LAPIC_PPR 0xA0
+#define LAPIC_EOI 0xB0
+#define LAPIC_LDR 0xD0
+#define LAPIC_DFR 0xE0
+
+// 伪中断向量寄存器
+// Spurious Interrupt Vector Register
+// bit[7:0][VECTOR] 伪中断向量
+// bit[8][Enable] 1启用LAPIC; 0禁用LAPIC
+// bit[11:9][Reserved]
+// bit[12][Suppress EOI Broadcast] 1禁用EOI广播; 0启用EOI广播
+// bit[15:13][Reserved]
+// bit[31:16][Reserved]
+#define LAPIC_SVR 0xF0
+
+// 以下三个寄存器都是256位,按如下偏移访问
+// 0x00 [031:000]
+// 0x10 [063:032]
+// 0x20 [095:064]
+// 0x30 [127:096]
+// 0x40 [159:128]
+// 0x50 [191:160]
+// 0x60 [223:192]
+// 0x70 [255:224]
+#define LAPIC_ISR 0x100
+#define LAPIC_TMR 0x180
+#define LAPIC_IRR 0x200
+
+#define LAPIC_ESR 0x280
+#define LAPIC_LVT_CMCI 0x2F0
+#define LAPIC_ICR_LOW 0x300
+#define LAPIC_ICR_HIGH 0x310
+#define LAPIC_LVT_TIMER 0x320
+#define LAPIC_LVT_THERMAL 0x330
+#define LAPIC_LVT_PERF 0x340
+#define LAPIC_LVT_LINT0 0x350
+#define LAPIC_LVT_LINT1 0x360
+#define LAPIC_LVT_ERROR 0x370
+#define LAPIC_TIMER_INITIAL 0x380
+#define LAPIC_TIMER_COUNTER 0x390
+#define LAPIC_TIMER_DIVIDE 0x3E0
+
+typedef struct lapic {
+ const char* name;
+
+ uint32_t (*read)(uint32_t reg_offset);
+ void (*write)(uint32_t reg_offset, uint32_t value);
+
+ uint32_t (*get_lapic_id)();
+} lapic_t;
#include <cpuid.h>
#include <system.h>
#include <fixmap.h>
+#include <apic.h>
-void lapic_init() {
- system.x2apic = false;
+static inline uint32_t apic_offset_to_msr(uint32_t offset) {
+ return 0x800 + (offset >> 4);
+}
+
+uint32_t apic_read_lapic(uint32_t offset) {
+ assert(offset < PAGE_SIZE);
+ uint8_t* base = (uint8_t*)fixid_to_vaddr(FIX_LAPIC_BASE);
+ return *(uint32_t*)(base + offset);
+}
+
+void apic_write_lapic(uint32_t offset, uint32_t value) {
+ assert(offset < PAGE_SIZE);
+ uint8_t* base = (uint8_t*)fixid_to_vaddr(FIX_LAPIC_BASE);
+ *(uint32_t*)(base + offset) = value;
+}
+
+uint32_t apic_get_lapic_id() {
+ // return lapic_read(LAPIC_ID) >> 24;
+ return apic_read_lapic(LAPIC_ID) >> 24;
+}
+
+static lapic_t apic_lapic = {
+ .name = "apic - lapic",
+ .read = apic_read_lapic,
+ .write = apic_write_lapic,
+ .get_lapic_id = apic_get_lapic_id,
+};
+
+uint32_t x2apic_read_lapic(uint32_t offset) {
+ assert(offset < PAGE_SIZE);
+ uint32_t msr = apic_offset_to_msr(offset);
+ return read_msr32(msr);
+}
+
+void x2apic_write_lapic(uint32_t offset, uint32_t value) {
+ assert(offset < PAGE_SIZE);
+ uint32_t msr = apic_offset_to_msr(offset);
+ write_msr32(msr, value);
+}
+
+uint32_t x2apic_get_lapic_id() {
+ uint32_t msr = apic_offset_to_msr(LAPIC_ID);
+ return read_msr32(msr);
+}
+
+static lapic_t x2apic_lapic = {
+ .name = "x2apic - lapic",
+ .read = x2apic_read_lapic,
+ .write = x2apic_write_lapic,
+ .get_lapic_id = x2apic_get_lapic_id,
+};
+
+void lapic_lvt_detect() {
+ lapic_t* lapic = system.lapic;
+ struct {
+ uint32_t offset;
+ char* name;
+ } lvt[] = {
+ {LAPIC_LVT_CMCI, "CMCI"}, //
+ {LAPIC_LVT_TIMER, "TIMER"}, //
+ {LAPIC_LVT_THERMAL, "THERMAL"}, //
+ {LAPIC_LVT_PERF, "PERF"}, //
+ {LAPIC_LVT_LINT0, "LINT0"}, //
+ {LAPIC_LVT_LINT1, "LINT1"}, //
+ {LAPIC_LVT_ERROR, "ERROR"}, //
+ };
+
+ for (int i = 0; i < sizeof(lvt) / sizeof(lvt[0]); i++) {
+ uint32_t lvt_value = lapic->read(lvt[i].offset);
+ printk("LVT[%d] %s: %08x\n", i, lvt[i].name, lvt_value);
+ }
+}
+void lapic_init() {
+ bool x2apic = false;
cpuid_regs_t r;
r = cpuid(1);
if (r.edx & (1 << 9)) {
printk("local apic supported\n");
if (r.ecx & (1 << 21)) {
printk("x2apic supported\n");
- system.x2apic = true;
} else {
- printk("x2apic not supported\n");
+ panic("x2apic not supported\n");
}
} else {
panic("local apic not supported\n");
}
- uint64_t apic_base = read_msr(MSR_IA32_APIC_BASE);
- printk("apic base: %016lx\n", apic_base);
+ uint32_t apic_base = read_msr32(MSR_IA32_APIC_BASE);
+ printk("LAPIC BASE: %08x\n", apic_base);
- // apic 必然已经开启q
+ // apic 必然已经开启
assert((apic_base & (1 << 11)) != 0);
- // 开启2xapic
- apic_base |= (1 << 10);
- write_msr(MSR_IA32_APIC_BASE, apic_base);
+ // 映射LAPIC到内核空间
+ uint32_t apic_phys_base_addr = (uint32_t)apic_base & PAGE_MASK;
+ set_fixmap(FIX_LAPIC_BASE, apic_phys_base_addr);
+ //
+ vaddr_t apic_virt_base_addr = fixid_to_vaddr(FIX_LAPIC_BASE);
+ printk("LAPIC base %08x mapped to %08x\n", apic_phys_base_addr, apic_virt_base_addr);
- apic_base = read_msr(MSR_IA32_APIC_BASE);
- printk("after 2xapic enable apic base: %016lx\n", apic_base);
+ if (x2apic) {
+ // 开启2xapic
+ apic_base |= (1 << 10);
+ write_msr(MSR_IA32_APIC_BASE, apic_base);
- uint64_t apic_version = read_msr(MSR_IA32_X2APIC_VERSION);
- printk("apic version: %08lx\n", apic_version);
+ apic_base = read_msr32(MSR_IA32_APIC_BASE);
+ assert((apic_base & (1 << 10)) != 0);
+ printk("after 2xapic enable apic base: %016lx\n", apic_base);
- uint32_t apic_phys_base_addr = (uint32_t)apic_base & PAGE_MASK;
- set_fixmap(FIX_LAPIC_BASE, apic_phys_base_addr);
+ system.lapic = &x2apic_lapic;
+ } else {
+ system.lapic = &apic_lapic;
+ }
- vaddr_t apic_virt_base_addr = fixid_to_vaddr(FIX_LAPIC_BASE);
+ lapic_t* lapic = system.lapic;
- printk("LAPIC base %08x mapped to %08x\n", apic_phys_base_addr, apic_virt_base_addr);
+ uint32_t version = lapic->read(LAPIC_VERSION);
+ uint32_t id = lapic->get_lapic_id();
+ uint32_t lvt_cnt = ((version >> 16) & 0xff) + 1;
- {
- volatile uint32_t* base = (volatile uint32_t*)apic_virt_base_addr;
- uint32_t id = base[0x20 / 4]; // APIC ID 偏移
- uint32_t version = base[0x30 / 4];
- printk("APIC id %08x version %08x\n", id, version);
+ printk("LAPIC id %08x version %08x lvt_cnt %d\n", id, version, lvt_cnt);
+ if ((version & 0xFF) < 0x10) {
+ printk(" Intel 82489DX APIC\n");
+ } else {
+ printk(" Integrated APIC\n");
}
+
+ // 在LAPIC_SVR中开启LAPIC同时禁用EOI广播
+ uint32_t lapic_svr = lapic->read(LAPIC_SVR);
+ printk("LAPIC_SVR: %08x\n", lapic_svr);
+ lapic_svr |= (1 << 8); // 启用LAPIC
+ lapic_svr &= ~(1 << 12); // 禁用EOI广播
+
+ lapic->write(LAPIC_SVR, lapic_svr);
+
+ lapic_svr = lapic->read(LAPIC_SVR);
+ printk("LAPIC_SVR: %08x\n", lapic_svr);
+ assert((lapic_svr & (1 << 8)) != 0);
+ assert((lapic_svr & (1 << 12)) == 0);
+ printk("LAPIC enabled and EOI broadcast disabled\n");
+
+ lapic_lvt_detect();
+
+ // 屏蔽LVT所有中断功能
+ uint32_t lvt_mask = 0x10000;
+ // lapic->write(LAPIC_LVT_CMCI, lvt_mask);
+ lapic->write(LAPIC_LVT_TIMER, lvt_mask);
+ lapic->write(LAPIC_LVT_THERMAL, lvt_mask);
+ lapic->write(LAPIC_LVT_PERF, lvt_mask);
+ lapic->write(LAPIC_LVT_LINT0, lvt_mask);
+ lapic->write(LAPIC_LVT_LINT1, lvt_mask);
+ lapic->write(LAPIC_LVT_ERROR, lvt_mask);
+
+ // lapic_lvt_detect();
+
+ // TPR: Task Priority Register
+ // bit[7:4]: 任务优先级
+ // bit[3:0]: 任务子优先级,新版本处理要求为0
+ lapic->write(LAPIC_TPR, 0);
+ uint32_t TPR = lapic->read(LAPIC_TPR);
+
+ // PPR: Processor Priority Register
+ // bit[7:4]: 处理器优先级
+ // bit[3:0]: 处理器子优先级
+ // 只读寄存器
+ // PPR是TPR和ISRV相比较得到的。ISRV代表ISR(In-Service Register共256bit)寄存器当前服务的最高优先级中断的向量号
+ // PPR = max(TPR, (ISRV != 0) ? (ISRV & 0xF0) : 0)
+ // 这里的含义是: 处理器当前的优先级水平(PPR)取软件设置的任务优先级(TPR)
+ // 和当前正在服务的中断的优先级(ISRV的高4位) 中的较大者
+ // 当一个高优先级中断正在被服务时(ISRV值大),即使TPR设置得很低,PPR也会保持高位,从而阻止同级或更低级的中断嵌套。
+ // TPR是软件的“计划”,ISR是硬件的“现状”,而PPR是综合二者得出的“当前执行标准”。
+ // 内核只能通过设置TPR来影响PPR
+ uint32_t PPR = lapic->read(LAPIC_PPR);
}
void init_apic() {
- lapic_init();
+ ioapic_init();
}