]> Zhao Yanbai Git Server - kernel.git/commitdiff
初步初始化Local APIC
authoracevest <zhaoyanbai@126.com>
Thu, 1 Jan 2026 14:40:35 +0000 (22:40 +0800)
committeracevest <zhaoyanbai@126.com>
Thu, 1 Jan 2026 14:40:35 +0000 (22:40 +0800)
include/apic.h [new file with mode: 0644]
include/msr.h
include/system.h
kernel/apic.c

diff --git a/include/apic.h b/include/apic.h
new file mode 100644 (file)
index 0000000..63346f2
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * ------------------------------------------------------------------------
+ *   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;
index c4c7debe2db64c6259c902bea01486e73cc0df5b..1a0d14536cb5848ef255ad6ddf6561882f27fab0 100644 (file)
 #define MSR_IA32_PERF_STATUS 0x198
 #define MSR_IA32_PERF_CRTL 0x199
 
-// APIC的ID寄存器
-#define MSR_IA32_X2APIC_APICID 0x802
-// APIC的版本寄存器
-// bit[7:0][VERSION] APIC版本
-//   - 0x0x Intel 82489DX (外部APIC芯片)
-//   - 0x1x Integrated APIC (内部APIC芯片)
-// bit[23:16][MAXLVT] 最大本地向量表
-// bit[24][EOI] 禁止广播EOI消息标志位
-#define MSR_IA32_X2APIC_VERSION 0x803
-
 #define rdmsr(msr, lowval, highval)                             \
     do {                                                        \
         asm("rdmsr;" : "=a"(lowval), "=d"(highval) : "c"(msr)); \
@@ -61,6 +51,15 @@ static inline uint64_t read_msr(uint32_t msr) {
     return ((uint64_t)highval << 32) | lowval;
 }
 
+static inline uint32_t read_msr32(uint32_t msr) {
+    uint32_t lowval = 0;
+    uint32_t highval = 0;
+
+    rdmsr(msr, lowval, highval);
+
+    return lowval;
+}
+
 static inline void write_msr(uint32_t msr, uint64_t value) {
     uint32_t lowval = value & 0xFFFFFFFF;
     uint32_t highval = value >> 32;
@@ -68,4 +67,8 @@ static inline void write_msr(uint32_t msr, uint64_t value) {
     wrmsr(msr, lowval, highval);
 }
 
+static inline void write_msr32(uint32_t msr, uint32_t value) {
+    wrmsr(msr, value, 0);
+}
+
 #endif  //_MSR_H
index 67f8e299c1dd275cd059c17cda5014e27e3dc33a..9502b9eee46d8e292db7f8c2e1e4f58fbc735b70 100644 (file)
@@ -42,6 +42,7 @@
 #ifndef ASM
 #include "printk.h"
 #include "types.h"
+#include <apic.h>
 
 #define likely(x) __builtin_expect(!!(x), 1)
 #define unlikely(x) __builtin_expect(!!(x), 0)
@@ -157,7 +158,9 @@ typedef struct system {
 
     dev_t root_dev;
 
-    bool x2apic;
+    // 按理这个信息应该按CPU存储,简化实现
+    lapic_t* lapic;
+
 #define CMD_LINE_SIZE 128
     const char* cmdline;
 
index 8e3f3dca654873cc06f88b5bda3c3d50a7b2ce57..153b17960257686447b26b4a5ccf95926beaddb0 100644 (file)
 #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();
 }