#pragma once
+#include <linkage.h>
+
#define LAPIC_MSR_BASE 0x800
// APIC的ID寄存器
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;
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);
// 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
// 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);
}