252 lines
9.2 KiB
Python
252 lines
9.2 KiB
Python
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
|
|
import t, c
|
|
|
|
|
|
IA32_APIC_BASE: t.CDefine = 0x1B
|
|
APIC_BASE_MSR_ENABLE: t.CDefine = 0x800
|
|
APIC_BASE_ADDR_MASK: t.CDefine = 0xFFFFF000
|
|
|
|
LAPIC_ID: t.CDefine = 0x020
|
|
LAPIC_VERSION: t.CDefine = 0x030
|
|
LAPIC_TPR: t.CDefine = 0x080
|
|
LAPIC_EOI: t.CDefine = 0x0B0
|
|
LAPIC_LDR: t.CDefine = 0x0D0
|
|
LAPIC_SVR: t.CDefine = 0x0F0
|
|
LAPIC_ISR0: t.CDefine = 0x100
|
|
LAPIC_ICR_LOW: t.CDefine = 0x300
|
|
LAPIC_ICR_HIGH: t.CDefine = 0x310
|
|
LAPIC_LVT_TIMER: t.CDefine = 0x320
|
|
LAPIC_LVT_LINT0: t.CDefine = 0x350
|
|
LAPIC_LVT_LINT1: t.CDefine = 0x360
|
|
LAPIC_LVT_ERROR: t.CDefine = 0x370
|
|
LAPIC_TIMER_INIT: t.CDefine = 0x380
|
|
LAPIC_TIMER_CURRENT: t.CDefine = 0x390
|
|
LAPIC_TIMER_DIVIDE: t.CDefine = 0x3E0
|
|
|
|
LAPIC_SVR_ENABLE: t.CDefine = 0x100
|
|
LAPIC_LVT_MASKED: t.CDefine = 0x10000
|
|
LAPIC_LVT_DELIVERY_EXTINT: t.CDefine = 0x700
|
|
LAPIC_LVT_DELIVERY_NMI: t.CDefine = 0x400
|
|
LAPIC_LVT_TIMER_PERIODIC: t.CDefine = 0x20000
|
|
|
|
LAPIC_ICR_INIT: t.CDefine = 0x500
|
|
LAPIC_ICR_STARTUP: t.CDefine = 0x600
|
|
LAPIC_ICR_ASSERT: t.CDefine = 0x4000
|
|
LAPIC_ICR_DEASSERT: t.CDefine = 0x8000
|
|
LAPIC_ICR_LEVEL: t.CDefine = 0x8000
|
|
|
|
MAX_CPUS: t.CDefine = 8
|
|
AP_TRAMPOLINE_ADDR: t.CDefine = 0x7000
|
|
|
|
_lapic_base: t.CStatic | t.CUInt64T = 0
|
|
_cpu_count: t.CStatic | t.CUInt32T = 1
|
|
_bsp_lapic_id: t.CStatic | t.CUInt32T = 0
|
|
_ap_ready: t.CStatic | t.CUInt32T = 0
|
|
|
|
def read(offset: t.CUInt32T) -> t.CUInt32T:
|
|
addr: t.CUInt64T = _lapic_base + t.CUInt64T(offset)
|
|
return c.Deref(t.CUInt32T(addr, t.CPtr))
|
|
|
|
def write(offset: t.CUInt32T, value: t.CUInt32T):
|
|
addr: t.CUInt64T = _lapic_base + t.CUInt64T(offset)
|
|
c.Set(c.Deref(t.CUInt32T(addr, t.CPtr)), value)
|
|
|
|
def eoi():
|
|
write(LAPIC_EOI, 0)
|
|
|
|
def get_id() -> t.CUInt32T:
|
|
return read(LAPIC_ID) >> 24
|
|
|
|
@c.Attribute(t.attr.noinline)
|
|
def init():
|
|
global _lapic_base, _bsp_lapic_id
|
|
msr_val: t.CUInt64T = 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()
|
|
|
|
@c.Attribute(t.attr.noinline)
|
|
def detect_cpu_count() -> t.CUInt32T:
|
|
res: cpu.cpuid_result
|
|
cpu.cpuid(0, c.Addr(res))
|
|
max_leaf: t.CUInt32T = res.eax
|
|
|
|
if max_leaf >= 0x0B:
|
|
cpu.cpuid_sub(0x0B, 0, c.Addr(res))
|
|
num_threads: t.CUInt32T = res.ebx & 0xFFFF
|
|
if num_threads > 0 and num_threads <= MAX_CPUS:
|
|
return num_threads
|
|
|
|
cpu.cpuid(1, c.Addr(res))
|
|
if res.edx & cpu.CPU_FEATURE_HTT:
|
|
count: t.CUInt32T = (res.ebx >> 16) & 0xFF
|
|
if count > 0 and count <= MAX_CPUS:
|
|
return count
|
|
return 1
|
|
|
|
def _wait_icr():
|
|
timeout: t.CUInt32T = 1000000
|
|
while timeout > 0:
|
|
val: t.CUInt32T = read(LAPIC_ICR_LOW)
|
|
if (val & (1 << 12)) == 0:
|
|
return
|
|
timeout -= 1
|
|
|
|
def _send_ipi(apic_id: t.CUInt32T, vector: t.CUInt32T, delivery_mode: t.CUInt32T):
|
|
_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: t.CUInt32T = get_id()
|
|
buf: t.CArray[t.CChar, 64]
|
|
viperlib.snprintf(c.Addr(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: t.CStatic | t.CArray[t.CUInt8T, 128] = [
|
|
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, 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, 0x28, 0x7F, 0x00, 0x00, # mov rax, [0x7F28]
|
|
0xFF, 0xE0 # jmp rax
|
|
]
|
|
|
|
def _setup_trampoline(entry_addr: t.CUInt64T, stack_addr: t.CUInt64T, cr3_val: t.CUInt64T):
|
|
dest: t.CUInt8T | t.CPtr = t.CUInt8T(AP_TRAMPOLINE_ADDR, t.CPtr)
|
|
src: t.CUInt8T | t.CPtr = c.Addr(_trampoline_bin)
|
|
i: t.CInt
|
|
for i in range(128):
|
|
c.Set(c.Deref(dest + t.CUInt64T(i)), c.Deref(src + t.CUInt64T(i)))
|
|
|
|
gdt_ptr_addr: t.CUInt64T = AP_TRAMPOLINE_ADDR + 0x0E00
|
|
c.Set(c.Deref(t.CUInt16T(gdt_ptr_addr, t.CPtr)), t.CUInt16T(23))
|
|
c.Set(c.Deref(t.CUInt64T(gdt_ptr_addr + 2, t.CPtr)), t.CUInt64T(AP_TRAMPOLINE_ADDR + 0x0E10))
|
|
|
|
gdt_addr: t.CUInt64T = AP_TRAMPOLINE_ADDR + 0x0E10
|
|
c.Set(c.Deref(t.CUInt64T(gdt_addr, t.CPtr)), t.CUInt64T(0))
|
|
c.Set(c.Deref(t.CUInt64T(gdt_addr + 8, t.CPtr)), t.CUInt64T(0x00AF9A000000FFFF))
|
|
c.Set(c.Deref(t.CUInt64T(gdt_addr + 16, t.CPtr)), t.CUInt64T(0x00CF92000000FFFF))
|
|
|
|
c.Set(c.Deref(t.CUInt32T(AP_TRAMPOLINE_ADDR + 0x0F00, t.CPtr)), t.CUInt32T(cr3_val))
|
|
c.Set(c.Deref(t.CUInt64T(AP_TRAMPOLINE_ADDR + 0x0F08, t.CPtr)), stack_addr)
|
|
|
|
gp_addr: t.CUInt64T = t.CUInt64T(c.Addr(gdt.gp))
|
|
gdt_base: t.CUInt64T = c.Deref(t.CUInt64T(gp_addr + 2, t.CPtr))
|
|
gdt_limit: t.CUInt16T = c.Deref(t.CUInt16T(gp_addr, t.CPtr))
|
|
c.Set(c.Deref(t.CUInt16T(AP_TRAMPOLINE_ADDR + 0x0F10, t.CPtr)), gdt_limit)
|
|
c.Set(c.Deref(t.CUInt64T(AP_TRAMPOLINE_ADDR + 0x0F12, t.CPtr)), gdt_base)
|
|
|
|
idt_addr: t.CUInt64T = t.CUInt64T(c.Addr(idt.idp))
|
|
idt_base: t.CUInt64T = c.Deref(t.CUInt64T(idt_addr + 2, t.CPtr))
|
|
idt_limit: t.CUInt16T = c.Deref(t.CUInt16T(idt_addr, t.CPtr))
|
|
c.Set(c.Deref(t.CUInt16T(AP_TRAMPOLINE_ADDR + 0x0F20, t.CPtr)), idt_limit)
|
|
c.Set(c.Deref(t.CUInt64T(AP_TRAMPOLINE_ADDR + 0x0F22, t.CPtr)), idt_base)
|
|
|
|
c.Set(c.Deref(t.CUInt64T(AP_TRAMPOLINE_ADDR + 0x0F28, t.CPtr)), entry_addr)
|
|
|
|
def start_aps():
|
|
global _cpu_count, _ap_ready
|
|
if _cpu_count <= 1: return
|
|
|
|
cr3_val: t.CUInt64T = 0
|
|
c.Asm(f"""mov rax, cr3
|
|
mov {c.AsmOut(cr3_val, t.ASM_DESCR.OUTPUT_REG)}, rax""",
|
|
[t.ASM_DESCR.CLOBBER_RAX])
|
|
|
|
stack_size: t.CUInt64T = 8192
|
|
i: t.CUInt32T
|
|
for i in range(1, _cpu_count):
|
|
_ap_ready = 0
|
|
stack: t.CVoid | t.CPtr = mm.malloc(stack_size)
|
|
if stack is None: continue
|
|
stack_top: t.CUInt64T = t.CUInt64T(stack) + stack_size
|
|
stack_top = stack_top & ~t.CUInt64T(15)
|
|
|
|
_setup_trampoline(t.CUInt64T(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)
|
|
|
|
wait: t.CUInt32T = 0
|
|
while _ap_ready == 0 and wait < 5000:
|
|
timer.timer_msleep(1)
|
|
wait += 1
|
|
|
|
def early_init():
|
|
global _cpu_count, _bsp_lapic_id
|
|
features: t.CUInt32T = cpu.detect_features_static()
|
|
if features & cpu.CPU_FEATURE_APIC:
|
|
init()
|
|
_cpu_count = detect_cpu_count()
|
|
else:
|
|
_cpu_count = 1
|
|
|
|
def get_cpu_count() -> t.CUInt32T:
|
|
return _cpu_count
|
|
|
|
def get_lapic_id() -> t.CUInt32T:
|
|
return get_id()
|