]> Zhao Yanbai Git Server - kernel.git/commitdiff
HPET#0初步路由到CPU0的64号中断[中断向量64+32=96]
authoracevest <zhaoyanbai@126.com>
Wed, 7 Jan 2026 02:45:11 +0000 (10:45 +0800)
committeracevest <zhaoyanbai@126.com>
Wed, 7 Jan 2026 02:45:11 +0000 (10:45 +0800)
drivers/keyboard.c
include/apic.h
include/linkage.h
include/printk.h
include/system.h
kernel/apic.c
kernel/clock.c
kernel/interrupts.S
kernel/system.c

index 48690fc3d9c517bf3e056607fb8007edab1045bf..2a3fbb1a5e1dd862247969b65659c60d4a20c7bf 100644 (file)
@@ -75,7 +75,7 @@ extern void tty_switch_to_next();
 void kbd_debug(uint8_t scan_code) {
     static unsigned long kbd_cnt = 0;
     // printl(MPL_KEYBOARD, "keyboard irq: %d scan code %02x", kbd_cnt++, scan_code);
-    printlxy(MPL_IRQ, MPO_KEYBOARD, "KBD irq: %d %02x", kbd_cnt++, scan_code);
+    printlxy(MPL_IRQ, MPO_KEYBOARD, "KBD: %02x %d", scan_code, kbd_cnt++);
 
     if (scan_code == 0x01) {  // Esc
         // reboot();
index 0323ccfaebaa1d58d3c2a3cf92a80e2ea681b612..c023249750a63b4cdc8915f088e6e88d7125bfd5 100644 (file)
@@ -9,6 +9,8 @@
 
 #pragma once
 
+#include <linkage.h>
+
 #define LAPIC_MSR_BASE 0x800
 
 // APIC的ID寄存器
@@ -141,3 +143,80 @@ typedef struct ioapic_map {
     vaddr_t io_win;
     vaddr_t eoi;
 } ioapic_map_t;
+
+#define IOAPIC_DELIVERY_MODE_FIXED (0b000)
+#define IOAPIC_DELIVERY_MODE_LOWEST_PRIORITY (0b001)
+#define IOAPIC_DELIVERY_MODE_SMI (0b010)
+#define IOAPIC_DELIVERY_MODE_NMI (0b100)
+#define IOAPIC_DELIVERY_MODE_INIT (0b101)
+#define IOAPIC_DELIVERY_MODE_EXTINT (0b111)
+
+#define IOAPIC_PHYSICAL_DESTINATION (0b0)
+#define IOAPIC_LOGIC_DESTINATION (0b1)
+
+#define IOAPIC_DELIVERY_STATUS_IDLE (0b0)
+#define IOAPIC_DELIVERY_STATUS_SEND_PENDING (0b1)
+
+#define IOAPIC_POLARITY_ACTIVE_HIGH (0b0)
+#define IOAPIC_POLARITY_ACTIVE_LOW (0b1)
+
+#define IOAPIC_TRIGGER_MODE_EDGE (0b0)
+#define IOAPIC_TRIGGER_MODE_LEVEL (0b1)
+
+#define IOAPIC_INT_MASKED (0b1)
+#define IOAPIC_INT_UNMASKED (0b0)
+
+#define IOAPIC_32BIT_TIMER (0b0)
+#define IOAPIC_64BIT_TIMER (0b1)
+
+union ioapic_rte {
+    uint64_t value;
+    struct {
+        uint64_t vector : 8;
+        uint64_t delivery_mode : 3;
+        uint64_t destination_mode : 1;
+        uint64_t delivery_status : 1;
+        uint64_t polarity : 1;
+        uint64_t remote_irr : 1;  // read only
+        uint64_t trigger_mode : 1;
+        uint64_t mask : 1;
+        uint64_t reserved : 39;
+        uint64_t destination : 8;
+    };
+} PACKED;
+
+typedef union ioapic_rte ioapic_rte_t;
+
+union hpet_timn_conf_cap {
+    uint64_t value;
+    struct {
+        // BYTE 1
+        uint64_t reserved0 : 1;     // bit0
+        uint64_t trigger_mode : 1;  // bit1
+        uint64_t enable_int : 1;    // bit2
+        uint64_t type : 1;          // bit3
+        uint64_t periodic : 1;      // bit4 read only
+        uint64_t bit_size : 1;      // bit5 read only 0 32位位宽 1 64位位宽
+        uint64_t val_set : 1;       // bit6 标志位只对处于周期定时模式下的定时器0起作⽤
+                                    // 置位此标志位可使软件在定时器运⾏时直接修改定时值。
+        //  bit7
+        uint64_t reserved1 : 1;
+
+        // >>> bin(0x174C)
+        //  '0b1 0111 0100 1100'
+        // >>> bin(0x2E7C)
+        // '0b10 1110 0111 1100'
+        // BYTE 2
+        uint64_t counter_bit_size : 1;
+        uint64_t int_route : 5;
+        uint64_t fsb_en : 1;
+        uint64_t fsb_delivery_status : 1;  // 15 read only
+
+        // BYTE 3~4
+        uint64_t reserved2 : 16;
+
+        // BYTE 5~8
+        uint64_t int_route_cap : 32;  // read only
+    };
+} PACKED;
+typedef union hpet_timn_conf_cap hpet_timn_conf_cap_t;
index 134d6a94dc831f49e3529d7619cbb64fc2972fb9..f59878d4602543d76fdf16b690e659d31e4a6420 100644 (file)
@@ -33,3 +33,5 @@
 
 #define ALIGN8 __attribute__((__aligned__(8)))
 #define ALIGN4 __attribute__((__aligned__(4)))
+
+#define PACKED __attribute__((__packed__))
index f34b2c69749c44b0da947d8699f3ea79d19837f4..0fbe477fb7ea7f783108fb8c90c32abccf8f7e17 100644 (file)
@@ -50,7 +50,8 @@ enum {
 // monitor print offset
 enum {
     MPO_CLOCK = 1,
-    MPO_KEYBOARD = 50,
+    MPO_HPET = 28,
+    MPO_KEYBOARD = 48,
     MPO_IDE = 1,
 };
 
index d5c5ef1d7f8b2c6f0173624a67c801c29c2c7f0c..5d177a0ca42ac9bf5c44d9239520fde76eecfec5 100644 (file)
@@ -158,6 +158,8 @@ typedef struct system {
 
     dev_t root_dev;
 
+    vaddr_t hpet_base;
+
     // 按理这些信息应该按CPU存储,简化实现
     lapic_t* lapic;
     paddr_t lapic_addr;
index e30c6c1470e5ba8c4d1236f79758dc53ca96e622..aaab186a0bb6187fce402aca0b3c1317a89f4340 100644 (file)
@@ -247,6 +247,31 @@ void ioapic_rte_write(uint32_t index, uint64_t v) {
     io_mfence();
 }
 
+static uint64_t hpet_ticks = 0;
+
+void hpet0_bh_handler() {
+    // hpet_ticks++;
+    printlxy(MPL_IRQ, MPO_HPET, "HPET: %lu", hpet_ticks);
+}
+
+void hpet0_irq_handler(unsigned int irq, pt_regs_t* regs, void* dev_id) {
+    hpet_ticks++;
+
+    vaddr_t hpet_base = system.hpet_base;
+    assert(hpet_base != 0);
+
+    uint8_t* p = (uint8_t*)0xC00B8002;
+    *p = *p == ' ' ? 'E' : ' ';
+
+    add_irq_bh_handler(hpet0_bh_handler, NULL);
+
+    // uint64_t main_cnt = *(volatile uint64_t*)(hpet_base + 0xF0);
+    // uint64_t counter = *(volatile uint64_t*)(hpet_base + 0x108);
+    // printk("main_cnt: %lu counter: %lu abc\n", main_cnt, counter);
+
+    system.lapic->write(LAPIC_EOI, 0);
+}
+
 void ioapic_init() {
     // 先找到RCBA: Root Complex Base Address寄存器
     uint32_t cmd = PCI_CMD(0, 31, 0, 0xF0);
@@ -320,13 +345,15 @@ void ioapic_init() {
     // TODO
     // iounmap(rcba_virt_base);
 
-#if 1
+    uint64_t dst_cpuid = 0;
+
     extern irq_chip_t ioapic_chip;
+#if 1
     // 把8253的中断通过IOAPIC转发到CPU0的0号中断
     // 8253/8254连在i8259的0号引脚,但连在IO APIC的2号引脚上
-    ioapic_rte_write(IOAPIC_RTE(2), 0x20 + 0);
+    ioapic_rte_write(IOAPIC_RTE(2), 0x20 + 0 | (dst_cpuid << 56));
     // 把键盘中断通过IOAPIC转发到CPU0的1号中断
-    ioapic_rte_write(IOAPIC_RTE(1), 0x20 + 1);
+    ioapic_rte_write(IOAPIC_RTE(1), 0x20 + 1 | (dst_cpuid << 56));
     irq_set_chip(0x00, &ioapic_chip);
     irq_set_chip(0x01, &ioapic_chip);
 #endif
@@ -340,21 +367,146 @@ void ioapic_init() {
     // bit[7] 地址映射使能标志位,用于控制HPET设备访问地址的开启与否
     //        只有它置位时芯片组才会将HPET配置寄存器映射到内存空间
     uint32_t* pHPTC = (uint32_t*)((uint8_t*)rcba_virt_base + 0x3404);
-    printk("HPTC: %08x\n", *pHPTC);
-    *pHPTC = (1 << 7) | (0x00);
+    printk("HPTC: %08x %08x\n", *pHPTC, pHPTC);
+    *pHPTC = *pHPTC | (1 << 7) | (0x00);
+    io_mfence();
     printk("HPTC: %08x\n", *pHPTC);
 
     vaddr_t hpet_base = (vaddr_t)ioremap(0xFED00000, 0x3FF);
+    system.hpet_base = hpet_base;
     printk("HPET base %08x mapped to %08x\n", 0xFED00000, hpet_base);
 
-    uint64_t GCAP_ID = 0;
-    GCAP_ID = *(volatile uint32_t*)(hpet_base + 4);
-    GCAP_ID <<= 32;
-    GCAP_ID |= *(volatile uint32_t*)(hpet_base + 0);
-    printk("GCAP_ID: %016x\n", GCAP_ID);
+    uint64_t GEN_CONF = *(volatile uint64_t*)(hpet_base + 0x10);
+    printk("GEN_CONF: 0x%08x%08x\n", (uint32_t)(GEN_CONF >> 32), (uint32_t)GEN_CONF);
+    // // DISABLE HPET
+    // *(volatile uint64_t*)(hpet_base + 0x10) &= ~(1 << 0);
+    // io_mfence();
+
+    // 获取HPET的频率
+    uint64_t GCAP_ID = *(volatile uint64_t*)(hpet_base + 0);
+    // printk("GCAP_ID: %016x\n", GCAP_ID);
+    printk("GCAP_ID: 0x%08x%08x\n", (uint32_t)(GCAP_ID >> 32), (uint32_t)GCAP_ID);
+    uint32_t clock_period = (GCAP_ID >> 32);
+    uint64_t freq_mhz = 1000000000U / clock_period;
+    printk("HPET clock period: %08x\n", clock_period);
+    printk("HPET clock %s compatible\n", (GCAP_ID & (1 << 15)) == 0 ? "not" : "");
+    printk("HPET clock %s 64-bit capable\n", (GCAP_ID & (1 << 13)) == 0 ? "not" : "");
+    printk("HPET clock timer count: %d\n", ((GCAP_ID >> 8) & 0x1F) + 1);
+    printk("HPET clock frequency: %u MHz\n", freq_mhz);
+
+    GEN_CONF = *(volatile uint64_t*)(hpet_base + 0x10);
+    printk("GEN_CONF: 0x%08x%08x\n", (uint32_t)(GEN_CONF >> 32), (uint32_t)GEN_CONF);
+    printk("HPET enabled: %s\n", (GEN_CONF & (1 << 0)) == 0 ? "no" : "yes");
+    printk("HPET legacy replacement: %s\n", (GEN_CONF & (1 << 1)) == 0 ? "no" : "yes");
+
+    uint32_t cpu_irq_vec = 64;
+    uint32_t ioapic_irq = 23;
+
+    // TIM0_CONF
+    uint64_t TIM0_CONF = *(volatile uint64_t*)(hpet_base + 0x100);
+    printk("TIM0_CONF: 0x%08x%08x\n", (uint32_t)(TIM0_CONF >> 32), (uint32_t)TIM0_CONF);
+
+    request_irq(cpu_irq_vec, hpet0_irq_handler, "HPET#0", "HPET#0");
+    // HEPT TIM0 连在 IO-APIC的2号引脚上
+    // ioapic_rte_write(IOAPIC_RTE(2), 0x20 + 3 | (dst_cpuid << 56));
+
+#define TRIGGER_MODE IOAPIC_TRIGGER_MODE_EDGE
+    ioapic_rte_t rte;
+    printk("sizeof(ioapic_rte_t): %d\n", sizeof(ioapic_rte_t));
+    assert(sizeof(ioapic_rte_t) == 8);
+    rte.value = 0;
+    rte.vector = 0x20 + cpu_irq_vec;
+    rte.delivery_mode = IOAPIC_DELIVERY_MODE_FIXED;
+    rte.destination_mode = IOAPIC_PHYSICAL_DESTINATION;
+    rte.trigger_mode = TRIGGER_MODE;
+    rte.mask = IOAPIC_INT_UNMASKED;
+    rte.destination = dst_cpuid;
+
+    printk("RTE VALUE %08x", rte.value);
+
+    ioapic_rte_write(IOAPIC_RTE(ioapic_irq), rte.value);
+    irq_set_chip(cpu_irq_vec, &ioapic_chip);
+
+    // DISABLE HPET
+    *(volatile uint64_t*)(hpet_base + 0x10) &= ~(1ULL << 0);
+    io_mfence();
+
+    hpet_timn_conf_cap_t tim0_conf_cap;
+    assert(sizeof(tim0_conf_cap) == 8);
+    tim0_conf_cap.value = TIM0_CONF;
+    tim0_conf_cap.trigger_mode = TRIGGER_MODE;
+    tim0_conf_cap.enable_int = 1;  // enable
+    tim0_conf_cap.type = 1;        // periodic
+    tim0_conf_cap.periodic = 0;    // readonly
+    tim0_conf_cap.bit_size = 1;    // 0 32bit; 1 64bit
+    tim0_conf_cap.val_set = 1;
+
+    tim0_conf_cap.counter_bit_size = 0;  // 1 32 0 64
+    tim0_conf_cap.int_route = ioapic_irq;
+    tim0_conf_cap.fsb_en = 0;
+    tim0_conf_cap.fsb_delivery_status = 0;  // read only
+    tim0_conf_cap.reserved0 = 0;
+    tim0_conf_cap.reserved1 = 0;
+    tim0_conf_cap.reserved2 = 0;
+    tim0_conf_cap.int_route_cap = 0;
+
+    TIM0_CONF = tim0_conf_cap.value;
+
+    printk("TIM0_CONF: 0x%08x%08x\n", (uint32_t)(TIM0_CONF >> 32), (uint32_t)TIM0_CONF);
+
+    uint64_t x = freq_mhz * 1000000ULL;
+    *(volatile uint32_t*)(hpet_base + 0x108 + 0x04) = (uint32_t)(x >> 32);
+    *(volatile uint32_t*)(hpet_base + 0x108 + 0x00) = (uint32_t)(x & 0xFFFFFFFF);
+    // *(volatile uint64_t*)(hpet_base + 0x108) = x;
+    io_mfence();
+
+    *(volatile uint32_t*)(hpet_base + 0x100 + 0x04) = (uint32_t)(TIM0_CONF >> 32);
+    *(volatile uint32_t*)(hpet_base + 0x100 + 0x00) = (uint32_t)(TIM0_CONF & 0xFFFFFFFF);
+    // *(volatile uint64_t*)(hpet_base + 0x100) = TIM0_CONF;
+    io_mfence();
+
+    // 如果TIMn_CONF的bit6置位(只能在周期模式下置位)的话,这个比较寄存器要写两次
+    // 第一次是初始值
+    // 第二次是累加值,也就是当MAIN_CNT达到比较寄存器的值时,会自动加上这个
+    // 需要说明的是: 第一次要在写TIMn_CONF之前写入
+    // 在<<Softwater Developers HPET Specification>>中是这样描述bit6的
+    // Software uses this read/write bit only for timers that have been set to periodic mode.
+    // By writing this bit to a 1, the software is then allowed to directly set a periodic timer's accumulator.
+    *(volatile uint32_t*)(hpet_base + 0x108 + 0x04) = (uint32_t)(x >> 32);
+    *(volatile uint32_t*)(hpet_base + 0x108 + 0x00) = (uint32_t)(x & 0xFFFFFFFF);
+    // *(volatile uint64_t*)(hpet_base + 0x108) = x;
+    io_mfence();
+
+    // *(volatile uint32_t*)(hpet_base + 0x108 + 0x04) = (uint32_t)(x >> 32);
+    // *(volatile uint32_t*)(hpet_base + 0x108 + 0x00) = (uint32_t)(x & 0xFFFFFFFF);
+    // // *(volatile uint64_t*)(hpet_base + 0x108) = x;
+    // io_mfence();
+
+    // MAIN_CNT
+    *(volatile uint64_t*)(hpet_base + 0xF0) = 0;
+    // *(volatile uint32_t*)(hpet_base + 0xF0) = 0;
+    io_mfence();
+
+    // ENABLE HPET
+    *(volatile uint64_t*)(hpet_base + 0x10) |= (1ULL << 0);
+    io_mfence();
+
+    // // 禁用 Legacy Replacement 模式
+    // *(volatile uint64_t*)(hpet_base + 0x10) &= ~(1ULL << 1);
+    // io_mfence();
+
+    TIM0_CONF = *(volatile uint64_t*)(hpet_base + 0x100);
+
+    uint32_t ESR = system.lapic->read(LAPIC_ESR);
+    uint64_t TPR = system.lapic->read(LAPIC_TPR);
+    uint32_t PPR = system.lapic->read(LAPIC_PPR);
+    printk("ESR: %08x TPR: %08x PPR: %08x\n", ESR, TPR, PPR);
+    printk("AFTER TIM0_CONF: 0x%08x%08x\n", (uint32_t)(TIM0_CONF >> 32), (uint32_t)TIM0_CONF);
 
-    uint64_t iddd = *(volatile uint64_t*)(hpet_base + 0);
-    printk("GCAP_ID: %016x\n", iddd);
+    // while (1) {
+    //     asm("sti");
+    //     asm("hlt;");
+    // }
     // TODO
     // iounmap(hpet_base);
 }
index 2a4bbb4bbf95263705155792da6e37d712aa77df..efa644fd506d79b4cc3b6ddcd20bbaefb3a15599 100644 (file)
@@ -32,7 +32,7 @@ extern volatile bool enable_clock_irq_delay;
 void clk_handler(unsigned int irq, pt_regs_t* regs, void* dev_id) {
     // if (jiffies % 100 == 0) {
     // printl(MPL_CLOCK, "clock irq: %d", jiffies);
-    printlxy(MPL_IRQ, MPO_CLOCK, "CLK irq: %d", jiffies);
+    printlxy(MPL_IRQ, MPO_CLOCK, "CLK: %d", jiffies);
     // printk("CLK irq %d\n", jiffies);
     // }
 
index 7a7db3a07ce6579d65629ecd257c5ff57d66a446..a2c9bd073b79ed1fce12065f72d3bfe70f7c8ccd 100644 (file)
@@ -49,6 +49,18 @@ DEF_IRQ(0,D)
 DEF_IRQ(0,E)
 DEF_IRQ(0,F)
 
+// 64
+DEF_IRQ(4,0)
+#.extern hpet0_irq_handler
+#.global irq_0x40_handler
+#irq_0x40_handler:
+#    SAVE_REGS
+#
+#    call    hpet0_irq_handler
+#
+#    RESTORE_REGS
+#    iret
+
 
 .global _irq_handler
 .extern irq_handler
index 112480e689e419ef16de0a798202ff450ae49ef9..a37f32ea59e6df247bf2fd84cd4b62aa1de25f32 100644 (file)
@@ -120,6 +120,9 @@ void setup_irqs() {
     set_sys_int(0x2E, INTR_GATE, PRIVILEGE_KRNL, irq_0x0E_handler);
     set_sys_int(0x2F, INTR_GATE, PRIVILEGE_KRNL, irq_0x0F_handler);
 
+    // 64
+    set_sys_int(0x20 + 0x40, INTR_GATE, PRIVILEGE_KRNL, irq_0x40_handler);
+
     for (int i = 0; i < NR_IRQS; i++) {
         irq_desc[i] = no_irq_desc;
 
@@ -135,8 +138,8 @@ void setup_irqs() {
     request_irq(0x00, clk_handler, "Intel 8254", "Clock Chip");
     request_irq(0x01, kbd_handler, "Intel 8042", "PS/2 Keyboard");
     // request_irq(0x0E, default_ide_irq_handler, "hard", "IDE");
-    for (int i = 0; i < 16; i++) {
-        if (i != 0 && i != 1 && i != 10 && i != 14 && i != 15) {
+    for (int i = 0; i < 256; i++) {
+        if (i != 0 && i != 1 && i != 10 && i != 14 && i != 15 && i != 64) {
             request_irq(i, default_irq_handler, "default", "default");
         }
     }