diff --git a/README.md b/README.md index 4908885e..6e9eb824 100644 --- a/README.md +++ b/README.md @@ -133,3 +133,5 @@ fslongjin - the GNU GRUB manual - Intel® 64 and IA-32 Architectures Software Developer’s Manual + +- IA-PC HPET (High Precision Event Timers) Specification diff --git a/README_EN.md b/README_EN.md index af26baf9..8526e2e2 100644 --- a/README_EN.md +++ b/README_EN.md @@ -130,3 +130,5 @@ This project refers to the following materials. I sincerely give my thanks to th - the GNU GRUB manual - Intel® 64 and IA-32 Architectures Software Developer’s Manual + +- IA-PC HPET (High Precision Event Timers) Specification diff --git a/kernel/Makefile b/kernel/Makefile index 94116600..dbd250f9 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -112,16 +112,19 @@ ahci.o: driver/disk/ahci/ahci.c rtc.o: driver/timers/rtc/rtc.c gcc $(CFLAGS) -c driver/timers/rtc/rtc.c -o driver/timers/rtc/rtc.o +HPET.o: driver/timers/HPET/HPET.c + gcc $(CFLAGS) -c driver/timers/HPET/HPET.c -o driver/timers/HPET/HPET.o + all: kernel objcopy -I elf64-x86-64 -O elf64-x86-64 -R ".comment" -R ".eh_frame" kernel ../bin/kernel/kernel.elf # -kernel: head.o entry.o main.o printk.o trap.o mm.o slab.o irq.o pic.o process.o syscall.o multiboot2.o cpu.o acpi.o ps2_keyboard.o ps2_mouse.o ata.o pci.o ahci.o smp.o apu_boot.o rtc.o $(OBJ_LIST) +kernel: head.o entry.o main.o printk.o trap.o mm.o slab.o irq.o pic.o process.o syscall.o multiboot2.o cpu.o acpi.o ps2_keyboard.o ps2_mouse.o ata.o pci.o ahci.o smp.o apu_boot.o rtc.o HPET.o $(OBJ_LIST) ld -b elf64-x86-64 -z muldefs -o kernel head.o exception/entry.o main.o common/printk.o exception/trap.o exception/irq.o mm/mm.o mm/slab.o process/process.o syscall/syscall.o driver/multiboot2/multiboot2.o \ common/cpu.o smp/smp.o smp/apu_boot.o \ - driver/acpi/acpi.o driver/interrupt/pic.o driver/keyboard/ps2_keyboard.o driver/mouse/ps2_mouse.o driver/disk/ata.o driver/pci/pci.o driver/disk/ahci/ahci.o driver/timers/rtc/rtc.o \ + driver/acpi/acpi.o driver/interrupt/pic.o driver/keyboard/ps2_keyboard.o driver/mouse/ps2_mouse.o driver/disk/ata.o driver/pci/pci.o driver/disk/ahci/ahci.o driver/timers/rtc/rtc.o driver/timers/HPET/HPET.o \ $(LD_LIST) \ -T link.lds diff --git a/kernel/driver/acpi/acpi.c b/kernel/driver/acpi/acpi.c index f1764eba..cfbacf2d 100644 --- a/kernel/driver/acpi/acpi.c +++ b/kernel/driver/acpi/acpi.c @@ -84,6 +84,22 @@ bool acpi_get_MADT(const struct acpi_system_description_table_header_t *_iter_da return true; } +/** + * @brief 获取HPET HPET_description_table + * + * @param _iter_data 要被迭代的信息的结构体 + * @param _data 返回的HPET表的虚拟地址 + * @return true + * @return false + */ +bool acpi_get_HPET(const struct acpi_system_description_table_header_t *_iter_data, void *_data) +{ + if (!(_iter_data->Signature[0] == 'H' && _iter_data->Signature[1] == 'P' && _iter_data->Signature[2] == 'E' && _iter_data->Signature[3] == 'T')) + return false; + *(ul *)_data = (ul)_iter_data; + return true; +} + /** * @brief 初始化acpi模块 diff --git a/kernel/driver/acpi/acpi.h b/kernel/driver/acpi/acpi.h index 2ae3a21f..f2dfaa3b 100644 --- a/kernel/driver/acpi/acpi.h +++ b/kernel/driver/acpi/acpi.h @@ -42,7 +42,7 @@ struct acpi_RSDP_t // 32bit physical address of the RSDT uint RsdtAddress; -}; +} __attribute__((packed)); struct acpi_RSDP_2_t { @@ -56,7 +56,7 @@ struct acpi_RSDP_2_t unsigned char ExtendedChecksum; // 整个表的checksum,包括了之前的checksum区域 unsigned char Reserved[3]; -}; +} __attribute__((packed)); struct acpi_system_description_table_header_t { @@ -74,7 +74,32 @@ struct acpi_system_description_table_header_t uint OEMRevision; uint CreatorID; uint CreatorRevision; -}; +} __attribute__((packed)); + +// HPET描述符结构体,sign为HPET +struct acpi_HPET_description_table_t +{ + struct acpi_system_description_table_header_t header; + + uint8_t hardware_rev_id; + uint8_t comparator_count : 5; // Number of Comparators in 1st Timer Block + uint8_t counter_size : 1; // COUNT_SIZE_CAP counter size + uint8_t reserved0 : 1; + uint8_t legacy_replacement : 1; // LegacyReplacement IRQ Routing Capable + uint16_t pci_vendor_id; // PCI Vendor ID of 1st Timer Block + + uint8_t address_space_id; // 0 - system memory, 1 - system I/O + uint8_t register_bit_width; + uint8_t register_bit_offset; + uint8_t reserved1; + uint64_t address; + + uint8_t hpet_number; + uint16_t minimum_tick; // The minimum clock ticks can be set without lost interrupts while the counter is programmed to operate in periodic mode + + uint8_t page_protection; + +} __attribute__((packed)); // =========== MADT结构,其中Signature为APIC ============ struct acpi_Multiple_APIC_Description_Table_t @@ -161,7 +186,15 @@ void acpi_iter_SDT(bool (*_fun)(const struct acpi_system_description_table_heade */ bool acpi_get_MADT(const struct acpi_system_description_table_header_t *_iter_data, void *_data); - +/** + * @brief 获取HPET HPET_description_table + * + * @param _iter_data 要被迭代的信息的结构体 + * @param _data 返回的HPET表的虚拟地址 + * @return true + * @return false + */ +bool acpi_get_HPET(const struct acpi_system_description_table_header_t *_iter_data, void *_data); // 初始化acpi模块 void acpi_init(); \ No newline at end of file diff --git a/kernel/driver/interrupt/apic/apic.c b/kernel/driver/interrupt/apic/apic.c index a81bef32..aafd0e28 100644 --- a/kernel/driver/interrupt/apic/apic.c +++ b/kernel/driver/interrupt/apic/apic.c @@ -27,9 +27,9 @@ void apic_io_apic_init() acpi_iter_SDT(acpi_get_MADT, &madt_addr); madt = (struct acpi_Multiple_APIC_Description_Table_t *)madt_addr; - //kdebug("MADT->local intr controller addr=%#018lx", madt->Local_Interrupt_Controller_Address); - //kdebug("MADT->length= %d bytes", madt->header.Length); - // 寻找io apic的ICS + // kdebug("MADT->local intr controller addr=%#018lx", madt->Local_Interrupt_Controller_Address); + // kdebug("MADT->length= %d bytes", madt->header.Length); + // 寻找io apic的ICS void *ent = (void *)(madt_addr) + sizeof(struct acpi_Multiple_APIC_Description_Table_t); struct apic_Interrupt_Controller_Structure_header_t *header = (struct apic_Interrupt_Controller_Structure_header_t *)ent; while (header->length > 2) @@ -181,7 +181,7 @@ void apic_init_ap_core_local_apic() kdebug("cmci = %#018lx", *(uint *)(APIC_LOCAL_APIC_VIRT_BASE_ADDR + LOCAL_APIC_OFFSET_Local_APIC_LVT_CMCI)); */ //*(uint *)(APIC_LOCAL_APIC_VIRT_BASE_ADDR + LOCAL_APIC_OFFSET_Local_APIC_LVT_TIMER) = 0x10000; - //io_mfence(); + // io_mfence(); /* *(uint *)(APIC_LOCAL_APIC_VIRT_BASE_ADDR + LOCAL_APIC_OFFSET_Local_APIC_LVT_THERMAL) = 0x1000000; io_mfence(); @@ -628,4 +628,46 @@ uint apic_get_ics(const uint type, ul ret_vaddr[], uint *total) return APIC_E_NOTFOUND; else return APIC_SUCCESS; +} + +/** + * @brief 构造RTE Entry结构体 + * + * @param entry 返回的结构体 + * @param vector 中断向量 + * @param deliver_mode 投递模式 + * @param dest_mode 目标模式 + * @param deliver_status 投递状态 + * @param polarity 电平触发极性 + * @param irr 远程IRR标志位(只读) + * @param trigger 触发模式 + * @param mask 屏蔽标志位,(0为未屏蔽, 1为已屏蔽) + * @param dest_apicID 目标apicID + */ +void apic_make_rte_entry(struct apic_IO_APIC_RTE_entry *entry, uint8_t vector, uint8_t deliver_mode, uint8_t dest_mode, + uint8_t deliver_status, uint8_t polarity, uint8_t irr, uint8_t trigger, uint8_t mask, uint8_t dest_apicID) +{ + + entry->vector = vector; + entry->deliver_mode = deliver_mode; + entry->dest_mode = dest_mode; + entry->deliver_status = deliver_status; + entry->polarity = polarity; + entry->remote_IRR = irr; + entry->trigger_mode = trigger; + entry->mask = mask; + + entry->reserved = 0; + + if (dest_mode == DEST_PHYSICAL) + { + entry->destination.physical.phy_dest = dest_apicID; + entry->destination.physical.reserved1 = 0; + entry->destination.physical.reserved2 = 0; + } + else + { + entry->destination.logical.logical_dest = dest_apicID; + entry->destination.logical.reserved1 = 0; + } } \ No newline at end of file diff --git a/kernel/driver/interrupt/apic/apic.h b/kernel/driver/interrupt/apic/apic.h index 5875c1cb..8df6b0b3 100644 --- a/kernel/driver/interrupt/apic/apic.h +++ b/kernel/driver/interrupt/apic/apic.h @@ -73,8 +73,6 @@ // 分频配置寄存器(定时器专用) #define LOCAL_APIC_OFFSET_Local_APIC_CLKDIV 0x3e0 - - /* 1: LVT CMCI @@ -269,10 +267,9 @@ ul apic_ioapic_read_rte(unsigned char index); */ void apic_ioapic_write_rte(unsigned char index, ul value); - /** * @brief 初始化AP处理器的Local apic - * + * */ void apic_init_ap_core_local_apic(); @@ -284,13 +281,13 @@ void apic_init(); /** * @brief 读取指定类型的 Interrupt Control Structure - * + * * @param type ics的类型 * @param ret_vaddr 对应的ICS的虚拟地址数组 * @param total 返回数组的元素总个数 - * @return uint + * @return uint */ -uint apic_get_ics(const uint type, ul ret_vaddr[], uint * total); +uint apic_get_ics(const uint type, ul ret_vaddr[], uint *total); // =========== 中断控制操作接口 ============ void apic_ioapic_enable(ul irq_num); @@ -301,4 +298,21 @@ void apic_ioapic_level_ack(ul irq_num); // ioapic电平触发 应答 void apic_ioapic_edge_ack(ul irq_num); // ioapic边沿触发 应答 // void apic_local_apic_level_ack(ul irq_num);// local apic电平触发 应答 -void apic_local_apic_edge_ack(ul irq_num);// local apic边沿触发 应答 +void apic_local_apic_edge_ack(ul irq_num); // local apic边沿触发 应答 + +/** + * @brief 构造RTE Entry结构体 + * + * @param entry 返回的结构体 + * @param vector 中断向量 + * @param deliver_mode 投递模式 + * @param dest_mode 目标模式 + * @param deliver_status 投递状态 + * @param polarity 电平触发极性 + * @param irr 远程IRR标志位(只读) + * @param trigger 触发模式 + * @param mask 屏蔽标志位,(0为未屏蔽, 1为已屏蔽) + * @param dest_apicID 目标apicID + */ +void apic_make_rte_entry(struct apic_IO_APIC_RTE_entry *entry, uint8_t vector, uint8_t deliver_mode, uint8_t dest_mode, + uint8_t deliver_status, uint8_t polarity, uint8_t irr, uint8_t trigger, uint8_t mask, uint8_t dest_apicID); \ No newline at end of file diff --git a/kernel/driver/timers/HPET/HPET.c b/kernel/driver/timers/HPET/HPET.c new file mode 100644 index 00000000..1933f73b --- /dev/null +++ b/kernel/driver/timers/HPET/HPET.c @@ -0,0 +1,102 @@ +#include "HPET.h" +#include +#include +#include + +static struct acpi_HPET_description_table_t *hpet_table; +static uint64_t HPET_REG_BASE = 0; +static uint32_t HPET_COUNTER_CLK_PERIOD = 0; // 主计数器时间精度(单位:飞秒) +static double HPET_freq = 0; // 主计时器频率 +static uint8_t HPET_NUM_TIM_CAP = 0; // 定时器数量 + +extern struct rtc_time_t rtc_now; // 导出全局墙上时钟 + +enum +{ + GCAP_ID = 0x00, + GEN_CONF = 0x10, + GINTR_STA = 0x20, + MAIN_CNT = 0xf0, + TIM0_CONF = 0x100, + TIM0_COMP = 0x108, + TIM1_CONF = 0x120, + TIM1_COMP = 0x128, + TIM2_CONF = 0x140, + TIM2_COMP = 0x148, + TIM3_CONF = 0x160, + TIM3_COMP = 0x168, + TIM4_CONF = 0x180, + TIM4_COMP = 0x188, + TIM5_CONF = 0x1a0, + TIM5_COMP = 0x1a8, + TIM6_CONF = 0x1c0, + TIM6_COMP = 0x1c8, + TIM7_CONF = 0x1e0, + TIM7_COMP = 0x1e8, +}; + +hardware_intr_controller HPET_intr_controller = + { + .enable = apic_ioapic_enable, + .disable = apic_ioapic_disable, + .install = apic_ioapic_install, + .uninstall = apic_ioapic_uninstall, + .ack = apic_ioapic_edge_ack, +}; + +void HPET_handler(uint64_t number, uint64_t param, struct pt_regs *regs) +{ + //printk_color(ORANGE, BLACK, "(HPET)"); + switch (param) + { + case 0: + rtc_get_cmos_time(&rtc_now); + break; + + default: + break; + } +} + +int HPET_init() +{ + // 从acpi获取hpet结构体 + ul hpet_table_addr = 0; + acpi_iter_SDT(acpi_get_HPET, &hpet_table_addr); + if (hpet_table_addr == 0) + { + kerror("HPET Not Found On This Computer!"); + return E_HPET_INIT_FAILED; + } + hpet_table = (struct acpi_HPET_description_table_t *)hpet_table_addr; + // 由于这段内存与io/apic的映射在同一物理页内,因此不需要重复映射 + HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + hpet_table->address; + + // 读取计时精度并计算频率 + uint64_t tmp; + tmp = *(uint64_t *)(HPET_REG_BASE + GCAP_ID); + HPET_COUNTER_CLK_PERIOD = (tmp >> 32) & 0xffffffff; + HPET_freq = 1.0 * 1e15 / HPET_COUNTER_CLK_PERIOD; + HPET_NUM_TIM_CAP = (tmp >> 8) & 0x1f; // 读取计时器数量 + + kinfo("HPET CLK_PERIOD=%#03lx Frequency=%f", HPET_COUNTER_CLK_PERIOD, HPET_freq); + struct apic_IO_APIC_RTE_entry entry; + // 使用I/O APIC 的IRQ2接收hpet定时器0的中断 + apic_make_rte_entry(&entry, 34, IO_APIC_FIXED, DEST_PHYSICAL, IDLE, POLARITY_HIGH, IRR_RESET, EDGE_TRIGGER, MASKED, 0); + // 注册中断 + irq_register(34, &entry, &HPET_handler, 0, &HPET_intr_controller, "HPET0"); + + *(uint64_t *)(HPET_REG_BASE + GEN_CONF) = 3; // 置位旧设备中断路由兼容标志位、定时器组使能标志位 + io_mfence(); + + *(uint64_t *)(HPET_REG_BASE + TIM0_CONF) = 0x004c; // 设置定时器0为周期定时,边沿触发,投递到IO APIC的2号引脚(这里有点绕,写的是8259的引脚号,但是因为禁用了8259,因此会被路由到IO APIC的2号引脚) + io_mfence(); + *(uint64_t *)(HPET_REG_BASE + TIM0_COMP) = HPET_freq; // 1s触发一次中断 + io_mfence(); + + rtc_get_cmos_time(&rtc_now); + *(uint64_t *)(HPET_REG_BASE + MAIN_CNT) = 0; + io_mfence(); + + +} \ No newline at end of file diff --git a/kernel/driver/timers/HPET/HPET.h b/kernel/driver/timers/HPET/HPET.h new file mode 100644 index 00000000..3217f3ae --- /dev/null +++ b/kernel/driver/timers/HPET/HPET.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include +#include + +#define E_HPET_INIT_FAILED 1 +int HPET_init(); \ No newline at end of file diff --git a/kernel/driver/timers/rtc/rtc.c b/kernel/driver/timers/rtc/rtc.c index 239471d3..219b0a5a 100644 --- a/kernel/driver/timers/rtc/rtc.c +++ b/kernel/driver/timers/rtc/rtc.c @@ -2,12 +2,12 @@ #include /*置位0x70的第7位,禁止不可屏蔽中断*/ -/* + #define read_cmos(addr) ({ \ io_out8(0x70, 0x80 | addr); \ io_in8(0x71); \ }) -*/ + enum CMOSTimeSelector { T_SECOND = 0x0, @@ -18,14 +18,8 @@ enum CMOSTimeSelector T_YEAR = 0x9, }; -int read_cmos(uint8_t addr) -{ - io_out8(0x70, 0x80 | addr); - io_mfence(); - return (uint8_t)(io_in8(0x71) & 0xff); -} -int rtc_get_cmos_time(struct time *t) +int rtc_get_cmos_time(struct rtc_time_t *t) { // 为防止中断请求打断该过程,需要先关中断 cli(); diff --git a/kernel/driver/timers/rtc/rtc.h b/kernel/driver/timers/rtc/rtc.h index cae8224a..244f0a9a 100644 --- a/kernel/driver/timers/rtc/rtc.h +++ b/kernel/driver/timers/rtc/rtc.h @@ -1,6 +1,6 @@ #pragma once #include -struct time +struct rtc_time_t { int second; int minute; @@ -8,7 +8,7 @@ struct time int day; int month; int year; -}; +}rtc_now; // rtc_now为墙上时钟,由HPET定时器0维护 /** * @brief 从主板cmos中获取时间 @@ -16,5 +16,4 @@ struct time * @param t time结构体 * @return int 成功则为0 */ -int rtc_get_cmos_time(struct time*t); -int get_cmos_time(struct time *time); \ No newline at end of file +int rtc_get_cmos_time(struct rtc_time_t*t); \ No newline at end of file diff --git a/kernel/main.c b/kernel/main.c index 4765ca1f..46d2a2be 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -23,6 +23,7 @@ #include "driver/pci/pci.h" #include "driver/disk/ahci/ahci.h" #include +#include unsigned int *FR_address = (unsigned int *)0xb8000; //帧缓存区的地址 @@ -166,8 +167,9 @@ void system_initialize() acpi_init(); // 初始化中断模块 irq_init(); - - + + HPET_init(); + smp_init(); // 先初始化系统调用模块 @@ -229,10 +231,16 @@ void Start_Kernel(void) */ // ipi_send_IPI(DEST_PHYSICAL, IDLE, ICR_LEVEL_DE_ASSERT, EDGE_TRIGGER, 0xc8, ICR_APIC_FIXED, ICR_No_Shorthand, true, 1); // 测试ipi - struct time tt; - rtc_get_cmos_time(&tt); - kinfo("Current Time: %04d/%02d/%02d %02d:%02d:%02d", tt.year, tt.month, tt.day, tt.hour, tt.minute, tt.second); + int last_sec = rtc_now.second; + while (1) + { + if (last_sec != rtc_now.second) + { + last_sec = rtc_now.second; + kinfo("Current Time: %04d/%02d/%02d %02d:%02d:%02d", rtc_now.year, rtc_now.month, rtc_now.day, rtc_now.hour, rtc_now.minute, rtc_now.second); + } + } while (1) hlt(); }