- devfs.vp: string.split 传 PATH_BUFFER_SIZE(配合 split 新增容量参数) - fat32_types.vp: lfn_extract 越界钳制(base+i > MAX_LFN 返回 -1) - sheet.vp: gfx.Renderer 实参修正为 (fb, zb, w, h)(原单参导致 w/h=0 渲染空操作) - 同批包含 VKernel 其余既有移植改动与 vpsdk/Apps/Libs 工作区内容
377 lines
16 KiB
Plaintext
377 lines
16 KiB
Plaintext
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: int = -1
|
||
mse_tid: int = -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, uint64(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: uint64 = apic.get_base()
|
||
_dbg_ver: uint32 = apic.read(0x030) # LAPIC_VERSION:真 LAPIC 应为 0x50014
|
||
_dbg_svr: uint32 = apic.read(0x0F0) # LAPIC_SVR:init 已写 0x1FF
|
||
apic.write(0x350, 0x700) # 再次写 LINT0,验证写粘性
|
||
_dbg_lint0: uint32 = apic.read(0x350)
|
||
_dbg_feat: uint32 = 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: uint64 = (uint64(intr.idt.idt[32].base_low) |
|
||
(uint64(intr.idt.idt[32].base_middle) << 16) |
|
||
(uint64(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: uint8 = 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: uint32 = 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.CPtr = t.CPtr(BootInfo.framebuffer_addr)
|
||
width: uint64 = BootInfo.framebuffer_width
|
||
height: uint64 = BootInfo.framebuffer_height
|
||
pitch: uint64 = BootInfo.framebuffer_pitch
|
||
fb_format: uint64 = BootInfo.framebuffer_format
|
||
fb_size: uint64 = 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",
|
||
uint64(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: int = 0
|
||
while _dbi < 88:
|
||
if (_dbi % 16) == 0:
|
||
viperlib.snprintf(c.Addr(_dbraw), 16, "\n[DB] %04x:", _dbi)
|
||
serial.puts(c.Addr(_dbraw))
|
||
_dbv: uint8 = c.Deref(uint8(uint64(BootInfo) + uint64(_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: uint64 = (fb_size + uint64(0xFFF)) >> uint64(12)
|
||
fpi: uint64 = 0
|
||
while fpi < fb_pages:
|
||
fva: uint64 = uint64(fb) + fpi * uint64(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.CPtr = mm.malloc(uint64(513) * uint64(4096))
|
||
paging.init_pool(pt_pool_buf, uint64(513) * uint64(4096))
|
||
else:
|
||
paging.init(0, 0)
|
||
mm.init(0, 0, 0, 0, 0)
|
||
pt_pool_buf2: t.CPtr = mm.malloc(uint64(513) * uint64(4096))
|
||
paging.init_pool(pt_pool_buf2, uint64(513) * uint64(4096))
|
||
serial.puts("[main] paging and mm init done\n")
|
||
# 把 .rodata(字符串常量/vtable)映射只读 + CR0.WP=1:
|
||
# 越界写立即 #PF 暴露 cr2/rip(此前静默破坏格式串/路径/符号名 → P2/P3/P4/P5 连锁)
|
||
paging.ProtectKernelRO()
|
||
# [PG] 页表诊断:确认 .rodata 是否真正只读(2MB 大页会令 MakeRangeRO 失效)
|
||
paging.DumpMapping(0x100000, "kern")
|
||
paging.DumpMapping(0x60000, "bstk")
|
||
paging.DumpMapping(0x80000000, "fb")
|
||
|
||
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: int = fat32.scan_drives()
|
||
ndrives: int = 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: int = fat32.mount()
|
||
if mount_res == 0:
|
||
dp: fat32_types.dirobj
|
||
res: int = fat32.opendir("/", c.Addr(dp))
|
||
if res == 0:
|
||
serial.puts("[fat32] listing 0:/\n")
|
||
count: int = 0
|
||
while count < 20:
|
||
info: fat32_types.fileinfo
|
||
res2: int = fat32.readdir(c.Addr(dp), c.Addr(info))
|
||
if res2 != 0: break
|
||
if info.fname[0] == 0: break
|
||
is_dir: int = 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: int = fat32.opendir("/APPS", c.Addr(dp_apps))
|
||
if apps_res == 0:
|
||
serial.puts("[main] APPS dir listing:\n")
|
||
ac: int = 0
|
||
while ac < 20:
|
||
ai: fat32_types.fileinfo
|
||
ar: int = 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")
|
||
|
||
# [DIAG] app 自动启动已禁用:不 spawn 任何 .elf,不加载任何 .so,
|
||
# 专注排查内核自身的概率性崩溃(sched 启动 / desktop 启动后 30s)。
|
||
sched.Scheduler.disable()
|
||
serial.puts("[main] app autostart disabled\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: int = 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: uint32 = 0
|
||
loop_cnt: uint32 = 0
|
||
while True:
|
||
cur_sec: uint32 = 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() -> int:
|
||
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 = t.CPtr(saved_rdi) # 转为 bootinfo*
|
||
VKP = c.Addr(ViperKernel())
|
||
while True:
|
||
asm.sti()
|
||
asm.hlt()
|
||
sched.Scheduler._yield()
|
||
return 0
|
||
|