Initial import of ViperOS
This commit is contained in:
251
VKernel/Kernel/drivers/core/cpu/apic.py
Normal file
251
VKernel/Kernel/drivers/core/cpu/apic.py
Normal file
@@ -0,0 +1,251 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user