From: acevest Date: Fri, 9 Jan 2026 13:54:51 +0000 (+0800) Subject: 用HPET#0校准AP的LAPIC时钟中断 X-Git-Url: http://repos.zhaoyanbai.com/?a=commitdiff_plain;h=ad741da734ca6b35e210137a4d02d8c55453686c;p=kernel.git 用HPET#0校准AP的LAPIC时钟中断 --- diff --git a/include/apic.h b/include/apic.h index 8a34a17..2955094 100644 --- a/include/apic.h +++ b/include/apic.h @@ -84,6 +84,18 @@ #define LAPIC_TIMER_MODE_TSC_DEADLINE (2 << 17) #define LAPIC_TIMER_MODE_RESERVED (3 << 17) +// 需要注意的是,分频器用的是第0,1,3位。第2位为0 +#define LAPIC_TIMER_DIVIDE_BY_1 (0b1011) +#define LAPIC_TIMER_DIVIDE_BY_2 (0b0000) +#define LAPIC_TIMER_DIVIDE_BY_4 (0b0001) +#define LAPIC_TIMER_DIVIDE_BY_8 (0b0010) +#define LAPIC_TIMER_DIVIDE_BY_16 (0b0011) +#define LAPIC_TIMER_DIVIDE_BY_32 (0b1000) +#define LAPIC_TIMER_DIVIDE_BY_64 (0b1001) +#define LAPIC_TIMER_DIVIDE_BY_128 (0b1010) + +#define LAPIC_TIMER_DIVIDE_INVALID (0b1111) + typedef struct lapic { const char* name; diff --git a/include/hpet.h b/include/hpet.h index bbe6b00..e421fd4 100644 --- a/include/hpet.h +++ b/include/hpet.h @@ -10,3 +10,16 @@ #pragma once void hpet_init(); + +void hpet_init_timer0(); + +// 使能HPET定时器 +void hpet_enable(); + +// +void hpet_disable(); + +// 调用此函数前后需要手动调用 hpet_enable 和 hpet_disable +void hpet_prepare_calibration(); + +bool hpet_calibration_end(); diff --git a/kernel/ap.c b/kernel/ap.c index 8985482..b866167 100644 --- a/kernel/ap.c +++ b/kernel/ap.c @@ -14,6 +14,7 @@ #include #include #include +#include extern pde_t* ap_pre_pgd; @@ -26,6 +27,9 @@ uint8_t ALIGN8 ap_idtr[6]; #define AP_CS_SELECTOR 0x08 #define AP_DS_SELECTOR 0x10 +const uint32_t ap_pit_irq = 0x00; +const uint32_t ap_lapic_irq = 0x08; + static inline desc_t _create_ap_gate(u32 handler, u8 type, u8 DPL) { desc_t d; gate_t* p = &d.gate; @@ -96,6 +100,9 @@ void ap_kernel_entry() { set_ap_sys_int(i, INTR_GATE, PRIVILEGE_KRNL, ap_no_irq_handler); } + set_ap_sys_int(0x20 + ap_pit_irq, INTR_GATE, PRIVILEGE_KRNL, ap_pit_irq_handler); + set_ap_sys_int(0x20 + ap_lapic_irq, INTR_GATE, PRIVILEGE_KRNL, ap_lapic_irq_handler); + // 加载 ap_idtr *((unsigned short*)ap_idtr) = AP_IDT_CNT * sizeof(gate_t); *((unsigned long*)(ap_idtr + 2)) = (unsigned long)ap_idt; @@ -103,6 +110,7 @@ void ap_kernel_entry() { cpuid_regs_t r; r = cpuid(1); + printk("cpuid 1: eax=%08x ebx=%08x ecx=%08x edx=%08x\n", r.eax, r.ebx, r.ecx, r.edx); if (r.edx & (1 << 9)) { printk("local apic supported\n"); if (r.ecx & (1 << 21)) { @@ -116,7 +124,6 @@ void ap_kernel_entry() { // 开房时钟中断 lapic_t* lapic = system.lapic; - uint32_t clkvec = 0x28; uint32_t apic_base = 0; apic_base = read_msr32(MSR_IA32_APIC_BASE); @@ -126,34 +133,93 @@ void ap_kernel_entry() { write_msr(MSR_IA32_APIC_BASE, apic_base); } - system.ap_cpuid = lapic->get_lapic_id(); - printk("AP CPU id: %d\n", system.ap_cpuid); - uint32_t lapic_svr = lapic->read(LAPIC_SVR); lapic_svr |= (1 << 8); // 启用LAPIC lapic_svr &= ~(1 << 12); // 禁用EOI广播 lapic->write(LAPIC_SVR, lapic_svr); -#if 0 - // 先显示地屏蔽时钟中断 +#if 1 + uint32_t calibration_counter = 0; + uint32_t calibration_divider = 0; + struct { + uint32_t value; + const char* name; + } divisors[] = { + {LAPIC_TIMER_DIVIDE_BY_1, "1"}, // + {LAPIC_TIMER_DIVIDE_BY_2, "2"}, // + {LAPIC_TIMER_DIVIDE_BY_4, "4"}, // + {LAPIC_TIMER_DIVIDE_BY_8, "8"}, // + {LAPIC_TIMER_DIVIDE_BY_16, "16"}, // + {LAPIC_TIMER_DIVIDE_BY_32, "32"}, // + {LAPIC_TIMER_DIVIDE_BY_64, "64"}, // + {LAPIC_TIMER_DIVIDE_BY_128, "128"}, // + {LAPIC_TIMER_DIVIDE_INVALID, "invalid"}, // END + }; + lapic->write(LAPIC_LVT_TIMER, LAPIC_LVT_MASKED); + for (int i = 0; divisors[i].value != LAPIC_TIMER_DIVIDE_INVALID; i++) { + const uint32_t timn = 0; + + // + calibration_divider = divisors[i].value; + lapic->write(LAPIC_TIMER_DIVIDER, calibration_divider); + uint32_t divide = lapic->read(LAPIC_TIMER_DIVIDER); + assert(divide == calibration_divider); + + // + hpet_disable(); + + // 只要不hpet_enable, HPET的计数器就不会启动 + hpet_prepare_calibration(timn, 2 /*hz*/); + + // + hpet_enable(); + + // 写入最大值 + lapic->write(LAPIC_TIMER_INITIAL, 0xFFFFFFFF); + + uint32_t lapic_timer_counter = 0; + while (!hpet_calibration_end(timn)) { + lapic_timer_counter = lapic->read(LAPIC_TIMER_COUNTER); + if (unlikely(lapic_timer_counter < 0x80000000)) { + goto next; + } else { + asm("pause"); + } + } + + lapic_timer_counter = lapic->read(LAPIC_TIMER_COUNTER); - // 设置分频频率 - // 000: divide by 2 - // 001: divide by 4 - // 010: divide by 8 - // 011: divide by 16 - // 100: divide by 32 - // 101: divide by 64 - // 110: divide by 128 - // 111: divide by 1 - lapic->write(LAPIC_TIMER_DIVIDER, 0x03); + calibration_counter = 0xFFFFFFFF - lapic_timer_counter; + assert(lapic_timer_counter >= 0x80000000); + printk("AP chose divider: %s %02x\n", divisors[i].name, divisors[i].value); + printk("AP calibration counter: %08x\n", calibration_counter); + break; + + next: + continue; + } + + if (calibration_counter == 0) { + panic("AP calibration counter is 0\n"); + } + + // + hpet_disable(); + + // 关闭时钟中断 + lapic->write(LAPIC_LVT_TIMER, LAPIC_LVT_MASKED); // 设置时钟中断周期 - lapic->write(LAPIC_TIMER_INITIAL, 10000000); + lapic->write(LAPIC_TIMER_DIVIDER, calibration_divider); + lapic->write(LAPIC_TIMER_INITIAL, calibration_counter); // - lapic->write(LAPIC_LVT_TIMER, LAPIC_TIMER_MODE_PERIODIC | clkvec); + lapic->write(LAPIC_LVT_TIMER, LAPIC_TIMER_MODE_PERIODIC | (0x20 + ap_lapic_irq)); #endif + + uint32_t ap_cpuid = lapic->get_lapic_id(); + printk("AP CPU id: %d\n", ap_cpuid); + system.ap_cpuid = ap_cpuid; asm("sti;"); while (1) { @@ -161,14 +227,23 @@ void ap_kernel_entry() { } } -void do_ap_no_irq_handler() { - // do nothing - // printk("AP no irq handler\n"); - uint8_t* p = (uint8_t*)0xC00B8000; - *p = *p == ' ' ? 'K' : ' '; +void do_ap_lapic_irq_handler() { + uint8_t* p = (uint8_t*)0xC00B8002; + *p = *p == ' ' ? 'E' : ' '; system.lapic->write(LAPIC_EOI, 0); } +void do_ap_pit_irq_handler() { + uint8_t* p = (uint8_t*)0xC00B8004; + *p = *p == ' ' ? 'R' : ' '; + + system.lapic->write(LAPIC_EOI, 0); +} + +void do_ap_no_irq_handler() { + panic("AP unexpected irq\n"); +} + bool ap_ready() { return system.ap_cpuid != 0; } diff --git a/kernel/apic.c b/kernel/apic.c index 802f02d..7b71419 100644 --- a/kernel/apic.c +++ b/kernel/apic.c @@ -312,52 +312,19 @@ void ioapic_init() { extern irq_chip_t ioapic_chip; #if 1 - // 把8253的中断通过IOAPIC转发到CPU0的0号中断 + // 8253/8254连在i8259的0号引脚,但连在IO APIC的2号引脚上 + // 把8253/8254的中断通过IOAPIC转发到CPU0的0号中断 // ioapic_rte_write(IOAPIC_RTE(2), 0x20 + 0 | (dst_cpuid << 56)); + // 把8253/8254的中断通过IOAPIC转发到CPU1的0号中断 ioapic_rte_write(IOAPIC_RTE(2), 0x20 + 0 | (1ULL << 56)); // 把键盘中断通过IOAPIC转发到CPU0的1号中断 ioapic_rte_write(IOAPIC_RTE(1), 0x20 + 1 | (dst_cpuid << 56)); - irq_set_chip(0x00, &ioapic_chip); + // irq_set_chip(0x00, &ioapic_chip); // ap不需要这个 irq_set_chip(0x01, &ioapic_chip); #endif } -void prepare_ap_code(paddr_t paddr) { - // 注意: 最开始时AP是运行在实模式 - paddr += KERNEL_VADDR_BASE; - // *(volatile uint8_t*)(paddr + 0) = 0x90; - // *(volatile uint8_t*)(paddr + 1) = 0x90; - // *(volatile uint8_t*)(paddr + 2) = 0x90; - // *(volatile uint8_t*)(paddr + 3) = 0xEA; // jmp - // *(volatile uint16_t*)(paddr + 4) = 0x0000; // offset: 0000 - // *(volatile uint16_t*)(paddr + 6) = 0x0100; // cs:0100 - - extern char ap_boot_bgn; - extern char ap_boot_end; - uint32_t bytes = &ap_boot_end - &ap_boot_bgn; - - for (int i = 0; i < bytes; i++) { - ((uint8_t*)paddr)[i] = ((uint8_t*)&ap_boot_bgn)[i]; - } - - // 修正代码里跳入保护模式的地址和gdtr里的gdt的base地址 - extern uint8_t ap_code32_entry_address; - extern uint8_t ap_gdtr_base; - - uint32_t* dst = 0; - - // - dst = (uint32_t*)(paddr + (uint32_t)(&ap_code32_entry_address) - (uint32_t)(&ap_boot_bgn)); - (*dst) -= (uint32_t)(&ap_boot_bgn); - (*dst) += (paddr - KERNEL_VADDR_BASE); - - // - dst = (uint32_t*)(paddr + (uint32_t)(&ap_gdtr_base) - (uint32_t)(&ap_boot_bgn)); - (*dst) -= (uint32_t)(&ap_boot_bgn); - (*dst) += (paddr - KERNEL_VADDR_BASE); -} - void wakeup_ap(paddr_t paddr) { assert(PAGE_DOWN(paddr) == paddr); @@ -433,11 +400,6 @@ void init_apic() { // imcr_init(); lapic_init(); ioapic_init(); - - paddr_t ap_code_addr = 0x1000; - prepare_ap_code(ap_code_addr); - wakeup_ap(ap_code_addr); - } // ## 中断路由路径配置矩阵 diff --git a/kernel/hpet.c b/kernel/hpet.c index 00603a9..e115a74 100644 --- a/kernel/hpet.c +++ b/kernel/hpet.c @@ -26,6 +26,8 @@ const static int hpet_trigger_mode = IOAPIC_TRIGGER_MODE_EDGE; static paddr_t hpet_phys_addr = 0; +static uint32_t hpet_timer_count = 0; + void hpet_hw_enable() { uint32_t map_offset = 3 * PAGE_SIZE; vaddr_t rcba_phys_base = (vaddr_t)get_rcba_paddr(); @@ -56,11 +58,14 @@ vaddr_t hpet_base() { } uint64_t hpet_read(uint32_t regoffset) { - return *(uint64_t*)(hpet_base() + regoffset); + uint64_t value = *(uint64_t*)(hpet_base() + regoffset); + io_mfence(); + return value; } uint64_t hpet_write(uint32_t regoffset, uint64_t value) { *(volatile uint64_t*)(hpet_base() + regoffset) = value; + io_mfence(); return value; } @@ -74,12 +79,10 @@ uint64_t hpet_write(uint32_t regoffset, uint64_t value) { void hpet_enable() { hpet_write(HPET_REG_CONFIG, hpet_read(HPET_REG_CONFIG) | (1ULL << 0)); - io_mfence(); } void hpet_disable() { hpet_write(HPET_REG_CONFIG, hpet_read(HPET_REG_CONFIG) & ~(1ULL << 0)); - io_mfence(); } uint32_t hpet_clock_period = 0; @@ -93,40 +96,26 @@ void hpet0_bh_handler() { void hpet0_irq_handler(unsigned int irq, pt_regs_t* regs, void* dev_id) { hpet_ticks++; - uint8_t* p = (uint8_t*)0xC00B8002; - *p = *p == ' ' ? 'E' : ' '; + uint8_t* p = (uint8_t*)0xC00B8000; + *p = *p == ' ' ? 'K' : ' '; add_irq_bh_handler(hpet0_bh_handler, NULL); system.lapic->write(LAPIC_EOI, 0); } -extern irq_chip_t ioapic_chip; -void hpet_init() { - assert(hpet_use_phys_addr_index < sizeof(hpet_phys_addrs) / sizeof(hpet_phys_addrs[0])); - hpet_phys_addr = hpet_phys_addrs[hpet_use_phys_addr_index]; - - set_fixmap(FIX_HPET_BASE, hpet_phys_addr); - printk("HPET base %08x mapped to %08x\n", hpet_phys_addr, hpet_base()); +uint64_t hpet_get_tick_counter(uint32_t hz) { + assert(hz > 0); + assert(hpet_clock_mhz_freq > 0); - // - hpet_hw_enable(); + uint32_t r = 1000000 / hz; - uint64_t capid = hpet_read(HPET_REG_CAPABILITY_ID); - hpet_clock_period = capid >> 32; - hpet_clock_mhz_freq = 1000000000U / hpet_clock_period; // 32位除法 - printk("HPET Capability and ID: 0x%08x%08x\n", (uint32_t)(capid >> 32), (uint32_t)capid); - printk("HPET legacy replacement route capable: %s\n", (capid & (1ULL << 15)) ? "Y" : "N"); - printk("HPET 64bit capable: %s\n,", (capid & (1ULL << 13)) ? "Y" : "N"); - printk("HPET timer count: %d\n", ((capid >> 8) & 0x1F) + 1); - printk("HPET clock period: %u ns\n", hpet_clock_period); - printk("HPET clock frequency: %u MHz\n", hpet_clock_mhz_freq); + return r * hpet_clock_mhz_freq; +} - uint64_t config = hpet_read(HPET_REG_CONFIG); - printk("HPET Configuration: 0x%08x%08x\n", (uint32_t)(config >> 32), (uint32_t)config); - printk("HPET enabled: %s\n", (config & (1ULL << 0)) ? "Y" : "N"); - printk("HPET legacy replacement: %s\n", (config & (1ULL << 1)) ? "Y" : "N"); +extern irq_chip_t ioapic_chip; +void hpet_init_timer0(uint32_t hz) { // hpet_disable(); @@ -155,7 +144,7 @@ void hpet_init() { ioapic_rte_write(IOAPIC_RTE(ioapic_irq), rte.value); irq_set_chip(irq, &ioapic_chip); - uint64_t counter = hpet_clock_mhz_freq * 1000000ULL; + uint64_t counter = hpet_get_tick_counter(hz); // 配置HPET#0 uint64_t tim0_config = 0; @@ -169,13 +158,71 @@ void hpet_init() { printk("TIM0_CONF: 0x%08x%08x\n", (uint32_t)(tim0_config >> 32), (uint32_t)tim0_config); hpet_write(HPET_REG_TIMn_CONFIG_CAPABILITY(0), tim0_config); - io_mfence(); hpet_write(HPET_REG_TIMn_COMPARATOR(0), counter); - io_mfence(); hpet_write(HPET_REG_MAIN_COUNTER_VALUE, 0); - io_mfence(); hpet_enable(); } + +void hpet_prepare_calibration(uint32_t timn, uint32_t hz) { + assert(timn < hpet_timer_count); + // + // hpet_disable(); + + uint64_t counter = hpet_get_tick_counter(hz); + + // 配置HPET#0 + uint64_t timn_config = 0; + timn_config |= (hpet_trigger_mode << 1); + timn_config |= (0 << 2); // enable interrupt + timn_config |= (0 << 3); // periodic + timn_config |= (1 << 5); // 64bit + // timn_config |= (1 << 6); // .... + // timn_config |= (ioapic_irq << 9); // ioapic irq + + printk("TIM0_CONF: 0x%08x%08x\n", (uint32_t)(timn_config >> 32), (uint32_t)timn_config); + + hpet_write(HPET_REG_TIMn_CONFIG_CAPABILITY(timn), timn_config); + + hpet_write(HPET_REG_TIMn_COMPARATOR(timn), counter); + + hpet_write(HPET_REG_MAIN_COUNTER_VALUE, 0); + + // hpet_enable(); +} + +bool hpet_calibration_end(uint32_t timn) { + if (hpet_read(HPET_REG_MAIN_COUNTER_VALUE) >= hpet_read(HPET_REG_TIMn_COMPARATOR(timn))) { + return true; + } + return false; +} + +void hpet_init() { + assert(hpet_use_phys_addr_index < sizeof(hpet_phys_addrs) / sizeof(hpet_phys_addrs[0])); + hpet_phys_addr = hpet_phys_addrs[hpet_use_phys_addr_index]; + + set_fixmap(FIX_HPET_BASE, hpet_phys_addr); + printk("HPET base %08x mapped to %08x\n", hpet_phys_addr, hpet_base()); + + // + hpet_hw_enable(); + + uint64_t capid = hpet_read(HPET_REG_CAPABILITY_ID); + hpet_clock_period = capid >> 32; + hpet_clock_mhz_freq = 1000000000U / hpet_clock_period; // 32位除法 + hpet_timer_count = ((capid >> 8) & 0x1F) + 1; + printk("HPET Capability and ID: 0x%08x%08x\n", (uint32_t)(capid >> 32), (uint32_t)capid); + printk("HPET legacy replacement route capable: %s\n", (capid & (1ULL << 15)) ? "Y" : "N"); + printk("HPET 64bit capable: %s\n", (capid & (1ULL << 13)) ? "Y" : "N"); + printk("HPET timer count: %d\n", hpet_timer_count); + printk("HPET clock period: %u ns\n", hpet_clock_period); + printk("HPET clock frequency: %u MHz\n", hpet_clock_mhz_freq); + + uint64_t config = hpet_read(HPET_REG_CONFIG); + printk("HPET Configuration: 0x%08x%08x\n", (uint32_t)(config >> 32), (uint32_t)config); + printk("HPET enabled: %s\n", (config & (1ULL << 0)) ? "Y" : "N"); + printk("HPET legacy replacement: %s\n", (config & (1ULL << 1)) ? "Y" : "N"); +} diff --git a/kernel/interrupts.S b/kernel/interrupts.S index a2c9bd0..f4128be 100644 --- a/kernel/interrupts.S +++ b/kernel/interrupts.S @@ -123,8 +123,21 @@ _boot_kbd_irq_handler: .global ap_no_irq_handler ap_no_irq_handler: SAVE_REGS - call do_ap_no_irq_handler + RESTORE_REGS + iret +.global ap_lapic_irq_handler +ap_lapic_irq_handler: + SAVE_REGS + call do_ap_lapic_irq_handler + RESTORE_REGS + iret + + +.global ap_pit_irq_handler +ap_pit_irq_handler: + SAVE_REGS + call do_ap_pit_irq_handler RESTORE_REGS iret diff --git a/kernel/setup.c b/kernel/setup.c index 6017144..e3b13d8 100644 --- a/kernel/setup.c +++ b/kernel/setup.c @@ -75,8 +75,49 @@ void print_kernel_version() { printk(version); } +void prepare_ap_code(paddr_t paddr) { + // 注意: 最开始时AP是运行在实模式 + paddr += KERNEL_VADDR_BASE; + // *(volatile uint8_t*)(paddr + 0) = 0x90; + // *(volatile uint8_t*)(paddr + 1) = 0x90; + // *(volatile uint8_t*)(paddr + 2) = 0x90; + // *(volatile uint8_t*)(paddr + 3) = 0xEA; // jmp + // *(volatile uint16_t*)(paddr + 4) = 0x0000; // offset: 0000 + // *(volatile uint16_t*)(paddr + 6) = 0x0100; // cs:0100 + + extern char ap_boot_bgn; + extern char ap_boot_end; + uint32_t bytes = &ap_boot_end - &ap_boot_bgn; + + for (int i = 0; i < bytes; i++) { + ((uint8_t*)paddr)[i] = ((uint8_t*)&ap_boot_bgn)[i]; + } + + // 修正代码里跳入保护模式的地址和gdtr里的gdt的base地址 + extern uint8_t ap_code32_entry_address; + extern uint8_t ap_gdtr_base; + + uint32_t* dst = 0; + + // + dst = (uint32_t*)(paddr + (uint32_t)(&ap_code32_entry_address) - (uint32_t)(&ap_boot_bgn)); + (*dst) -= (uint32_t)(&ap_boot_bgn); + (*dst) += (paddr - KERNEL_VADDR_BASE); + + // + dst = (uint32_t*)(paddr + (uint32_t)(&ap_gdtr_base) - (uint32_t)(&ap_boot_bgn)); + (*dst) -= (uint32_t)(&ap_boot_bgn); + (*dst) += (paddr - KERNEL_VADDR_BASE); +} + void wait_ap_boot() { - printk("wait AP ready..."); + paddr_t ap_code_addr = 0x1000; + prepare_ap_code(ap_code_addr); + + void wakeup_ap(paddr_t paddr); + wakeup_ap(ap_code_addr); + + printk("wait AP ready...\n"); extern bool ap_ready(); while (!ap_ready()) { asm("pause"); @@ -140,12 +181,15 @@ void setup_kernel() { init_apic(); #endif - wait_ap_boot(); - #if 1 hpet_init(); #endif + // ap 启动需要用到 hpet来校准 + wait_ap_boot(); + + hpet_init_timer0(1); + #if !DISABLE_IDE void ide_init(); ide_init();