From 4c9719f477371f00eafff342748a7231d8b35e05 Mon Sep 17 00:00:00 2001 From: fslongjin Date: Mon, 11 Jul 2022 18:40:23 +0800 Subject: [PATCH] =?UTF-8?q?:new:=20=E6=B5=8B=E9=87=8Flocal=20apic=E5=AE=9A?= =?UTF-8?q?=E6=97=B6=E5=99=A8=E9=A2=91=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/Makefile | 13 +- kernel/driver/Makefile | 4 +- kernel/driver/interrupt/Makefile | 14 ++ kernel/driver/interrupt/apic/apic.c | 5 +- kernel/driver/interrupt/apic/apic.h | 1 + kernel/driver/interrupt/apic/apic_timer.c | 3 + kernel/driver/interrupt/apic/apic_timer.h | 60 +++++++++ kernel/driver/timers/HPET/HPET.c | 155 ++++++++++++++++------ kernel/driver/timers/HPET/HPET.h | 14 +- kernel/main.c | 9 +- kernel/process/spinlock.h | 36 +++++ 11 files changed, 260 insertions(+), 54 deletions(-) create mode 100644 kernel/driver/interrupt/Makefile create mode 100644 kernel/driver/interrupt/apic/apic_timer.c create mode 100644 kernel/driver/interrupt/apic/apic_timer.h diff --git a/kernel/Makefile b/kernel/Makefile index b06ebed3..e9276439 100644 --- a/kernel/Makefile +++ b/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 diff --git a/kernel/driver/Makefile b/kernel/driver/Makefile index 0e9cf176..f297f37e 100644 --- a/kernel/driver/Makefile +++ b/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 diff --git a/kernel/driver/interrupt/Makefile b/kernel/driver/interrupt/Makefile new file mode 100644 index 00000000..4d02de52 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/kernel/driver/interrupt/apic/apic.c b/kernel/driver/interrupt/apic/apic.c index 880639ab..3d50e0c9 100644 --- a/kernel/driver/interrupt/apic/apic.c +++ b/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; diff --git a/kernel/driver/interrupt/apic/apic.h b/kernel/driver/interrupt/apic/apic.h index 82e6ab99..1e20b712 100644 --- a/kernel/driver/interrupt/apic/apic.h +++ b/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 // 边沿触发 diff --git a/kernel/driver/interrupt/apic/apic_timer.c b/kernel/driver/interrupt/apic/apic_timer.c new file mode 100644 index 00000000..04e18ee9 --- /dev/null +++ b/kernel/driver/interrupt/apic/apic_timer.c @@ -0,0 +1,3 @@ +#include "apic_timer.h" + +uint64_t apic_timer_ticksIn1ms = 0; \ No newline at end of file diff --git a/kernel/driver/interrupt/apic/apic_timer.h b/kernel/driver/interrupt/apic/apic_timer.h new file mode 100644 index 00000000..2c064020 --- /dev/null +++ b/kernel/driver/interrupt/apic/apic_timer.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#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)) \ No newline at end of file diff --git a/kernel/driver/timers/HPET/HPET.c b/kernel/driver/timers/HPET/HPET.c index 118b3c89..35a0a361 100644 --- a/kernel/driver/timers/HPET/HPET.c +++ b/kernel/driver/timers/HPET/HPET.c @@ -8,14 +8,16 @@ #include #include #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; // 导出全局墙上时钟 +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"); -} \ No newline at end of file +} diff --git a/kernel/driver/timers/HPET/HPET.h b/kernel/driver/timers/HPET/HPET.h index 4d41e25b..d94fc413 100644 --- a/kernel/driver/timers/HPET/HPET.h +++ b/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(); \ No newline at end of file +int HPET_init(); + +/** + * @brief 测定apic定时器的频率 + * + */ +void HPET_measure_apic_timer_freq(); + +/** + * @brief 启用HPET周期中断(5ms) + * + */ +void HPET_enable(); \ No newline at end of file diff --git a/kernel/main.c b/kernel/main.c index 211fe8b4..787a64bd 100644 --- a/kernel/main.c +++ b/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(); } //操作系统内核从这里开始执行 diff --git a/kernel/process/spinlock.h b/kernel/process/spinlock.h index d9ccaef6..36653b5d 100644 --- a/kernel/process/spinlock.h +++ b/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 尝试加锁 *