Browse Source

:new: 测量local apic定时器频率

fslongjin 2 years ago
parent
commit
4c9719f477

+ 3 - 10
kernel/Makefile

@@ -89,14 +89,7 @@ ipi.o: arch/x86_64/x86_64_ipi.c
 endif
 
 # 驱动程序
-# 中断处理芯片的驱动程序
-ifeq ($(PIC), _INTR_8259A_)
-pic.o: driver/interrupt/8259A/8259A.c
-	gcc $(CFLAGS) -c driver/interrupt/8259A/8259A.c -o driver/interrupt/pic.o
-else
-pic.o: driver/interrupt/apic/apic.c
-	gcc $(CFLAGS) -c driver/interrupt/apic/apic.c -o driver/interrupt/pic.o
-endif
+
 
 multiboot2.o: driver/multiboot2/multiboot2.c 
 	gcc $(CFLAGS) -c driver/multiboot2/multiboot2.c  -o driver/multiboot2/multiboot2.o
@@ -154,12 +147,12 @@ all: kernel
 	echo "Done."
 
 
-kernel: head.o entry.o main.o printk.o trap.o mm.o slab.o irq.o pic.o sched.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 softirq.o $(OBJ_LIST)
+kernel: head.o entry.o main.o printk.o trap.o mm.o slab.o irq.o sched.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 softirq.o $(OBJ_LIST)
 	
 	@list='$(kernel_subdirs)'; for subdir in $$list; do \
     		echo "make all in $$subdir";\
     		cd $$subdir;\
-    		$(MAKE) all CFLAGS="$(CFLAGS)" ASFLAGS="$(ASFLAGS)" kernel_root_path="$(shell pwd)";\
+    		$(MAKE) all CFLAGS="$(CFLAGS)" ASFLAGS="$(ASFLAGS)" PIC="$(PIC)" kernel_root_path="$(shell pwd)";\
     		cd ..;\
 	done
 

+ 2 - 2
kernel/driver/Makefile

@@ -1,13 +1,13 @@
 
 CFLAGS += -I .
 
-kernel_driver_subdirs:=video
+kernel_driver_subdirs:=video interrupt
 
 all: 
 	@list='$(kernel_driver_subdirs)'; for subdir in $$list; do \
     		echo "make all in $$subdir";\
     		cd $$subdir;\
-    		$(MAKE) all CFLAGS="$(CFLAGS)";\
+    		$(MAKE) all CFLAGS="$(CFLAGS)" PIC="$(PIC)";\
     		cd ..;\
 	done
 

+ 14 - 0
kernel/driver/interrupt/Makefile

@@ -0,0 +1,14 @@
+
+all: pic.o
+
+# 中断处理芯片的驱动程序
+ifeq ($(PIC), _INTR_8259A_)
+pic.o: 8259A/8259A.c
+	gcc $(CFLAGS) -c 8259A/8259A.c -o pic.o
+else
+pic.o: apic/apic.c apic_timer.o
+	gcc $(CFLAGS) -c apic/apic.c -o pic.o
+
+apic_timer.o: apic/apic_timer.c
+	gcc $(CFLAGS) -c apic/apic_timer.c -o apic/apic_timer.o
+endif

+ 3 - 2
kernel/driver/interrupt/apic/apic.c

@@ -187,7 +187,7 @@ void apic_init_ap_core_local_apic()
 void apic_local_apic_init()
 {
     // 映射Local APIC 寄存器地址
-    mm_map_phys_addr(APIC_LOCAL_APIC_VIRT_BASE_ADDR, 0xfee00000, PAGE_2M_SIZE, PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD);
+    mm_map_phys_addr(APIC_LOCAL_APIC_VIRT_BASE_ADDR, 0xfee00000UL, PAGE_2M_SIZE, PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD);
     uint a, b, c, d;
 
     cpu_cpuid(1, 0, &a, &b, &c, &d);
@@ -235,6 +235,7 @@ void apic_local_apic_init()
     // 检测是否成功启用xAPIC和x2APIC
     if (eax & 0xc00)
         kinfo("xAPIC & x2APIC enabled!");
+    
     /*
         io_mfence();
         uint *svr = (uint *)(APIC_LOCAL_APIC_VIRT_BASE_ADDR + LOCAL_APIC_OFFSET_Local_APIC_SVR);
@@ -336,7 +337,7 @@ void apic_local_apic_init()
     io_mfence();
     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;
+    *(uint *)(APIC_LOCAL_APIC_VIRT_BASE_ADDR + LOCAL_APIC_OFFSET_Local_APIC_LVT_TIMER) = APIC_LVT_INT_MASKED;
     io_mfence();
     /*
     *(uint *)(APIC_LOCAL_APIC_VIRT_BASE_ADDR + LOCAL_APIC_OFFSET_Local_APIC_LVT_THERMAL) = 0x1000000;

+ 1 - 0
kernel/driver/interrupt/apic/apic.h

@@ -202,6 +202,7 @@ struct apic_IO_APIC_RTE_entry
 // 屏蔽
 #define UNMASKED 0
 #define MASKED 1
+#define APIC_LVT_INT_MASKED 0x10000
 
 // 触发模式
 #define EDGE_TRIGGER 0  // 边沿触发

+ 3 - 0
kernel/driver/interrupt/apic/apic_timer.c

@@ -0,0 +1,3 @@
+#include "apic_timer.h"
+
+uint64_t apic_timer_ticksIn1ms = 0;

+ 60 - 0
kernel/driver/interrupt/apic/apic_timer.h

@@ -0,0 +1,60 @@
+#pragma once
+
+#include <common/unistd.h>
+#include "apic.h"
+
+extern uint64_t apic_timer_ticksIn1ms;
+
+/**
+ * @brief 设置apic定时器的分频计数
+ *
+ * @param divider 分频除数
+ */
+#define apic_timer_set_div(divider) \
+    do                              \
+    {                               \
+        wrmsr(0x83e, divider);      \
+    } while (0)
+
+/**
+ * @brief 设置apic定时器的初始计数值
+ *
+ * @param init_cnt 初始计数值
+ */
+#define apic_timer_set_init_cnt(init_cnt) \
+    do                                    \
+    {                                     \
+        wrmsr(0x838, init_cnt);           \
+    } while (0)
+
+/**
+ * @brief 停止apic定时器
+ * 
+ */
+#define apic_timer_stop()                  \
+    do                                     \
+    {                                      \
+        wrmsr(0x832, APIC_LVT_INT_MASKED); \
+    } while (0)
+
+/**
+ * @brief 设置apic定时器的lvt,并启动定时器
+ *
+ */
+#define apic_timer_set_LVT(vector, mode)     \
+    do                                       \
+    {                                        \
+        wrmsr(0x832, (mode << 17) | vector); \
+        io_mfence();                         \
+    } while (0)
+
+/**
+ * @brief 获取apic定时器的LVT的值
+ * 
+ */
+#define apic_timer_get_LVT() (rdmsr(0x832))
+/**
+ * @brief 获取apic定时器当前计数值
+ *
+ */
+#define apic_timer_get_current() (rdmsr(0x839))

+ 118 - 37
kernel/driver/timers/HPET/HPET.c

@@ -8,14 +8,16 @@
 #include <sched/sched.h>
 #include <smp/ipi.h>
 #include <driver/video/video.h>
+#include <driver/interrupt/apic/apic_timer.h>
+#include <process/spinlock.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; // 导出全局墙上时钟
+static char measure_apic_timer_flag;         // 初始化apic时钟时所用到的标志变量
+extern struct rtc_time_t rtc_now;            // 导出全局墙上时钟
 
 enum
 {
@@ -75,7 +77,6 @@ void HPET_handler(uint64_t number, uint64_t param, struct pt_regs *regs)
         {
             raise_softirq(VIDEO_REFRESH_SIRQ);
         }
-        
 
         sched_update_jiffies();
 
@@ -87,6 +88,118 @@ void HPET_handler(uint64_t number, uint64_t param, struct pt_regs *regs)
     }
 }
 
+/**
+ * @brief 测量apic定时器频率的中断回调函数
+ *
+ */
+void HPET_measure_apic_timer_handler()
+{
+    // 停止apic定时器
+    // 写入每1ms的ticks
+    apic_timer_stop();
+    apic_timer_ticksIn1ms = 0xFFFFFFFF - apic_timer_get_current();
+    measure_apic_timer_flag = true;
+}
+
+/**
+ * @brief 测定apic定时器的频率
+ *
+ */
+void HPET_measure_apic_timer_freq()
+{
+    kinfo("Measuring local APIC timer's frequency...");
+    const uint64_t interval = 1; // 测量1毫秒内的计数
+    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);
+
+    // 计算HPET0间隔多少个时钟周期触发一次中断
+    uint64_t clks_to_intr = 0.001 * interval * HPET_freq;
+    // kdebug("clks_to_intr=%#ld", clks_to_intr);
+    if (clks_to_intr <= 0 || clks_to_intr > (HPET_freq * 8))
+    {
+        kBUG("HPET0: Numof clocks to generate interrupt is INVALID! value=%lld", clks_to_intr);
+        while (1)
+            hlt();
+    }
+    *(uint64_t *)(HPET_REG_BASE + MAIN_CNT) = 0;
+    io_mfence();
+    *(uint64_t *)(HPET_REG_BASE + TIM0_CONF) = 0x0044; // 设置定时器0为非周期,边沿触发,默认投递到IO APIC的2号引脚
+    io_mfence();
+    *(uint64_t *)(HPET_REG_BASE + TIM0_COMP) = clks_to_intr;
+
+    io_mfence();
+
+    measure_apic_timer_flag = false;
+
+    // 注册中断
+    irq_register(34, &entry, &HPET_measure_apic_timer_handler, 0, &HPET_intr_controller, "HPET0 measure");
+
+    // 设置div16
+    apic_timer_set_div(0x3);
+    apic_timer_stop();
+    // 设置初始计数
+
+    
+    apic_timer_set_init_cnt(0xFFFFFFFF);
+
+    // 启动apic定时器
+    apic_timer_set_LVT(151, APIC_LVT_Timer_One_Shot);
+    *(uint64_t *)(HPET_REG_BASE + GEN_CONF) = 3; // 置位旧设备中断路由兼容标志位、定时器组使能标志位,开始计时
+    io_mfence();
+
+    while (measure_apic_timer_flag == false)
+        ;
+
+    irq_unregister(34);
+
+    *(uint64_t *)(HPET_REG_BASE + GEN_CONF) = 0; // 停用HPET定时器
+    io_mfence();
+    kinfo("Local APIC timer's freq: %d ticks/ms.", apic_timer_ticksIn1ms);
+}
+
+/**
+ * @brief 启用HPET周期中断(5ms)
+ *
+ */
+void HPET_enable()
+{
+    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);
+
+    // 计算HPET0间隔多少个时钟周期触发一次中断
+    uint64_t clks_to_intr = 0.001 * HPET0_INTERVAL * HPET_freq;
+    // kdebug("clks_to_intr=%#ld", clks_to_intr);
+    if (clks_to_intr <= 0 || clks_to_intr > (HPET_freq * 8))
+    {
+        kBUG("HPET0: Numof clocks to generate interrupt is INVALID! value=%lld", clks_to_intr);
+        while (1)
+            hlt();
+    }
+    // kdebug("[HPET0] conf register=%#018lx  conf register[63:32]=%#06lx", (*(uint64_t *)(HPET_REG_BASE + TIM0_CONF)), ((*(uint64_t *)(HPET_REG_BASE + TIM0_CONF))>>32)&0xffffffff);
+    *(uint64_t *)(HPET_REG_BASE + MAIN_CNT) = 0;
+    io_mfence();
+    *(uint64_t *)(HPET_REG_BASE + TIM0_CONF) = 0x004c; // 设置定时器0为周期定时,边沿触发,默认投递到IO APIC的2号引脚(看conf寄存器的高32bit,哪一位被置1,则可以投递到哪一个I/O apic引脚)
+    io_mfence();
+    *(uint64_t *)(HPET_REG_BASE + TIM0_COMP) = clks_to_intr; // 5ms触发一次中断
+
+    io_mfence();
+
+    // kdebug("[HPET0] conf register after modify=%#018lx", ((*(uint64_t *)(HPET_REG_BASE + TIM0_CONF))));
+    // kdebug("[HPET1] conf register =%#018lx", ((*(uint64_t *)(HPET_REG_BASE + TIM1_CONF))));
+
+    rtc_get_cmos_time(&rtc_now);
+
+    kinfo("HPET0 enabled.");
+
+    *(uint64_t *)(HPET_REG_BASE + GEN_CONF) = 3; // 置位旧设备中断路由兼容标志位、定时器组使能标志位
+    io_mfence();
+    // 注册中断
+    irq_register(34, &entry, &HPET_handler, 0, &HPET_intr_controller, "HPET0");
+}
+
 int HPET_init()
 {
     kinfo("Initializing HPET...");
@@ -151,40 +264,8 @@ int HPET_init()
     HPET_NUM_TIM_CAP = (tmp >> 8) & 0x1f; // 读取计时器数量
     kinfo("Total HPET timers: %d", HPET_NUM_TIM_CAP);
 
+    kinfo("HPET driver Initialized.");
     // kinfo("HPET CLK_PERIOD=%#03lx Frequency=%f", HPET_COUNTER_CLK_PERIOD, (double)HPET_freq);
     // kdebug("HPET_freq=%ld", (long)HPET_freq);
     // kdebug("HPET_freq=%lf", 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);
-
-    // 计算HPET0间隔多少个时钟周期触发一次中断
-    uint64_t clks_to_intr = 0.001 * HPET0_INTERVAL * HPET_freq;
-    // kdebug("clks_to_intr=%#ld", clks_to_intr);
-    if (clks_to_intr <= 0 || clks_to_intr > (HPET_freq * 8))
-    {
-        kBUG("HPET0: Numof clocks to generate interrupt is INVALID! value=%lld", clks_to_intr);
-        while (1)
-            hlt();
-    }
-    // kdebug("[HPET0] conf register=%#018lx  conf register[63:32]=%#06lx", (*(uint64_t *)(HPET_REG_BASE + TIM0_CONF)), ((*(uint64_t *)(HPET_REG_BASE + TIM0_CONF))>>32)&0xffffffff);
-    *(uint64_t *)(HPET_REG_BASE + MAIN_CNT) = 0;
-    io_mfence();
-    *(uint64_t *)(HPET_REG_BASE + TIM0_CONF) = 0x004c; // 设置定时器0为周期定时,边沿触发,默认投递到IO APIC的2号引脚(看conf寄存器的高32bit,哪一位被置1,则可以投递到哪一个I/O apic引脚)
-    io_mfence();
-    *(uint64_t *)(HPET_REG_BASE + TIM0_COMP) = clks_to_intr; // 5ms触发一次中断
-
-    io_mfence();
-
-    // kdebug("[HPET0] conf register after modify=%#018lx", ((*(uint64_t *)(HPET_REG_BASE + TIM0_CONF))));
-    // kdebug("[HPET1] conf register =%#018lx", ((*(uint64_t *)(HPET_REG_BASE + TIM1_CONF))));
-
-    rtc_get_cmos_time(&rtc_now);
-
-    kinfo("HPET Initialized.");
-    *(uint64_t *)(HPET_REG_BASE + GEN_CONF) = 3; // 置位旧设备中断路由兼容标志位、定时器组使能标志位
-    io_mfence();
-    // 注册中断
-    irq_register(34, &entry, &HPET_handler, 0, &HPET_intr_controller, "HPET0");
-}
+}

+ 13 - 1
kernel/driver/timers/HPET/HPET.h

@@ -7,4 +7,16 @@
 #define E_HPET_INIT_FAILED 1
 
 #define HPET0_INTERVAL 5    // HPET0定时器的中断间隔为5ms
-int HPET_init();
+int HPET_init();
+
+/**
+ * @brief 测定apic定时器的频率
+ *
+ */
+void HPET_measure_apic_timer_freq();
+
+/**
+ * @brief 启用HPET周期中断(5ms)
+ *
+ */
+void HPET_enable();

+ 7 - 2
kernel/main.c

@@ -140,13 +140,18 @@ void system_initialize()
     // test_mm();
 
     // process_init();
-    current_pcb->preempt_count = 0;
+    HPET_init();
+    HPET_measure_apic_timer_freq();
+    // current_pcb->preempt_count = 0;
+    // kdebug("cpu_get_core_crysral_freq()=%ld", cpu_get_core_crysral_freq());
+    // while(1);
     process_init();
     // 对显示模块进行高级初始化,启用double buffer
     video_init(true);
-    HPET_init();
+
     // fat32_init();
     // 系统初始化到此结束,剩下的初始化功能应当放在初始内核线程中执行
+    HPET_enable();
 }
 
 //操作系统内核从这里开始执行

+ 36 - 0
kernel/process/spinlock.h

@@ -51,6 +51,11 @@ void spin_lock(spinlock_t *lock)
     preempt_disable();
 }
 
+/**
+ * @brief 自旋锁解锁
+ * 
+ * @param lock 
+ */
 void spin_unlock(spinlock_t *lock)
 {
     preempt_enable();
@@ -58,6 +63,37 @@ void spin_unlock(spinlock_t *lock)
                          : "=m"(lock->lock)::"memory");
 }
 
+/**
+ * @brief 自旋锁加锁(不改变自旋锁持有计数)
+ * 
+ * @warning 慎用此函数,除非你有十足的把握不会产生自旋锁计数错误
+ */
+void spin_lock_no_preempt(spinlock_t *lock)
+{
+     __asm__ __volatile__("1:    \n\t"
+                         "lock decq %0   \n\t" // 尝试-1
+                         "jns 3f    \n\t"      // 加锁成功,跳转到步骤3
+                         "2:    \n\t"          // 加锁失败,稍后再试
+                         "pause \n\t"
+                         "cmpq $0, %0   \n\t"
+                         "jle   2b  \n\t" // 若锁被占用,则继续重试
+                         "jmp 1b    \n\t" // 尝试加锁
+                         "3:"
+                         : "=m"(lock->lock)::"memory");
+}
+/**
+ * @brief 自旋锁解锁(不改变自旋锁持有计数)
+ * 
+ * @warning 慎用此函数,除非你有十足的把握不会产生自旋锁计数错误
+ */
+void spin_unlock_no_preempt(spinlock_t * lock)
+{
+    __asm__ __volatile__("movq $1, %0   \n\t"
+                         : "=m"(lock->lock)::"memory");
+}
+
+
+
 /**
  * @brief 尝试加锁
  *