From: acevest Date: Thu, 1 Jan 2026 14:40:35 +0000 (+0800) Subject: 初步初始化Local APIC X-Git-Url: http://repos.zhaoyanbai.com/?a=commitdiff_plain;h=66b2b515904f9b9c25e6fc70f49e40d516a32150;p=kernel.git 初步初始化Local APIC --- diff --git a/include/apic.h b/include/apic.h new file mode 100644 index 0000000..63346f2 --- /dev/null +++ b/include/apic.h @@ -0,0 +1,77 @@ +/* + * ------------------------------------------------------------------------ + * File Name: apic.h + * Author: Zhao Yanbai + * 2026-01-01 16:08:19 Thursday CST + * Description: none + * ------------------------------------------------------------------------ + */ + +#pragma once + +// APIC的ID寄存器 +// 在x2apic上,LAPIC_ID是32位 +// 在Pentium 4和Xeon上,LAPIC_ID是8位,位置在[31:24] +// 早期Pentium和P6家族上,LAPIC_ID是4位,位置在[27:24] +#define LAPIC_ID 0x20 + +// APIC的版本寄存器 +// bit[7:0][VERSION] APIC版本 +// - 0x0x Intel 82489DX (外部APIC芯片) +// - 0x1x Integrated APIC (内部APIC芯片) +// bit[23:16][MAXLVT] 最大本地向量表 <- 此值+1代表LVT表项数 +// bit[24][EOI] 禁止广播EOI消息标志位 +#define LAPIC_VERSION 0x30 + +#define LAPIC_TPR 0x80 +#define LAPIC_APR 0x90 +#define LAPIC_PPR 0xA0 +#define LAPIC_EOI 0xB0 +#define LAPIC_LDR 0xD0 +#define LAPIC_DFR 0xE0 + +// 伪中断向量寄存器 +// Spurious Interrupt Vector Register +// bit[7:0][VECTOR] 伪中断向量 +// bit[8][Enable] 1启用LAPIC; 0禁用LAPIC +// bit[11:9][Reserved] +// bit[12][Suppress EOI Broadcast] 1禁用EOI广播; 0启用EOI广播 +// bit[15:13][Reserved] +// bit[31:16][Reserved] +#define LAPIC_SVR 0xF0 + +// 以下三个寄存器都是256位,按如下偏移访问 +// 0x00 [031:000] +// 0x10 [063:032] +// 0x20 [095:064] +// 0x30 [127:096] +// 0x40 [159:128] +// 0x50 [191:160] +// 0x60 [223:192] +// 0x70 [255:224] +#define LAPIC_ISR 0x100 +#define LAPIC_TMR 0x180 +#define LAPIC_IRR 0x200 + +#define LAPIC_ESR 0x280 +#define LAPIC_LVT_CMCI 0x2F0 +#define LAPIC_ICR_LOW 0x300 +#define LAPIC_ICR_HIGH 0x310 +#define LAPIC_LVT_TIMER 0x320 +#define LAPIC_LVT_THERMAL 0x330 +#define LAPIC_LVT_PERF 0x340 +#define LAPIC_LVT_LINT0 0x350 +#define LAPIC_LVT_LINT1 0x360 +#define LAPIC_LVT_ERROR 0x370 +#define LAPIC_TIMER_INITIAL 0x380 +#define LAPIC_TIMER_COUNTER 0x390 +#define LAPIC_TIMER_DIVIDE 0x3E0 + +typedef struct lapic { + const char* name; + + uint32_t (*read)(uint32_t reg_offset); + void (*write)(uint32_t reg_offset, uint32_t value); + + uint32_t (*get_lapic_id)(); +} lapic_t; diff --git a/include/msr.h b/include/msr.h index c4c7deb..1a0d145 100644 --- a/include/msr.h +++ b/include/msr.h @@ -32,16 +32,6 @@ #define MSR_IA32_PERF_STATUS 0x198 #define MSR_IA32_PERF_CRTL 0x199 -// APIC的ID寄存器 -#define MSR_IA32_X2APIC_APICID 0x802 -// APIC的版本寄存器 -// bit[7:0][VERSION] APIC版本 -// - 0x0x Intel 82489DX (外部APIC芯片) -// - 0x1x Integrated APIC (内部APIC芯片) -// bit[23:16][MAXLVT] 最大本地向量表 -// bit[24][EOI] 禁止广播EOI消息标志位 -#define MSR_IA32_X2APIC_VERSION 0x803 - #define rdmsr(msr, lowval, highval) \ do { \ asm("rdmsr;" : "=a"(lowval), "=d"(highval) : "c"(msr)); \ @@ -61,6 +51,15 @@ static inline uint64_t read_msr(uint32_t msr) { return ((uint64_t)highval << 32) | lowval; } +static inline uint32_t read_msr32(uint32_t msr) { + uint32_t lowval = 0; + uint32_t highval = 0; + + rdmsr(msr, lowval, highval); + + return lowval; +} + static inline void write_msr(uint32_t msr, uint64_t value) { uint32_t lowval = value & 0xFFFFFFFF; uint32_t highval = value >> 32; @@ -68,4 +67,8 @@ static inline void write_msr(uint32_t msr, uint64_t value) { wrmsr(msr, lowval, highval); } +static inline void write_msr32(uint32_t msr, uint32_t value) { + wrmsr(msr, value, 0); +} + #endif //_MSR_H diff --git a/include/system.h b/include/system.h index 67f8e29..9502b9e 100644 --- a/include/system.h +++ b/include/system.h @@ -42,6 +42,7 @@ #ifndef ASM #include "printk.h" #include "types.h" +#include #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) @@ -157,7 +158,9 @@ typedef struct system { dev_t root_dev; - bool x2apic; + // 按理这个信息应该按CPU存储,简化实现 + lapic_t* lapic; + #define CMD_LINE_SIZE 128 const char* cmdline; diff --git a/kernel/apic.c b/kernel/apic.c index 8e3f3dc..153b179 100644 --- a/kernel/apic.c +++ b/kernel/apic.c @@ -11,55 +11,184 @@ #include #include #include +#include -void lapic_init() { - system.x2apic = false; +static inline uint32_t apic_offset_to_msr(uint32_t offset) { + return 0x800 + (offset >> 4); +} + +uint32_t apic_read_lapic(uint32_t offset) { + assert(offset < PAGE_SIZE); + uint8_t* base = (uint8_t*)fixid_to_vaddr(FIX_LAPIC_BASE); + return *(uint32_t*)(base + offset); +} + +void apic_write_lapic(uint32_t offset, uint32_t value) { + assert(offset < PAGE_SIZE); + uint8_t* base = (uint8_t*)fixid_to_vaddr(FIX_LAPIC_BASE); + *(uint32_t*)(base + offset) = value; +} + +uint32_t apic_get_lapic_id() { + // return lapic_read(LAPIC_ID) >> 24; + return apic_read_lapic(LAPIC_ID) >> 24; +} + +static lapic_t apic_lapic = { + .name = "apic - lapic", + .read = apic_read_lapic, + .write = apic_write_lapic, + .get_lapic_id = apic_get_lapic_id, +}; + +uint32_t x2apic_read_lapic(uint32_t offset) { + assert(offset < PAGE_SIZE); + uint32_t msr = apic_offset_to_msr(offset); + return read_msr32(msr); +} + +void x2apic_write_lapic(uint32_t offset, uint32_t value) { + assert(offset < PAGE_SIZE); + uint32_t msr = apic_offset_to_msr(offset); + write_msr32(msr, value); +} + +uint32_t x2apic_get_lapic_id() { + uint32_t msr = apic_offset_to_msr(LAPIC_ID); + return read_msr32(msr); +} + +static lapic_t x2apic_lapic = { + .name = "x2apic - lapic", + .read = x2apic_read_lapic, + .write = x2apic_write_lapic, + .get_lapic_id = x2apic_get_lapic_id, +}; + +void lapic_lvt_detect() { + lapic_t* lapic = system.lapic; + struct { + uint32_t offset; + char* name; + } lvt[] = { + {LAPIC_LVT_CMCI, "CMCI"}, // + {LAPIC_LVT_TIMER, "TIMER"}, // + {LAPIC_LVT_THERMAL, "THERMAL"}, // + {LAPIC_LVT_PERF, "PERF"}, // + {LAPIC_LVT_LINT0, "LINT0"}, // + {LAPIC_LVT_LINT1, "LINT1"}, // + {LAPIC_LVT_ERROR, "ERROR"}, // + }; + + for (int i = 0; i < sizeof(lvt) / sizeof(lvt[0]); i++) { + uint32_t lvt_value = lapic->read(lvt[i].offset); + printk("LVT[%d] %s: %08x\n", i, lvt[i].name, lvt_value); + } +} +void lapic_init() { + bool x2apic = false; cpuid_regs_t r; r = cpuid(1); if (r.edx & (1 << 9)) { printk("local apic supported\n"); if (r.ecx & (1 << 21)) { printk("x2apic supported\n"); - system.x2apic = true; } else { - printk("x2apic not supported\n"); + panic("x2apic not supported\n"); } } else { panic("local apic not supported\n"); } - uint64_t apic_base = read_msr(MSR_IA32_APIC_BASE); - printk("apic base: %016lx\n", apic_base); + uint32_t apic_base = read_msr32(MSR_IA32_APIC_BASE); + printk("LAPIC BASE: %08x\n", apic_base); - // apic 必然已经开启q + // apic 必然已经开启 assert((apic_base & (1 << 11)) != 0); - // 开启2xapic - apic_base |= (1 << 10); - write_msr(MSR_IA32_APIC_BASE, apic_base); + // 映射LAPIC到内核空间 + uint32_t apic_phys_base_addr = (uint32_t)apic_base & PAGE_MASK; + set_fixmap(FIX_LAPIC_BASE, apic_phys_base_addr); + // + vaddr_t apic_virt_base_addr = fixid_to_vaddr(FIX_LAPIC_BASE); + printk("LAPIC base %08x mapped to %08x\n", apic_phys_base_addr, apic_virt_base_addr); - apic_base = read_msr(MSR_IA32_APIC_BASE); - printk("after 2xapic enable apic base: %016lx\n", apic_base); + if (x2apic) { + // 开启2xapic + apic_base |= (1 << 10); + write_msr(MSR_IA32_APIC_BASE, apic_base); - uint64_t apic_version = read_msr(MSR_IA32_X2APIC_VERSION); - printk("apic version: %08lx\n", apic_version); + apic_base = read_msr32(MSR_IA32_APIC_BASE); + assert((apic_base & (1 << 10)) != 0); + printk("after 2xapic enable apic base: %016lx\n", apic_base); - uint32_t apic_phys_base_addr = (uint32_t)apic_base & PAGE_MASK; - set_fixmap(FIX_LAPIC_BASE, apic_phys_base_addr); + system.lapic = &x2apic_lapic; + } else { + system.lapic = &apic_lapic; + } - vaddr_t apic_virt_base_addr = fixid_to_vaddr(FIX_LAPIC_BASE); + lapic_t* lapic = system.lapic; - printk("LAPIC base %08x mapped to %08x\n", apic_phys_base_addr, apic_virt_base_addr); + uint32_t version = lapic->read(LAPIC_VERSION); + uint32_t id = lapic->get_lapic_id(); + uint32_t lvt_cnt = ((version >> 16) & 0xff) + 1; - { - volatile uint32_t* base = (volatile uint32_t*)apic_virt_base_addr; - uint32_t id = base[0x20 / 4]; // APIC ID 偏移 - uint32_t version = base[0x30 / 4]; - printk("APIC id %08x version %08x\n", id, version); + printk("LAPIC id %08x version %08x lvt_cnt %d\n", id, version, lvt_cnt); + if ((version & 0xFF) < 0x10) { + printk(" Intel 82489DX APIC\n"); + } else { + printk(" Integrated APIC\n"); } + + // 在LAPIC_SVR中开启LAPIC同时禁用EOI广播 + uint32_t lapic_svr = lapic->read(LAPIC_SVR); + printk("LAPIC_SVR: %08x\n", lapic_svr); + lapic_svr |= (1 << 8); // 启用LAPIC + lapic_svr &= ~(1 << 12); // 禁用EOI广播 + + lapic->write(LAPIC_SVR, lapic_svr); + + lapic_svr = lapic->read(LAPIC_SVR); + printk("LAPIC_SVR: %08x\n", lapic_svr); + assert((lapic_svr & (1 << 8)) != 0); + assert((lapic_svr & (1 << 12)) == 0); + printk("LAPIC enabled and EOI broadcast disabled\n"); + + lapic_lvt_detect(); + + // 屏蔽LVT所有中断功能 + uint32_t lvt_mask = 0x10000; + // lapic->write(LAPIC_LVT_CMCI, lvt_mask); + lapic->write(LAPIC_LVT_TIMER, lvt_mask); + lapic->write(LAPIC_LVT_THERMAL, lvt_mask); + lapic->write(LAPIC_LVT_PERF, lvt_mask); + lapic->write(LAPIC_LVT_LINT0, lvt_mask); + lapic->write(LAPIC_LVT_LINT1, lvt_mask); + lapic->write(LAPIC_LVT_ERROR, lvt_mask); + + // lapic_lvt_detect(); + + // TPR: Task Priority Register + // bit[7:4]: 任务优先级 + // bit[3:0]: 任务子优先级,新版本处理要求为0 + lapic->write(LAPIC_TPR, 0); + uint32_t TPR = lapic->read(LAPIC_TPR); + + // PPR: Processor Priority Register + // bit[7:4]: 处理器优先级 + // bit[3:0]: 处理器子优先级 + // 只读寄存器 + // PPR是TPR和ISRV相比较得到的。ISRV代表ISR(In-Service Register共256bit)寄存器当前服务的最高优先级中断的向量号 + // PPR = max(TPR, (ISRV != 0) ? (ISRV & 0xF0) : 0) + // 这里的含义是: 处理器当前的优先级水平(PPR)取软件设置的任务优先级(TPR)​ + // 和当前正在服务的中断的优先级(ISRV的高4位)​ 中的较大者 + // 当一个高优先级中断正在被服务时(ISRV值大),即使TPR设置得很低,PPR也会保持高位,从而阻止同级或更低级的中断嵌套。 + // TPR是软件的“计划”,ISR是硬件的“现状”,而PPR是综合二者得出的“当前执行标准”。 + // 内核只能通过设置TPR来影响PPR + uint32_t PPR = lapic->read(LAPIC_PPR); } void init_apic() { - lapic_init(); + ioapic_init(); }