Commit Graph
52 Commits
Author SHA1 Message Date
TermiNexus 453c353e01 fix(sched): sched_yield 改为无条件切换,原实现只在时间片到期时才切
现场:app 用 4000 次 yield_cpu() 等 /dev/desk ready(explorer 负责 format 并置 ready=1),却全部超时——三个 app 都打出 [win] desk not ready (attach timeout) / create_window: connect FAILED / geometry win_id=-1,最后静默退出;而 explorer 的 [elf] wrapper pid=5 出现在所有 app 退完之后。根因:YIELD syscall 走 sched.Scheduler.try_reschedule(),而它只在 needs_reschedule(时间片到期,由 tick 置位)时才真正 _yield(),因此 N 次 sched_yield 全落在同一个时间片内一次都不切换 ⇒ 主动等待外设的进程被饿死、初始化者永远轮不上。修法:新增 Scheduler.yield_now() 无条件切换,YIELD 分支改用它;try_reschedule() 仍保留给中断/抢占路径使用。
2026-10-16 20:57:55 +08:00
TermiNexus d6d07b25ee feat(s5): 删除 TELL/SIZE 私有 syscall —— 用 lseek 的 SEEK_CUR/SEEK_END 表达
内核:SEEK 补真正的 whence(0=SET 1=CUR 2=END,END 时 offset 为距末尾的距离,offset>size 返回 -1);删除 TELL/SIZE 的枚举项、派发分支与 TELL/fsize 函数(fat32.tell/size 仍留给 SEEK 内部用)。

SDK:vpsdk/fs.vp 的 seek() 增加 whence 参数(默认 0,调用方不变);tell() 改为 lseek(fd,0,SEEK_CUR);fsize() 改为三次 lseek 往返(记住位置 → 跳末尾取大小 → 复位),不改变最终位置。

私有段只剩 5 项:OPENDIR/READDIR/CLOSEDIR(openat+getdents64)与 LOAD_SO/SO_CALL1(动态库加载下沉)。
2026-10-16 19:33:46 +08:00
TermiNexus 5e1cd04478 feat(s4): 删除 SERIAL_PUTS 私有 syscall,用户态改走 /dev/serial0
内核:删掉 SERIAL_PUTS 的枚举项/派发分支/处理函数(内核自身的 serial.puts 是直接驱动调用,不受影响)。SDK:vpsdk/devfs.vp 新增 serial_puts()(惰性 open /dev/serial0 一次 + write),syscall.vp 删掉该成员。

调用点全部切到 devfs.serial_puts:vpsdk/stdio.vp 的 printf、vpsdk/vpui.vp 的 _serial_puts、Apps/Terminal 的 _sdbg、Apps/Explorer 的 _dbg、Libs/SerialLogger 的 log_info/log_warn(.so 同样走设备节点)。顺带清掉因此失效的 import(vpui 的 _syscall、Terminal 的 _syscall、Explorer 的 syscall)。

至此私有段只剩 7 项:TELL/SIZE/OPENDIR/READDIR/CLOSEDIR(可由 lseek/fstat/openat+getdents64 表达)与 LOAD_SO/SO_CALL1(动态库加载待下沉用户层)。
2026-10-16 19:17:39 +08:00
TermiNexus 13eee8e8ea feat(s3e)!: 内核 UI 层整体下沉用户态 —— 删 11 个 UI syscall + services/desktop.vp
内核 intr/syscall.vp:删除 WAIT_WINDOW/CREATE_WINDOW/DRAW_TEXT/GET_WINBUF/WIN_FLUSH/POLL_EVENT/SET_CURSOR/SET_TITLE/SET_GEOMETRY/SET_RESIZABLE/GET_WIN_SIZE 的枚举项、派发分支与对应函数;去掉 services.desktop 依赖;EXIT 不再拆窗口(内核已不持有窗口语义)。

删除 VKernel/Kernel/services/desktop.vp(~2900 行内核桌面/合成/输入分发);main.vp 不再启动 desktop 线程,只保留一次 fb 局部量一致性校验。剩余 syscall = Linux 对齐段 + 8 个待 devfs 化的私有项(SERIAL_PUTS/TELL/SIZE/OPENDIR/READDIR/CLOSEDIR/LOAD_SO/SO_CALL1)。

SDK:yield_cpu() 从 WAIT_WINDOW(0,2) 改为 Linux 对齐的 sched_yield(内核同样是 try_reschedule);syscall.vp 同步删掉 11 个 UI 成员。

explorer:关闭按钮改为先通知 CLOSE 再把槽位 used=0 —— window.vp 的 is_active() 见 used==0 返回 0,app 的 while is_active() 自然退出;像素缓冲不复用(bump 分配)⇒ 无别名风险。至此输入只剩 explorer 一家在读(内核桌面已删),两个合成器之争结束。
2026-10-16 19:11:44 +08:00
TermiNexus bb82c2fe75 fix(desk)!: 事件环改为单环+按 pid 过滤,并补 getpid(S3d 前置)
缺陷:原设计把事件环按 win_id 索引,但建窗答复 CREATED 必须在 app 知道 win_id 之前送达 —— 按 win_id 索引物理上不可能送达。改为**单一事件环**,每条事件带目标 pid,由 app 用自己的 pid 过滤(与内核旧接口 poll_event(buf, caller_pid) 同口径)。

DeskMsg 增 pid 字段(ring_push/ring_pop 同步搬运,漏搬则事件全被当成 pid=0);desk_evt_ring_off() 去掉 win_id 参数;desk_format 只初始化一个事件环;服务端 push_event 从窗口槽取 pid、建窗答复走新增的 push_event_pid(此时槽里还没有 pid)。

新增 GETPID(Linux 号 39,此前是"留空"项,现在真正实现):内核 GETPID() 返回 process.current(),SDK 侧 process.get_pid()。app 需要它才能过滤自己的窗口事件。
2026-10-16 18:58:43 +08:00
TermiNexus 7d5380148b feat(mmap): S1c —— MMAP 改 Linux 六参形态并支持映射 /dev/fb0
内核 MMAP(addr,length,prot,flags,fd,offset):fd=-1 + MAP_ANONYMOUS 走匿名映射(分配物理页、清零后映射到本进程新 VA,对齐 Linux"匿名必为零"语义);fd 是带 mmap_info 的 devfs 设备(当前仅 /dev/fb0)则把 [phys+offset, phys+offset+len) 按页映射进调用者地址空间(PTE_USER|WRITE)。addr/MAP_FIXED 暂未实现,一律由内核挑 VA 并返回真实起始地址。新增 PROT_*/MAP_*/MMAP_FD_ANON 常量。

参数次序与 Linux mmap 完全一致(rdi/rsi/rdx/r10/r8/r9),syscall_entry 的搬运顺序本就是这个约定,SDK 侧 vpsdk/syscall.vp 加 mmap() 助手走 _syscall6。stdlib.malloc 随之改为匿名映射调用(fd=-1、MAP_PRIVATE|MAP_ANONYMOUS、PROT_READ|PROT_WRITE),行为与原 MMAP(size) 一致且多一层清零。
2026-10-16 18:40:14 +08:00
TermiNexus f4c653958e feat(devfs): S1 —— 暴露 /dev/fb0 并让用户态可达 /dev/*(含 ioctl)
devfs:新增 /dev/fb0(framebuffer_device),提供 ioctl(FB_GET_INFO) 回填几何/格式/物理区间,并新增 devfs_file.mmap_info 虚方法(默认不可映射)把物理区间交给内核 mmap 用;main.vp 在 devfs_init 后登记它。输入设备(/dev/input/keyboard、/dev/input/mouse)此前已存在,本轮起从用户态可达。

用户态可达性:Process 增 fd_kind[](0=FAT32 fileobj*,1=devfs_fd+1),OPEN 对 "/dev/" 前缀改走 devfs,READ/WRITE/CLOSE 按 kind 分派;Process.exit 只对 kind=0 调 fat32.close(把 devfs fd 当 fileobj* 会跑飞)。fd 种类常量放在 process.vp 供 syscall.vp 引用,避免 process 反向 import devfs(devfs→keyboard/mouse→intr.idt→sched→process 会成环)。

新增 IOCTL syscall(Linux 号 16,本轮开始实现):只放给 devfs 设备,SDK 侧 vpsdk/fs.vp 加 ioctl() 助手。
2026-10-16 18:38:21 +08:00
TermiNexus ca72ddbbf7 refactor(syscall): 调用号改用 enum 并全面对齐 Linux x86-64 ABI
内核 intr/syscall.vp 与 vpsdk/syscall.vp 各一份 1..34 的 define 表改为同名 enum Syscall。编号口径:与 Linux 同名同号;Linux 有而我们未实现的留空不占号(12 brk / 39 getpid / 84 rmdir / 217 getdents64 / 257 openat … 需要时按号补)。

15 个语义重合的调用直接用 Linux 号:READ 0 / WRITE 1 / OPEN 2 / CLOSE 3 / STAT 4 / SEEK 8 / MMAP 9 / MUNMAP 11 / YIELD 24 / THREAD_CREATE 56(≈clone) / SPAWN 59(≈execve) / THREAD_EXIT 60(exit) / MKDIR 83 / REMOVE 87 / EXIT 231(exit_group)。

其余 19 个(UI 11 个 + SERIAL_PUTS/TELL/SIZE/OPENDIR/READDIR/CLOSEDIR/LOAD_SO/SO_CALL1)暂放 0x1000 起的**临时私有段**,每条带迁移标注:私有能力最终不占 syscall 号、改走 devfs 设备文件;UI 11 个待用户态 explorer(图形壳)接管 framebuffer 与输入后删除。

内核派发链改 n == int(Syscall.X)(n 是 int/i64、成员是 i32,显式 int() 规避未证实的混合宽度比较);SDK 全部调用点统一为 syscall.Syscall.X;stdlib 的 SYSCALL_MMAP 别名并入 Syscall.MMAP;Terminal 里硬编码的 uint64(18) 改为常量。
2026-10-16 18:32:33 +08:00
TermiNexus 4e1be2be88 fix(intr): 修正 isr80 的调用号/arg4 压栈顺序(int 0x80 一直把 arg4 当调用号)
栈向下增长,先压的落在高地址,而要的布局是 [S+0]=调用号(rax)、[S+8]=arg4(r10),所以必须先 push r10 后 push rax;原实现反了 ⇒ mov rax,[rsp+40] 取到 arg4 当调用号、mov r8,[rsp+168] 取到调用号当 arg4 ⇒ syscall_handler 收到错误的 n。

现场(探针实测):用户线程出口跳板 mov eax,34(THREAD_EXIT); int 0x80; jmp $ —— int 0x80 带着错的 n 进 syscall_handler,不匹配任何分支返回 0,iretq 回用户态落到 jmp $ 死转。该线程永不再 yield ⇒ needs_reschedule 无人消费 ⇒ 全系统停止切换:探针显示 cur 冻结在 5、t5 恒为 RUNNING,intr_cs=2B intr_rip=0x7FFFFFDE0007(正是跳板页偏移 7 的 jmp $)rsp=0x7FFFFFDE0000 ⇒ 桌面线程(tid 6)饿死,[perf]/[chk] 永不再打印。

这条路径此前无任何调用者(vpsdk 全走 syscall 指令),所以从未暴露。同时移除定位用的临时探针(timer.vp 心跳/调度器快照、isr.s 的 _intr_*)。
2026-10-16 18:07:57 +08:00
TermiNexus c29c1953df chore(intr): 探针补记被中断代码的 RIP/CS/RSP,用于定位卡死线程停在哪条指令
isr.s: ISR_NOERR/ISR_ERR 的 _frame_ready 里把 r8(RIP)/r9(CS)/rax(RSP) 写进新增的 _intr_rip/_intr_cs/_intr_rsp(只读这三个寄存器,不影响随后压栈)。timer.vp: 心跳行追加 3 个 hex32 字段。定时器 1kHz,所以这三个值几乎总是最后一次定时中断打断的现场:intr_cs=2B 表示该线程卡在用户态 intr_rip 处,=08 表示卡在内核。
2026-10-16 17:58:51 +08:00
TermiNexus 06d24ae1aa chore(kernel): 恢复最小异常现场 dump;EXIT 增加不返回硬保护
isr.s:ISR_ERR 两条入口写 _fault_rip/_fault_err/_fault_rsp;idt.vp:带错误码向量打印 rip/err/rsp(#PF 追加 cr2),返回槽三连仅在出错 RSP 落在内核栈范围时读取(用户栈下方页未映射,读 [rsp-8] 会触发次生 #PF)。syscall.vp:EXIT 在 exit_current_thread 之后禁止返回(返回会一路 ret 回 app 的 _start 末尾、弹到 0 取指,现场 [0E] rip=0x0 err=0x15)。
2026-10-16 16:29:04 +08:00
TermiNexus 3a497e38ec fix(usb): 控制传输短读判失败+描述符链完整性校验;CCB 读改写屏蔽 IRQ1;清理已验证探针
uhci.control_transfer:除错误位外再比对 DATA TD 的 Actual Length(bit10:0)。短读时错误位可能全 0(QEMU/硬件不把短包当错误),只查错误位会漏 —— 现场是 USB 鼠标的 25 字节配置描述符只落下前 9 字节 ⇒ 接口解析为 0 ⇒ [usb_mse] no USB mouse found。

usb._parse_config 改为返回实际走完的偏移,enumerate_port 要求恰好走完 total_len,否则重读、仍不完整即报错返回(fail-fast,不带着半截描述符继续);同时删除两条探针。

keyboard.init/mouse.init 读-改-写 CCB 全程 pic.setMask(1):读 CCB 的 0x20 会把 CCB 值放进 0x60 输出缓冲并拉高 IRQ1,IRQ1 handler 会把它当扫描码抢走(现场 sc=0x47),使回读拿到残留值。改为只在校验失败时打印错误,去掉全部 [kbd] 探针。

清理已验证调试输出:idt 的 IRQ1 handler 探针及其计数器、desktop 的 [dbg] back_fb/row_buf/bg_row_buf、main 的 [DBG] LAPIC/idt32/swint/pic_isr 与 [DB] BootInfo 转储、timer 的 [PIT] 与自旋打印及随之无用的 import。
2026-10-16 15:26:35 +08:00
TermiNexus d37eac32fe fix(keyboard): init() 改为幂等(避免重复初始化禁用键盘接口);按键时打印扫描码/字符到串口便于验证 2026-10-16 14:12:50 +08:00
TermiNexus 152c4c5683 feat(vkernel/vpsdk): 用户线程生命周期 - THREAD_EXIT 显式退出;异常终止时回收用户栈并释放槽位;返回哨兵可辨识 2026-10-15 16:50:58 +08:00
TermiNexus 29e1bc6e34 chore(debug): 移除定位用的 MMAP/get_winbuf/redraw 临时探针(线程模型修复后 PF 与卡死均已消失) 2026-10-15 16:48:19 +08:00
TermiNexus a7faeed21b fix(vkernel): THREAD_CREATE 改用户态线程 - 独立用户栈与内核栈经 drop_to_user_mode 进 Ring3;调度器按线程切 TSS.RSP0/gs[8];同步 _thread_entry 硬编码偏移并加 fail-fast 自检 2026-10-15 16:45:09 +08:00
TermiNexus 89a319e1be chore(debug): 加临时探针定位 MMAP 返回高半污染与 Tk.buf 野指针 - MMAP/get_winbuf/redraw 2026-10-15 16:20:14 +08:00
TermiNexus 9745f8832a fix(vkernel): UHCI frame list 步长越界 12KB 砸坏 buddy 堆(堆损坏根因);paging 页表记录指针缩放;清理临时探针
根因:uhci.vp 两处写成 _frame_list + uint64(i) * 4 —— uint32* 的 +n 已按元素(4字节)缩放,
再乘 4 使实际步长变成 16 字节;1024 次循环写到 16KB,而缓冲只有 UHCI_NUM_FRAMES*4 = 4KB
⇒ 越界 12KB 覆盖相邻 buddy 空闲块头(现场:[0E][PF] cr2=0xba4c000 err=0x3 rip=0x10e598
rax=0x3000=12288),把空闲链 next/prev 写坏 ⇒ 随机 [mm] BAD blk、malloc 失败、app 崩溃。
定位手段:把空闲链表头页在内核页表标只读(paging.init 已置 CR0.WP=1),任何写入立即 #PF
并给出 rip/cr2 直接指认写者(该探针定位完已删除)。

paging:save_pt_range / restore_pt_range 里的 save_buf + i*16 在 uint64* 上实际是 128 字节
步长、+8 实际是 +64 字节(IR 取证:getelementptr i64 ⇒ 按 8 字节元素缩放),改为显式字节
地址推进记录 + 元素索引(+1 = +8 字节),两处布局保持一致。

保留的加固(非探针):memory_block.magic 与 allocate_block/free_block/merge_blocks 的块头
校验、free() 的堆范围校验、slab 建页的 total 边界护栏、页表池按真实 buf_size 计页与
pt_pool_magic/pt_pool_limit 哨兵、#GP 分支补打 rax 便于定位非规范地址写。

elf.vp 新增 ELF_LOAD 开关(=1 正常装载;置 0 可整体跳过 app/.so 装载,便于隔离内核问题)。
2026-10-15 13:35:34 +08:00
TermiNexus ec57603bb3 fix(vkernel): 分配器与页表池加固(块头魔数/双链校验、池边界与哨兵),并清理定位用临时探针
mm: memory_block 加 magic(塞进原有 padding,布局仍 32B);allocate/free/merge 前校验块头(magic、size==2^order、state、prev/next 在堆内且 8 字节对齐),坏块只报不改并限流;free() 增加堆范围校验;slab 建页前校验 total 与地址范围自洽(防 uint64 下溢 + uint16 截断导致成片写页外);NO FREE BLOCK 附带 used/free/total。paging: 页表池按调用者给的真实 buf_size 计算可用页数(此前丢掉 buf_size、上限硬编码 PT_POOL_PAGES ⇒ 池会发到缓冲之外,把页表内容盖到 buddy 空闲块头);加 pt_pool_magic/pt_pool_limit 哨兵与 CORRUPT/EXHAUSTED fail-fast。idt: #GP 分支补打 rax(rax+0x10 即触发访存地址),与 #PF 分支同口径。同时移除本轮定位用临时探针(elf.vp/isr.s/main.vp 恢复原状)。
2026-10-15 12:47:39 +08:00
TermiNexus ee29ce37d1 fix(vkernel): 异常处理器 rbp 链回溯改为每跳校验,避免处理 #PF 时再触发 #PF 造成无限重入
旧实现用 mov rax,[rax] / mov rax,[rax+8] 裸链式解引用。当崩溃上下文是 ring3(或被 iret 帧影响)时,链上会取到 iret 帧里压入的 user_rsp=0x7fffffe00000 并对其解引用 ⇒ 在 #PF 处理中再次 #PF,rsp 每次下移 ~0x2A0,无限重入耗尽内核栈 ⇒ 三重故障(QEMU 窗口消失、看不到真正的首故障)。现新增 _SafeStackWord(仅 0x1000..0x80000000 内才解引用),三处分支(#PF/#GP/其他)统一改用安全回溯,并照旧跳过用户态 rbp。
2026-10-14 21:48:58 +08:00
TermiNexus c977caec91 fix(vkernel/vpsdk): 窗口缓冲改走“用户缓冲 + WIN_FLUSH 拷贝”,修 app 写内核页 #PF(err=0x7)
- 现场:四个 app 一画就用户态 #PF,cr2 全在内核堆区(~0xb7c1000/0x9f81000/0xb851000/0xb881000),err=0x7(present + 用户写),RAX 正是各自颜色常量(0x1E1E32/0xC0C0C/0xF0F19)

- 根因:GET_WINBUF 把 desktop 的**内核**窗口后备缓冲地址直接交给 app(该页 U=0),app 当用户指针写 → 必然 #PF,四个 app 全中(tid=5 那条 cr2=0xffffffff52209000 是同一指针继续走坏)

- 内核:WIN_FLUSH 增加源指针 `WIN_FLUSH(win_id, src)`:先 desktop.get_fb 取窗口缓冲、再 desktop.get_fb_bytes 取字节数(配对取,避免 resize 重建导致尺寸/指针不一致),memcpy 后 desktop.flush;syscall_handler 的 WIN_FLUSH 分支透传 arg2;desktop 新增 get_fb_bytes

- app(vpsdk/window.vp):get_winbuf 改为按窗口分配**用户态**缓冲(stdlib.malloc → MMAP,落在 0x20000000 用户堆)并缓存;flush(win_id) 改为 WIN_FLUSH(win_id, 用户缓冲)
2026-10-14 19:30:29 +08:00
TermiNexus 4289d9b190 fix(vkernel/intr): GDT/STAR 根治 —— 用户代码段移到第5项(0x28)、TSS 移 0x30,统一用户 CS=0x2B
- 背景:syscall 返回走 sysretq,硬件按 STAR[63:48]=0x1B 算 CS=+16=0x2B、SS=+8=0x23;而 GDT 原来把用户代码放在第 3 项(0x18)、第 5 项(0x28)放 TSS ⇒ app 实际是“拿 TSS 描述符当代码段”在跑(QEMU 不校验 SYSRET 目标描述符类型才没暴露;严格实现会在 sysretq 处 #GP)

- gdt.vp:GDT 扩到 8 项、limit 0x37→0x3F;新增 gdt[5]=用户代码段(0xFA/0x20,与 gdt[3] 同属性;第 3 项保留为兼容镜像);TSS 改放第 6/7 项(setTSS64(6,…) = 选择子 0x30),ltr 改用 0x30;USER_CODE_SEG→0x28、TSS_SEG→0x30

- process.vp:drop_to_user_mode 首次进用户态的 iretq 也改用 CS=0x2B(与 sysretq 一致,两条入口不再产生不同 CS)

- isr.s:用户返回校验同时接受 0x2B/0x1B(0x1B 仅作兼容镜像兜底);上一个 commit 已修“只认 0x1B 导致用户态一被中断就 cli;hlt 假死”
2026-10-14 19:07:54 +08:00
TermiNexus 79e9dfd5a4 fix(vkernel/intr): ISR 用户返回同时接受 CS=0x2B(sysretq 选择子),修“用户态一被中断就 cli;hlt 假死”
- 现象:日志尾部只有 "B 2B 23",随后整机假死(QEMU 窗口消失、没有 dump)

- 根因:isr\num\()_ret_user 硬编码校验 CS==0x1B;但 app 只要发过一次 syscall,返回走 sysretq 后 CS 就是 STAR[63:48]=0x1B 算出的 0x2B(SS=0x23)→ 之后任何用户态中断/异常(例如定时器抢占)都被判“坏 CS/SS” → 走 cli;hlt 死循环,既假死又掩盖真实异常现场

- 现在同时接受 0x1B 与 0x2B(SS 仍须 0x23);注释里记录 GDT/STAR 隐患:0x2B 当前指向 GDT 第 5 项,而那里是 TSS(QEMU 不校验 SYSRET 目标描述符类型才没暴露),根治需把用户代码段挪到第 5 项
2026-10-14 19:04:07 +08:00
TermiNexus c232c0c0d5 fix(vkernel/intr): syscall 来源标记改放内核栈(可嵌套),修 app 调完 .so 后跑飞
- 现象:app 在用户态成功调 .so(“[so] call returned”)后立刻跑飞:用户态 #BP,rip=0x7c0b9(既不在 app 映像 0x400000-0x41c824、也不在 app 栈 0xbb11000)、rsp=0

- 根因:来源标记原来放在全局 _syscall_from_kernel,但 syscall 会**嵌套**:app 的 SO_CALL1 → 内核侧执行 .so → .so 内部又发 3 次 syscall(SERIAL_PUTS,内核态发起)→ 内层把标记改成 1 → 外层(用户态)返回时误走内核返回分支:jmp rcx 回到用户 rip 却不换回用户栈 → app 用内核栈继续跑 → 栈错位跑飞

- 改为把来源标记 push 在内核栈上(入口 push 0/1,出口 cmp [rsp] 判定),每层各压各的、天然可嵌套;内核返回路径 add rsp,8 弹掉本层标记槽

- 对齐保持:and rsp,~15 之后的深度 = 补齐8 + arg6 8 + call 8 = 24 ≡ 8 (mod 16) ✓(标记槽在 and 之前,不参与)
2026-10-14 18:56:40 +08:00
TermiNexus 5916822184 fix(vkernel/intr): syscall 入口按 rsp bit46 判来源并记标记,支持内核态发起(修三重故障)
- 现场:内核自动启动里直接调 .so 的 log_info(其内部走 `syscall`)→ 入口用 rsp bit47 判来源时,本系统内核栈是**低位**(boot 0x7d8d0 / 线程 ks≈0xbb6b000)被误判成用户态 → 多执行一次 swapgs → gs:[8] 读到垃圾 → rsp 变垃圾 → 三重故障(QEMU 窗口直接消失、无任何 dump)

- 入口改用 bit46(64TB)阈值:用户栈由 drop_to_user_mode 固定在 0x7fff_ffff_...(≈2^47)、内核栈 <1GB;并把来源记进全局 _syscall_from_kernel(不用 gs:[…]:引导早期可能还没 swapgs 过)

- 出口改为读该标记决定 sysretq(用户)或 jmp rcx(内核)。原 rcx>>47 判据两个错:覆盖 rax 返回值;且用户 rip(0x40xxxx) 与内核 rip(0x10xxxx) 都 <2^47,无法区分
2026-10-14 18:47:32 +08:00
TermiNexus 09b72614d9 fix(vkernel/intr): isr80(int 0x80) 修调用号/arg4 被覆盖 + 收尾原地 iretq;syscall stub 补 SysV 栈对齐
- isr80:prologue 原拿 rax/r10 当临时(取 CS 与 RFLAGS),之后 push rax/push r10 存进保存块的其实是 CS/RFLAGS → handler 收到乱掉的调用号与 arg4。现在入口先 push rax + push r10 保住二者,CS 判定改用内存操作数 `test byte ptr [rsp+24],3`,翻译段按新偏移取 arg4([rsp+168])

- isr80 收尾:原实现把帧字段 pop 进 r8/r9/r10/rax 再重新 push iret 帧,其中 `pop rax(RSP)` 会冲掉 handler 返回值,且 is_user 判断早被 `pop rcx`(arg2) 覆盖(等于没用)。改为跳过统一帧/保存槽回到 CPU 压下的原始帧直接 iretq:环切换与 RSP/SS 由 iretq 依 CS.RPL 自行处理(内核弹 3、用户弹 5),rax=返回值,其余寄存器全部原样恢复

- syscall stub:`and rsp,~15` 后直接 push arg6 使 syscall_handler 入口 rsp≡0;Viper 代码生成依赖标准 SysV(入口 rsp≡8,内核 printf 的 movaps %xmm0,0x60(%rsp) 即证据)→ 补 `sub rsp,8` 于 arg6 之前
2026-10-14 18:38:17 +08:00
TermiNexus 4f9660d23b fix(vkernel/intr): 修正 syscall_entry 两处 rax 覆盖(系统调用号/返回值),用户 syscall 不再恒返回 0
- 入口:原 `mov rax,rsp; shr rax,47`(判入口来源)把 rax 中的系统调用号覆盖成 rsp>>47(用户态=0);随后 `mov rdi,rax` 使 syscall_handler 收到 num=0 → 落入未知调用 `return 0`。改为 `bt rsp,47; jc`(只读 CF,与旧判据等价,不占用 rax)

- 返回:原 `mov rax,rcx; shr rax,47`(判返回去向)把 handler 的返回值覆盖成返回地址>>47(用户地址 < 2^47 → 0)→ app 的 syscall trampoline 永远取到 0。改为 `bt rcx,47; jc`

- 现场:elf 自启动后 app 的 malloc→syscall 29(MMAP) 拿到 0 → 紧接着 `movq %r14,(%rax)` 写 NULL → 用户态 [0E][PF] cr2=0x0 err=0x7(hello rip=0x400153、terminal rip=0x40007c)
2026-10-14 18:31:24 +08:00
TermiNexus 66eb70b131 feat(vkernel): #GP 分支补 rbp 链回溯(与 #PF 分支同款)
#GP 此前只打印 rip/err/rsp/cr4/cr0,看不到调用链;#PF 与“其他异常”分支早已有
[STK] rbp 回溯。现给 #GP 补上同样的 5 级回溯(rbp + 4 个返回地址),
内核态 rbp 低地址直接回溯,用户态(>=0x80000000)跳过以免读用户栈嵌套 #PF。

用于定位当前 “paging.init 的 MakePTWritable 页表遍历读到坏表项 → #GP
rip=0x11a7be” 的调用来源。
2026-10-14 13:46:57 +08:00
TermiNexus 0c6396912b fix(vkernel): ISR 保存/恢复 rax/rcx/r8-r11,修中断破坏被中断代码寄存器
根因:isr.s 的 ISR_NOERR/ISR_ERR 宏把 rax/rcx/r8-r11 当临时寄存器用
(读 RIP/CS/RFLAGS、算 is_user),却从未保存/恢复原值,rcx 更是根本不在
保存列表里。于是任意一次中断(定时器 1000Hz)只要落在“这些寄存器里存有
活跃值”的瞬间,就会破坏被中断代码的活跃值:
  - r8 被写成被中断处的 RIP;rcx 被清 0(xor ecx,ecx / setnz cl 造 is_user)

实测吻合:DumpMapping 把 tag 放 rcx、va 放 r8,中断落点恰在其中 → 打印
“ [PG] (None) va=0x11ab82 ”,其中 0x11ab82 正是 DumpMapping 内部的 RIP;
同一机制把 main.vp 的 width 槽写成 0 → “ [desktop] back_fb malloc FAILED ”。
最初日志里的 [0D][GP] rip=0x130d45(vsnprintf 读 %d 变参)亦由此而来。
旧 TPC 编译的寄存器分配没踩到该窗口故“正常”,新版编译器踩到 → 概率性破坏
(对应 VKERNELERR.md 记录的“新版概率性崩溃”)。

- isr.s: 新增 ISR_SAVE_CLOBBERED / ISR_RESTORE_CLOBBERED 与 _isr_scratch;
  入口第一件事保存 rax/rcx/r8-r11,各返回路径在 push 完 iretq 帧之后恢复
  (iretq 从栈取帧,恢复寄存器不影响返回)
- mm.vp: arena 起点抬高后做 4KB 对齐(KernelEnd 未必页对齐),避免伙伴系统块错位
2026-10-14 13:06:26 +08:00
TermiNexus 0919a058a8 fix(vkernel): 预留内核 .bss 与栈区,修堆 arena 覆盖未预留内核内存
根因:bootloader 只按 kernel 文件大小 AllocatePages(.bss 不占文件),于是 .bss
尾部(ticks/_perf_* 等全局量)与主线程栈区(0x60000~0x80000)在 memmap 里仍是
EfiConventionalMemory,被内核 mm 的堆 arena 当作空闲内存占用 → 内核全局量被写坏、
堆元数据被反向破坏,最终桌面线程栈上的 va_list 被改坏,vsnprintf 读 %d 变参时
#GP(rip=0x130d45, err=0x0)。

- linker.ld: .bss 末尾加 LONG(0) 强制 PROGBITS,使 kernel.bin 的 FileSize 涵盖
  .bss,bootloader 的 PagesNeeded 得以预留整块内核镜像(含 .bss)
- mm.init: 新增 KernelAddr/KernelEnd,挑 arena 时把与内核镜像 [kernel_PhysAddr,
  __bss_end) 重叠的 region 起点抬到镜像末端(不整段丢弃)
- main.vp: 取 __bss_end 并传入 mm.init
- boot.c: ExitBootServices 前用 AllocatePages(AllocateAddress, EfiLoaderData)
  占位内核栈区 0x60000~0x80000,使其在 memmap 中标记已用

另含在途整理:多处补显式收窄转换(uint8()/uint16() 等)、kbd_svc 构造改为
kbd_svc.__init__() 等。
2026-10-14 12:54:43 +08:00
TermiNexus 52728bc8da refactor(gdt): setTSS64 改用 gdt_entry64 整体写入 TSS 描述符
以 gdt_entry64*(&gdt[num]) 复用同一地址写入 16 字节,替代原先手写拆分
gdt[num]/gdt[num+1] 的方式;语义与字节布局均不变。
2026-10-13 15:45:59 +08:00
TermiNexus 1c8d2e2c1d fix(gdt): TSS64 描述符 granularity 编码 limit 高 4 位
原写法硬编码 0x00,丢弃 limit[19:16],当前 limit=103 侥幸正确,
改为 (limit >> 16) & 0x0F 以消除隐患。
2026-10-13 15:44:41 +08:00
TermiNexus dcb7c5c2c0 chore(ViperOS): 落库内核侧进行中的改动(26 文件,+1573/-1805)
内容由用户产出,本次仅代为提交、未做任何语义修改;含 static 修饰符前置迁移等(新增 static 前缀 16 行)。提交消息为中性描述——如需更精确的说明请 amend。
2026-09-28 15:24:52 +08:00
TermiNexus 2a54e2bafb 清理了 t.CVoid 2026-09-16 16:07:04 +08:00
TermiNexus 6a54373c26 fix(vkernel): 补 ISR/调度 call 的 rsi/rdi clobber、rep stosb 内存 clobber,修 mm 别名笔误
N8  paging.vp:AllocPage 的 rep stosb 写 4096B 补 CLOBBER_MEMORY,避免优化器
    重排/消除其前后的内存读写
N9  coroutine.vp / sched.vp:_coro_entry/_thread_entry 的 call rax 补
    CLOBBER_RSI/CLOBBER_RDI(RSP 由 sub rsp,8 / add rsp,8 配对抵消,无需声明)
N15 process.vp:mm_mm.malloc/free → mm.malloc/free(该文件只有 import mm.mm as mm)
N16 elf.vp:读 RAX 的 c.Asm 补 CLOBBER_RAX([DIAG] 探针)
N20 elf.vp / idt.vp(12 处):外部 ELF 入口 / ISR handler 的 call 补
    CLOBBER_RSI/CLOBBER_RDI(被调方破坏全部 caller-saved 寄存器)
2026-09-11 17:40:21 +08:00
TermiNexus a518f75b24 chore(vkernel): 重置基线 — 纳入 TPV codegen 修复配合的内核侧调整与既有工作区内容
- 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 工作区内容
2026-09-11 14:27:01 +08:00
TermiNexus cd1cc915c5 修正了build.py,重置基线 2026-09-11 10:34:34 +08:00
TermiNexus 2a03330a9d fix(kernel): 分裂 2MB 大页使 .rodata 真正只读,加页表与格式串校验诊断并禁用 App 自动启动 2026-08-30 07:27:45 +08:00
TermiNexus efa99a8c4b fix(kernel): [STK] 用户态 rbp 跳过链回溯(防嵌套 #PF 死循环)
用户进程异常时 rbp=0x7fffff...(用户栈),链回溯读 [rbp+8] 触发嵌套 #PF 无限循环(rsp 递减耗尽)。fr0>=0x80000000 时跳过链回溯。
2026-08-29 18:14:48 +08:00
TermiNexus 3a3d026f95 fix(debug): _fault_rax 改为 ISR 入口最早保存(避免被读 CS 覆盖)
ISR_ERR/NOERR 入口 mov rax,[rsp+16] 读 CS 已覆盖崩溃 rax,且内核路径 xor rax 后再 push——_fault_rax 恒 0。改为入口第一条 mov [_fault_rax],rax,删除 push 后冗余覆盖。
2026-08-29 18:06:03 +08:00
TermiNexus 25af08ecf4 debug(kernel): 保存并打印崩溃时 RAX(验证 cr2=rax-偏移 关系)
[STK] 显示崩溃帧 rbp=0x7d640 正常,但 cr2=0x0 需 rax=0x6f0——矛盾待解。isr.s 新增 _fault_rax,异常打印 [RAX] 行确认崩溃时 rax 真实值。
2026-08-29 18:00:13 +08:00
TermiNexus 084061f97e debug(kernel): [STK] rbp 链回溯扩展到所有异常(含 #UD int 6)
本次崩溃为 int=6 rip=0x7bada(执行流跳栈地址),走 [EX] 分支未打印链。统一 [PF]/[EX] 均输出 [STK] rbp 链。
2026-08-29 17:53:40 +08:00
TermiNexus a85f826adf debug(kernel): #PF 打印 rbp 链回溯(崩溃帧 + 4 级返回地址)
devfs_directory.find 崩溃(cr2=0x0 写 NULL,rax=rsp=0x6f0 异常)说明调用链栈已被破坏。isr.s 新增 _fault_rbp 保存异常现场 rbp,idt.vp [PF] 分支打印 [STK] 链定位栈损坏源。
2026-08-29 17:50:32 +08:00
TermiNexus b887d3d9c2 更新,部分功能可用 2026-08-27 09:47:47 +08:00
TermiNexus 4296d2a19e fix(asm-inline): cmos_read 裸 in/out al 补 CLOBBER_RAX + 显式 mov;rdmsr out 绑定与 clobber 冲突(lo/hi 读不到 EAX/EDX)改显式 mov——同类 EAX 破坏根因,残留间歇性数据损坏源 2026-08-27 01:33:19 +08:00
TermiNexus cdee5e0e87 debug(idt): 非 #GP/#PF 异常打印 rip/rsp,定位 tid=1 #UD 执行位置 2026-08-26 22:22:56 +08:00
TermiNexus ff802c2eed debug(intr): #GP 打印 CR0/CR4
movaps 地址 0x7d780 16 对齐仍 #GP——需确认 SSE 是否真启用(CR4.OSFXSR bit9)
与 CR0.EM(bit2)。若 OSFXSR=0 则 enable_sse 未生效(可能是 stub 解析问题)。
2026-08-26 19:40:13 +08:00
TermiNexus 4ff48efde9 debug(intr): #GP/#PF 打印 faulting RSP
memmap 修复后栈写 #PF 消失,但 sched started 后出现 [GP] rip=0x134e1b
err=0x0——0x134e1b 是 serial.printf(变参)序言的 movaps(SSE 对齐存储),
说明调用方 RSP 未对齐。isr.s 新增 _fault_rsp 捕获(ISR_ERR 宏保存 faulting
RSP),idt.vp #GP/#PF 打印 rsp=0x%lx,定位 RSP 偏差来源。
2026-08-26 19:34:01 +08:00
TermiNexus f675540c64 chore(boot): 移除全部中断排查诊断(int20 测试 + T/[T]/D 打印)
定时中断已确认恢复(APIC LINT0=EXTINT)。移除 int 0x20 软件触发测试与
isr32_handler 'T'、timer_handler '[T]'、isr_default 'D' 打印,回归干净
内核,定位 sti 后新出现的 #GP(疑似 int 0x20 测试触发)。
2026-08-26 18:19:41 +08:00
TermiNexus 10857e58ed debug(intr): isr32_handler 入口打印 T + isr_default 打印 D,定位定时中断丢失
前一轮 timer_handler [T] 未打印 → timer_handler 未被调用。本轮加两处
确定性诊断:isr32_handler 入口 'T'(IRQ0 是否到达 isr32)、isr_default
'D'(是否有中断向量到默认处理)。三种结果:
- 无输出 → 定时中断根本没触发(PIC/PIT/APIC 路由问题)
- D 输出 → 中断触发但向量到默认处理(IDT gate 未设置)
- T 输出 → IRQ0 到达 isr32(问题在 handler 内)
用后移除。
2026-08-26 18:11:47 +08:00