🆕 测量local apic定时器频率

This commit is contained in:
fslongjin 2022-07-11 18:40:23 +08:00
parent 7d64ad6c1a
commit 4c9719f477
11 changed files with 260 additions and 54 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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 // 边沿触发

View File

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

View File

@ -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))

View File

@ -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");
}
}

View File

@ -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();

View File

@ -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();
}
//操作系统内核从这里开始执行

View File

@ -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
*