#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;
#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();
#include <linkage.h>
#include <msr.h>
#include <cpuid.h>
+#include <hpet.h>
extern pde_t* ap_pre_pgd;
#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;
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;
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)) {
// 开房时钟中断
lapic_t* lapic = system.lapic;
- uint32_t clkvec = 0x28;
uint32_t apic_base = 0;
apic_base = read_msr32(MSR_IA32_APIC_BASE);
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) {
}
}
-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;
}
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);
// imcr_init();
lapic_init();
ioapic_init();
-
- paddr_t ap_code_addr = 0x1000;
- prepare_ap_code(ap_code_addr);
- wakeup_ap(ap_code_addr);
-
}
// ## 中断路由路径配置矩阵
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();
}
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;
}
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;
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();
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;
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");
+}
.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
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");
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();