Files
TermiNexus dcb7c5c2c0 chore(ViperOS): 落库内核侧进行中的改动(26 文件,+1573/-1805)
内容由用户产出,本次仅代为提交、未做任何语义修改;含 static 修饰符前置迁移等(新增 static 前缀 16 行)。提交消息为中性描述——如需更精确的说明请 amend。
2026-09-28 15:24:52 +08:00

259 lines
9.3 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import asm
import intr.gdt as gdt
import intr.idt as idt
import drivers.serial.uart.serial as serial
import drivers.core.cpu.cpu as cpu
import mm.mm as mm
import platform.pch.timer as timer
import viperlib
define IA32_APIC_BASE = 0x1B
define APIC_BASE_MSR_ENABLE = 0x800
define APIC_BASE_ADDR_MASK = 0xFFFFF000
define LAPIC_ID = 0x020
define LAPIC_VERSION = 0x030
define LAPIC_TPR = 0x080
define LAPIC_EOI = 0x0B0
define LAPIC_LDR = 0x0D0
define LAPIC_SVR = 0x0F0
define LAPIC_ISR0 = 0x100
define LAPIC_ICR_LOW = 0x300
define LAPIC_ICR_HIGH = 0x310
define LAPIC_LVT_TIMER = 0x320
define LAPIC_LVT_LINT0 = 0x350
define LAPIC_LVT_LINT1 = 0x360
define LAPIC_LVT_ERROR = 0x370
define LAPIC_TIMER_INIT = 0x380
define LAPIC_TIMER_CURRENT = 0x390
define LAPIC_TIMER_DIVIDE = 0x3E0
define LAPIC_SVR_ENABLE = 0x100
define LAPIC_LVT_MASKED = 0x10000
define LAPIC_LVT_DELIVERY_EXTINT = 0x700
define LAPIC_LVT_DELIVERY_NMI = 0x400
define LAPIC_LVT_TIMER_PERIODIC = 0x20000
define LAPIC_ICR_INIT = 0x500
define LAPIC_ICR_STARTUP = 0x600
define LAPIC_ICR_ASSERT = 0x4000
define LAPIC_ICR_DEASSERT = 0x8000
define LAPIC_ICR_LEVEL = 0x8000
define MAX_CPUS = 8
define AP_TRAMPOLINE_ADDR = 0x7000
# trampoline 机器码字节数
define AP_TRAMPOLINE_LEN = 138
_lapic_base: uint64 = 0
_cpu_count: uint32 = 1
_bsp_lapic_id: uint32 = 0
_ap_ready: uint32 = 0
def read(offset: uint32) -> uint32:
addr: uint64 = _lapic_base + uint64(offset)
return *(uint32*(addr))
def write(offset: uint32, value: uint32):
addr: uint64 = _lapic_base + uint64(offset)
*(uint32*(addr)) = value
def eoi():
write(LAPIC_EOI, 0)
def get_id() -> uint32:
return read(LAPIC_ID) >> 24
@__attribute__(noinline)
def init():
global _lapic_base, _bsp_lapic_id
msr_val: uint64 = cpu.msr_read(IA32_APIC_BASE)
_lapic_base = msr_val & APIC_BASE_ADDR_MASK
cpu.msr_write(IA32_APIC_BASE, msr_val | APIC_BASE_MSR_ENABLE)
_bsp_lapic_id = read(LAPIC_ID) >> 24
write(LAPIC_SVR, LAPIC_SVR_ENABLE | 0xFF)
write(LAPIC_TPR, 0)
write(LAPIC_LVT_TIMER, LAPIC_LVT_MASKED)
write(LAPIC_LVT_LINT0, LAPIC_LVT_DELIVERY_EXTINT)
write(LAPIC_LVT_LINT1, LAPIC_LVT_DELIVERY_NMI)
write(LAPIC_LVT_ERROR, LAPIC_LVT_MASKED)
write(LAPIC_TIMER_DIVIDE, 0x0B)
write(LAPIC_LDR, 0x01000000)
eoi()
@__attribute__(noinline)
def detect_cpu_count() -> uint32:
res: cpu.cpuid_result
cpu.cpuid(0, &res)
max_leaf: uint32 = res.eax
if max_leaf >= 0x0B:
cpu.cpuid_sub(0x0B, 0, &res)
num_threads: uint32 = res.ebx & 0xFFFF
if num_threads > 0 and num_threads <= MAX_CPUS:
return num_threads
cpu.cpuid(1, &res)
if res.edx & cpu.CPU_FEATURE_HTT:
count: uint32 = (res.ebx >> 16) & 0xFF
if count > 0 and count <= MAX_CPUS:
return count
return 1
def _wait_icr():
timeout: uint32 = 1000000
while timeout > 0:
val: uint32 = read(LAPIC_ICR_LOW)
if (val & (1 << 12)) == 0:
return
timeout -= 1
def _send_ipi(apic_id: uint32, vector: uint32, delivery_mode: uint32):
_wait_icr()
write(LAPIC_ICR_HIGH, apic_id << 24)
write(LAPIC_ICR_LOW, vector | delivery_mode | LAPIC_ICR_LEVEL | LAPIC_ICR_ASSERT)
_wait_icr()
def ap_entry():
global _ap_ready
my_id: uint32 = get_id()
buf: [64]char
viperlib.snprintf(&buf, 64, "[AP] core %u started (lapic=%u)\n", my_id, my_id)
serial.puts(buf)
_ap_ready = 1
while True:
asm.sti()
asm.hlt()
_trampoline_bin: [AP_TRAMPOLINE_LEN]uint8 = [
0xFA, # cli
0x31, 0xC0, # xor ax, ax
0x8E, 0xD8, # mov ds, ax
0x8E, 0xC0, # mov es, ax
0x0F, 0x01, 0x16, 0x00, 0x7E, # lgdt [0x7E00]
0x0F, 0x20, 0xC0, # mov eax, cr0
0x83, 0xC8, 0x01, # or eax, 1
0x0F, 0x22, 0xC0, # mov cr0, eax
0xEA, 0x1A, 0x07, 0x00, 0x00, 0x08, 0x00, # jmp 0x08:0x701A
# 32-bit 保护模式开始
0xB8, 0x10, 0x00, 0x00, 0x00, # mov ax, 0x10
0x8E, 0xD8, # mov ds, ax
0x8E, 0xC0, # mov es, ax
0x8E, 0xE0, # mov fs, ax
0x8E, 0xE8, # mov gs, ax
0x8E, 0xD0, # mov ss, ax
0x0F, 0x20, 0xE0, # mov eax, cr4
0x83, 0xC8, 0x20, # or eax, 0x20 (PAE)
0x0F, 0x22, 0xE0, # mov cr4, eax
0x8B, 0x04, 0x25, 0x00, 0x7F, 0x00, 0x00, # mov eax, [0x7F00]
0x0F, 0x22, 0xD8, # mov cr3, eax
0xB9, 0x80, 0x00, 0x00, 0xC0, # mov ecx, 0xC0000080 (EFER)
0x0F, 0x32, # rdmsr
# 0x83 C8 只能编码 1 字节立即数;0x100 须用 0x81 C8 imm32(6 字节)。
# 原写 0x83 会把后 4 字节 00 01 00 00 当作下一条指令执行(N7)。
0x81, 0xC8, 0x00, 0x01, 0x00, 0x00, # or eax, 0x100 (LME)
0x0F, 0x79, # wrmsr
0x0F, 0x20, 0xC0, # mov eax, cr0
0x0D, 0x00, 0x00, 0x00, 0x80, # or eax, 0x80000000 (PG)
0x0F, 0x22, 0xC0, # mov cr0, eax
0xEA, 0x5A, 0x07, 0x00, 0x00, 0x08, 0x00, # jmp 0x08:0x705A
# 64-bit 长模式开始
0x48, 0x8B, 0x24, 0x25, 0x08, 0x7F, 0x00, 0x00, # mov rsp, [0x7F08]
0x0F, 0x01, 0x16, 0x10, 0x7F, # lgdt [0x7F10]
0x0F, 0x01, 0x1E, 0x20, 0x7F, # lidt [0x7F20]
0xB8, 0x10, 0x00, 0x00, 0x00, # mov ax, 0x10
0x8E, 0xD8, # mov ds, ax
0x8E, 0xC0, # mov es, ax
0x8E, 0xE0, # mov fs, ax
0x8E, 0xE8, # mov gs, ax
0x8E, 0xD0, # mov ss, ax
0x48, 0x8B, 0x04, 0x25, 0x30, 0x7F, 0x00, 0x00, # mov rax, [0x7F30]
0xFF, 0xE0 # jmp rax
]
def _setup_trampoline(entry_addr: uint64, stack_addr: uint64, cr3_val: uint64):
dest: uint8* = uint8*(AP_TRAMPOLINE_ADDR)
src: uint8* = &_trampoline_bin
for i in range(AP_TRAMPOLINE_LEN):
*(dest + uint64(i)) = *(src + uint64(i))
gdt_ptr_addr: uint64 = AP_TRAMPOLINE_ADDR + 0x0E00
*(uint16*(gdt_ptr_addr)) = uint16(23)
*(uint64*(gdt_ptr_addr + 2)) = uint64(AP_TRAMPOLINE_ADDR + 0x0E10)
gdt_addr: uint64 = AP_TRAMPOLINE_ADDR + 0x0E10
*(uint64*(gdt_addr + 0)) = uint64(0)
*(uint64*(gdt_addr + 8)) = uint64(0x00AF9A000000FFFF)
*(uint64*(gdt_addr + 16)) = uint64(0x00CF92000000FFFF)
*(uint32*(AP_TRAMPOLINE_ADDR + 0x0F00)) = uint32(cr3_val)
*(uint64*(AP_TRAMPOLINE_ADDR + 0x0F08)) = stack_addr
gp_addr: uint64 = uint64(&gdt.gp)
gdt_base: uint64 = *(uint64*(gp_addr + 2))
gdt_limit: uint16 = *(uint16*(gp_addr))
*(uint16*(AP_TRAMPOLINE_ADDR + 0x0F10)) = gdt_limit
*(uint64*(AP_TRAMPOLINE_ADDR + 0x0F12)) = gdt_base
idt_addr: uint64 = uint64(&idt.idt)
idt_base: uint64 = *(uint64*(idt_addr + 2))
idt_limit: uint16 = *(uint16*(idt_addr))
*(uint16*(AP_TRAMPOLINE_ADDR + 0x0F20)) = idt_limit
*(uint64*(AP_TRAMPOLINE_ADDR + 0x0F22)) = idt_base
# entry 槽位后移到 0x0F30:idt 伪描述符占 0x0F20..0x0F29(limit 2 + base 8),
# 原来 entry 放 0x0F28 会与其 base 尾部 2 字节重叠 → lidt 读到被覆盖的基址。
*(uint64*(AP_TRAMPOLINE_ADDR + 0x0F30)) = entry_addr
def start_aps():
global _cpu_count, _ap_ready
if _cpu_count <= 1: return
cr3_val: uint64 = 0
__asm__(f"""mov rax, cr3
mov {asmout(cr3_val, ASM_DESCR.OUTPUT_REG)}, rax""", op = [ASM_DESCR.CLOBBER_RAX])
stack_size: uint64 = 8192
for i in range(1, _cpu_count):
_ap_ready = 0
stack: rawptr = mm.malloc(stack_size)
if stack is None: continue
stack_top: uint64 = uint64(stack) + stack_size
stack_top = stack_top & ~uint64(15)
_setup_trampoline(uint64(ap_entry), stack_top, cr3_val)
_send_ipi(i, 0, LAPIC_ICR_INIT)
timer.timer_msleep(10)
_send_ipi(i, (AP_TRAMPOLINE_ADDR >> 12) & 0xFF, LAPIC_ICR_STARTUP)
timer.timer_msleep(1)
_send_ipi(i, (AP_TRAMPOLINE_ADDR >> 12) & 0xFF, LAPIC_ICR_STARTUP)
for i in range(5000):
if _ap_ready != 0: break
timer.timer_msleep(1)
def early_init():
global _cpu_count, _bsp_lapic_id
# 直接以 IA32_APIC_BASE 的 EN 位(bit 11)判断 APIC 是否可用:
# QEMU/OVMF 必启用,且 msr_read 路径已在前置流程验证可用;
# 比依赖 cpuid 特性位更可靠(避免 cpuid 包装被误判时静默跳过 init)。
msr_val: uint64 = cpu.msr_read(IA32_APIC_BASE)
if msr_val & APIC_BASE_MSR_ENABLE:
init()
_cpu_count = detect_cpu_count()
else:
_cpu_count = 1
def get_cpu_count() -> uint32:
return _cpu_count
def get_base() -> uint64:
return _lapic_base
def get_lapic_id() -> uint32:
return get_id()