183 lines
5.8 KiB
C
183 lines
5.8 KiB
C
/* lapic_timer.c — 宿主自持 LAPIC 定时器(调度器 tick 源)
|
||||
|
|
*
|
|||
|
|
* 为什么不用 PIT 周期中断:默认 FUCK apic_route=1 时 apic.drv 验证完
|
|||
|
|
* tick 链路后重新 mask GSI2(PIT IRQ0 线),PIT 周期中断不可达;
|
|||
|
|
* LAPIC timer 不经 IOAPIC/PIC 路由,与路由模式解耦,且是 ROADMAP
|
|||
|
|
* Phase 2 "LAPIC timer 替代 PIT" 的目标方案。
|
|||
|
|
*
|
|||
|
|
* 流程:MSR 0x1B → 物理基址 → map_mmio(SAS-R0 直接扩当前 CR3)
|
|||
|
|
* → PIT ch0 单发 10ms 采样 CCR 差值校准
|
|||
|
|
* → irq_vector_alloc 动态向量 + LVT periodic(先 mask,start 再放开)
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
#include <utsm/lapic_timer.h>
|
|||
|
|
#include <utsm/pit.h>
|
|||
|
|
#include <utsm/paging.h>
|
|||
|
|
#include <utsm/idt.h>
|
|||
|
|
#include <utsm/dkm.h>
|
|||
|
|
#include <utsm/log.h>
|
|||
|
|
|
|||
|
|
extern void outb(u16 port, u8 value);
|
|||
|
|
extern u8 inb(u16 port);
|
|||
|
|
|
|||
|
|
#define MSR_IA32_APIC_BASE 0x1Bu
|
|||
|
|
#define APIC_BASE_ENABLE (1ULL << 11)
|
|||
|
|
#define APIC_BASE_X2APIC (1ULL << 10)
|
|||
|
|
#define LAPIC_DEFAULT_PHYS 0xFEE00000ULL
|
|||
|
|
|
|||
|
|
#define LAPIC_EOI 0x0B0u
|
|||
|
|
#define LAPIC_TPR 0x080u
|
|||
|
|
#define LAPIC_SPURIOUS 0x0F0u
|
|||
|
|
#define LAPIC_LVT_TIMER 0x320u
|
|||
|
|
#define LAPIC_DIV_CONF 0x3E0u
|
|||
|
|
#define LAPIC_INIT_CNT 0x380u
|
|||
|
|
#define LAPIC_CUR_CNT 0x390u
|
|||
|
|
|
|||
|
|
#define LVT_MASK (1u << 16)
|
|||
|
|
#define LVT_PERIODIC (1u << 17)
|
|||
|
|
|
|||
|
|
static volatile u32 *g_lapic = 0;
|
|||
|
|
static int g_timer_vector = -1;
|
|||
|
|
|
|||
|
|
static inline u64 rdmsr_(u32 msr) {
|
|||
|
|
u32 lo, hi;
|
|||
|
|
__asm__ volatile("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr));
|
|||
|
|
return ((u64)hi << 32) | lo;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static inline void wrmsr_(u32 msr, u64 v) {
|
|||
|
|
u32 lo = (u32)v, hi = (u32)(v >> 32);
|
|||
|
|
__asm__ volatile("wrmsr" :: "c"(msr), "a"(lo), "d"(hi));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static u32 lapic_read(u32 reg) { return g_lapic[reg / 4]; }
|
|||
|
|
static void lapic_write(u32 reg, u32 v) { g_lapic[reg / 4] = v; }
|
|||
|
|
|
|||
|
|
int lapic_timer_init(u32 freq_hz) {
|
|||
|
|
if (freq_hz == 0) freq_hz = 100;
|
|||
|
|
|
|||
|
|
if (!g_lapic) {
|
|||
|
|
u64 base = rdmsr_(MSR_IA32_APIC_BASE);
|
|||
|
|
if (!(base & APIC_BASE_ENABLE)) {
|
|||
|
|
wrmsr_(MSR_IA32_APIC_BASE, base | APIC_BASE_ENABLE);
|
|||
|
|
base = rdmsr_(MSR_IA32_APIC_BASE);
|
|||
|
|
}
|
|||
|
|
if (base & APIC_BASE_X2APIC) {
|
|||
|
|
log_warn("[LAPIC] x2APIC mode, MMIO timer unsupported");
|
|||
|
|
return -1;
|
|||
|
|
}
|
|||
|
|
u64 phys = base & 0xFFFFF000ULL;
|
|||
|
|
if (phys == 0) phys = LAPIC_DEFAULT_PHYS;
|
|||
|
|
|
|||
|
|
const dkm_mmio_api *mm = paging_get_api();
|
|||
|
|
if (!mm || !mm->map_mmio) return -2;
|
|||
|
|
if (mm->map_mmio(phys, 0x1000) != 0) {
|
|||
|
|
log_warn("[LAPIC] map_mmio failed");
|
|||
|
|
return -3;
|
|||
|
|
}
|
|||
|
|
u64 hhdm = 0;
|
|||
|
|
const dkm_kernel_api *api = dkm_get_kernel_api();
|
|||
|
|
if (api) hhdm = api->hhdm_offset;
|
|||
|
|
g_lapic = (volatile u32 *)(hhdm + phys);
|
|||
|
|
if (mm->is_mapped && !mm->is_mapped((u64)g_lapic)) {
|
|||
|
|
g_lapic = 0;
|
|||
|
|
log_warn("[LAPIC] mapped page not reachable");
|
|||
|
|
return -4;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 软件使能(spurious bit8);vector 保留既有值,无则 0xFF */
|
|||
|
|
u32 svr = lapic_read(LAPIC_SPURIOUS);
|
|||
|
|
if (!(svr & 0x100u)) {
|
|||
|
|
u8 vec = (u8)(svr & 0xFFu);
|
|||
|
|
if (vec == 0) vec = 0xFFu;
|
|||
|
|
lapic_write(LAPIC_SPURIOUS, (u32)vec | 0x100u);
|
|||
|
|
}
|
|||
|
|
lapic_write(LAPIC_TPR, 0);
|
|||
|
|
log_info("[LAPIC] mmio mapped");
|
|||
|
|
log_hex64("[LAPIC] phys=", phys);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ---- PIT 单发校准:divide=16,10ms 窗口采样 CCR 差值 ---- */
|
|||
|
|
lapic_write(LAPIC_DIV_CONF, 0x3); /* divide by 16 */
|
|||
|
|
lapic_write(LAPIC_LVT_TIMER, (u32)0xFF | LVT_MASK | LVT_PERIODIC); /* 校准期 mask */
|
|||
|
|
lapic_write(LAPIC_INIT_CNT, 0xFFFFFFFFu);
|
|||
|
|
u32 ccr0 = 0;
|
|||
|
|
for (int i = 0; i < 1000; i++) {
|
|||
|
|
ccr0 = lapic_read(LAPIC_CUR_CNT);
|
|||
|
|
if (ccr0 != 0) break;
|
|||
|
|
__asm__ volatile("pause");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
pit_oneshot_ms(10);
|
|||
|
|
u16 prev = pit_latch_count();
|
|||
|
|
u32 loops = 0;
|
|||
|
|
for (;;) {
|
|||
|
|
u16 cur = pit_latch_count();
|
|||
|
|
if (cur > prev && loops > 10) break; /* 回绕 = 10ms 到期 */
|
|||
|
|
prev = cur;
|
|||
|
|
loops++;
|
|||
|
|
}
|
|||
|
|
u32 ccr1 = lapic_read(LAPIC_CUR_CNT);
|
|||
|
|
u64 delta = (u64)ccr0 - (u64)ccr1; /* 16 分频时钟 10ms tick 数 */
|
|||
|
|
if (delta < 1000) {
|
|||
|
|
log_warn("[LAPIC] timer calibration implausible");
|
|||
|
|
lapic_timer_stop();
|
|||
|
|
return -5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 期望周期 = delta * (1000 / freq_hz) / 10 */
|
|||
|
|
u64 init = delta * 1000ULL / (u64)freq_hz / 10ULL;
|
|||
|
|
if (init == 0) init = 1;
|
|||
|
|
if (init > 0xFFFFFFFFULL) init = 0xFFFFFFFFULL;
|
|||
|
|
|
|||
|
|
if (g_timer_vector < 0) {
|
|||
|
|
int vec = irq_vector_alloc();
|
|||
|
|
if (vec < 0) {
|
|||
|
|
log_warn("[LAPIC] irq_vector_alloc failed");
|
|||
|
|
return -6;
|
|||
|
|
}
|
|||
|
|
g_timer_vector = vec;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 按校准值定频编程(LVT 保持 mask,start() 再放开)。
|
|||
|
|
* 顺序:divide config → LVT(masked) → initial count。 */
|
|||
|
|
lapic_write(LAPIC_DIV_CONF, 0x3);
|
|||
|
|
lapic_write(LAPIC_LVT_TIMER,
|
|||
|
|
((u32)g_timer_vector & 0xFFu) | LVT_MASK | LVT_PERIODIC);
|
|||
|
|
lapic_write(LAPIC_INIT_CNT, (u32)init);
|
|||
|
|
|
|||
|
|
log_info("[LAPIC] timer calibrated");
|
|||
|
|
log_hex64("[LAPIC] ticks/10ms=", delta);
|
|||
|
|
log_hex64("[LAPIC] init_count=", init);
|
|||
|
|
log_hex64("[LAPIC] vector=", (u64)g_timer_vector);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int lapic_timer_vector(void) {
|
|||
|
|
return g_timer_vector;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void lapic_timer_start(void) {
|
|||
|
|
if (!g_lapic || g_timer_vector < 0) return;
|
|||
|
|
/* 保持 divide/init 不变,仅解除 mask(periodic 模式已配) */
|
|||
|
|
lapic_write(LAPIC_LVT_TIMER,
|
|||
|
|
((u32)g_timer_vector & 0xFFu) | LVT_PERIODIC);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void lapic_timer_stop(void) {
|
|||
|
|
if (g_lapic) {
|
|||
|
|
lapic_write(LAPIC_LVT_TIMER, lapic_read(LAPIC_LVT_TIMER) | LVT_MASK);
|
|||
|
|
lapic_write(LAPIC_INIT_CNT, 0);
|
|||
|
|
}
|
|||
|
|
if (g_timer_vector >= 0) {
|
|||
|
|
irq_vector_free(g_timer_vector);
|
|||
|
|
g_timer_vector = -1;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void lapic_timer_eoi(void) {
|
|||
|
|
if (g_lapic) {
|
|||
|
|
lapic_write(LAPIC_EOI, 0);
|
|||
|
|
}
|
|||
|
|
}
|