Selaa lähdekoodia

:wrench: 改用local apic定时器进行进程时间片更新

fslongjin 2 vuotta sitten
vanhempi
commit
defb9e769c

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

@@ -20,6 +20,17 @@ uint local_apic_max_LVT_entries;
 
 static struct acpi_Multiple_APIC_Description_Table_t *madt;
 static struct acpi_IO_APIC_Structure_t *io_apic_ICS;
+
+#define send_EOI()                                      \
+    do                                                  \
+    {                                                   \
+        __asm__ __volatile__("movq	$0x00,	%%rdx	\n\t"   \
+                             "movq	$0x00,	%%rax	\n\t"   \
+                             "movq 	$0x80b,	%%rcx	\n\t" \
+                             "wrmsr	\n\t" ::            \
+                                 : "memory");            \
+    } while (0)
+
 /**
  * @brief 初始化io_apic
  *
@@ -235,7 +246,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);
@@ -378,6 +389,10 @@ void apic_init()
     for (int i = 32; i <= 55; ++i)
         set_intr_gate(i, 0, interrupt_table[i - 32]);
 
+    // 设置local apic中断门
+    for (int i = 150; i < 160; ++i)
+        set_intr_gate(i, 0, local_apic_interrupt_table[i - 150]);
+
     /*
     // 初始化主芯片
     io_out8(0x20, 0x11); // 初始化主芯片的icw1
@@ -461,7 +476,7 @@ void do_IRQ(struct pt_regs *rsp, ul number)
         // ps: 当前已经将系统调用直接使用系统调用门实现,不走这里。。
         do_syscall_int(rsp, 0);
     }
-    else if (number > 0x80)
+    else if (number >= 200)
 
     {
         // printk_color(RED, BLACK, "SMP IPI [ %d ]\n", number);
@@ -473,6 +488,25 @@ void do_IRQ(struct pt_regs *rsp, ul number)
                 irq->handler(number, irq->parameter, rsp);
         }
     }
+    else if (number >= 150 && number < 160)
+    {
+        irq_desc_t *irq = &local_apic_interrupt_desc[number - 150];
+
+        // 执行中断上半部处理程序
+        if (irq != NULL && irq->handler != NULL)
+            irq->handler(number, irq->parameter, rsp);
+        else
+            kwarn("Intr vector [%d] does not have a handler!");
+        // 向中断控制器发送应答消息
+        if (irq->controller != NULL && irq->controller->ack != NULL)
+            irq->controller->ack(number);
+        else
+        {
+
+            // 向EOI寄存器写入0x00表示结束中断
+            send_EOI();
+        }
+    }
     else
     {
 
@@ -482,7 +516,7 @@ void do_IRQ(struct pt_regs *rsp, ul number)
     // kdebug("before softirq");
     // 进入软中断处理程序
     do_softirq();
-    
+
     // kdebug("after softirq");
     // 检测当前进程是否持有自旋锁,若持有自旋锁,则不进行抢占式的进程调度
     if (current_pcb->preempt_count > 0)

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

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

+ 82 - 1
kernel/driver/interrupt/apic/apic_timer.c

@@ -1,3 +1,84 @@
 #include "apic_timer.h"
+#include <exception/irq.h>
+#include <process/process.h>
+#include <common/kprint.h>
+#include <sched/sched.h>
 
-uint64_t apic_timer_ticksIn1ms = 0;
+uint64_t apic_timer_ticks_result = 0;
+
+void apic_timer_enable(uint64_t irq_num)
+{
+    // 启动apic定时器
+    uint64_t val = apic_timer_get_LVT();
+    val &= (~APIC_LVT_INT_MASKED);
+    apic_timer_write_LVT(val);
+}
+
+void apic_timer_disable(uint64_t irq_num)
+{
+    apic_timer_stop();
+}
+
+/**
+ * @brief 安装local apic定时器中断
+ *
+ * @param irq_num 中断向量号
+ * @param arg 初始计数值
+ * @return uint64_t
+ */
+uint64_t apic_timer_install(ul irq_num, void *arg)
+{
+    // 设置div16
+    apic_timer_stop();
+    apic_timer_set_div(APIC_TIMER_DIVISOR);
+
+    // 设置初始计数
+    apic_timer_set_init_cnt(*(uint64_t *)arg);
+    // 填写LVT
+    apic_timer_set_LVT(APIC_TIMER_IRQ_NUM, 1, APIC_LVT_Timer_Periodic);
+}
+
+void apic_timer_uninstall(ul irq_num)
+{
+    apic_timer_write_LVT(APIC_LVT_INT_MASKED);
+}
+
+hardware_intr_controller apic_timer_intr_controller =
+    {
+        .enable = apic_timer_enable,
+        .disable = apic_timer_disable,
+        .install = apic_timer_install,
+        .uninstall = apic_timer_uninstall,
+        .ack = apic_local_apic_edge_ack,
+};
+
+/**
+ * @brief local apic定时器的中断处理函数
+ *
+ * @param number 中断向量号
+ * @param param 参数
+ * @param regs 寄存器值
+ */
+void apic_timer_handler(uint64_t number, uint64_t param, struct pt_regs *regs)
+{
+
+    sched_update_jiffies();
+}
+
+/**
+ * @brief 初始化local APIC定时器
+ *
+ */
+void apic_timer_init()
+{
+    if (apic_timer_ticks_result == 0)
+    {
+        kBUG("APIC timer ticks in 5ms is equal to ZERO!");
+        while (1)
+            hlt();
+    }
+    kinfo("Initializing apic timer for cpu %d", proc_current_cpu_id);
+    
+    irq_register(APIC_TIMER_IRQ_NUM, &apic_timer_ticks_result, &apic_timer_handler, 0, &apic_timer_intr_controller, "apic timer");
+    kinfo("Successfully initialized apic timer for cpu %d", proc_current_cpu_id);
+}

+ 39 - 18
kernel/driver/interrupt/apic/apic_timer.h

@@ -3,7 +3,12 @@
 #include <common/unistd.h>
 #include "apic.h"
 
-extern uint64_t apic_timer_ticksIn1ms;
+extern uint64_t apic_timer_ticks_result;
+// 5ms产生一次中断
+#define APIC_TIMER_INTERVAL 5
+#define APIC_TIMER_DIVISOR 3
+
+#define APIC_TIMER_IRQ_NUM 151
 
 /**
  * @brief 设置apic定时器的分频计数
@@ -27,34 +32,50 @@ extern uint64_t apic_timer_ticksIn1ms;
         wrmsr(0x838, init_cnt);           \
     } while (0)
 
-/**
- * @brief 停止apic定时器
- * 
- */
-#define apic_timer_stop()                  \
-    do                                     \
-    {                                      \
-        wrmsr(0x832, APIC_LVT_INT_MASKED); \
-    } while (0)
-
 /**
  * @brief 设置apic定时器的lvt,并启动定时器
  *
+ * @param vector 中断向量号
+ * @param mask 是否屏蔽(1:屏蔽, 0:不屏蔽)
+ * @param mode 计时模式
  */
-#define apic_timer_set_LVT(vector, mode)     \
-    do                                       \
-    {                                        \
-        wrmsr(0x832, (mode << 17) | vector); \
-        io_mfence();                         \
+#define apic_timer_set_LVT(vector, mask, mode)                                    \
+    do                                                                            \
+    {                                                                             \
+        wrmsr(0x832, (mode << 17) | vector | (mask ? (APIC_LVT_INT_MASKED) : 0)); \
+    } while (0)
+
+#define apic_timer_write_LVT(value) \
+    do                              \
+    {                               \
+        wrmsr(0x832, value);        \
     } while (0)
 
 /**
  * @brief 获取apic定时器的LVT的值
- * 
+ *
  */
 #define apic_timer_get_LVT() (rdmsr(0x832))
 /**
  * @brief 获取apic定时器当前计数值
  *
  */
-#define apic_timer_get_current() (rdmsr(0x839))
+#define apic_timer_get_current() (rdmsr(0x839))
+
+/**
+ * @brief 停止apic定时器
+ *
+ */
+#define apic_timer_stop()                    \
+    do                                       \
+    {                                        \
+        uint64_t val = apic_timer_get_LVT(); \
+        val |= APIC_LVT_INT_MASKED;          \
+        apic_timer_write_LVT(val);           \
+    } while (0)
+
+/**
+ * @brief 初始化local APIC定时器
+ *
+ */
+void apic_timer_init();

+ 7 - 13
kernel/driver/timers/HPET/HPET.c

@@ -70,16 +70,11 @@ void HPET_handler(uint64_t number, uint64_t param, struct pt_regs *regs)
         if (container_of(list_next(&timer_func_head.list), struct timer_func_list_t, list)->expire_jiffies <= timer_jiffies)
             raise_softirq((1 << TIMER_SIRQ));
 
-        // if (current_pcb->pid == 2)
-        //     kwarn("timer_jiffies = %ld video_refresh_expire_jiffies=%ld", timer_jiffies, video_refresh_expire_jiffies);
         // 当时间到了,或进程发生切换时,刷新帧缓冲区
         if (timer_jiffies >= video_refresh_expire_jiffies || (video_last_refresh_pid != current_pcb->pid))
         {
             raise_softirq(VIDEO_REFRESH_SIRQ);
         }
-
-        sched_update_jiffies();
-
         break;
 
     default:
@@ -92,12 +87,12 @@ void HPET_handler(uint64_t number, uint64_t param, struct pt_regs *regs)
  * @brief 测量apic定时器频率的中断回调函数
  *
  */
-void HPET_measure_apic_timer_handler()
+void HPET_measure_apic_timer_handler(uint64_t number, uint64_t param, struct pt_regs *regs)
 {
     // 停止apic定时器
     // 写入每1ms的ticks
     apic_timer_stop();
-    apic_timer_ticksIn1ms = 0xFFFFFFFF - apic_timer_get_current();
+    apic_timer_ticks_result = 0xFFFFFFFF - apic_timer_get_current();
     measure_apic_timer_flag = true;
 }
 
@@ -108,7 +103,7 @@ void HPET_measure_apic_timer_handler()
 void HPET_measure_apic_timer_freq()
 {
     kinfo("Measuring local APIC timer's frequency...");
-    const uint64_t interval = 1; // 测量1毫秒内的计数
+    const uint64_t interval = APIC_TIMER_INTERVAL; // 测量给定时间内的计数
     struct apic_IO_APIC_RTE_entry entry;
 
     // 使用I/O APIC 的IRQ2接收hpet定时器0的中断
@@ -137,15 +132,14 @@ void HPET_measure_apic_timer_freq()
     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_div(APIC_TIMER_DIVISOR);
 
-    
+    // 设置初始计数
     apic_timer_set_init_cnt(0xFFFFFFFF);
 
     // 启动apic定时器
-    apic_timer_set_LVT(151, APIC_LVT_Timer_One_Shot);
+    apic_timer_set_LVT(151, 0, APIC_LVT_Timer_One_Shot);
     *(uint64_t *)(HPET_REG_BASE + GEN_CONF) = 3; // 置位旧设备中断路由兼容标志位、定时器组使能标志位,开始计时
     io_mfence();
 
@@ -156,7 +150,7 @@ void HPET_measure_apic_timer_freq()
 
     *(uint64_t *)(HPET_REG_BASE + GEN_CONF) = 0; // 停用HPET定时器
     io_mfence();
-    kinfo("Local APIC timer's freq: %d ticks/ms.", apic_timer_ticksIn1ms);
+    kinfo("Local APIC timer's freq: %d ticks/ms.", apic_timer_ticks_result);
 }
 
 /**

+ 43 - 7
kernel/exception/irq.c

@@ -1,4 +1,5 @@
 #include "irq.h"
+#include <common/errno.h>
 
 // 对进行
 #if _INTR_8259A_
@@ -148,6 +149,31 @@ void (*SMP_interrupt_table[SMP_IRQ_NUM])(void) =
         IRQ0xd1interrupt,
 };
 
+// 初始化local apic中断服务程序数组
+Build_IRQ(0x96);
+Build_IRQ(0x97);
+Build_IRQ(0x98);
+Build_IRQ(0x99);
+Build_IRQ(0x9a);
+Build_IRQ(0x9b);
+Build_IRQ(0x9c);
+Build_IRQ(0x9d);
+Build_IRQ(0x9e);
+Build_IRQ(0x9f);
+void (*local_apic_interrupt_table[LOCAL_APIC_IRQ_NUM])(void) =
+    {
+        IRQ0x96interrupt,
+        IRQ0x97interrupt,
+        IRQ0x98interrupt,
+        IRQ0x99interrupt,
+        IRQ0x9ainterrupt,
+        IRQ0x9binterrupt,
+        IRQ0x9cinterrupt,
+        IRQ0x9dinterrupt,
+        IRQ0x9einterrupt,
+        IRQ0x9finterrupt,
+};
+
 /**
  * @brief 中断注册函数
  *
@@ -162,14 +188,24 @@ void (*SMP_interrupt_table[SMP_IRQ_NUM])(void) =
 int irq_register(ul irq_num, void *arg, void (*handler)(ul irq_num, ul parameter, struct pt_regs *regs), ul paramater, hardware_intr_controller *controller, char *irq_name)
 {
     // 由于为I/O APIC分配的中断向量号是从32开始的,因此要减去32才是对应的interrupt_desc的元素
-    irq_desc_t *p = &interrupt_desc[irq_num - 32];
-
+    irq_desc_t *p = NULL;
+    if (irq_num >= 32 && irq_num < 0x80)
+        p = &interrupt_desc[irq_num - 32];
+    else if (irq_num >= 150 && irq_num < 160)
+        p = &local_apic_interrupt_desc[irq_num - 150];
+    else
+    {
+        kerror("irq_register(): invalid irq num: %ld.", irq_num);
+        return -EINVAL;
+    }
     p->controller = controller;
-
-    int namelen = sizeof(strlen(irq_name)+1);
-    p->irq_name = (char *)kmalloc(namelen,0);
-    memset(p->irq_name, 0, namelen);
-    strncpy(p->irq_name, irq_name, namelen);
+    if (p->irq_name == NULL)
+    {
+        int namelen = sizeof(strlen(irq_name) + 1);
+        p->irq_name = (char *)kmalloc(namelen, 0);
+        memset(p->irq_name, 0, namelen);
+        strncpy(p->irq_name, irq_name, namelen);
+    }
 
     p->parameter = paramater;
     p->flags = 0;

+ 4 - 1
kernel/exception/irq.h

@@ -16,6 +16,7 @@
 #include "../process/ptrace.h"
 
 #define SMP_IRQ_NUM 10
+#define LOCAL_APIC_IRQ_NUM 10
 extern void (*interrupt_table[24])(void);
 extern void do_IRQ(struct pt_regs *regs, ul number);
 
@@ -23,6 +24,7 @@ extern void do_IRQ(struct pt_regs *regs, ul number);
 extern void (*SMP_interrupt_table[SMP_IRQ_NUM])(void);
 
 extern void (*syscall_intr_table[1])(void);
+extern void (*local_apic_interrupt_table[LOCAL_APIC_IRQ_NUM])(void);
 
 /* ========= 中断向量分配表 ==========
 
@@ -128,9 +130,10 @@ typedef struct
 #define IRQ_NUM 24
 // 这两个表一定要放在这里,否则在HPET初始化后收到中断,会产生page fault
 irq_desc_t interrupt_desc[IRQ_NUM] = {0};
-
+irq_desc_t local_apic_interrupt_desc[20] = {0};
 irq_desc_t SMP_IPI_desc[SMP_IRQ_NUM] = {0};
 
+
 /**
  * @brief 中断注册函数
  * 

+ 6 - 5
kernel/main.c

@@ -32,6 +32,8 @@
 #include <driver/uart/uart.h>
 #include <driver/video/video.h>
 
+#include <driver/interrupt/apic/apic_timer.h>
+
 unsigned int *FR_address = (unsigned int *)0xb8000; //帧缓存区的地址
 ul bsp_idt_size, bsp_gdt_size;
 
@@ -39,7 +41,6 @@ struct memory_desc memory_management_struct = {{0}, 0};
 // struct Global_Memory_Descriptor memory_management_struct = {{0}, 0};
 void test_slab();
 
-
 struct gdtr gdtp;
 struct idtr idtp;
 void reload_gdt()
@@ -144,14 +145,15 @@ void system_initialize()
     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);
 
     // fat32_init();
-    // 系统初始化到此结束,剩下的初始化功能应当放在初始内核线程中执行
     HPET_enable();
+    // 系统初始化到此结束,剩下的初始化功能应当放在初始内核线程中执行
+    apic_timer_init();
 }
 
 //操作系统内核从这里开始执行
@@ -178,7 +180,6 @@ void Start_Kernel(void)
 
     system_initialize();
 
-    
     while (1)
         hlt();
 }
@@ -186,5 +187,5 @@ void Start_Kernel(void)
 void ignore_int()
 {
     kwarn("Unknown interrupt or fault at RIP.\n");
-    return;
+    while(1);
 }

+ 1 - 2
kernel/sched/sched.c

@@ -123,8 +123,7 @@ void sched_cfs()
  */
 void sched_update_jiffies()
 {
-    // if (current_pcb->cpu_id == 0)
-    //     return;
+    
     switch (current_pcb->priority)
     {
     case 0: