]> Zhao Yanbai Git Server - kernel.git/commitdiff
支持键盘中断通过IO APIC到达CPU
authoracevest <zhaoyanbai@126.com>
Fri, 2 Jan 2026 12:52:21 +0000 (20:52 +0800)
committeracevest <zhaoyanbai@126.com>
Fri, 2 Jan 2026 12:52:21 +0000 (20:52 +0800)
12 files changed:
boot/acpi.c
boot/boot.c
include/apic.h
include/system.h
kernel/apic.c
kernel/i8259.c
kernel/innerint.c
kernel/irq.c
kernel/setup.c
kernel/system.c
kernel/task_disk.c
kernel/task_init.c

index 2fffd0361367dcc3d2a3aebb6e38e9deb337e8c4..43e643f94dbe99d5540ea8d54de523ae0f0fb9be 100644 (file)
@@ -153,7 +153,7 @@ void parse_rsdt(paddr_t addr) {
     for (int i = 0; i < table_count; i++) {
         uint32_t table_phys_addr = rsdt->table_ptrs[i];
         // TODO unioremap
-        printk("ACPI table %u addr %08x\n", i, table_phys_addr);
+        // printk("ACPI table %u addr %08x\n", i, table_phys_addr);
         acpi_sdt_header_t* table = (acpi_sdt_header_t*)(ioremap(PAGE_ALIGN(table_phys_addr), PAGE_SIZE) +
                                                         (table_phys_addr - PAGE_ALIGN(table_phys_addr)));
         if (table == 0) {
@@ -176,6 +176,7 @@ void parse_rsdt(paddr_t addr) {
 
 void init_acpi() {
     parse_rsdt((paddr_t)system.rsdt_addr);
+    // asm("cli;hlt;");
 }
 
 void parse_acpi(void* tag) {
index d30b077f38b22ba0871acf1e994f7c9e2d652c68..9176addfda6c7d62d0da9e2f8184c5ed23be0098 100644 (file)
@@ -342,6 +342,10 @@ void init_system_info() {
 
     printk("kernel [%x, %x] bootmem bitmap: %x\n", system.kernel_begin, system.kernel_end, system.bootmem_bitmap_begin);
 
+    // 先写一个默认值
+    system.ioapic_addr = 0xFEC00000;
+    system.lapic_addr = 0xFEE00000;
+
     printk("bootloader: %s\n", boot_params.bootloader);
     printk("boot device: bios dev %x partition %x sub partition %x\n", boot_params.biosdev, boot_params.partition,
            boot_params.sub_partition);
index 63346f2426872ca8022437487a1941c60501ce6c..54d903533cf09289e35f54430961132a711e3926 100644 (file)
@@ -75,3 +75,51 @@ typedef struct lapic {
 
     uint32_t (*get_lapic_id)();
 } lapic_t;
+
+// IO APIC
+
+// IO APIC IOREGSEL 的索引值
+// bit[23:0] Reserved
+// bit[27:24] IO APIC ID
+// bit[31:28] Reserved
+#define IOAPIC_ID 0x00
+
+// IO APIC 版本寄存器
+// bit[7:0] IO APIC版本
+// bit[15:8] Reserved
+// bit[23:16] 可用RTE数 <- 此值+1代表RTE数
+// bit[31:24] Reserved
+#define IOAPIC_VERSION 0x01
+// 0x02-0x0F Reserved
+#define IOAPIC_REDTBL_BASE 0x10
+// #define IOAPIC_REDTBL00 (IOAPIC_REDTBL_BASE + 0x00 * 0x02)
+// #define IOAPIC_REDTBL01 (IOAPIC_REDTBL_BASE + 0x01 * 0x02)
+// ...
+// #define IOAPIC_REDTBL23 (IOAPIC_REDTBL_BASE + 0x17 * 0x02)
+// 0x40-0xFF Reserved
+
+#define IOAPIC_RTE(n) (IOAPIC_REDTBL_BASE + (n) * 0x02)
+
+// 总共24个Redirection Table
+// 每个2个字节,低字节索引低32位,高字节索引高32位
+// 也就是索引总位宽为64位
+
+// Redirect Table Entry
+// bit[7:0] 中断向量号
+// bit[10:8] 中断投递模式
+// bit[11] 目标模式
+// bit[12] 投递状态
+// bit[13] 电平触发极性
+// bit[14] 远程IRR标志位
+// bit[15] 触发模式
+// bit[16] 屏蔽标志位
+// bit[55:17] Reserved
+// bit[63:56] 中断投递目标
+#define IOAPIC_RTE_MASK 0x00010000
+
+typedef struct ioapic_map {
+    paddr_t phys_base;
+    vaddr_t io_reg_sel;  // 也是 virt_base
+    vaddr_t io_win;
+    vaddr_t eoi;
+} ioapic_map_t;
index bcbb8e1f7ec6e05237b62da07e5e64bea220db88..d5c5ef1d7f8b2c6f0173624a67c801c29c2c7f0c 100644 (file)
@@ -164,6 +164,7 @@ typedef struct system {
 
     //
     paddr_t ioapic_addr;
+    ioapic_map_t* ioapic_map;
 
 #define CMD_LINE_SIZE 128
     const char* cmdline;
@@ -260,6 +261,9 @@ extern volatile int reenter;
 #define DEFAULT_BOOT_DELAY_TICKS 300
 void boot_delay(int ticks);
 
+void io_mfence();
 #endif
 
+#define DISABLE_IDE 1
+
 #endif  //_SYSTEM_H
index 24bdc899b52f0a7cf27477b09f7f36a9f65ce344..206d138e7ffad315a9415fc6aeb92702c94f29ec 100644 (file)
@@ -14,6 +14,8 @@
 #include <apic.h>
 #include <pci.h>
 #include <ioremap.h>
+#include <io.h>
+#include <i8259.h>
 
 static inline uint32_t apic_offset_to_msr(uint32_t offset) {
     return 0x800 + (offset >> 4);
@@ -73,7 +75,7 @@ void lapic_lvt_detect() {
         uint32_t offset;
         char* name;
     } lvt[] = {
-        {LAPIC_LVT_CMCI, "CMCI"},        //
+        // {LAPIC_LVT_CMCI, "CMCI"},        //
         {LAPIC_LVT_TIMER, "TIMER"},      //
         {LAPIC_LVT_THERMAL, "THERMAL"},  //
         {LAPIC_LVT_PERF, "PERF"},        //
@@ -95,6 +97,7 @@ void lapic_init() {
     if (r.edx & (1 << 9)) {
         printk("local apic supported\n");
         if (r.ecx & (1 << 21)) {
+            x2apic = true;
             printk("x2apic supported\n");
         } else {
             panic("x2apic not supported\n");
@@ -119,11 +122,13 @@ void lapic_init() {
     if (x2apic) {
         // 开启2xapic
         apic_base |= (1 << 10);
+        printk("after 2xapic enable apic base: %08x\n", apic_base);
         write_msr(MSR_IA32_APIC_BASE, apic_base);
+        printk("after 2xapic enable apic base: %08x\n", apic_base);
 
         apic_base = read_msr32(MSR_IA32_APIC_BASE);
         assert((apic_base & (1 << 10)) != 0);
-        printk("after 2xapic enable apic base: %016lx\n", apic_base);
+        printk("after 2xapic enable apic base: %08x\n", apic_base);
 
         system.lapic = &x2apic_lapic;
     } else {
@@ -191,6 +196,39 @@ void lapic_init() {
     uint32_t PPR = lapic->read(LAPIC_PPR);
 }
 
+static ioapic_map_t ioapic_map;
+
+uint64_t ioapic_rte_read(uint32_t index) {
+    uint64_t v = 0;
+    *(volatile uint32_t*)ioapic_map.io_reg_sel = index + 1;
+    io_mfence();
+    v = *(volatile uint32_t*)ioapic_map.io_win;
+    io_mfence();
+
+    v <<= 32;
+
+    *(volatile uint32_t*)ioapic_map.io_reg_sel = index + 0;
+    io_mfence();
+    v |= *(volatile uint32_t*)ioapic_map.io_win;
+    io_mfence();
+
+    return v;
+}
+
+void ioapic_rte_write(uint32_t index, uint64_t v) {
+    *(volatile uint32_t*)ioapic_map.io_reg_sel = index + 0;
+    io_mfence();
+    *(volatile uint32_t*)ioapic_map.io_win = v & 0xFFFFFFFF;
+    io_mfence();
+
+    v >>= 32;
+
+    *(volatile uint32_t*)ioapic_map.io_reg_sel = index + 1;
+    io_mfence();
+    *(volatile uint32_t*)ioapic_map.io_win = v & 0xFFFFFFFF;
+    io_mfence();
+}
+
 void ioapic_init() {
     // 先找到RCBA: Root Complex Base Address寄存器
     uint32_t cmd = PCI_CMD(0, 31, 0, 0xF0);
@@ -201,6 +239,39 @@ void ioapic_init() {
         panic("RCBA not enabled\n");
     }
 
+    // 把IO APIC映射进地址空间
+    ioapic_map.phys_base = system.ioapic_addr;
+    ioapic_map.io_reg_sel = (vaddr_t)ioremap(ioapic_map.phys_base, PAGE_SIZE);
+    ioapic_map.io_win = ioapic_map.io_reg_sel + 0x10;
+    ioapic_map.eoi = ioapic_map.io_reg_sel + 0x40;
+    assert(ioapic_map.phys_base != 0);
+    assert(ioapic_map.io_reg_sel != 0);
+    printk("IO-APIC mapped %08x to %08x\n", ioapic_map.phys_base, ioapic_map.io_reg_sel);
+    printk("IO-APIC io_win %08x eoi %08x\n", ioapic_map.io_win, ioapic_map.eoi);
+
+    system.ioapic_map = &ioapic_map;
+
+    // IO APIC ID
+    *(volatile uint32_t*)ioapic_map.io_reg_sel = IOAPIC_ID;
+    io_mfence();
+    uint32_t ioapic_id = *(volatile uint32_t*)ioapic_map.io_win;
+    io_mfence();
+
+    // IO APIC VERSION
+    *(volatile uint32_t*)ioapic_map.io_reg_sel = IOAPIC_VERSION;
+    io_mfence();
+    uint32_t ioapic_version = *(volatile uint32_t*)ioapic_map.io_win;
+    io_mfence();
+
+    int rte_cnt = ((ioapic_version >> 16) & 0xFF) + 1;
+    printk("IO-APIC id %08x version %08x RTE cnt %d\n", ioapic_id, ioapic_version, rte_cnt);
+
+    // 屏蔽所有中断
+    for (int i = 0; i < rte_cnt; i++) {
+        uint32_t irq = 0x20 + i;
+        ioapic_rte_write(IOAPIC_RTE(i), IOAPIC_RTE_MASK | irq);
+    }
+
     // RCBA
     // bit[0]: 使能位
     // bit[13:1]: 保留
@@ -228,9 +299,63 @@ void ioapic_init() {
     printk("OIC: %04x\n", *pOIC);
     *pOIC = *pOIC | (1 << 8);
     printk("OIC: %04x\n", *pOIC);
+    // TODO
+    // iounmap(rcba_virt_base);
+
+    // 打开键盘中断
+    ioapic_rte_write(IOAPIC_RTE(1), 0x21);
 }
 
+void disable_i8259();
 void init_apic() {
+    // mask_i8259();
+    disable_i8259();
+    // imcr_init();
     lapic_init();
     ioapic_init();
 }
+
+// ## 中断路由路径配置矩阵
+
+// | 路径 | IMCR bit0 | 8259A | LAPIC | IOAPIC | 中断引脚连接 |
+// |------|-----------|-------|-------|--------|------------|
+// | **路径A** | 0 (PIC) | 启用 | 禁用 | 禁用/忽略 | INTR直接到CPU |
+// | **路径B** | 1 (APIC) | 启用 | 启用 | 禁用 | INTR到LINT0 |
+// | **路径C** | 1 (APIC) | 启用 | 启用 | 启用 | 8259A到IOAPIC |
+// | **路径D** | 1 (APIC) | 禁用/掩码 | 启用 | 启用 | 直连IOAPIC |
+
+// ### 说明:
+// - **IMCR bit0**: 0 = PIC模式, 1 = APIC模式
+// - **路径A**: 传统PIC模式,用于实模式/早期保护模式
+// - **路径B**: 过渡模式,8259A中断通过LAPIC的LINT0引脚
+// - **路径C**: 兼容模式,现代系统启动时常用
+// - **路径D**: 现代标准模式,性能最优
+
+// A. 8259直接到CPU
+//  实模式/早期保护模式
+//  中断向量表直接指向ISR
+//  8259A通过INTR引脚直接触发CPU中断
+
+// B. 8259 → LAPIC → CPU
+//  本地APIC启用,但IOAPIC未启用
+//  8259A中断通过LAPIC的LINTO引脚
+//  需要配置LAPIC的LVT LINT0/1寄存器
+
+// C. 8259 → IOAPIC → LAPIC → CPU
+//  现代系统兼容模式
+//  8259A中断连接到IOAPIC的IRQ0-15
+//  IOAPIC重定向到LAPIC
+//  需要IMCR寄存器配置
+
+// D. IOAPIC → LAPIC → CPU
+//  纯APIC模式
+//  设备中断直接连接到IOAPIC
+//  IOAPIC重定向到LAPIC
+//  8259A被禁用或掩码
+
+// irq_chip_t apic_chip = {
+//     .name = "APIC",
+//     .enable = enable_apic_irq,
+//     .disable = disable_apic_irq,
+//     .ack = ack_apic_irq,
+// };
index f9ef9f1bef62218b3faf066d17be4c458f648a67..04f2a398afea72623b216881a56a0cd42cce61e0 100644 (file)
@@ -157,3 +157,56 @@ __attribute__((regparm(1))) void boot_irq_handler(pt_regs_t* regs) {
     // 解除屏蔽当前中断
     enable_i8259_irq(irq);
 }
+
+// IMCR
+#define IMCR_ADDR_PORT 0x22
+#define IMCR_DATA_PORT 0x23
+// IMCR寄存器位定义
+#define IMCR_REG_SELECT 0x70  // 选择寄存器70h
+//
+#define IMCR_BIT_APIC 0x01          // bit0: 1=APIC模式, 0=PIC模式
+#define IMCR_BIT_PIC_MASK 0x02      // bit1: 0=启用PIC, 1=禁用PIC
+#define IMCR_BIT_IMCR_PRESENT 0x80  // bit7: 1=IMCR存在, 0=IMCR不存在
+uint8_t imcr_read(uint8_t reg) {
+    outb(IMCR_ADDR_PORT, reg);
+    return inb(IMCR_DATA_PORT);
+}
+void imcr_write(uint8_t reg, uint8_t value) {
+    outb(IMCR_ADDR_PORT, reg);
+    outb(IMCR_DATA_PORT, value);
+}
+
+void imcr_enable_apic_disable_pic() {
+    uint8_t value = imcr_read(IMCR_REG_SELECT);
+    printk("IMCR: %02x\n", value);
+    if ((value & IMCR_BIT_IMCR_PRESENT) == 0) {
+        panic("IMCR not present\n");
+    }
+    imcr_write(IMCR_REG_SELECT, value | IMCR_BIT_APIC | IMCR_BIT_PIC_MASK);
+    value = imcr_read(IMCR_REG_SELECT);
+    printk("IMCR: %02x\n", value);
+
+    if ((value & IMCR_BIT_APIC) == 0) {
+        panic("IMCR not set to APIC mode\n");
+    } else {
+        printk("IMCR set to APIC mode\n");
+    }
+}
+
+void disable_i8259() {
+    mask_i8259();
+
+    imcr_enable_apic_disable_pic();
+
+    // 禁用8259级联
+    outb(0x11, PIC_MASTER_CMD);  // ICW1: 初始化
+    outb(0x20, PIC_MASTER_IMR);  // ICW2: IR0-7 mapped to 0x20-0x27
+    outb(0x04, PIC_MASTER_IMR);  // ICW3: IR2连接从片
+    outb(0x01, PIC_MASTER_IMR);  // ICW4: Normal EOI
+    outb(0x11, PIC_SLAVE_CMD);   // ICW1: 初始化
+    outb(0x28, PIC_SLAVE_IMR);   // ICW2: IR0-7 mapped to 0x28-0x2F
+    outb(0x02, PIC_SLAVE_IMR);   // ICW3: IR2连接从片
+    outb(0x01, PIC_SLAVE_IMR);   // ICW4: Normal EOI
+
+    mask_i8259();
+}
index 76b3b680665c983d412937046b9719855275b016..c3c22d00613534c56baf0a20d216b941bf438a38 100644 (file)
@@ -23,8 +23,7 @@
     do {                                                                                                 \
         printk("Unsupport Now...[%s]\n", __FUNCTION__);                                                  \
         printk("EFLAGS:%08x CS:%02x EIP:%08x ERRCODE:%x", regs.eflags, regs.cs, regs.eip, regs.errcode); \
-        while (1)                                                                                        \
-            ;                                                                                            \
+        asm("cli;hlt;");                                                                                 \
     } while (0);
 
 void doDivideError(pt_regs_t regs) {
@@ -95,7 +94,7 @@ US RW  P - Description
 
     asm("movl %%cr2,%%eax" : "=a"(addr));
 
-    // printk("do page fault errcode %x addr %08x [%08x]\n", errcode, addr, current);
+    printk("do page fault errcode %x addr %08x [%08x] %s\n", errcode, addr, current, current->name);
 
     // assert(errcode != 2 && errcode != 6);
 
index 2eea8a5286ca3d40564d3f48f607094026ca961b..101f64067a26e4e742ad2876df6dfe93a72f6ba6 100644 (file)
@@ -32,6 +32,9 @@ volatile int reenter_count = 0;
 
 volatile uint32_t clk_irq_cnt = 0;
 
+#include <io.h>
+#include <msr.h>
+#include <fixmap.h>
 __attribute__((regparm(1))) void irq_handler(pt_regs_t* regs) {
     assert(current->magic == TASK_MAGIC);
     unsigned int irq = regs->irq;
@@ -39,6 +42,34 @@ __attribute__((regparm(1))) void irq_handler(pt_regs_t* regs) {
         panic("invalid irq %d\n", irq);
     }
 
+    assert(1 == irq);
+    uint8_t b = inb(0x60);
+    printk("irq %d b %02x\n", irq, b);
+    // write_msr32(0x80b, 0);
+    system.lapic->write(0xB0, 0);
+    // vaddr_t apic_virt_base_addr = fixid_to_vaddr(FIX_LAPIC_BASE);
+    // apic_virt_base_addr += 0xB0;
+    // *((volatile uint32_t*)apic_virt_base_addr) = 0x0;
+
+#if 1
+    // 检查IMCR
+    outb(0x22, 0x70);
+    uint8_t imcr = inb(0x23);
+    printk("IMCR: 0x%02x (bit0=%s, bit1=%s)\n", imcr, (imcr & 0x01) ? "APIC" : "PIC",
+           (imcr & 0x02) ? "PIC masked" : "PIC active");
+
+    // 检查8259A状态
+    uint8_t pic1_imr = inb(0x21);
+    uint8_t pic2_imr = inb(0xA1);
+    printk("8259A IMR: Master=0x%02x, Slave=0x%02x\n", pic1_imr, pic2_imr);
+
+    // 检查IOAPIC是否响应
+    volatile uint32_t* ioapic = (volatile uint32_t*)system.ioapic_map->io_reg_sel;
+    ioapic[0] = 0x01;  // 选择版本寄存器
+    uint32_t version = ioapic[4];
+    printk("IOAPIC version: 0x%08x\n", version);
+#endif
+#if 1
     irq_desc_t* p = irq_desc + irq;
     irq_action_t* action = p->action;
 
@@ -51,9 +82,6 @@ __attribute__((regparm(1))) void irq_handler(pt_regs_t* regs) {
 
     // TODO 判断打断的是否是内核态代码
 
-    // 发送EOI
-    p->chip->ack(irq);
-
     // 屏蔽当前中断
     // p->chip->disable(irq);
 
@@ -79,6 +107,10 @@ __attribute__((regparm(1))) void irq_handler(pt_regs_t* regs) {
     // 代表当前中断程序打断了前一个中断程序的“开中断处理的底半部分逻辑”
     // 即前一个中断处理尚未完全完成
     assert(irq_disabled());
+
+    // 发送EOI
+    p->chip->ack(irq);
+
     if (reenter != 0) {
         reenter--;
         return;
@@ -113,6 +145,7 @@ __attribute__((regparm(1))) void irq_handler(pt_regs_t* regs) {
 
     // 如果需要调度程序
     schedule();
+#endif
 }
 
 extern uint32_t jiffies;
index 6cd244ae9fd1bb71f929fb2ee1c60da6f4feb72e..ffb4b3e32886be601f87cbb8d228111c95f8af6c 100644 (file)
@@ -135,9 +135,10 @@ void setup_kernel() {
     setup_i8254(100);
     setup_irqs();
 
+#if !DISABLE_IDE
     void ide_init();
     ide_init();
-
+#endif
     void init_sata();
     init_sata();
 
index f8382e7b4e640cbee09b23954313a176fbbdba68..14bdfd8f5d57dfe50803c6db26af9873b904b948 100644 (file)
@@ -225,3 +225,7 @@ int sysc_reboot(int mode) {
 
     return 0;
 }
+
+void io_mfence() {
+    asm volatile("mfence" ::: "memory");
+}
index 094acd729447e2e31f3ece32ef346c9a6977b029..5623a1ba17f1a0040340fdd3b2264a4974d155cf 100644 (file)
@@ -17,6 +17,9 @@ void ata_pio_read_data(int drvid, int sect_cnt, void* dst);
 void ata_dma_read_ext(int drv, uint64_t pos, uint16_t count, void* dest);
 
 int send_disk_request(disk_request_t* r) {
+#if DISABLE_IDE
+    return 0;
+#endif
     if (NULL == r) {
         panic("null disk request");
     }
index 7b0eae75fbd14167141f9e3430d395943dfa9ba8..2dab08b028d1f2c23d89c24d8d21320aeca975f1 100644 (file)
@@ -174,6 +174,7 @@ void init_task_entry() {
     void init_rootfs();
     init_rootfs();
 
+#if !DISABLE_IDE
 #if 1
     kernel_task("ide/0", disk_task_entry, (void*)0);
 
@@ -183,6 +184,7 @@ void init_task_entry() {
     void ata_read_ext2_sb();
     ata_read_ext2_sb();
 #endif
+#endif
 
 #if 0
     extern int ide_read_partions_done;
@@ -193,7 +195,9 @@ void init_task_entry() {
 #endif
 
 #if 1
+#if !DISABLE_IDE
     kernel_task("ide/1", disk_task_entry, (void*)1);
+#endif
     kernel_task("user", user_task_entry, NULL);
     kernel_task("tskA", taskA_entry, NULL);
     kernel_task("tskB", taskB_entry, NULL);