Files
ViperOS/VKernel/Kernel/main.vp
T
TermiNexus 61208676c6 fix(main): _start 加 stackrealign,修复变参函数 movaps #GP
根因链:_start 由 bootloader jmp 进入(rsp≡0 mod 16),编译器/llc 按 call
约定对齐函数帧(假设入口 rsp≡8 mod 16)→ 帧整体偏 8 → _start 内所有调用
rsp≡8 mod 16 → __init__ 的 float 变参调用(snprintf %.2f 保存 xmm)movaps
#GP(cr4.OSFXSR 已启用、地址 16 对齐仍 #GP 正是此因)。

加 t.attr.stackrealign 使 _start 序言动态重对齐栈,与入口约定无关。
2026-08-26 20:18:09 +08:00

415 lines
18 KiB
Plaintext
Raw 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.
from stdint import *
import bootinfo
import intr
import platform.pch as pch
import platform.pch.timer as timer
import platform.pch.rtc as rtc
import drivers.serial.uart.su8250 as su8250
import drivers.serial.uart.serial as serial
import drivers.devfs.devfs as devfs
import paging.paging as paging
import drivers.video.vesafb.vga as vga
import drivers.video.vesafb.gfx as gfx
import drivers.storage.ide.ide as ide
import drivers.fs.fat32.fat32 as fat32
import drivers.fs.fat32.fat32_types as fat32_types
import mm.mm as mm
import string
import sched.sched as sched
import sched.process as proc
import drivers.input.keyboard.keyboard as keyboard
import drivers.input.mouse.mouse as mouse
import drivers.usb.usb as usb
import drivers.core.cpu.cpu as cpu
import drivers.core.cpu.apic as apic
import viperlib
import services.keyboard as kbd_svc
import services.mouse as mse_svc
import services.desktop as desktop
import intr.syscall as syscall
import execrunner.elf as elf
import t, c
import asm
kbd_tid: t.CInt = -1
mse_tid: t.CInt = -1
BootInfo: bootinfo.bootinfo | t.CPtr
@t.Object
# @t.CVTable
class ViperKernel:
BootInfo: bootinfo.bootinfo | t.CPtr
VGA: vga._VGAScreenDriver | t.CPtr
VGA_drv: vga._VGAScreenDriver
def __init__(self):
global BootInfo
self.BootInfo = BootInfo
# 启用 SSE/FPU:OVMF 启动后 CR4.OSFXSR 未置位,movaps 会 #GP——
# 变参函数传 float/double 保存 xmm 寄存器触发(sched started 后崩溃根因)。
cpu.enable_sse()
# 注意:此处不能再写 `BootInfo: bootinfo.bootinfo | t.CPtr = self.BootInfo`
# 局部重声明全局名——编译器会创建局部 alloca 却不写入(赋值落全局),
# 后续 BootInfo.X 读未初始化局部 → 全 0 → memmap 永不可用。直接用 global。
serial.init()
serial.puts("===== ViperOS VKernel Test\n =====")
serial.puts("V2.3.8\n")
intr.gdt.init()
# Set IA32_KERNEL_GS_BASE = &_per_cpu so swapgs in ISR/syscall works.
# _CPUObject is never instantiated, so __init_tss__ never runs;
# without this, GS base stays 0 and gs:[0] writes hit null -> triple fault.
cpu.msr_write(cpu.IA32_KERNEL_GS_BASE, t.CUInt64T(c.Addr(cpu._per_cpu)))
serial.puts("[main] gdt init done\n")
intr.idt.init()
syscall.init()
serial.puts("[main] syscall init done\n")
# 必须在 sti() 之前初始化调度器,否则中断触发时 _sched_ptr 为 NULL 会导致三重故障
sched.Scheduler()
serial.puts("[main] sched init done\n")
timer.timer_init()
serial.puts("[main] timer init done\n")
#mouse.init()
#serial.puts("[main] mouse init done\n")
# 必须初始化 APIC:QEMU/OVMF(UEFI)下本地 APIC 已启用,PIC 外部中断
# 须经 LINT0 送达 CPU。apic.init() 将 LINT0 配为 EXTINT(取消屏蔽),
# 否则定时器中断被 APIC 吞掉 → sti 后 msleep 永远等不到 ticks 挂死。
apic.early_init()
serial.puts("[main] apic init done\n")
# [DBG] 综合诊断:判定 LAPIC MMIO 是否真正可访问/可写(用后移除)
_dbg_base: t.CUInt64T = apic.get_base()
_dbg_ver: t.CUInt32T = apic.read(0x030) # LAPIC_VERSION:真 LAPIC 应为 0x50014
_dbg_svr: t.CUInt32T = apic.read(0x0F0) # LAPIC_SVR:init 已写 0x1FF
apic.write(0x350, 0x700) # 再次写 LINT0,验证写粘性
_dbg_lint0: t.CUInt32T = apic.read(0x350)
_dbg_feat: t.CUInt32T = cpu.detect_features_static()
_dbg_buf: t.CArray[t.CChar, 160]
viperlib.snprintf(c.Addr(_dbg_buf), 160,
"[DBG] base=0x%lx ver=0x%x svr=0x%x lint0=0x%x feat=0x%x apic=%d\n",
_dbg_base, _dbg_ver, _dbg_svr, _dbg_lint0, _dbg_feat,
1 if (_dbg_feat & cpu.CPU_FEATURE_APIC) else 0)
serial.puts(c.Addr(_dbg_buf))
# [DBG] 读回 IDT 门 32,验证 isr32 分发地址是否正确安装(用后移除)
_g32: t.CUInt64T = (t.CUInt64T(intr.idt.idt[32].base_low) |
(t.CUInt64T(intr.idt.idt[32].base_middle) << 16) |
(t.CUInt64T(intr.idt.idt[32].base_high) << 32))
_g32buf: t.CArray[t.CChar, 96]
viperlib.snprintf(c.Addr(_g32buf), 96, "[DBG] idt32 base=0x%lx sel=0x%x type=0x%x\n",
_g32, intr.idt.idt[32].selector, intr.idt.idt[32].type_attr)
serial.puts(c.Addr(_g32buf))
asm.sti()
serial.puts("[main] sti done\n")
# [DBG] 软件触发 int 0x20:门 32 有效则分发到 isr32→timer_handler 打印 H 并返回;
# 门无效则 #GP(isrCommonhandler 打印 [GP] err=0x20),一次性区分分发路径(用后移除)
serial.puts("[DBG] before swint\n")
c.Asm("int 0x20", op=[t.ASM_DESCR.CLOBBER_MEMORY])
serial.puts("[DBG] after swint\n")
# [DBG] 读 PIC1 ISR:bit0 置位说明 PIT IRQ0 已到达 PIC 并被响应(用后移除)
asm.outb(0x20, 0x0B) # OCW3 读 ISR
asm.io_wait()
_pic_isr: t.CUInt8T = asm.inb(0x20)
_isrbuf: t.CArray[t.CChar, 48]
viperlib.snprintf(c.Addr(_isrbuf), 48, "[DBG] pic_isr=0x%x\n", _pic_isr)
serial.puts(c.Addr(_isrbuf))
timer.timer_msleep(100)
serial.puts("[main] msleep done\n")
rtc.rtc_init()
serial.puts("[main] rtc init done\n")
ncpu: t.CUInt32T = apic.get_cpu_count()
ncpu_cr: t.CArray[t.CChar, 32]
viperlib.snprintf(c.Addr(ncpu_cr), 32, "[main] cpu_count=%u\n", ncpu)
serial.puts(ncpu_cr)
if ncpu > 1:
apic.start_aps()
serial.puts("[main] kernel init done\n")
fb: t.CVoid | t.CPtr = BootInfo.framebuffer_addr
width: t.CUInt64T = BootInfo.framebuffer_width
height: t.CUInt64T = BootInfo.framebuffer_height
pitch: t.CUInt64T = BootInfo.framebuffer_pitch
fb_format: t.CUInt64T = BootInfo.framebuffer_format
fb_size: t.CUInt64T = BootInfo.framebuffer_size
# [DBG] 打印 BootInfo 指针与 memmap 原始字段,定位全 0 根因(用后移除)
_dbbuf: t.CArray[t.CChar, 140]
viperlib.snprintf(c.Addr(_dbbuf), 140, "[main] BootInfo=0x%lx mmap=0x%lx mmsz=%lu mmd=%lu fb=0x%lx\n",
t.CUInt64T(BootInfo), BootInfo.MemmapAddr, BootInfo.MemmapSize,
BootInfo.MemmapDescSize, BootInfo.framebuffer_addr)
serial.puts(c.Addr(_dbbuf))
# [DBG] 转储 BootInfo 地址处原始 88 字节(用后移除)
_dbraw: t.CArray[t.CChar, 16]
_dbi: t.CInt = 0
while _dbi < 88:
if (_dbi % 16) == 0:
viperlib.snprintf(c.Addr(_dbraw), 16, "\n[DB] %04x:", _dbi)
serial.puts(c.Addr(_dbraw))
_dbv: t.CUInt8T = c.Deref(t.CUInt8T(t.CUInt64T(BootInfo) + t.CUInt64T(_dbi), t.CPtr))
viperlib.snprintf(c.Addr(_dbraw), 16, " %02x", _dbv)
serial.puts(c.Addr(_dbraw))
_dbi += 1
serial.puts("\n[DB] dump end\n")
if BootInfo is not None:
paging.init(BootInfo.MemmapAddr, BootInfo.MemmapSize)
fb_pages: t.CUInt64T = (fb_size + t.CUInt64T(0xFFF)) >> t.CUInt64T(12)
fpi: t.CUInt64T = 0
while fpi < fb_pages:
fva: t.CUInt64T = t.CUInt64T(fb) + fpi * t.CUInt64T(0x1000)
paging.MapPage(fva, fva, paging.PTE_WRITABLE | paging.PTE_PCD)
fpi += 1
mm.init(BootInfo.MemmapAddr, BootInfo.MemmapSize, BootInfo.MemmapDescSize, BootInfo.framebuffer_addr, fb_size)
pt_pool_buf: t.CVoid | t.CPtr = mm.malloc(t.CUInt64T(513) * t.CUInt64T(4096))
paging.init_pool(pt_pool_buf, t.CUInt64T(513) * t.CUInt64T(4096))
else:
paging.init(0, 0)
mm.init(0, 0, 0, 0, 0)
pt_pool_buf2: t.CVoid | t.CPtr = mm.malloc(t.CUInt64T(513) * t.CUInt64T(4096))
paging.init_pool(pt_pool_buf2, t.CUInt64T(513) * t.CUInt64T(4096))
serial.puts("[main] paging and mm init done\n")
proc.ProcessManager()
serial.puts("[main] process manager init done\n")
ide.init()
serial.puts("[main] ide init done\n")
serial.puts("[main] initializing FAT32 filesystem\n")
scan_res: t.CInt = fat32.scan_drives()
ndrives: t.CInt = fat32.get_drive_count()
ndrives_cr: t.CArray[t.CChar, 64]
viperlib.snprintf(c.Addr(ndrives_cr), 64, "[fat32] scan_res=%d found %d drives\n", scan_res, ndrives)
serial.puts(ndrives_cr)
if ndrives > 0:
mount_res: t.CInt = fat32.mount()
if mount_res == 0:
dp: fat32_types.dirobj
res: t.CInt = fat32.opendir("/", c.Addr(dp))
if res == 0:
serial.puts("[fat32] listing 0:/\n")
count: t.CInt = 0
while count < 20:
info: fat32_types.fileinfo
res2: t.CInt = fat32.readdir(c.Addr(dp), c.Addr(info))
if res2 != 0: break
if info.fname[0] == 0: break
is_dir: t.CInt = 1 if (info.attr & fat32_types.AM_DIR) else 0
entry_cr: t.CArray[t.CChar, 300]
viperlib.snprintf(c.Addr(entry_cr), 300, " %s size=%lu dir=%d\n", c.Addr(info.fname[0]), info.file_size, is_dir)
serial.puts(entry_cr)
count = count + 1
fat32.closedir(c.Addr(dp))
serial.puts("[fat32] directory listing done\n")
else:
serial.puts("[fat32] failed to open root directory\n")
# Load ELF apps BEFORE running destructive FAT32 tests
serial.puts("[main] loading ELF apps...\n")
dp_apps: fat32_types.dirobj
apps_res: t.CInt = fat32.opendir("/APPS", c.Addr(dp_apps))
if apps_res == 0:
serial.puts("[main] APPS dir listing:\n")
ac: t.CInt = 0
while ac < 20:
ai: fat32_types.fileinfo
ar: t.CInt = fat32.readdir(c.Addr(dp_apps), c.Addr(ai))
if ar != 0: break
if ai.fname[0] == 0: break
ae: t.CArray[t.CChar, 300]
viperlib.snprintf(c.Addr(ae), 300, " %s size=%lu\n", c.Addr(ai.fname[0]), ai.file_size)
serial.puts(ae)
ac = ac + 1
fat32.closedir(c.Addr(dp_apps))
else:
serial.puts("[main] APPS dir not found\n")
sched.Scheduler.disable()
hello_pid: t.CInt = elf.spawn_elf("/APPS/hello.elf")
if hello_pid < 0:
serial.puts("[main] Hello spawn failed\n")
else:
slog: t.CArray[t.CChar, 64]
viperlib.snprintf(c.Addr(slog), 64, "[main] Hello spawned as pid=%d\n", hello_pid)
serial.puts(slog)
term_pid: t.CInt = elf.spawn_elf("/APPS/terminal.elf")
if term_pid < 0:
serial.puts("[main] Terminal spawn failed\n")
else:
tlog: t.CArray[t.CChar, 64]
viperlib.snprintf(c.Addr(tlog), 64, "[main] Terminal spawned as pid=%d\n", term_pid)
serial.puts(tlog)
garg_pid: t.CInt = elf.spawn_elf("/APPS/gargantua.elf")
if garg_pid < 0:
serial.puts("[main] Gargantua spawn failed\n")
else:
glog: t.CArray[t.CChar, 64]
viperlib.snprintf(c.Addr(glog), 64, "[main] Gargantua spawned as pid=%d\n", garg_pid)
serial.puts(glog)
s3d_pid: t.CInt = elf.spawn_elf("/APPS/scene3d.elf")
if s3d_pid < 0:
serial.puts("[main] Scene3D spawn failed\n")
else:
s3d_log: t.CArray[t.CChar, 64]
viperlib.snprintf(c.Addr(s3d_log), 64, "[main] Scene3D spawned as pid=%d\n", s3d_pid)
serial.puts(s3d_log)
so_base: t.CVoid | t.CPtr = elf.load_so("/LIBS/SLOG.SO")
if so_base is not None:
serial.puts("[main] slog.so loaded, calling log_info...\n")
log_info_fn: t.CVoid | t.CPtr = elf.get_so_symbol(so_base, "log_info", "/LIBS/SLOG.SO")
if log_info_fn is not None:
msg: t.CConst | t.CChar | t.CPtr = "Hello from shared library!"
c.Asm(f"""mov rdi, {c.AsmInp(msg, t.ASM_DESCR.REG_ANY)}
call {c.AsmInp(log_info_fn, t.ASM_DESCR.REG_ANY)}""",
op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RAX,
t.ASM_DESCR.CLOBBER_RCX, t.ASM_DESCR.CLOBBER_RDX,
t.ASM_DESCR.CLOBBER_RDI, t.ASM_DESCR.CLOBBER_RSI,
t.ASM_DESCR.CLOBBER_R8, t.ASM_DESCR.CLOBBER_R9,
t.ASM_DESCR.CLOBBER_R10, t.ASM_DESCR.CLOBBER_R11])
else:
serial.puts("[main] log_info symbol not found\n")
else:
serial.puts("[main] failed to load slog.so\n")
sched.Scheduler.enable()
# FAT32 destructive tests removed to prevent disk.img corruption
# (write/rename/delete/mkdir/rmdir permanently modify the disk image)
fat32.unmount()
serial.puts("[fat32] unmounted\n")
else:
serial.puts("[fat32] mount failed\n")
else:
serial.puts("[fat32] no drives found\n")
devfs.devfs_init()
devfs.devfs_register_serial0()
devfs.devfs_register_serial1()
devfs.devfs_register_keyboard()
devfs.devfs_register_mouse()
serial.puts("[main] sched started\n")
fd: t.CInt = devfs.devfs_open("/dev/serial0", 0)
buf: t.CArray[t.CChar, 64]
string.memset(c.Addr(buf), 0, 64)
devfs.devfs_write(fd, "hello from devfs!", 17)
devfs.devfs_read(fd, c.Addr(buf), 64)
devfs.devfs_close(fd)
cr: t.CArray[t.CChar, 30]
string.memset(c.Addr(cr), 0, 30)
viperlib.snprintf(c.Addr(cr), 30, "hello %d %.2f", 42, float(0.24))
serial.puts(cr)
serial.puts("\n")
bootinfo_cr: t.CArray[t.CChar, 256]
string.memset(c.Addr(bootinfo_cr), 0, 256)
viperlib.snprintf(c.Addr(bootinfo_cr), 256, "Bootinfo 0x%016lx %lu %lu %lu %lu %lu map 0x%lx+%lu descsize=%lu",
BootInfo.framebuffer_addr,
BootInfo.framebuffer_format,
BootInfo.framebuffer_width,
BootInfo.framebuffer_height,
BootInfo.framebuffer_pitch,
BootInfo.framebuffer_size,
BootInfo.MemmapAddr,
BootInfo.MemmapSize,
BootInfo.MemmapDescSize)
serial.puts(bootinfo_cr)
serial.puts("\n")
a: t.CConst | str = "Hello"
serial.puts(c.Addr(a[4])) # o
serial.puts("\n")
d: t.CConst | int = 42
b: t.CConst | int | t.CPtr = c.Addr(d)
string.memset(c.Addr(cr), 0, 4)
viperlib.snprintf(c.Addr(cr), 30, "%d", b[0]) # 42
serial.puts(cr)
serial.puts("\n")
# paging and mm already initialized before FAT32
usb.init()
serial.puts("[main] starting keyboard service\n")
kbd_svc.start()
serial.puts("[main] starting mouse service\n")
mse_svc.start()
serial.puts("[main] services started\n")
self.VGA_drv = vga._VGAScreenDriver(fb, width, height, pitch, fb_format)
self.VGA = c.Addr(self.VGA_drv)
self.VGA.Print(100, 100, "ViperOS VKernel Test")
serial.puts("你好\n")
serial.puts("[main] starting desktop\n")
desktop.start(fb, width, height, pitch)
serial.puts("[main] desktop started\n")
# ELF apps already loaded before FAT32 tests
serial.puts("[main] entering main loop\n")
# sched.sched_dump()
last_sec: t.CUInt32T = 0
loop_cnt: t.CUInt32T = 0
while True:
cur_sec: t.CUInt32T = timer.timer_get_seconds()
if cur_sec != last_sec:
last_sec = cur_sec
#if cur_sec % 5 == 0:
# sched.sched_dump()
loop_cnt += 1
sched.Scheduler._yield()
#while True:
# cube.RenderScene(back_fb, cube.SCREEN_W, cube.SCREEN_H, time)
# string.memcpy(fb, back_fb, width * height * 4)
# serial.puts("time")
# time += float(0.1)
# circle(width / 2, height / 2, 180, fb)
# circle(640, 400, 180, 0xFFFFFFFF)
# X 字符串遍历
# c.Cast 更换
# 时钟
# PCI
# SHA1 它者文件更新
# assets
# X attributes语法和谐
# 合成式类型转换
VKP: ViperKernel | t.CPtr
# stackrealign:_start 由 bootloader jmp 进入(rsp≡0 mod 16),llc 按 call 约定
# 假设入口 rsp≡8 → 帧偏 8 → 变参函数 movaps #GP。动态重对齐栈修复。
@c.Attribute(t.attr.section(".text.startup"), t.attr.aligned(16), t.attr.stackrealign)
def _start() -> t.CInt:
global VKP, BootInfo
saved_rdi: t.CUnsignedLong
c.Asm(f"mov {c.AsmOut(saved_rdi, t.ASM_DESCR.OUTPUT_REG)}, rdi",
op = [t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RDI])
asm.BSSClean()
BootInfo = saved_rdi # 隐式转为 bootinfo*
VKP = c.Addr(ViperKernel())
while True:
asm.sti()
asm.hlt()
sched.Scheduler._yield()
return 0