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()