现场(serial.log 330-339):[fb0] ioctl fd=6 req=0x4600 arg=0x7fffffdffb78 w=0 h=0 ⇒ request 与 arg 传递都正常、写入回读也正常,真正的问题是设备对象里的 fb_w/fb_h/fb_pitch/fb_format/fb_phys/fb_size 全为 0;随后 explorer 以 pitch=0 调 mmap 失败退出。两个独立缺陷:① 内核侧——devfs_register_fb0 收到的是被清零的 width/height/pitch/fb_format/fb_size(main.vp 顶部注释记录的"__init__ 的 fb/width/height/pitch 局部量被偶发写坏"同类问题:这些长寿命局部量在 L121 之后、L304 之前被写坏,而 L366+ 的 _CheckFrameLocals 又不报,属时点相关),改为在调用点直接从 BootInfo 重取几何并补一条 _CheckFrameLocals(pre_devfs_fb0);② SDK 侧——fb_get_info 按值返回 32 字节 FbInfo,现场表现为只有前 16 字节有效、后 16 字节是调用方栈残留(size=1802724708 phys=0x130c30),改为出参 out + 返回 ioctl 返回码;③ explorer 侧改为判返回码与 pitch/size 非 0,失败即 _dbg 说明原因后 fail-fast 退出,不再带着 0 几何继续。
93 lines
3.6 KiB
Plaintext
93 lines
3.6 KiB
Plaintext
import vpsdk.fs as fs
|
||
import vpsdk.syscall as syscall
|
||
import string
|
||
from stdint import *
|
||
|
||
|
||
# ============================================================================
|
||
# devfs 设备层:设备节点路径 + 各设备的专用读写/控制助手。
|
||
#
|
||
# 设备节点由内核在启动时注册(见 VKernel/Kernel/main.vp 的 devfs_register_*),
|
||
# 用户态用普通 open/read/write/ioctl 访问 —— 不再有"设备专用 syscall"。
|
||
# ============================================================================
|
||
|
||
# 路径必须与内核注册名逐字一致
|
||
define DEV_SERIAL0 = "/dev/serial0"
|
||
define DEV_SERIAL1 = "/dev/serial1"
|
||
define DEV_FB0 = "/dev/fb0"
|
||
define DEV_KBD = "/dev/input/keyboard"
|
||
define DEV_MOUSE = "/dev/input/mouse"
|
||
define DEV_DESK = "/dev/desk"
|
||
|
||
# ioctl 命令与返回结构:必须与 VKernel/Kernel/drivers/devfs/devfs.vp 的
|
||
# FB_GET_INFO / FbInfo 逐字段一致(packed;4×u32 + 2×u64 = 32 字节)。
|
||
define FB_GET_INFO = 0x4600
|
||
|
||
@__attribute__(packed)
|
||
class FbInfo:
|
||
width: uint32
|
||
height: uint32
|
||
pitch: uint32
|
||
format: uint32
|
||
size: uint64
|
||
phys: uint64
|
||
|
||
|
||
def fb_open() -> int:
|
||
return fs.open(DEV_FB0, uint32(0))
|
||
|
||
def fb_get_info(fd: int, out: FbInfo*) -> int:
|
||
"""查 framebuffer 几何 / 格式 / 物理区间,写进调用者给出的 out。
|
||
|
||
返回 ioctl 的返回码(0 = 成功,非 0 = 失败,此时 out 内容不可信 —— 调用方必须判失败)。
|
||
|
||
为什么改为**出参**而不是按值返回 FbInfo:按值返回 32 字节结构体在现场表现为
|
||
"前 16 字节对、后 16 字节是调用方栈残留"(size/phys 打出 1802724708 / 0x130c30 这类值),
|
||
即结构体返回值只回传了一部分。出参写内存不依赖该 ABI,稳定可靠。
|
||
"""
|
||
if not out:
|
||
return -1
|
||
string.memset(out, 0, FbInfo.__sizeof__())
|
||
return fs.ioctl(fd, uint32(FB_GET_INFO), out)
|
||
|
||
def fb_map(fd: int, size: uint64) -> uint64:
|
||
"""把 framebuffer 映射进本进程,返回线性起始地址(0 = 失败),之后可直接写像素。
|
||
|
||
走通用 mmap(fd = 该设备 fd):内核按设备的 mmap_info 把物理区间按页映射进来。
|
||
"""
|
||
return syscall.mmap(0, size, uint64(syscall.PROT_READ | syscall.PROT_WRITE),
|
||
uint64(syscall.MAP_SHARED), uint64(fd), 0)
|
||
|
||
def kbd_open() -> int:
|
||
return fs.open(DEV_KBD, uint32(0))
|
||
|
||
def mouse_open() -> int:
|
||
return fs.open(DEV_MOUSE, uint32(0))
|
||
|
||
def shm_map(fd: int, size: uint64) -> uint64:
|
||
"""把共享内存设备映射进本进程,返回线性起始地址(0 = 失败)。
|
||
|
||
与 fb_map 同一机制(设备 mmap_info 给出的物理区间);不同点在于 /dev/desk
|
||
的区间对**所有**打开者都是同一块物理内存,因此天然可跨进程共享。
|
||
"""
|
||
return syscall.mmap(0, size, uint64(syscall.PROT_READ | syscall.PROT_WRITE),
|
||
uint64(syscall.MAP_SHARED), uint64(fd), 0)
|
||
|
||
# /dev/serial0 的 fd 惰性打开一次(-1 = 还没开;-2 = 开失败过,不再重试)
|
||
_serial_fd: int = -1
|
||
|
||
def serial_puts(msg: const str) -> int:
|
||
"""把字符串写到 /dev/serial0 —— 替代已删除的 SERIAL_PUTS syscall。
|
||
|
||
内核侧 serial0 设备的 write 按 C 字符串输出(内部就是 serial.puts),
|
||
所以这里只需把指针与长度递下去。
|
||
"""
|
||
global _serial_fd
|
||
if _serial_fd == -1:
|
||
_serial_fd = fs.open(DEV_SERIAL0, uint32(0))
|
||
if _serial_fd < 0:
|
||
_serial_fd = -2
|
||
if _serial_fd < 0: return -1
|
||
if msg is None: return -1
|
||
return fs.write(_serial_fd, msg, uint32(string.strlen(msg)))
|