Browse Source

:new: HPET驱动

fslongjin 2 years ago
parent
commit
d887f9a0f7

+ 2 - 0
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

+ 2 - 0
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

+ 5 - 2
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
 

+ 16 - 0
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模块

+ 37 - 4
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();

+ 46 - 4
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;
+    }
 }

+ 22 - 8
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);

+ 102 - 0
kernel/driver/timers/HPET/HPET.c

@@ -0,0 +1,102 @@
+#include "HPET.h"
+#include <common/kprint.h>
+#include <mm/mm.h>
+#include <driver/interrupt/apic/apic.h>
+
+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();
+
+    
+}

+ 8 - 0
kernel/driver/timers/HPET/HPET.h

@@ -0,0 +1,8 @@
+#pragma once
+
+#include <common/glib.h>
+#include <driver/acpi/acpi.h>
+#include<driver/timers/rtc/rtc.h>
+
+#define E_HPET_INIT_FAILED 1
+int HPET_init();

+ 3 - 9
kernel/driver/timers/rtc/rtc.c

@@ -2,12 +2,12 @@
 #include <common/kprint.h>
 
 /*置位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();

+ 3 - 4
kernel/driver/timers/rtc/rtc.h

@@ -1,6 +1,6 @@
 #pragma once
 #include <common/glib.h>
-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);
+int rtc_get_cmos_time(struct rtc_time_t*t);

+ 13 - 5
kernel/main.c

@@ -23,6 +23,7 @@
 #include "driver/pci/pci.h"
 #include "driver/disk/ahci/ahci.h"
 #include <driver/timers/rtc/rtc.h>
+#include <driver/timers/HPET/HPET.h>
 
 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();
 }