Files
ViperOS/VKernel/Kernel/drivers/devfs/devfs.vp
T
TermiNexus 441ea0c883 fix(devfs): files[] 改存指针,避免设备对象按值切片丢掉派生字段
现场:main.vp 探针读模块级 fb0_file 得 w=1360 h=768 pitch=5440(注册正确),而 explorer 侧 [fb0] ioctl 的 self 读得 w=0 h=0 pitch=0 fmt=0。IR 证据(devfs 编译单元 .text.ll 的 create_file):files[file_count] = *file_ptr 被生成成 llvm.load/llvm.store 整个 devfs_file 结构体(88 字节基类布局),派生字段 fb_phys/fb_size/fb_w/fb_h/fb_pitch/fb_format 从未被写入,保持 __init__ 的 memset 0;vptr 一并拷过去所以虚调用仍能进 framebuffer_device.ioctl,但读到的派生字段全是 0。这是对象切片而非编译器缺陷。

改法:files 由 [MAX_DEVICE_FILES]devfs_file 改为 devfs_file* 指针数组,create_file 只登记指针(设备对象由 devfs_register_* 以模块级实例持有,生命周期贯穿内核运行期);find_in_dir/list_in_dir 相应去掉取址;__init__ 的 memset 长度改为指针数组真实大小 8*MAX_DEVICE_FILES(原来按 devfs_file.__sizeof__() 清会越界清掉后面全局量)。顺带修好同样依赖派生字段的 /dev/desk(shm_device.shm_phys/shm_size)。
2026-10-16 20:32:12 +08:00

600 lines
22 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.
import drivers.serial.uart.serial as serial
import string
import viperlib
define MAX_FILE_DESCRIPTORS = 64
define MAX_DEVICE_FILES = 32
define MAX_DIRECTORIES = 16
define PATH_BUFFER_SIZE = 256
directories: [MAX_DIRECTORIES]devfs_directory = [0]
directory_count: int = 0
# 存**指针**而不是基类值:按值数组会把设备对象**切片**成 devfs_file(只拷 88 字节基类部分),
# 派生字段(framebuffer_device.fb_w/fb_h/fb_phys…、shm_device.shm_phys…)全部丢失 ——
# 现场表现为 [main] fb0 reg w=1360(读的是模块级对象)而 [fb0] ioctl w=0(读的是数组里的切片)。
# IR 证据:create_file 里生成的是 llvm.load/llvm.store 整个 devfs_file 结构体,不含派生字段。
files: [MAX_DEVICE_FILES]devfs_file* = [0]
file_count: int = 0
class _fd_table:
file: devfs_file*
flags: int
fd_table: [MAX_FILE_DESCRIPTORS]_fd_table = [0]
class devfs_directory:
name: [64]char
parent: devfs_directory*
@staticmethod
def find(path: const str, root_directory: devfs_directory*) -> devfs_directory*:
if not path or string.strchr(path, '/') != path: return None
if not root_directory: return None
current_dir: devfs_directory* = root_directory
result: [PATH_BUFFER_SIZE]str
token = string.split(path, "/", result, PATH_BUFFER_SIZE)
# `for i in result` 按 NULL 终止语义遍历(i8* 元素,split 尾哨兵 NULL 停):
# 编译器 _handle_array_iter 已实现"长度上限 + 解引用为空提前终止"。
for i in result:
found: bool = False
for d in range(directory_count):
dirp: devfs_directory* = &directories[d]
if dirp.parent == current_dir and string.samestr(dirp.name, i):
current_dir = dirp
found = True
break
if not found: return None
return current_dir
@staticmethod
def find_child(name: const str, parent: devfs_directory*) -> devfs_directory*:
for d in range(directory_count):
dirp: devfs_directory* = &directories[d]
if dirp.parent == parent and string.samestr(dirp.name, name):
return dirp
return None
@staticmethod
def list_subdirs(dir_ptr: devfs_directory*, buffer: str, size: uint32, pos: int) -> int:
for d in range(directory_count):
dirp: devfs_directory* = &directories[d]
if dirp.parent == dir_ptr:
name_len: int = int(string.strlen(dirp.name))
if pos + name_len + 1 > size:
break
string.strcpy(buffer + pos, dirp.name)
pos += name_len
buffer[pos] = '\n'
pos += 1
return pos
virtual class devfs_file:
name: [64]char
private_data: rawptr
parent_dir: devfs_directory*
@staticmethod
def find_in_dir(file_name: const str, dir_ptr: devfs_directory*) -> devfs_file*:
for f in range(file_count):
fp: devfs_file* = files[f]
if fp.parent_dir == dir_ptr and string.samestr(fp.name, file_name):
return fp
return None
@staticmethod
def list_in_dir(dir_ptr: devfs_directory*, buffer: str, size: uint32, pos: int) -> int:
for f in range(file_count):
filep: devfs_file* = files[f]
if filep.parent_dir == dir_ptr:
name_len: int = int(string.strlen(filep.name))
if pos + name_len + 1 > size:
break
string.strcpy(buffer + pos, filep.name)
pos += name_len
buffer[pos] = '\n'
pos += 1
return pos
def open(self, path: const str, flags: int) -> int: return 0
def close(self, fd: int) -> int: return 0
def read(self, fd: int, buffer: rawptr, size: uint32) -> int: return 0
def write(self, fd: int, buffer: const rawptr, size: uint32) -> int: return 0
def ioctl(self, fd: int, request: int, arg: rawptr) -> int: return 0
# mmap 支持:默认不可映射(返回 -1)。可映射设备(/dev/fb0)覆写它,
# 把自己的物理区间交给内核 mmap 去映射进用户地址空间。
def mmap_info(self, fd: int, out_phys: uint64*, out_size: uint64*) -> int: return -1
class _DevFSObject:
root_directory: devfs_directory*
def __init__(self):
global directory_count, file_count
self.root_directory = None
directory_count = 0
file_count = 0
string.memset(&directories, 0, devfs_directory.__sizeof__() * MAX_DIRECTORIES)
# files 是指针数组(8 字节/项),不能再用 devfs_file.__sizeof__() —— 那会越界清掉后面的全局量。
string.memset(&files, 0, 8 * MAX_DEVICE_FILES)
string.memset(&fd_table, 0, _fd_table.__sizeof__() * MAX_FILE_DESCRIPTORS)
def create_single_dir(self, parent: devfs_directory*, dir_name: const str) -> devfs_directory*:
global directory_count
if not parent or not dir_name: return None
if directory_count >= MAX_DIRECTORIES: return None
if string.strlen(dir_name) >= 64: return None
new_dir: devfs_directory* = &directories[directory_count]
directory_count += 1
string.strcpy(new_dir.name, dir_name)
new_dir.parent = parent
return new_dir
def find_or_create_directory(self, path: const str) -> devfs_directory*:
global directory_count
if not path or string.samestr(path, "/"): return None
if not self.root_directory:
self.root_directory = &directories[directory_count]
directory_count += 1
string.strcpy(self.root_directory.name, "")
self.root_directory.parent = None
current: devfs_directory* = self.root_directory
buf: [PATH_BUFFER_SIZE]char
string.memset(&buf, 0, PATH_BUFFER_SIZE)
buf_idx: int = 0
p: int = 0
if path[p] == '/': p += 1
while path[p] != '\0':
if path[p] == '/':
if buf_idx > 0:
found_child: devfs_directory* = devfs_directory.find_child(buf, current)
if not found_child:
found_child = self.create_single_dir(current, buf)
if not found_child: return None
current = found_child
string.memset(&buf, 0, PATH_BUFFER_SIZE)
buf_idx = 0
p += 1
else:
buf[buf_idx] = path[p]
buf_idx += 1
p += 1
if buf_idx > 0:
found_child: devfs_directory* = devfs_directory.find_child(buf, current)
if not found_child:
found_child = self.create_single_dir(current, buf)
if not found_child: return None
current = found_child
return current
def init(self) -> int:
self.create_directory("/dev")
self.create_directory("/dev/input")
return 0
def create_directory(self, path: const str) -> int:
dir_ptr: devfs_directory* = self.find_or_create_directory(path)
return 0 if dir_ptr else -1
def create_file(self, path: const str, file_ptr: devfs_file*, private_data: rawptr) -> int:
global file_count
path_copy: [PATH_BUFFER_SIZE]char
string.memset(&path_copy, 0, PATH_BUFFER_SIZE)
string.strcpy(&path_copy, path)
last_slash: str = string.strrchr(path_copy, '/')
if not last_slash: return -1
last_slash[0] = '\0'
dir_path = path_copy
file_name: str = last_slash + 1
dir_ptr: devfs_directory* = self.find_or_create_directory(dir_path)
if not dir_ptr: return -1
if devfs_file.find_in_dir(file_name, dir_ptr): return -1
if file_count >= MAX_DEVICE_FILES: return -1
if file_ptr:
string.strcpy(file_ptr.name, file_name)
file_ptr.private_data = private_data
file_ptr.parent_dir = dir_ptr
# 只登记指针。设备对象由调用方(devfs_register_*)以模块级实例持有,
# 生命周期贯穿内核运行期,因此指针引用安全且能看见派生字段。
files[file_count] = file_ptr
else:
# 调用方没给对象时自建基类实例(无派生字段,方法即基类默认实现)。
new_file: devfs_file* = devfs_file()
string.strcpy(new_file.name, file_name)
new_file.private_data = private_data
new_file.parent_dir = dir_ptr
files[file_count] = new_file
file_count += 1
return 0
def open(self, path: const str, flags: int) -> int:
path_copy: [PATH_BUFFER_SIZE]char
string.memset(&path_copy, 0, PATH_BUFFER_SIZE)
string.strcpy(&path_copy, path)
last_slash: str = string.strrchr(path_copy, '/')
if not last_slash: return -1
last_slash[0] = '\0'
dir_path = path_copy
file_name: str = last_slash + 1
dir_ptr: devfs_directory* = devfs_directory.find(dir_path, self.root_directory)
if not dir_ptr: return -1
file_ptr: devfs_file* = devfs_file.find_in_dir(file_name, dir_ptr)
if not file_ptr: return -1
fd: int = -1
for i in range(MAX_FILE_DESCRIPTORS):
if not fd_table[i].file:
fd = i
break
if fd == -1: return -1
ret: int = file_ptr.open(path, flags)
if ret != 0: return ret
fd_table[fd].file = file_ptr
fd_table[fd].flags = flags
return fd
def close(self, fd: int) -> int:
if fd < 0 or fd >= MAX_FILE_DESCRIPTORS or not fd_table[fd].file:
return -1
file_ptr: devfs_file* = fd_table[fd].file
file_ptr.close(fd)
fd_table[fd].file = None
fd_table[fd].flags = 0
return 0
def read(self, fd: int, buffer: rawptr, size: uint32) -> int:
if fd < 0 or fd >= MAX_FILE_DESCRIPTORS or not fd_table[fd].file:
return -1
file_ptr: devfs_file* = fd_table[fd].file
return file_ptr.read(fd, buffer, size)
def write(self, fd: int, buffer: const rawptr, size: uint32) -> int:
if fd < 0 or fd >= MAX_FILE_DESCRIPTORS or not fd_table[fd].file:
return -1
file_ptr: devfs_file* = fd_table[fd].file
return file_ptr.write(fd, buffer, size)
def ioctl(self, fd: int, cmd: uint32, arg: rawptr) -> int:
if fd < 0 or fd >= MAX_FILE_DESCRIPTORS or not fd_table[fd].file:
return -1
file_ptr: devfs_file* = fd_table[fd].file
return file_ptr.ioctl(fd, cmd, arg)
def get_mmap_info(self, fd: int, out_phys: uint64*, out_size: uint64*) -> int:
if fd < 0 or fd >= MAX_FILE_DESCRIPTORS or not fd_table[fd].file:
return -1
file_ptr: devfs_file* = fd_table[fd].file
return file_ptr.mmap_info(fd, out_phys, out_size)
def list_directory(self, path: const str,
buffer: str,
size: uint32) -> int:
dir_ptr: devfs_directory* = devfs_directory.find(path, self.root_directory)
if not dir_ptr or not buffer or size == 0: return -1
pos: int = devfs_directory.list_subdirs(dir_ptr, buffer, size, 0)
pos = devfs_file.list_in_dir(dir_ptr, buffer, size, pos)
return pos
DevFSObject: _DevFSObject* = _DevFSObject()
def devfs_init() -> int:
global DevFSObject
DevFSObject.__init__()
DevFSObject.init()
return 0
def devfs_create_directory(path: const str) -> int:
return DevFSObject.create_directory(path)
def devfs_create_file(path: const str, file_ptr: devfs_file*, private_data: rawptr) -> int:
return DevFSObject.create_file(path, file_ptr, private_data)
def devfs_open(path: const str, flags: int) -> int:
return DevFSObject.open(path, flags)
def devfs_close(fd: int) -> int:
return DevFSObject.close(fd)
def devfs_read(fd: int, buffer: rawptr, size: uint32) -> int:
return DevFSObject.read(fd, buffer, size)
def devfs_write(fd: int, buffer: const rawptr, size: uint32) -> int:
return DevFSObject.write(fd, buffer, size)
def devfs_ioctl(fd: int, cmd: uint32, arg: rawptr) -> int:
return DevFSObject.ioctl(fd, cmd, arg)
def devfs_get_mmap_info(fd: int, out_phys: uint64*, out_size: uint64*) -> int:
return DevFSObject.get_mmap_info(fd, out_phys, out_size)
def devfs_list_directory(path: const str, buffer: str, size: uint32) -> int:
return DevFSObject.list_directory(path, buffer, size)
serial0_file: devfs_file* = devfs_file()
def serial0_open(self: rawptr, path: const str, flags: int) -> int:
serial.puts("[serial0] open: ")
serial.puts(path)
serial.puts("\n")
return 0
def serial0_close(self: rawptr, fd: int) -> int:
serial.puts("[serial0] close\n")
return 0
def serial0_read(self: rawptr, fd: int, buffer: rawptr, size: uint32) -> int:
serial.puts("[serial0] read\n")
return 0
def serial0_write(self: rawptr, fd: int, buffer: const rawptr, size: uint32) -> int:
serial.puts("[serial0] write\n")
serial.puts(buffer)
serial.puts("\n")
return size
def devfs_register_serial0() -> int:
global serial0_file
serial0_file.__init__()
serial0_file.open = serial0_open
serial0_file.close = serial0_close
serial0_file.read = serial0_read
serial0_file.write = serial0_write
return devfs_create_file("/dev/serial0", serial0_file, None)
virtual class serial1_device(devfs_file):
def open(self, path: const str, flags: int) -> int:
serial.puts("[serial1] open: ")
serial.puts(path)
serial.puts("\n")
return 0
def close(self, fd: int) -> int:
serial.puts("[serial1] close\n")
return 0
def read(self, fd: int, buffer: rawptr, size: uint32) -> int:
serial.puts("[serial1] read\n")
return 0
def write(self, fd: int, buffer: const rawptr, size: uint32) -> int:
serial.puts("[serial1] write\n")
serial.puts(str(buffer))
serial.puts("\n")
return size
serial1_file: serial1_device* = serial1_device()
def devfs_register_serial1() -> int:
global serial1_file
serial1_file.__init__()
return devfs_create_file("/dev/serial1", serial1_file, None)
import drivers.input.keyboard.keyboard as keyboard
import drivers.input.mouse.mouse as mouse
virtual class keyboard_device(devfs_file):
def open(self, path: const str, flags: int) -> int:
return 0
def close(self, fd: int) -> int:
return 0
def read(self, fd: int, buffer: rawptr, size: uint32) -> int:
count: uint32 = 0
while count < size and keyboard.has_data():
sc: uint8 = keyboard.read_scancode()
ch: char = keyboard.scancode_to_ascii(sc)
if ch != '\0':
char*(buffer)[count] = ch
count += 1
return count
def write(self, fd: int, buffer: const rawptr, size: uint32) -> int:
return -1
def ioctl(self, fd: int, request: int, arg: rawptr) -> int:
return 0
kbd_file: keyboard_device* = keyboard_device()
def devfs_register_keyboard() -> int:
global kbd_file
kbd_file.__init__()
keyboard.init()
return devfs_create_file("/dev/input/keyboard", kbd_file, None)
virtual class mouse_device(devfs_file):
def open(self, path: const str, flags: int) -> int:
return 0
def close(self, fd: int) -> int:
return 0
def read(self, fd: int, buffer: rawptr, size: uint32) -> int:
if not mouse.has_data(): return 0
pkt: mouse.mouse_packet
mouse.read_packet(&pkt)
if size >= 1:
uint8*(buffer)[0] = pkt.buttons
if size >= 9:
buf: uint32* = uint32*(uint64(buffer) + 1)
int32*(buf)[0] = mouse.get_x()
int32*(buf)[1] = mouse.get_y()
return 9
return 1
def write(self, fd: int, buffer: const rawptr, size: uint32) -> int:
return -1
def ioctl(self, fd: int, request: int, arg: rawptr) -> int:
return 0
mse_file: mouse_device* = mouse_device()
def devfs_register_mouse() -> int:
global mse_file
mse_file.__init__()
mouse.init()
return devfs_create_file("/dev/input/mouse", mse_file, None)
# ============================================================================
# /dev/fb0:framebuffer 设备(供用户态图形壳 explorer 接管桌面绘制)。
# · read/write 不支持(-1):像素由用户态经 mmap 取到线性地址后直接写;
# · ioctl(FB_GET_INFO):把几何/格式/物理区间回填进用户缓冲(FbInfo);
# · mmap_info:把本设备的物理区间交给内核 mmap 映射进用户地址空间。
# 本节点只记录 BootInfo 里的物理地址与几何,不自己持有 framebuffer 映射。
# ============================================================================
define FB_GET_INFO = 0x4600
@__attribute__(packed)
class FbInfo:
width: uint32
height: uint32
pitch: uint32
format: uint32
size: uint64
phys: uint64
virtual class framebuffer_device(devfs_file):
fb_phys: uint64
fb_size: uint64
fb_w: uint32
fb_h: uint32
fb_pitch: uint32
fb_format: uint32
def open(self, path: const str, flags: int) -> int:
return 0
def close(self, fd: int) -> int:
return 0
def read(self, fd: int, buffer: rawptr, size: uint32) -> int:
return -1
def write(self, fd: int, buffer: const rawptr, size: uint32) -> int:
return -1
def ioctl(self, fd: int, request: int, arg: rawptr) -> int:
# 【诊断·临时】确认 request/arg 是否真的到达内核、以及写入的目标 VA。
# 若这里打印的 request 不是 0x4600,说明 syscall 派发把请求号传错了(app 会读到未写入的残留)。
_io_b: [128]char
viperlib.snprintf(&_io_b, 128, "[fb0] ioctl fd=%d req=0x%lx arg=0x%lx w=%lu h=%lu\n",
fd, uint64(request), uint64(arg), uint64(self.fb_w), uint64(self.fb_h))
serial.puts(&_io_b)
if request != FB_GET_INFO or not arg:
return -1
info: FbInfo* = FbInfo*(uint64(arg))
info.width = self.fb_w
info.height = self.fb_h
info.pitch = self.fb_pitch
info.format = self.fb_format
info.size = self.fb_size
info.phys = self.fb_phys
# 【诊断·临时】回读刚写的 6 个字段:能读到新值说明写入确实落到了 arg 指向的内存。
_io_b2: [128]char
viperlib.snprintf(&_io_b2, 128, "[fb0] ioctl wrote w=%lu h=%lu pitch=%lu fmt=%lu\n",
uint64(info.width), uint64(info.height),
uint64(info.pitch), uint64(info.format))
serial.puts(&_io_b2)
return 0
def mmap_info(self, fd: int, out_phys: uint64*, out_size: uint64*) -> int:
if out_phys: *out_phys = self.fb_phys
if out_size: *out_size = self.fb_size
return 0
fb0_file: framebuffer_device* = framebuffer_device()
def devfs_register_fb0(fb: rawptr, width: int, height: int, pitch: int, format: int, size: uint64) -> int:
global fb0_file
fb0_file.__init__()
fb0_file.fb_phys = uint64(fb)
fb0_file.fb_size = size
fb0_file.fb_w = uint32(width)
fb0_file.fb_h = uint32(height)
fb0_file.fb_pitch = uint32(pitch)
fb0_file.fb_format = uint32(format)
return devfs_create_file("/dev/fb0", fb0_file, None)
# ============================================================================
# /dev/desk:图形壳 explorer 与各 app 之间的**共享内存通道**。
#
# 内核只做两件事:启动时分配一块物理内存、让每个 open 者 mmap 到**同一**物理区间。
# 区间内部的布局(窗口表 / 各窗口像素缓冲 / 请求与事件环形队列)完全由用户态协议
# 约定,见 vpsdk/desk.vp —— 内核不解析其内容,也不参与窗口语义。
#
# 为什么用 devfs 而不是新 syscall:syscall 表已对齐 Linux 且不留私有段;
# "共享一块内存给多方"正好能用 设备 + mmap 表达(Linux 用 shm/memfd + MAP_SHARED,
# 这里用 devfs 设备 + mmap 达到同样效果)。
# ============================================================================
# 共享区大小:vpsdk/desk.vp 里有一份同名同值的镜像(用户在 mmap 时必须先知道长度)。
define DESK_SHM_SIZE = 8388608
virtual class shm_device(devfs_file):
shm_phys: uint64
shm_size: uint64
def open(self, path: const str, flags: int) -> int:
return 0
def close(self, fd: int) -> int:
return 0
def read(self, fd: int, buffer: rawptr, size: uint32) -> int:
return -1
def write(self, fd: int, buffer: const rawptr, size: uint32) -> int:
return -1
def mmap_info(self, fd: int, out_phys: uint64*, out_size: uint64*) -> int:
if out_phys: *out_phys = self.shm_phys
if out_size: *out_size = self.shm_size
return 0
desk_file: shm_device* = shm_device()
def devfs_register_desk(shm: rawptr, size: uint64) -> int:
"""登记 /dev/desk。shm 是内核启动时 mm.malloc 出来的共享区;所有 open 者映射同一区间。"""
global desk_file
if not shm or size == 0:
serial.puts("[devfs] desk shm register skipped (no memory)\n")
return -1
desk_file.__init__()
desk_file.shm_phys = uint64(shm)
desk_file.shm_size = size
return devfs_create_file("/dev/desk", desk_file, None)