内容由用户产出,本次仅代为提交、未做任何语义修改;含 static 修饰符前置迁移等(新增 static 前缀 16 行)。提交消息为中性描述——如需更精确的说明请 amend。
259 lines
9.3 KiB
Plaintext
259 lines
9.3 KiB
Plaintext
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()
|