import drivers.serial.uart.serial as serial import string import asm 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: static 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: static 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: # 不再自建节点:`devfs_file()` 在函数内构造的是**栈实例**,登记进全局 files[] 会悬垂, # 编译器 LifetimeError 已按"栈地址逃逸"拦截。设备节点只能由调用方以长寿命实例 # (模块级 / 堆)提供;缺对象一律失败,不静默造一个假的占位对象。 return -1 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 # 以 read_scancode() 的返回值为准(0 = 队列空):每轮要么取到一个字节并前进, # 要么立即退出,不再依赖第二个计数量做循环条件(那次混合计数会自旋)。 while count < size: sc: uint8 = keyboard.read_scancode() if sc == uint8(0): break ch: char = keyboard.scancode_to_ascii(sc) 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():初始化(含 8042 CCB 读改写与 IRQ1 安装)由 # services/keyboard.vp 的 start() 统一负责。两处都调会二次读改写 CCB, # 第二次可能把键盘时钟/数据上报关掉 —— 表现为"设备能开、只收到一个残留字节"。 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: # 布局:[0] = 按键,[1..4] = dx(int32),[5..8] = dy(int32)。 # 【重要】dx/dy 是 PS/2 包里的**相对位移**,不是绝对坐标 —— 光标位置由 explorer # 持有并做边界钳制,内核只负责设备语义。旧写法填的是 mouse.get_x()/get_y(), # 而那两个静态量从未被更新(irq_handler 只把包入队),恒为 0 ⇒ 用户一动鼠标, # explorer 就把光标设到 (0,0),现场表现为"立马跑到左上角"。 buf: uint32* = uint32*(uint64(buffer) + 1) dx: int = int(pkt.dx) dy: int = int(pkt.dy) # uint8 原始字节 → 有符号增量(-128..127);不依赖 int8 转换的语义,直接判借位。 if dx >= 128: dx = dx - 256 if dy >= 128: dy = dy - 256 # PS/2 协议的 Y 正方向是**向上**,而屏幕坐标 Y 向下为正 —— 在这里统一取反成屏幕 # 方向,消费方直接累加即可(与历史 desktop.vp 里 pkt_dy 的用法一致)。 dy = 0 - dy int32*(buf)[0] = int32(dx) int32*(buf)[1] = int32(dy) 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 # 等一次垂直回扫:用户态(explorer)没有 IO 端口权限,不能自己 inb 0x3DA,由内核代做。 define FB_WAIT_VBLANK = 0x4601 @__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: if request == FB_WAIT_VBLANK: # 等进入垂直回扫期(0x3DA bit3)。返回等待消耗的迭代数:到上限即超时 #(当前没有 VGA 回扫信号),调用方可据此判断同步是否真的生效。 t: int = 100000 while (asm.inb(0x3DA) & 0x08) != 0 and t > 0: t -= 1 t2: int = 100000 while (asm.inb(0x3DA) & 0x08) == 0 and t2 > 0: t2 -= 1 return 100000 - t2 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 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)