401 lines
18 KiB
Plaintext
401 lines
18 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 platform.acpi.acpi as acpi
|
||
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 intr.syscall as syscall
|
||
import execrunner.elf as elf
|
||
import asm
|
||
|
||
|
||
kbd_tid: int = -1
|
||
mse_tid: int = -1
|
||
|
||
BootInfo: bootinfo.bootinfo*
|
||
|
||
class ViperKernel:
|
||
BootInfo: bootinfo.bootinfo*
|
||
VGA: vga._VGAScreenDriver*
|
||
|
||
def __init__(self):
|
||
global BootInfo
|
||
self.BootInfo = BootInfo
|
||
|
||
cpu.enable_sse()
|
||
|
||
serial.init()
|
||
serial.puts("===== ViperOS VKernel Test\n =====")
|
||
serial.puts("V2.3.8\n")
|
||
|
||
intr.gdt.init()
|
||
|
||
cpu.msr_write(cpu.IA32_KERNEL_GS_BASE, uint64(&cpu._per_cpu))
|
||
serial.puts("[main] gdt init done\n")
|
||
intr.idt.init()
|
||
syscall.init()
|
||
serial.puts("[main] syscall init done\n")
|
||
|
||
sched.Scheduler()
|
||
serial.puts("[main] sched init done\n")
|
||
|
||
timer.timer_init()
|
||
serial.puts("[main] timer init done\n")
|
||
|
||
# PS/2 鼠标:这里是唯一的初始化入口(注册 IRQ12 + 设采样率 + 使能数据上报)。
|
||
# services/mouse.vp 只负责 USB 鼠标,所以这行被注释掉时 /dev/input/mouse 能 open
|
||
# 却永远没有事件 —— explorer 的鼠标通路整条哑掉(现场:桌面有键盘无鼠标)。
|
||
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")
|
||
|
||
asm.sti()
|
||
serial.puts("[main] sti done\n")
|
||
|
||
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: [32]char
|
||
viperlib.snprintf(&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: rawptr = rawptr(BootInfo.framebuffer_addr)
|
||
width: int = int(BootInfo.framebuffer_width)
|
||
height: int = int(BootInfo.framebuffer_height)
|
||
pitch: int = int(BootInfo.framebuffer_pitch)
|
||
fb_format: int = int(BootInfo.framebuffer_format)
|
||
fb_size: uint64 = BootInfo.framebuffer_size
|
||
_dbbuf: [140]char
|
||
viperlib.snprintf(&_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(&_dbbuf)
|
||
|
||
# 内核镜像末端(含 .bss;__bss_end 见 VKernel/linker.ld):传给 mm.init 使其在挑选
|
||
# 堆 arena 时排除整块内核镜像 —— .bss 不占文件大小,bootloader 只按文件大小预留页,
|
||
# 否则 .bss 尾部(ticks/_perf_* 等全局量)会落在 memmap 空闲区里被堆占用。
|
||
_kern_end: uint64 = 0
|
||
__asm__(f"lea {asmout(_kern_end, ASM_DESCR.OUTPUT_REG)}, [rip + __bss_end]",
|
||
op=[ASM_DESCR.CLOBBER_MEMORY])
|
||
if BootInfo is not None:
|
||
paging.init(BootInfo.MemmapAddr, BootInfo.MemmapSize)
|
||
# PAT: entry 4 = write-combining. The framebuffer is mapped with
|
||
# PTE_PAT so large compositor blits stay fast on VMware/real HW
|
||
# (WB is fast but can be incoherent for scanout; UC is slow).
|
||
feats: uint32 = cpu.detect_features_static()
|
||
if (feats & uint32(cpu.CPU_FEATURE_PAT)) != 0:
|
||
pat_v: uint64 = cpu.msr_read(uint32(0x277))
|
||
pat_v = (pat_v & ~(uint64(7) << uint64(32))) | (uint64(1) << uint64(32))
|
||
cpu.msr_write(uint32(0x277), pat_v)
|
||
paging.set_pat_wc()
|
||
serial.puts("[main] PAT write-combining enabled\n")
|
||
fb_flags: uint64 = paging.PTE_WRITABLE
|
||
if paging.has_pat_wc() != 0:
|
||
fb_flags = fb_flags | paging.PTE_PAT
|
||
fb_pages: uint64 = (fb_size + uint64(0xFFF)) >> uint64(12)
|
||
for fpi in range(fb_pages):
|
||
fva: uint64 = uint64(fb) + fpi * uint64(0x1000)
|
||
# 帧缓冲是 MMIO:优先 PAT WC(快且对扫描输出可见);CPU 不支持
|
||
# PAT 时保持回写,避免用 UC 把整帧提交拖慢。
|
||
paging.MapPage(fva, fva, fb_flags)
|
||
mm.init(BootInfo.MemmapAddr, BootInfo.MemmapSize, BootInfo.MemmapDescSize, BootInfo.framebuffer_addr, fb_size, BootInfo.kernel_PhysAddr, _kern_end)
|
||
pt_pool_buf: rawptr = 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: rawptr = 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: [64]char
|
||
viperlib.snprintf(&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("/", &dp)
|
||
if res == 0:
|
||
serial.puts("[fat32] listing 0:/\n")
|
||
for count in range(20):
|
||
info: fat32_types.fileinfo
|
||
res2: int = fat32.readdir(&dp, &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: [300]char
|
||
viperlib.snprintf(&entry_cr, 300, " %s size=%lu dir=%d\n", &info.fname[0], info.file_size, is_dir)
|
||
serial.puts(&entry_cr)
|
||
fat32.closedir(&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", &dp_apps)
|
||
if apps_res == 0:
|
||
serial.puts("[main] APPS dir listing:\n")
|
||
for ac in range(20):
|
||
ai: fat32_types.fileinfo
|
||
ar: int = fat32.readdir(&dp_apps, &ai)
|
||
if ar != 0: break
|
||
if ai.fname[0] == 0: break
|
||
ae: [300]char
|
||
viperlib.snprintf(&ae, 300, " %s size=%lu\n", &ai.fname[0], ai.file_size)
|
||
serial.puts(&ae)
|
||
fat32.closedir(&dp_apps)
|
||
else:
|
||
serial.puts("[main] APPS dir not found\n")
|
||
|
||
# elf 自启动:sched 暂停 → 逐个 spawn .elf(hello / terminal / gargantua /
|
||
# scene3d / explorer)→ sched 恢复。spawn 要在 FAT32 破坏性测试之前完成。
|
||
sched.Scheduler.disable()
|
||
|
||
hello_pid: int = elf.spawn_elf("/APPS/hello.elf")
|
||
if hello_pid < 0:
|
||
serial.puts("[main] Hello spawn failed\n")
|
||
else:
|
||
hlog: [64]char
|
||
viperlib.snprintf(&hlog, 64, "[main] Hello spawned as pid=%d\n", hello_pid)
|
||
serial.puts(&hlog)
|
||
|
||
term_pid: int = elf.spawn_elf("/APPS/terminal.elf")
|
||
if term_pid < 0:
|
||
serial.puts("[main] Terminal spawn failed\n")
|
||
else:
|
||
tlog: [64]char
|
||
viperlib.snprintf(&tlog, 64, "[main] Terminal spawned as pid=%d\n", term_pid)
|
||
serial.puts(&tlog)
|
||
|
||
garg_pid: int = elf.spawn_elf("/APPS/gargantua.elf")
|
||
if garg_pid < 0:
|
||
serial.puts("[main] Gargantua spawn failed\n")
|
||
else:
|
||
glog: [64]char
|
||
viperlib.snprintf(&glog, 64, "[main] Gargantua spawned as pid=%d\n", garg_pid)
|
||
serial.puts(&glog)
|
||
|
||
s3d_pid: int = elf.spawn_elf("/APPS/scene3d.elf")
|
||
if s3d_pid < 0:
|
||
serial.puts("[main] Scene3D spawn failed\n")
|
||
else:
|
||
s3d_log: [64]char
|
||
viperlib.snprintf(&s3d_log, 64, "[main] Scene3D spawned as pid=%d\n", s3d_pid)
|
||
serial.puts(&s3d_log)
|
||
|
||
# 图形壳 explorer(图形壳应用):经 devfs 的 /dev/fb0 + mmap 在用户态直接写屏。
|
||
# S3e 之后内核侧已无任何窗口/合成语义(services/desktop.vp 已删除),
|
||
# 屏幕由 explorer 独占绘制。
|
||
exp_pid: int = elf.spawn_elf("/APPS/explorer.elf")
|
||
if exp_pid < 0:
|
||
serial.puts("[main] Explorer spawn failed\n")
|
||
else:
|
||
exp_log: [64]char
|
||
viperlib.snprintf(&exp_log, 64, "[main] Explorer spawned as pid=%d\n", exp_pid)
|
||
serial.puts(&exp_log)
|
||
|
||
font_pid: int = elf.spawn_elf("/APPS/fontdemo.elf")
|
||
if font_pid >= 0:
|
||
font_log: [64]char
|
||
viperlib.snprintf(&font_log, 64, "[main] FontDemo spawned as pid=%d\n", font_pid)
|
||
serial.puts(&font_log)
|
||
|
||
img_pid: int = elf.spawn_elf("/APPS/imagesee.elf")
|
||
if img_pid >= 0:
|
||
img_log: [64]char
|
||
viperlib.snprintf(&img_log, 64, "[main] ImageSee spawned as pid=%d\n", img_pid)
|
||
serial.puts(&img_log)
|
||
|
||
sched.Scheduler.enable()
|
||
|
||
# 破坏性 FAT32 测试已删除(write/rename/delete/mkdir/rmdir 会永久改坏 disk.img)。
|
||
# **不 unmount 根文件系统**:app 是 spawn 之后异步运行的,运行期还要读盘
|
||
# (load_so / 资产文件等),一旦卸载根 FS,它们的 fat32.open 会直接失败
|
||
# (现场:app 第一次 LOAD_SO 得到 "[so] failed to open file")。
|
||
# 卸载只应服务于非根挂载点(U 盘/多分区),并配挂载点表 + 引用计数。
|
||
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()
|
||
# /dev/fb0:把 framebuffer 的物理区间与几何登记成设备节点,供用户态图形壳
|
||
# explorer 经 open + ioctl(FB_GET_INFO) + mmap 接管屏幕绘制(S1)。
|
||
devfs.devfs_register_fb0(fb, width, height, pitch, fb_format, fb_size)
|
||
# /dev/desk:explorer 与各 app 的共享内存通道(S3)。分配失败就让登记跳过 ——
|
||
# explorer 的 open 会明确失败并打日志,不做静默兜底。
|
||
desk_shm: rawptr = mm.malloc(uint64(devfs.DESK_SHM_SIZE))
|
||
sl: [160]char
|
||
viperlib.snprintf(&sl, 160, "[main] desk shm=0x%lx size=0x%lx\n",
|
||
uint64(desk_shm), uint64(devfs.DESK_SHM_SIZE))
|
||
serial.puts(&sl)
|
||
devfs.devfs_register_desk(desk_shm, uint64(devfs.DESK_SHM_SIZE))
|
||
mm.check_lists(1)
|
||
|
||
serial.puts("[main] sched started\n")
|
||
|
||
fd: int = devfs.devfs_open("/dev/serial0", 0)
|
||
buf: [64]char
|
||
string.memset(&buf, 0, 64)
|
||
devfs.devfs_write(fd, "hello from devfs!", 17)
|
||
devfs.devfs_read(fd, &buf, 64)
|
||
devfs.devfs_close(fd)
|
||
|
||
cr: [30]char
|
||
string.memset(&cr, 0, 30)
|
||
viperlib.snprintf(&cr, 30, "hello %d %.2f", 42, float(0.24))
|
||
serial.puts(cr)
|
||
serial.puts("\n")
|
||
|
||
bootinfo_cr: [256]char
|
||
string.memset(&bootinfo_cr, 0, 256)
|
||
viperlib.snprintf(&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: const str = "Hello"
|
||
serial.puts(&a[4]) # o
|
||
serial.puts("\n")
|
||
d: const int = 42
|
||
b: const int* = &d
|
||
string.memset(&cr, 0, 4)
|
||
viperlib.snprintf(&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")
|
||
|
||
# S3e 之后屏幕由用户态 explorer 独占合成:内核不再创建 VGA 驱动、不再往 framebuffer
|
||
# 画任何东西。原先这里会 Print 一行测试文本,而它执行时间在 explorer 启动之后 ——
|
||
# 等于把内核测试内容盖在桌面上(也是屏幕上蓝色/文本残留的来源)。
|
||
serial.puts("你好\n")
|
||
|
||
# S3e:内核不再启动 desktop 线程 —— 桌面/窗口/合成/输入分发整体下沉到用户态
|
||
# 图形壳 explorer(它经 /dev/fb0 + /dev/input/* + /dev/desk 接管)。
|
||
|
||
# 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*
|
||
# stackrealign:_start 由 bootloader jmp 进入(rsp≡0 mod 16),llc 按 call 约定
|
||
# 假设入口 rsp≡8 → 帧偏 8 → 变参函数 movaps #GP。动态重对齐栈修复。
|
||
@__attribute__(section(".text.startup"), aligned(16), stackrealign)
|
||
def _start() -> int:
|
||
global VKP, BootInfo
|
||
saved_rdi: uint64
|
||
__asm__(f"mov {asmout(saved_rdi, ASM_DESCR.OUTPUT_REG)}, rdi",
|
||
op = [ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RDI])
|
||
asm.BSSClean()
|
||
BootInfo = bootinfo*(saved_rdi) # Cast the boot argument to bootinfo*
|
||
acpi.init(BootInfo.acpi_rsdp)
|
||
VKP = &ViperKernel()
|
||
while True:
|
||
asm.sti()
|
||
asm.hlt()
|
||
sched.Scheduler._yield()
|
||
return 0
|