Files
ViperOS/Scripts/syscall_table.py
T

186 lines
7.6 KiB
Python

#!/usr/bin/env python3
"""Canonical ViperOS syscall table.
The kernel and vpsdk keep separate copies (vpsdk must not depend on VKernel),
so both files are generated from this list. Never edit the generated block
by hand:
# BEGIN GENERATED SYSCALL TABLE
...
# END GENERATED SYSCALL TABLE
Usage:
python Scripts/syscall_table.py --write # regenerate both files
python Scripts/syscall_table.py --check # fail if out of sync
"""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
TARGETS = [
ROOT / "VKernel" / "Kernel" / "intr" / "syscall.vp",
ROOT / "vpsdk" / "syscall.vp",
]
BEGIN = "# BEGIN GENERATED SYSCALL TABLE"
END = "# END GENERATED SYSCALL TABLE"
# (kind, payload...) kind: "section" | "entry"
# Entry numbers follow Linux x86-64; Linux calls we do not implement yet are
# simply absent (the dispatcher returns -ENOSYS for unknown numbers).
TABLE = [
("section", "Linux 对齐段(号 = Linux x86-64 的号)"),
("entry", "READ", 0, "read(fd, buf, count)"),
("entry", "WRITE", 1, "write(fd, buf, count)"),
("entry", "OPEN", 2, "open(path, flags) - Linux O_*"),
("entry", "CLOSE", 3, "close(fd)"),
("entry", "STAT", 4, "stat(path, fileinfo*) - legacy struct"),
("entry", "FSTAT", 5, "fstat(fd, struct stat*)"),
("entry", "POLL", 7, "poll(fds, nfds, timeout) - stub 0"),
("entry", "SEEK", 8, "lseek(fd, offset, whence) -> new offset"),
("entry", "MMAP", 9, "mmap(addr, len, prot, flags, fd, off)"),
("entry", "MPROTECT", 10, "mprotect(addr, len, prot)"),
("entry", "MUNMAP", 11, "munmap(addr, len)"),
("entry", "BRK", 12, "brk(addr) -> new break"),
("entry", "RT_SIGACTION", 13, "rt_sigaction() - bookkeeping"),
("entry", "RT_SIGPROCMASK", 14, "rt_sigprocmask() - bookkeeping"),
("entry", "RT_SIGRETURN", 15, "rt_sigreturn()"),
("entry", "IOCTL", 16, "ioctl(fd, request, arg)"),
("entry", "PREAD64", 17, "pread64(fd, buf, count, offset)"),
("entry", "PWRITE64", 18, "pwrite64(fd, buf, count, offset)"),
("entry", "READV", 19, "readv(fd, iov, iovcnt)"),
("entry", "WRITEV", 20, "writev(fd, iov, iovcnt)"),
("entry", "ACCESS", 21, "access(path, mode)"),
("entry", "PIPE", 22, "pipe(int[2])"),
("entry", "YIELD", 24, "sched_yield()"),
("entry", "MREMAP", 25, "mremap(old, oldsz, newsz, flags, newaddr)"),
("entry", "MADVISE", 28, "madvise(addr, len, advice)"),
("entry", "DUP", 32, "dup(fd)"),
("entry", "DUP2", 33, "dup2(oldfd, newfd)"),
("entry", "NANOSLEEP", 35, "nanosleep(req, rem)"),
("entry", "GETPID", 39, "getpid()"),
("entry", "CLONE", 56, "clone(flags, stack, ptid, ctid, tls)"),
("entry", "FORK", 57, "fork() - copy address space"),
("entry", "SPAWN", 59, "execve(path, argv, envp)"),
("entry", "THREAD_EXIT", 60, "exit(): retire current thread"),
("entry", "WAIT4", 61, "wait4(pid, status, options, rusage)"),
("entry", "KILL", 62, "kill(pid, sig) - bookkeeping"),
("entry", "UNAME", 63, "uname(utsname*)"),
("entry", "FCNTL", 72, "fcntl(fd, cmd, arg)"),
("entry", "GETCWD", 79, "getcwd(buf, size)"),
("entry", "CHDIR", 80, "chdir(path)"),
("entry", "RENAME", 82, "rename(old, new)"),
("entry", "MKDIR", 83, "mkdir(path)"),
("entry", "RMDIR", 84, "rmdir(path)"),
("entry", "REMOVE", 87, "unlink(path)"),
("entry", "READLINK", 89, "readlink(path, buf, size) - no symlinks"),
("entry", "GETUID", 102, "getuid()"),
("entry", "GETGID", 104, "getgid()"),
("entry", "GETEUID", 107, "geteuid()"),
("entry", "GETEGID", 108, "getegid()"),
("entry", "GETTIMEOFDAY", 96, "gettimeofday(tv, tz)"),
("entry", "GETPPID", 110, "getppid()"),
("entry", "ARCH_PRCTL", 158, "arch_prctl(code, addr)"),
("entry", "REBOOT_LINUX", 169, "reboot(magic1, magic2, cmd, arg)"),
("entry", "GETTID", 186, "gettid()"),
("entry", "FUTEX", 202, "futex(uaddr, op, val, timeout)"),
("entry", "GETDENTS64", 217, "getdents64(fd, buf, count)"),
("entry", "SET_TID_ADDRESS", 218, "set_tid_address(tidptr)"),
("entry", "CLOCK_GETTIME", 228, "clock_gettime(clockid, timespec*)"),
("entry", "CLOCK_GETRES", 229, "clock_getres(clockid, timespec*)"),
("entry", "EXIT", 231, "exit_group(): terminate all threads"),
("entry", "OPENAT", 257, "openat(dirfd, path, flags, mode)"),
("entry", "NEWFSTATAT", 262, "newfstatat(dirfd, path, stat*, flags)"),
("entry", "UNLINKAT", 263, "unlinkat(dirfd, path, flags)"),
("entry", "READLINKAT", 267, "readlinkat(dirfd, path, buf, size)"),
("entry", "FACCESSAT", 269, "faccessat(dirfd, path, mode, flags)"),
("entry", "SET_ROBUST_LIST", 273, "set_robust_list(head, len)"),
("entry", "DUP3", 292, "dup3(oldfd, newfd, flags)"),
("entry", "PIPE2", 293, "pipe2(int[2], flags)"),
("entry", "PRLIMIT64", 302, "prlimit64(pid, resource, new, old)"),
("entry", "GETRANDOM", 318, "getrandom(buf, len, flags)"),
("entry", "STATX", 332, "statx(...) - -ENOSYS, glibc falls back"),
("entry", "RSEQ", 334, "rseq(rseq, len, flags) - -ENOSYS"),
("section", "临时私有段(0x1000+):Linux 无对应,逐条迁出"),
("entry", "LOAD_SO", 0x1006, "TODO(userspace): dynamic library loading"),
("entry", "SO_CALL1", 0x1007, "TODO(userspace): dynamic library call"),
("entry", "REBOOT", 0x1008, "TODO(linux): reboot(169)"),
("entry", "SHUTDOWN", 0x1009, "TODO(linux): reboot(RB_POWER_OFF)"),
("entry", "SPAWN_PROC", 0x100A, "TODO(userspace): Terminal run helper"),
("entry", "THREAD_CREATE_OLD", 0x100B, "TODO(userspace): ViperOS thread_create"),
]
def render() -> str:
out = ["enum Syscall:"]
for item in TABLE:
if item[0] == "section":
out.append(f" # ---- {item[1]} ----")
continue
_, name, num, comment = item
value = f"0x{num:04X}" if num >= 0x1000 else f"{num}"
out.append(f" {name:<18}= {value:<7}# {comment}")
return "\n".join(out) + "\n"
def split_lines(text: str):
"""Split keeping line endings (the tree mixes CRLF and a few LF lines)."""
return text.splitlines(keepends=True)
def replace_block(text: str) -> str:
lines = split_lines(text)
b = e = -1
for i, line in enumerate(lines):
stripped = line.strip()
if stripped == BEGIN:
b = i
elif stripped == END:
e = i
break
if b < 0 or e < 0 or e <= b:
raise SystemExit(f"markers not found: {BEGIN} / {END}")
newline = "\r\n" if lines[b].endswith("\r\n") else "\n"
block = [ln + newline for ln in render().splitlines()]
return "".join(lines[: b + 1] + block + lines[e:])
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--check", action="store_true")
ap.add_argument("--write", action="store_true")
args = ap.parse_args()
if not args.check and not args.write:
ap.error("pass --check or --write")
stale = []
for path in TARGETS:
if not path.exists():
print(f"[syscall] missing {path}")
return 1
with open(path, encoding="utf-8", newline="") as fh:
old = fh.read()
new = replace_block(old)
if new != old:
stale.append(path)
if args.write:
with open(path, "w", encoding="utf-8", newline="") as fh:
fh.write(new)
print(f"[syscall] updated {path.relative_to(ROOT)}")
else:
print(f"[syscall] ok {path.relative_to(ROOT)}")
if args.check and stale:
print("[syscall] out of sync; run: python Scripts/syscall_table.py --write")
return 1
return 0
if __name__ == "__main__":
sys.exit(main())