]> Zhao Yanbai Git Server - kernel.git/commitdiff
用HPET#0校准AP的LAPIC时钟中断
authoracevest <zhaoyanbai@126.com>
Fri, 9 Jan 2026 13:54:51 +0000 (21:54 +0800)
committeracevest <zhaoyanbai@126.com>
Fri, 9 Jan 2026 13:54:51 +0000 (21:54 +0800)
include/apic.h
include/hpet.h
kernel/ap.c
kernel/apic.c
kernel/hpet.c
kernel/interrupts.S
kernel/setup.c

index 8a34a176f55f0e145e3f29b6b61e18e492bcb579..29550948772197ecc38b3992c67718e925b4bd04 100644 (file)
 #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;
 
index bbe6b00f28ad5365b3d9688e9b3a621835083722..e421fd4481a17f08e0c06315594d4b64c1ed825c 100644 (file)
 #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();
index 898548204a721f9e5acf18163b2876765e409f10..b866167c33c4e724fc903c26056b2a9e91548e68 100644 (file)
@@ -14,6 +14,7 @@
 #include <linkage.h>
 #include <msr.h>
 #include <cpuid.h>
+#include <hpet.h>
 
 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;
 }
index 802f02d0260cae013357fedee517a2f85d5fe391..7b7141953efbcc96dc5b220fdf708e0b563c11cb 100644 (file)
@@ -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);
-
 }
 
 // ## 中断路由路径配置矩阵
index 00603a9f78c2320b43a89116cf9f597852f96725..e115a746b301f1d3e17882e3b26723c5287d0de5 100644 (file)
@@ -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");
+}
index a2c9bd073b79ed1fce12065f72d3bfe70f7c8ccd..f4128be656100031f537f35ebd9e2979881a8f79 100644 (file)
@@ -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
index 60171440053431b7c929f83e30cfedaf0f536ea7..e3b13d8858cbd5d9989cb9456753140219ea928c 100644 (file)
@@ -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();