From f2df05a7f28e9cd29e445b33fc97d2b664addc1e Mon Sep 17 00:00:00 2001 From: acevest Date: Wed, 7 Jan 2026 10:45:11 +0800 Subject: [PATCH] =?utf8?q?HPET#0=E5=88=9D=E6=AD=A5=E8=B7=AF=E7=94=B1?= =?utf8?q?=E5=88=B0CPU0=E7=9A=8464=E5=8F=B7=E4=B8=AD=E6=96=AD[=E4=B8=AD?= =?utf8?q?=E6=96=AD=E5=90=91=E9=87=8F64+32=3D96]?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- drivers/keyboard.c | 2 +- include/apic.h | 79 ++++++++++++++++++++ include/linkage.h | 2 + include/printk.h | 3 +- include/system.h | 2 + kernel/apic.c | 176 +++++++++++++++++++++++++++++++++++++++++--- kernel/clock.c | 2 +- kernel/interrupts.S | 12 +++ kernel/system.c | 7 +- 9 files changed, 268 insertions(+), 17 deletions(-) diff --git a/drivers/keyboard.c b/drivers/keyboard.c index 48690fc..2a3fbb1 100644 --- a/drivers/keyboard.c +++ b/drivers/keyboard.c @@ -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(); diff --git a/include/apic.h b/include/apic.h index 0323ccf..c023249 100644 --- a/include/apic.h +++ b/include/apic.h @@ -9,6 +9,8 @@ #pragma once +#include + #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; diff --git a/include/linkage.h b/include/linkage.h index 134d6a9..f59878d 100644 --- a/include/linkage.h +++ b/include/linkage.h @@ -33,3 +33,5 @@ #define ALIGN8 __attribute__((__aligned__(8))) #define ALIGN4 __attribute__((__aligned__(4))) + +#define PACKED __attribute__((__packed__)) diff --git a/include/printk.h b/include/printk.h index f34b2c6..0fbe477 100644 --- a/include/printk.h +++ b/include/printk.h @@ -50,7 +50,8 @@ enum { // monitor print offset enum { MPO_CLOCK = 1, - MPO_KEYBOARD = 50, + MPO_HPET = 28, + MPO_KEYBOARD = 48, MPO_IDE = 1, }; diff --git a/include/system.h b/include/system.h index d5c5ef1..5d177a0 100644 --- a/include/system.h +++ b/include/system.h @@ -158,6 +158,8 @@ typedef struct system { dev_t root_dev; + vaddr_t hpet_base; + // 按理这些信息应该按CPU存储,简化实现 lapic_t* lapic; paddr_t lapic_addr; diff --git a/kernel/apic.c b/kernel/apic.c index e30c6c1..aaab186 100644 --- a/kernel/apic.c +++ b/kernel/apic.c @@ -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之前写入 + // 在<>中是这样描述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); } diff --git a/kernel/clock.c b/kernel/clock.c index 2a4bbb4..efa644f 100644 --- a/kernel/clock.c +++ b/kernel/clock.c @@ -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); // } diff --git a/kernel/interrupts.S b/kernel/interrupts.S index 7a7db3a..a2c9bd0 100644 --- a/kernel/interrupts.S +++ b/kernel/interrupts.S @@ -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 diff --git a/kernel/system.c b/kernel/system.c index 112480e..a37f32e 100644 --- a/kernel/system.c +++ b/kernel/system.c @@ -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"); } } -- 2.47.0