2175 lines
82 KiB
Plaintext
2175 lines
82 KiB
Plaintext
import drivers.serial.uart.serial as serial
|
||
import drivers.fs.fat32.fat32 as fat32
|
||
import drivers.fs.fat32.fat32_types as fat32_types
|
||
import sched.sched as sched
|
||
import sched.process as process
|
||
import viperlib
|
||
import string
|
||
import mm.mm as mm
|
||
import paging.paging as paging
|
||
import execrunner.elf as elf
|
||
import drivers.devfs.devfs as devfs
|
||
import platform.acpi.acpi as acpi
|
||
import platform.pch.timer as timer
|
||
import platform.pch.rtc as rtc
|
||
import intr.gdt as gdt
|
||
import drivers.core.cpu.cpu as cpu
|
||
import abi.linux as abi
|
||
import asm
|
||
|
||
|
||
# ----------------------------------------------------------------------------
|
||
# Syscall entry frame (set up by the compiler-provided syscall_entry):
|
||
# rbp+0x00 r15 +0x08 r14 +0x10 r13 +0x18 r12
|
||
# +0x20 r10 +0x28 r9 +0x30 r8 +0x38 rbp
|
||
# +0x40 rdi +0x48 rsi +0x50 rdx +0x58 rbx
|
||
# +0x60 r11(user flags) +0x68 rcx(user rip)
|
||
# +0x70 from-user flag +0x78 user rsp
|
||
# The handler itself never touches rbp, so a naked read is stable.
|
||
# ----------------------------------------------------------------------------
|
||
define FR_R15 = 0
|
||
define FR_R14 = 8
|
||
define FR_R13 = 16
|
||
define FR_R12 = 24
|
||
define FR_R10 = 32
|
||
define FR_R9 = 40
|
||
define FR_R8 = 48
|
||
define FR_RBP = 56
|
||
define FR_RDI = 64
|
||
define FR_RSI = 72
|
||
define FR_RDX = 80
|
||
define FR_RBX = 88
|
||
define FR_FLAGS = 96
|
||
define FR_RIP = 104
|
||
define FR_FROMU = 112
|
||
define FR_RSP = 120
|
||
|
||
# Entry trampoline state (naked syscall_handler stores the entry rbp here).
|
||
export _entry_frame: uint64 = 0
|
||
export _handler_ptr: rawptr = None
|
||
|
||
|
||
# ============================================================================
|
||
# 系统调用号表(由 Scripts/syscall_table.py 生成,禁止手改)。
|
||
# 生成源:Scripts/syscall_table.py;build.py 在编译前自动同步。
|
||
# ============================================================================
|
||
# BEGIN GENERATED SYSCALL TABLE
|
||
enum Syscall:
|
||
# ---- Linux 对齐段(号 = Linux x86-64 的号) ----
|
||
READ = 0 # read(fd, buf, count)
|
||
WRITE = 1 # write(fd, buf, count)
|
||
OPEN = 2 # open(path, flags) - Linux O_*
|
||
CLOSE = 3 # close(fd)
|
||
STAT = 4 # stat(path, fileinfo*) - legacy struct
|
||
FSTAT = 5 # fstat(fd, struct stat*)
|
||
POLL = 7 # poll(fds, nfds, timeout) - stub 0
|
||
SEEK = 8 # lseek(fd, offset, whence) -> new offset
|
||
MMAP = 9 # mmap(addr, len, prot, flags, fd, off)
|
||
MPROTECT = 10 # mprotect(addr, len, prot)
|
||
MUNMAP = 11 # munmap(addr, len)
|
||
BRK = 12 # brk(addr) -> new break
|
||
RT_SIGACTION = 13 # rt_sigaction() - bookkeeping
|
||
RT_SIGPROCMASK = 14 # rt_sigprocmask() - bookkeeping
|
||
RT_SIGRETURN = 15 # rt_sigreturn()
|
||
IOCTL = 16 # ioctl(fd, request, arg)
|
||
PREAD64 = 17 # pread64(fd, buf, count, offset)
|
||
PWRITE64 = 18 # pwrite64(fd, buf, count, offset)
|
||
READV = 19 # readv(fd, iov, iovcnt)
|
||
WRITEV = 20 # writev(fd, iov, iovcnt)
|
||
ACCESS = 21 # access(path, mode)
|
||
PIPE = 22 # pipe(int[2])
|
||
YIELD = 24 # sched_yield()
|
||
MREMAP = 25 # mremap(old, oldsz, newsz, flags, newaddr)
|
||
MADVISE = 28 # madvise(addr, len, advice)
|
||
DUP = 32 # dup(fd)
|
||
DUP2 = 33 # dup2(oldfd, newfd)
|
||
NANOSLEEP = 35 # nanosleep(req, rem)
|
||
GETPID = 39 # getpid()
|
||
CLONE = 56 # clone(flags, stack, ptid, ctid, tls)
|
||
FORK = 57 # fork() - copy address space
|
||
SPAWN = 59 # execve(path, argv, envp)
|
||
THREAD_EXIT = 60 # exit(): retire current thread
|
||
WAIT4 = 61 # wait4(pid, status, options, rusage)
|
||
KILL = 62 # kill(pid, sig) - bookkeeping
|
||
UNAME = 63 # uname(utsname*)
|
||
FCNTL = 72 # fcntl(fd, cmd, arg)
|
||
GETCWD = 79 # getcwd(buf, size)
|
||
CHDIR = 80 # chdir(path)
|
||
RENAME = 82 # rename(old, new)
|
||
MKDIR = 83 # mkdir(path)
|
||
RMDIR = 84 # rmdir(path)
|
||
REMOVE = 87 # unlink(path)
|
||
READLINK = 89 # readlink(path, buf, size) - no symlinks
|
||
GETUID = 102 # getuid()
|
||
GETGID = 104 # getgid()
|
||
GETEUID = 107 # geteuid()
|
||
GETEGID = 108 # getegid()
|
||
GETTIMEOFDAY = 96 # gettimeofday(tv, tz)
|
||
GETPPID = 110 # getppid()
|
||
ARCH_PRCTL = 158 # arch_prctl(code, addr)
|
||
REBOOT_LINUX = 169 # reboot(magic1, magic2, cmd, arg)
|
||
GETTID = 186 # gettid()
|
||
FUTEX = 202 # futex(uaddr, op, val, timeout)
|
||
GETDENTS64 = 217 # getdents64(fd, buf, count)
|
||
SET_TID_ADDRESS = 218 # set_tid_address(tidptr)
|
||
CLOCK_GETTIME = 228 # clock_gettime(clockid, timespec*)
|
||
CLOCK_GETRES = 229 # clock_getres(clockid, timespec*)
|
||
EXIT = 231 # exit_group(): terminate all threads
|
||
OPENAT = 257 # openat(dirfd, path, flags, mode)
|
||
NEWFSTATAT = 262 # newfstatat(dirfd, path, stat*, flags)
|
||
UNLINKAT = 263 # unlinkat(dirfd, path, flags)
|
||
READLINKAT = 267 # readlinkat(dirfd, path, buf, size)
|
||
FACCESSAT = 269 # faccessat(dirfd, path, mode, flags)
|
||
SET_ROBUST_LIST = 273 # set_robust_list(head, len)
|
||
DUP3 = 292 # dup3(oldfd, newfd, flags)
|
||
PIPE2 = 293 # pipe2(int[2], flags)
|
||
PRLIMIT64 = 302 # prlimit64(pid, resource, new, old)
|
||
GETRANDOM = 318 # getrandom(buf, len, flags)
|
||
STATX = 332 # statx(...) - -ENOSYS, glibc falls back
|
||
RSEQ = 334 # rseq(rseq, len, flags) - -ENOSYS
|
||
# ---- 临时私有段(0x1000+):Linux 无对应,逐条迁出 ----
|
||
LOAD_SO = 0x1006 # TODO(userspace): dynamic library loading
|
||
SO_CALL1 = 0x1007 # TODO(userspace): dynamic library call
|
||
REBOOT = 0x1008 # TODO(linux): reboot(169)
|
||
SHUTDOWN = 0x1009 # TODO(linux): reboot(RB_POWER_OFF)
|
||
SPAWN_PROC = 0x100A # TODO(userspace): Terminal run helper
|
||
THREAD_CREATE_OLD = 0x100B # TODO(userspace): ViperOS thread_create
|
||
# END GENERATED SYSCALL TABLE
|
||
|
||
define IA32_EFER = 0xC0000080
|
||
define IA32_STAR = 0xC0000081
|
||
define IA32_LSTAR = 0xC0000082
|
||
define IA32_FMASK = 0xC0000084
|
||
|
||
# Reset via the 8042 controller; PCI reset and halt are fallbacks.
|
||
def power_reboot():
|
||
serial.puts("[power] reboot\n")
|
||
asm.cli()
|
||
for _ in range(1000):
|
||
if (asm.inb(0x64) & 0x02) == 0: break
|
||
asm.outb(0x64, 0xFE)
|
||
asm.outb(0xCF9, 0x02)
|
||
asm.io_wait()
|
||
asm.outb(0xCF9, 0x06)
|
||
while True:
|
||
asm.cli()
|
||
asm.hlt()
|
||
|
||
# ACPI S5 power-off.
|
||
def power_shutdown():
|
||
serial.puts("[power] shutdown\n")
|
||
asm.cli()
|
||
acpi.poweroff()
|
||
while True:
|
||
asm.cli()
|
||
asm.hlt()
|
||
|
||
def wrmsr(msr: uint32, val: uint64):
|
||
lo: uint32 = uint32(val & 0xFFFFFFFF)
|
||
hi: uint32 = uint32(val >> 32)
|
||
__asm__(f"""mov ecx, {asminp(msr, ASM_DESCR.REG_ANY)}
|
||
mov eax, {asminp(lo, ASM_DESCR.REG_ANY)}
|
||
mov edx, {asminp(hi, ASM_DESCR.REG_ANY)}
|
||
wrmsr""",
|
||
op=[ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RAX, ASM_DESCR.CLOBBER_RDX])
|
||
|
||
def rdmsr(msr: uint32) -> uint64:
|
||
lo: uint32
|
||
hi: uint32
|
||
# rdmsr 固定写 EDX:EAX;out 绑定 + CLOBBER_RAX/RDX 冲突会导致 lo/hi
|
||
# 分配到非 EAX/EDX 读到垃圾。显式 mov 到输出寄存器(与 asm.inb 修复同模式)。
|
||
__asm__(f"""mov ecx, {asminp(msr, ASM_DESCR.REG_ANY)}
|
||
rdmsr
|
||
mov {asmout(lo, ASM_DESCR.OUTPUT_REG)}, eax
|
||
mov {asmout(hi, ASM_DESCR.OUTPUT_REG)}, edx""",
|
||
out=[asmout(lo, ASM_DESCR.OUTPUT_REG), asmout(hi, ASM_DESCR.OUTPUT_REG)],
|
||
op=[ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RAX, ASM_DESCR.CLOBBER_RDX])
|
||
return uint64(lo) | (uint64(hi) << 32)
|
||
|
||
def _current_proc() -> process.Process*:
|
||
return process.ProcessManager.current()
|
||
|
||
def _fd_alloc(p: process.Process*) -> int:
|
||
for i in range(process.PROC_MAX_FDS):
|
||
if not p.fd_used[i]:
|
||
p.fd_used[i] = 1
|
||
p.fd_flags[i] = 0
|
||
p.fd_shared[i] = 0
|
||
return i
|
||
return -1
|
||
|
||
def _fd_free(p: process.Process*, fd: int):
|
||
if fd >= 0 and fd < process.PROC_MAX_FDS:
|
||
p.fd_used[fd] = None
|
||
p.fd_table[fd] = None
|
||
p.fd_kind[fd] = 0
|
||
p.fd_flags[fd] = 0
|
||
p.fd_shared[fd] = 0
|
||
|
||
# dup()/dup2() share the same underlying object; the last close wins.
|
||
def _fd_other_ref(p: process.Process*, fd: int) -> int:
|
||
fp: uint64 = uint64(p.fd_table[fd])
|
||
k: uint8 = p.fd_kind[fd]
|
||
for i in range(process.PROC_MAX_FDS):
|
||
if i == fd: continue
|
||
if not p.fd_used[i]: continue
|
||
if p.fd_kind[i] != k: continue
|
||
if uint64(p.fd_table[i]) == fp: return 1
|
||
return 0
|
||
|
||
def _dir_alloc(p: process.Process*) -> int:
|
||
for i in range(process.PROC_MAX_DIRS):
|
||
if not p.dir_used[i]:
|
||
p.dir_used[i] = 1
|
||
return i
|
||
return -1
|
||
|
||
def _dir_free(p: process.Process*, dd: int):
|
||
if dd >= 0 and dd < process.PROC_MAX_DIRS:
|
||
p.dir_used[dd] = None
|
||
p.dir_table[dd] = None
|
||
|
||
# ============================================================================
|
||
# Linux ABI helpers
|
||
# ============================================================================
|
||
|
||
# All handlers return int: non-negative on success, -errno on failure.
|
||
def _err(e: int) -> int:
|
||
return 0 - e
|
||
|
||
def _fr_errno(r: int) -> int:
|
||
"""Translate a FAT32 FRESULT into -errno (0 stays 0)."""
|
||
if r == 0: return 0
|
||
if r == int(fat32_types.FRESULT.FR_NO_FILE) or r == int(fat32_types.FRESULT.FR_NO_PATH):
|
||
return _err(abi.ENOENT)
|
||
if r == int(fat32_types.FRESULT.FR_INVALID_NAME):
|
||
return _err(abi.EINVAL)
|
||
if r == int(fat32_types.FRESULT.FR_DENIED):
|
||
return _err(abi.EACCES)
|
||
if r == int(fat32_types.FRESULT.FR_EXIST):
|
||
return _err(abi.EEXIST)
|
||
if r == int(fat32_types.FRESULT.FR_INVALID_OBJECT):
|
||
return _err(abi.EBADF)
|
||
if r == int(fat32_types.FRESULT.FR_WRITE_PROTECTED):
|
||
return _err(abi.EROFS)
|
||
if r == int(fat32_types.FRESULT.FR_NOT_ENOUGH_CORE):
|
||
return _err(abi.ENOMEM)
|
||
if r == int(fat32_types.FRESULT.FR_TOO_MANY_OPEN_FILES):
|
||
return _err(abi.EMFILE)
|
||
if r == int(fat32_types.FRESULT.FR_INVALID_PARAMETER):
|
||
return _err(abi.EINVAL)
|
||
if r == int(fat32_types.FRESULT.FR_TIMEOUT):
|
||
return _err(abi.EIO)
|
||
if r == int(fat32_types.FRESULT.FR_LOCKED):
|
||
return _err(abi.EBUSY)
|
||
return _err(abi.EIO)
|
||
|
||
def _open_mode(flags: uint32) -> uint8:
|
||
"""Linux O_* -> FatFs FA_* access/create mode."""
|
||
m: int = 0
|
||
acc: uint32 = flags & uint32(abi.O_ACCMODE)
|
||
if acc == uint32(abi.O_RDONLY):
|
||
m = int(fat32_types.FA_READ)
|
||
elif acc == uint32(abi.O_WRONLY):
|
||
m = int(fat32_types.FA_WRITE)
|
||
else:
|
||
m = int(fat32_types.FA_READ) | int(fat32_types.FA_WRITE)
|
||
if (flags & uint32(abi.O_EXCL)) != 0 and (flags & uint32(abi.O_CREAT)) != 0:
|
||
m = m | int(fat32_types.FA_CREATE_NEW)
|
||
elif (flags & uint32(abi.O_TRUNC)) != 0:
|
||
m = m | int(fat32_types.FA_CREATE_ALWAYS)
|
||
elif (flags & uint32(abi.O_CREAT)) != 0:
|
||
m = m | int(fat32_types.FA_OPEN_ALWAYS)
|
||
elif (flags & uint32(abi.O_APPEND)) != 0:
|
||
m = m | int(fat32_types.FA_OPEN_APPEND)
|
||
return uint8(m)
|
||
|
||
# Resolve `path` against `cwd` (dirfd == AT_FDCWD) or require an absolute path.
|
||
# Directory-relative lookups from a dir fd are added with openat (P1).
|
||
def _resolve_path(dirfd: int, path: const str, out: char*) -> int:
|
||
if path is None: return _err(abi.EFAULT)
|
||
n: int = 0
|
||
if path[0] == 47:
|
||
while path[n] != 0:
|
||
if n >= abi.PATH_MAX - 1: return _err(abi.ENAMETOOLONG)
|
||
out[n] = path[n]
|
||
n += 1
|
||
out[n] = 0
|
||
return 0
|
||
if dirfd != abi.AT_FDCWD:
|
||
return _err(abi.ENOTSUP)
|
||
p: process.Process* = _current_proc()
|
||
c: int = 0
|
||
while p.cwd[c] != 0:
|
||
if n >= abi.PATH_MAX - 1: return _err(abi.ENAMETOOLONG)
|
||
out[n] = p.cwd[c]
|
||
n += 1
|
||
c += 1
|
||
if n > 0 and out[n - 1] != 47:
|
||
out[n] = 47
|
||
n += 1
|
||
i: int = 0
|
||
while path[i] != 0:
|
||
if n >= abi.PATH_MAX - 1: return _err(abi.ENAMETOOLONG)
|
||
out[n] = path[i]
|
||
n += 1
|
||
i += 1
|
||
out[n] = 0
|
||
return 0
|
||
|
||
# Fill the common fields of a Linux struct stat. Times stay 0: FAT32 stores
|
||
# DOS timestamps, which are not converted yet (P1 with newfstatat).
|
||
def _fill_stat(st: abi.linux_stat*, size: uint64, mode: uint32, ino: uint64):
|
||
string.memset(rawptr(st), 0, uint64(abi.SIZEOF_LINUX_STAT))
|
||
st.st_ino = ino
|
||
st.st_nlink = 1
|
||
st.st_mode = mode
|
||
st.st_size = size
|
||
st.st_blksize = 512
|
||
st.st_blocks = (size + 511) / 512
|
||
|
||
# Software DRNG for getrandom(): no hardware entropy requirement, only used
|
||
# for stack canaries and similar non-cryptographic purposes (see docs).
|
||
_rng_state: uint64 = 0x9E3779B97F4A7C15
|
||
|
||
def _rng_next() -> uint64:
|
||
global _rng_state
|
||
# 64-bit LCG; the high bits are returned because low LCG bits are weak.
|
||
_rng_state = _rng_state * uint64(6364136223846793005) + uint64(1442695040888963407)
|
||
return _rng_state >> 16
|
||
|
||
def EXIT(code: int) -> int:
|
||
serial.puts("[syscall] exit called\n")
|
||
p: process.Process* = _current_proc()
|
||
p.exit_code = code
|
||
# S3e:不再在这里拆窗口 —— 窗口/输入已整体交给用户态图形壳 explorer(/dev/desk 共享区),
|
||
# 内核不再持有任何窗口语义。
|
||
# Linux exit_group:退役本进程的**所有**线程,绝不返回用户态。
|
||
# (THREAD_EXIT=60 只退当前线程。)
|
||
# 只 destroy 窗口不够:app 的 _start 在 process.exit(0) 之后还有 `return 0`,而内核是以
|
||
# rsp = USER_STACK_VTOP 进入 _start 的(elf.vp),一旦收尾 `ret` 就会去读 [VTOP]:
|
||
# #PF cr2=rsp=0x7fffffe00000,rip 在 _start 内,rbp=0,rax=退出码(实测现场)。
|
||
sched.Scheduler.exit_process(p.pid)
|
||
# 【硬保护】exit_process 不返回;这里仅作兜底。
|
||
while True:
|
||
sched.Scheduler._yield()
|
||
return 0
|
||
|
||
_loaded_so_base: rawptr = None
|
||
_loaded_so_path: [64]char
|
||
|
||
def LOAD_SO(path_ptr: str) -> uint64:
|
||
global _loaded_so_base
|
||
path: const str = path_ptr
|
||
serial.puts("[so] loading: ")
|
||
serial.puts(path)
|
||
serial.puts("\n")
|
||
base: rawptr = elf.load_so(path)
|
||
if base is not None:
|
||
_loaded_so_base = base
|
||
for i in range(63):
|
||
ch: char = path[i]
|
||
_loaded_so_path[i] = ch
|
||
if ch == 0: break
|
||
_loaded_so_path[63] = 0
|
||
return uint64(base)
|
||
return 0
|
||
|
||
def SO_CALL1(sym_name_ptr: str, arg1: rawptr) -> int:
|
||
if _loaded_so_base is None: return -1
|
||
sym_name: const str = sym_name_ptr
|
||
fn: rawptr = elf.get_so_symbol(_loaded_so_base, sym_name, &_loaded_so_path[0])
|
||
if fn is None:
|
||
serial.puts("[so] symbol not found\n")
|
||
return -2
|
||
__asm__(f"""mov rdi, {asminp(arg1, ASM_DESCR.REG_ANY)}
|
||
call {asminp(fn, ASM_DESCR.REG_ANY)}""",
|
||
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX,
|
||
ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RDX,
|
||
ASM_DESCR.CLOBBER_RDI, ASM_DESCR.CLOBBER_RSI,
|
||
ASM_DESCR.CLOBBER_R8, ASM_DESCR.CLOBBER_R9,
|
||
ASM_DESCR.CLOBBER_R10, ASM_DESCR.CLOBBER_R11])
|
||
serial.puts("[so] call returned\n")
|
||
return 0
|
||
|
||
def GETPID() -> uint64:
|
||
"""getpid():当前进程 pid。/dev/desk 的事件按 pid 过滤,app 需要它。"""
|
||
return uint64(process.current())
|
||
|
||
def _is_dev_path(path: const str) -> int:
|
||
"""路径是否以 "/dev/" 开头(走 devfs 设备节点而不是 FAT32)。逐字符比较,不引 str 库。"""
|
||
if path[0] != 47: return 0 # '/'
|
||
if path[1] != 100: return 0 # 'd'
|
||
if path[2] != 101: return 0 # 'e'
|
||
if path[3] != 118: return 0 # 'v'
|
||
if path[4] != 47: return 0 # '/'
|
||
return 1
|
||
|
||
def OPEN(path_ptr: str, mode: uint32) -> int:
|
||
p: process.Process* = _current_proc()
|
||
path: const str = path_ptr
|
||
# /dev/* 走 devfs 设备节点(serial / 输入 / fb…),其余仍走 FAT32 文件系统。
|
||
# 判据用逐字符比较(不引 str 库函数):"/dev/" = 47 '/'、100 'd'、101 'e'、118 'v'、47 '/'。
|
||
if _is_dev_path(path):
|
||
dfd: int = devfs.devfs_open(path, int(mode))
|
||
if dfd < 0:
|
||
return _err(abi.ENOENT)
|
||
fd: int = _fd_alloc(p)
|
||
if fd < 0:
|
||
devfs.devfs_close(dfd)
|
||
return _err(abi.EMFILE)
|
||
# 存 devfs_fd+1:devfs fd 0 是合法值,直接用会与"空槽位"混淆
|
||
p.fd_table[fd] = rawptr(uint64(dfd + 1))
|
||
p.fd_kind[fd] = process.FD_KIND_DEVFS
|
||
p.fd_flags[fd] = mode
|
||
return fd
|
||
fat32.lock()
|
||
fp: rawptr = fat32.open(path, _open_mode(mode))
|
||
if not fp:
|
||
fat32.unlock()
|
||
return _err(abi.ENOENT)
|
||
fd: int = _fd_alloc(p)
|
||
if fd < 0:
|
||
fat32.close(fp)
|
||
fat32.unlock()
|
||
return _err(abi.EMFILE)
|
||
p.fd_table[fd] = fp
|
||
p.fd_kind[fd] = process.FD_KIND_FAT32
|
||
p.fd_flags[fd] = mode
|
||
fat32.unlock()
|
||
return fd
|
||
|
||
def CLOSE(fd: int) -> int:
|
||
p: process.Process* = _current_proc()
|
||
if fd < 0 or fd >= process.PROC_MAX_FDS:
|
||
return _err(abi.EBADF)
|
||
fp: rawptr = p.fd_table[fd]
|
||
if not fp:
|
||
return _err(abi.EBADF)
|
||
# Pipes carry their own reference counts: every inherited/dup'd end was
|
||
# counted, so always decrement (fork's shared flag must not skip this).
|
||
if p.fd_kind[fd] == process.FD_KIND_PIPE:
|
||
res_pipe: int = _pipe_close(fp)
|
||
_fd_free(p, fd)
|
||
return res_pipe
|
||
# fork() copies the fd table; shared entries must not close the
|
||
# underlying object (the parent still owns it).
|
||
if p.fd_shared[fd] != 0:
|
||
_fd_free(p, fd)
|
||
return 0
|
||
# dup()/dup2() aliases: only the last close releases the object.
|
||
if _fd_other_ref(p, fd) != 0:
|
||
_fd_free(p, fd)
|
||
return 0
|
||
if p.fd_kind[fd] == process.FD_KIND_DEVFS:
|
||
res_dev: int = devfs.devfs_close(int(uint64(fp)) - 1)
|
||
_fd_free(p, fd)
|
||
if res_dev < 0: return _err(abi.EIO)
|
||
return 0
|
||
if p.fd_kind[fd] == process.FD_KIND_DIR:
|
||
dd: int = int(uint64(fp)) - 1
|
||
if dd >= 0 and dd < process.PROC_MAX_DIRS:
|
||
fat32.lock()
|
||
fat32.closedir(&p.dir_pool[dd])
|
||
fat32.unlock()
|
||
_dir_free(p, dd)
|
||
_fd_free(p, fd)
|
||
return 0
|
||
fat32.lock()
|
||
res: int = int(fat32.close(fp))
|
||
fat32.unlock()
|
||
_fd_free(p, fd)
|
||
return _fr_errno(res)
|
||
|
||
def READ(fd: int, buf_ptr: rawptr, count: uint32) -> int:
|
||
p: process.Process* = _current_proc()
|
||
if fd < 0 or fd >= process.PROC_MAX_FDS:
|
||
return _err(abi.EBADF)
|
||
fp: rawptr = p.fd_table[fd]
|
||
if not fp:
|
||
return _err(abi.EBADF)
|
||
if p.fd_kind[fd] == process.FD_KIND_DIR:
|
||
return _err(abi.EISDIR)
|
||
if p.fd_kind[fd] == process.FD_KIND_PIPE:
|
||
return _pipe_read(fp, buf_ptr, count)
|
||
if p.fd_kind[fd] == process.FD_KIND_DEVFS:
|
||
r: int = devfs.devfs_read(int(uint64(fp)) - 1, buf_ptr, count)
|
||
if r < 0: return _err(abi.EIO)
|
||
return r
|
||
fat32.lock()
|
||
br: uint32 = fat32.read(fp, buf_ptr, count)
|
||
fat32.unlock()
|
||
return int(br)
|
||
|
||
def WRITE(fd: int, buf_ptr: rawptr, count: uint32) -> int:
|
||
p: process.Process* = _current_proc()
|
||
if fd < 0 or fd >= process.PROC_MAX_FDS:
|
||
return _err(abi.EBADF)
|
||
fp: rawptr = p.fd_table[fd]
|
||
if not fp:
|
||
return _err(abi.EBADF)
|
||
if p.fd_kind[fd] == process.FD_KIND_DIR:
|
||
return _err(abi.EISDIR)
|
||
if p.fd_kind[fd] == process.FD_KIND_PIPE:
|
||
return _pipe_write(fp, buf_ptr, count)
|
||
if p.fd_kind[fd] == process.FD_KIND_DEVFS:
|
||
r: int = devfs.devfs_write(int(uint64(fp)) - 1, buf_ptr, count)
|
||
if r < 0: return _err(abi.EIO)
|
||
return r
|
||
fat32.lock()
|
||
bw: uint32 = fat32.write(fp, buf_ptr, count)
|
||
fat32.unlock()
|
||
return int(bw)
|
||
|
||
def IOCTL(fd: int, request: uint32, arg: rawptr) -> int:
|
||
"""设备控制(Linux ioctl 的子集):当前只有 devfs 设备支持,用于 /dev/fb0 查几何。"""
|
||
p: process.Process* = _current_proc()
|
||
if fd < 0 or fd >= process.PROC_MAX_FDS:
|
||
return _err(abi.EBADF)
|
||
if not p.fd_used[fd]:
|
||
return _err(abi.EBADF)
|
||
if p.fd_kind[fd] != process.FD_KIND_DEVFS:
|
||
return _err(abi.ENOTTY)
|
||
r: int = devfs.devfs_ioctl(int(uint64(p.fd_table[fd])) - 1, request, arg)
|
||
if r < 0: return _err(abi.EINVAL)
|
||
return r
|
||
|
||
def SEEK(fd: int, offset: uint64, whence: uint32) -> int:
|
||
"""lseek(fd, offset, whence) -> new offset, or -errno.
|
||
|
||
offset is 64-bit on the ABI side; FAT32 caps files at 4 GiB so anything
|
||
beyond that range is rejected with -EINVAL. Whence: 0=SET 1=CUR 2=END.
|
||
"""
|
||
p: process.Process* = _current_proc()
|
||
if fd < 0 or fd >= process.PROC_MAX_FDS:
|
||
return _err(abi.EBADF)
|
||
fp: rawptr = p.fd_table[fd]
|
||
if not fp:
|
||
return _err(abi.EBADF)
|
||
if p.fd_kind[fd] == process.FD_KIND_DIR:
|
||
return _err(abi.EINVAL)
|
||
if p.fd_kind[fd] == process.FD_KIND_PIPE:
|
||
return _err(abi.ESPIPE)
|
||
if p.fd_kind[fd] == process.FD_KIND_DEVFS:
|
||
return _err(abi.ESPIPE)
|
||
fat32.lock()
|
||
base: int = 0
|
||
if whence == uint32(abi.SEEK_CUR):
|
||
base = int(fat32.tell(fp))
|
||
elif whence == uint32(abi.SEEK_END):
|
||
base = int(fat32.size(fp))
|
||
elif whence != uint32(abi.SEEK_SET):
|
||
fat32.unlock()
|
||
return _err(abi.EINVAL)
|
||
tgt: int = base + int(offset)
|
||
if tgt < 0 or tgt > 0x7FFFFFFF:
|
||
fat32.unlock()
|
||
return _err(abi.EINVAL)
|
||
r: int = int(fat32.seek(fp, uint32(tgt)))
|
||
fat32.unlock()
|
||
if r != 0: return _fr_errno(r)
|
||
return tgt
|
||
|
||
def MKDIR(path_ptr: str) -> int:
|
||
path: const str = path_ptr
|
||
fat32.lock()
|
||
r: int = int(fat32.mkdir(path))
|
||
fat32.unlock()
|
||
return _fr_errno(r)
|
||
|
||
def REMOVE(path_ptr: str) -> int:
|
||
path: const str = path_ptr
|
||
fat32.lock()
|
||
r: int = int(fat32.remove(path))
|
||
fat32.unlock()
|
||
return _fr_errno(r)
|
||
|
||
# ---------------- P1: file system ABI ----------------
|
||
|
||
def _open_dir(path: const str) -> int:
|
||
p: process.Process* = _current_proc()
|
||
dd: int = _dir_alloc(p)
|
||
if dd < 0: return _err(abi.EMFILE)
|
||
dp: fat32_types.dirobj* = &p.dir_pool[dd]
|
||
fat32.lock()
|
||
res: int = int(fat32.opendir(path, dp))
|
||
fat32.unlock()
|
||
if res != 0:
|
||
_dir_free(p, dd)
|
||
return _fr_errno(res)
|
||
p.dir_table[dd] = rawptr(uint64(dp))
|
||
fd: int = _fd_alloc(p)
|
||
if fd < 0:
|
||
fat32.lock()
|
||
fat32.closedir(dp)
|
||
fat32.unlock()
|
||
_dir_free(p, dd)
|
||
return _err(abi.EMFILE)
|
||
p.fd_table[fd] = rawptr(uint64(dd + 1))
|
||
p.fd_kind[fd] = process.FD_KIND_DIR
|
||
return fd
|
||
|
||
def OPENAT(dirfd: int, path_ptr: str, flags: uint32, mode: uint32) -> int:
|
||
path: const str = path_ptr
|
||
buf: [256]char
|
||
r: int = _resolve_path(dirfd, path, &buf[0])
|
||
if r != 0: return r
|
||
if (flags & uint32(abi.O_DIRECTORY)) != 0:
|
||
return _open_dir(&buf[0])
|
||
return OPEN(&buf[0], flags)
|
||
|
||
def GETDENTS64(fd: int, buf_ptr: rawptr, count: uint64) -> int:
|
||
p: process.Process* = _current_proc()
|
||
if fd < 0 or fd >= process.PROC_MAX_FDS: return _err(abi.EBADF)
|
||
if not p.fd_used[fd]: return _err(abi.EBADF)
|
||
if p.fd_kind[fd] != process.FD_KIND_DIR: return _err(abi.ENOTDIR)
|
||
dd: int = int(uint64(p.fd_table[fd])) - 1
|
||
if dd < 0 or dd >= process.PROC_MAX_DIRS: return _err(abi.EBADF)
|
||
dp: fat32_types.dirobj* = &p.dir_pool[dd]
|
||
info: fat32_types.fileinfo
|
||
fat32.lock()
|
||
r: int = int(fat32.readdir(dp, &info))
|
||
fat32.unlock()
|
||
if r != 0:
|
||
if r == int(fat32_types.FRESULT.FR_NO_FILE): return 0
|
||
return _fr_errno(r)
|
||
name: str = &info.fname[0]
|
||
n: int = int(string.strlen(name))
|
||
reclen: int = (19 + n + 1 + 7) / 8 * 8
|
||
if uint64(reclen) > count: return _err(abi.EINVAL)
|
||
d: abi.linux_dirent64* = buf_ptr
|
||
d.d_ino = uint64(1)
|
||
d.d_off = uint64(0)
|
||
d.d_reclen = uint16(reclen)
|
||
if (int(info.attr) & 0x10) != 0:
|
||
d.d_type = uint8(abi.DT_DIR)
|
||
else:
|
||
d.d_type = uint8(abi.DT_REG)
|
||
dst: char* = char*(uint64(buf_ptr) + 19)
|
||
i: int = 0
|
||
while i < n:
|
||
dst[i] = name[i]
|
||
i += 1
|
||
dst[n] = 0
|
||
i = n + 1
|
||
while i < reclen - 19:
|
||
dst[i] = 0
|
||
i += 1
|
||
return reclen
|
||
|
||
def FSTAT(fd: int, st_ptr: rawptr) -> int:
|
||
p: process.Process* = _current_proc()
|
||
if fd < 0 or fd >= process.PROC_MAX_FDS: return _err(abi.EBADF)
|
||
if not p.fd_used[fd]: return _err(abi.EBADF)
|
||
if st_ptr is None: return _err(abi.EFAULT)
|
||
st: abi.linux_stat* = st_ptr
|
||
if p.fd_kind[fd] == process.FD_KIND_DEVFS:
|
||
_fill_stat(st, uint64(0), uint32(abi.MODE_CHAR), uint64(fd + 1))
|
||
elif p.fd_kind[fd] == process.FD_KIND_DIR:
|
||
_fill_stat(st, uint64(0), uint32(abi.MODE_DIR), uint64(fd + 1))
|
||
elif p.fd_kind[fd] == process.FD_KIND_PIPE:
|
||
_fill_stat(st, uint64(0), uint32(abi.MODE_FIFO), uint64(fd + 1))
|
||
else:
|
||
fp: rawptr = p.fd_table[fd]
|
||
size: uint64 = uint64(fat32.size(fp))
|
||
_fill_stat(st, size, uint32(abi.MODE_FILE), uint64(fd + 1))
|
||
return 0
|
||
|
||
def NEWFSTATAT(dirfd: int, path_ptr: str, st_ptr: rawptr, flags: uint32) -> int:
|
||
if st_ptr is None: return _err(abi.EFAULT)
|
||
path: const str = path_ptr
|
||
if path is None: return _err(abi.EFAULT)
|
||
if path[0] == 0:
|
||
if (flags & uint32(abi.AT_EMPTY_PATH)) != 0:
|
||
return FSTAT(dirfd, st_ptr)
|
||
return _err(abi.ENOENT)
|
||
st: abi.linux_stat* = st_ptr
|
||
buf: [256]char
|
||
r: int = _resolve_path(dirfd, path, &buf[0])
|
||
if r != 0: return r
|
||
if _is_dev_path(&buf[0]):
|
||
_fill_stat(st, uint64(0), uint32(abi.MODE_CHAR), uint64(1))
|
||
return 0
|
||
if buf[0] == 47 and buf[1] == 0:
|
||
_fill_stat(st, uint64(0), uint32(abi.MODE_DIR), uint64(1))
|
||
return 0
|
||
info: fat32_types.fileinfo
|
||
fat32.lock()
|
||
res: int = int(fat32.stat(&buf[0], &info))
|
||
fat32.unlock()
|
||
if res != 0: return _fr_errno(res)
|
||
if (int(info.attr) & 0x10) != 0:
|
||
_fill_stat(st, uint64(0), uint32(abi.MODE_DIR), uint64(1))
|
||
else:
|
||
_fill_stat(st, uint64(info.file_size), uint32(abi.MODE_FILE), uint64(1))
|
||
return 0
|
||
|
||
def DUP(fd: int) -> int:
|
||
p: process.Process* = _current_proc()
|
||
if fd < 0 or fd >= process.PROC_MAX_FDS: return _err(abi.EBADF)
|
||
if not p.fd_used[fd]: return _err(abi.EBADF)
|
||
nfd: int = _fd_alloc(p)
|
||
if nfd < 0: return _err(abi.EMFILE)
|
||
p.fd_table[nfd] = p.fd_table[fd]
|
||
p.fd_kind[nfd] = p.fd_kind[fd]
|
||
p.fd_flags[nfd] = p.fd_flags[fd]
|
||
p.fd_shared[nfd] = p.fd_shared[fd]
|
||
if p.fd_kind[nfd] == process.FD_KIND_PIPE:
|
||
_pipe_add_ref(p.fd_table[nfd])
|
||
return nfd
|
||
|
||
def DUP2(oldfd: int, newfd: int) -> int:
|
||
p: process.Process* = _current_proc()
|
||
if oldfd < 0 or oldfd >= process.PROC_MAX_FDS: return _err(abi.EBADF)
|
||
if not p.fd_used[oldfd]: return _err(abi.EBADF)
|
||
if newfd < 0 or newfd >= process.PROC_MAX_FDS: return _err(abi.EBADF)
|
||
if oldfd == newfd: return newfd
|
||
if p.fd_used[newfd]:
|
||
r: int = CLOSE(newfd)
|
||
if r != 0: return r
|
||
p.fd_used[newfd] = 1
|
||
p.fd_table[newfd] = p.fd_table[oldfd]
|
||
p.fd_kind[newfd] = p.fd_kind[oldfd]
|
||
p.fd_flags[newfd] = p.fd_flags[oldfd]
|
||
p.fd_shared[newfd] = p.fd_shared[oldfd]
|
||
if p.fd_kind[newfd] == process.FD_KIND_PIPE:
|
||
_pipe_add_ref(p.fd_table[newfd])
|
||
return newfd
|
||
|
||
def DUP3(oldfd: int, newfd: int, flags: uint32) -> int:
|
||
return DUP2(oldfd, newfd)
|
||
|
||
def FCNTL(fd: int, cmd: uint32, arg: uint64) -> int:
|
||
p: process.Process* = _current_proc()
|
||
if fd < 0 or fd >= process.PROC_MAX_FDS: return _err(abi.EBADF)
|
||
if not p.fd_used[fd]: return _err(abi.EBADF)
|
||
c: int = int(cmd)
|
||
if c == abi.F_DUPFD or c == abi.F_DUPFD_CLOEXEC:
|
||
return DUP(fd)
|
||
if c == abi.F_GETFD:
|
||
return 0
|
||
if c == abi.F_SETFD:
|
||
return 0
|
||
if c == abi.F_GETFL:
|
||
return int(p.fd_flags[fd])
|
||
if c == abi.F_SETFL:
|
||
p.fd_flags[fd] = uint32(arg)
|
||
return 0
|
||
return _err(abi.EINVAL)
|
||
|
||
def PREAD64(fd: int, buf_ptr: rawptr, count: uint32, offset: uint64) -> int:
|
||
p: process.Process* = _current_proc()
|
||
if fd < 0 or fd >= process.PROC_MAX_FDS: return _err(abi.EBADF)
|
||
if not p.fd_used[fd]: return _err(abi.EBADF)
|
||
if p.fd_kind[fd] != process.FD_KIND_FAT32: return _err(abi.ESPIPE)
|
||
cur: int = SEEK(fd, uint64(0), uint32(abi.SEEK_CUR))
|
||
if cur < 0: return cur
|
||
r: int = SEEK(fd, offset, uint32(abi.SEEK_SET))
|
||
if r < 0: return r
|
||
n: int = READ(fd, buf_ptr, count)
|
||
SEEK(fd, uint64(cur), uint32(abi.SEEK_SET))
|
||
return n
|
||
|
||
def PWRITE64(fd: int, buf_ptr: rawptr, count: uint32, offset: uint64) -> int:
|
||
p: process.Process* = _current_proc()
|
||
if fd < 0 or fd >= process.PROC_MAX_FDS: return _err(abi.EBADF)
|
||
if not p.fd_used[fd]: return _err(abi.EBADF)
|
||
if p.fd_kind[fd] != process.FD_KIND_FAT32: return _err(abi.ESPIPE)
|
||
cur: int = SEEK(fd, uint64(0), uint32(abi.SEEK_CUR))
|
||
if cur < 0: return cur
|
||
r: int = SEEK(fd, offset, uint32(abi.SEEK_SET))
|
||
if r < 0: return r
|
||
n: int = WRITE(fd, buf_ptr, count)
|
||
SEEK(fd, uint64(cur), uint32(abi.SEEK_SET))
|
||
return n
|
||
|
||
def RENAME(old_ptr: str, new_ptr: str) -> int:
|
||
oldp: const str = old_ptr
|
||
newp: const str = new_ptr
|
||
fat32.lock()
|
||
r: int = int(fat32.rename(oldp, newp))
|
||
fat32.unlock()
|
||
return _fr_errno(r)
|
||
|
||
def RMDIR(path_ptr: str) -> int:
|
||
path: const str = path_ptr
|
||
fat32.lock()
|
||
r: int = int(fat32.rmdir(path))
|
||
fat32.unlock()
|
||
return _fr_errno(r)
|
||
|
||
def UNLINKAT(dirfd: int, path_ptr: str, flags: uint32) -> int:
|
||
path: const str = path_ptr
|
||
buf: [256]char
|
||
r: int = _resolve_path(dirfd, path, &buf[0])
|
||
if r != 0: return r
|
||
if (flags & uint32(abi.AT_REMOVEDIR)) != 0:
|
||
return RMDIR(&buf[0])
|
||
return REMOVE(&buf[0])
|
||
|
||
def STAT(path_ptr: str, info_ptr: rawptr) -> int:
|
||
path: const str = path_ptr
|
||
info: fat32_types.fileinfo* = info_ptr
|
||
fat32.lock()
|
||
r: int = int(fat32.stat(path, info))
|
||
fat32.unlock()
|
||
return _fr_errno(r)
|
||
|
||
# Private launcher kept for Terminal's `run` command (a new child, caller stays alive).
|
||
def SPAWN_PROC(path_ptr: str, blocking: int) -> int:
|
||
path: const str = path_ptr
|
||
pid: int = elf.spawn_elf(path)
|
||
if pid < 0:
|
||
return _err(abi.ENOENT)
|
||
if blocking:
|
||
p: process.Process* = process.ProcessManager.get_process(pid)
|
||
if p:
|
||
while p.state != process.PROC_DONE:
|
||
sched.Scheduler.try_reschedule()
|
||
return pid
|
||
|
||
def WAIT4(pid: int, status_ptr: rawptr, options: uint32, rusage: rawptr) -> int:
|
||
cur: int = process.current()
|
||
while True:
|
||
found_child: int = 0
|
||
i: int = 0
|
||
while i < process.MAX_PROCESSES:
|
||
p: process.Process* = process.ProcessManager.get_process(i)
|
||
if p is not None:
|
||
matches: int = 0
|
||
if pid <= 0:
|
||
matches = 1 # any child (pid 0: same group, approximated)
|
||
elif p.pid == pid:
|
||
matches = 1
|
||
if matches != 0 and p.pid != cur and p.parent_pid == cur:
|
||
found_child = 1
|
||
if p.state == process.PROC_DONE:
|
||
if status_ptr is not None:
|
||
st: int* = int*(status_ptr)
|
||
st[0] = (p.exit_code & 0xFF) << 8
|
||
p.parent_pid = -1
|
||
return p.pid
|
||
i += 1
|
||
if found_child == 0:
|
||
return _err(abi.ECHILD)
|
||
if (options & uint32(1)) != 0: # WNOHANG
|
||
return 0
|
||
sched.Scheduler.yield_now()
|
||
|
||
# mmap bump allocator and brk base must not overlap (both live below the stack).
|
||
define USER_HEAP_BASE = 0x20000000
|
||
define USER_BRK_BASE = 0x40000000
|
||
define USER_VA_LIMIT = 0x7FFFFF000000
|
||
|
||
# fd = -1(Linux 匿名映射约定)
|
||
define MMAP_FD_ANON: uint64 = 0xFFFFFFFFFFFFFFFF
|
||
|
||
# ---- VMA bookkeeping -------------------------------------------------------
|
||
|
||
def _vma_find(p: process.Process*, addr: uint64) -> int:
|
||
for i in range(process.MAX_VMAS):
|
||
if p.vma_kind[i] == 0: continue
|
||
b: uint64 = p.vma_base[i]
|
||
if addr >= b and addr < b + p.vma_size[i]: return i
|
||
return -1
|
||
|
||
def _vma_overlap(p: process.Process*, start: uint64, size: uint64) -> int:
|
||
if size == 0: return 0
|
||
end: uint64 = start + size
|
||
for i in range(process.MAX_VMAS):
|
||
if p.vma_kind[i] == 0: continue
|
||
b: uint64 = p.vma_base[i]
|
||
e: uint64 = b + p.vma_size[i]
|
||
if start < e and b < end: return 1
|
||
return 0
|
||
|
||
def _vma_add(p: process.Process*, base: uint64, size: uint64, prot: uint32, kind: int, flags: uint32) -> int:
|
||
for i in range(process.MAX_VMAS):
|
||
if p.vma_kind[i] == 0:
|
||
p.vma_base[i] = base
|
||
p.vma_size[i] = size
|
||
p.vma_prot[i] = prot
|
||
p.vma_kind[i] = uint8(kind)
|
||
p.vma_flags[i] = flags
|
||
return 0
|
||
return _err(abi.ENOMEM)
|
||
|
||
def _vma_del(p: process.Process*, i: int):
|
||
p.vma_base[i] = 0
|
||
p.vma_size[i] = 0
|
||
p.vma_prot[i] = 0
|
||
p.vma_kind[i] = 0
|
||
p.vma_flags[i] = 0
|
||
|
||
def _unmap_pages(p: process.Process*, start: uint64, end: uint64, free_pages: int):
|
||
va: uint64 = start
|
||
while va < end:
|
||
if free_pages != 0:
|
||
phys: uint64 = paging.GetPagePhys(va)
|
||
if phys != 0:
|
||
mm.free_page(rawptr(phys))
|
||
paging.UnMapPage(va)
|
||
va = va + uint64(4096)
|
||
|
||
# Remove [addr, addr+length) from the VMA table, splitting partially covered
|
||
# areas, and release the backing frames of anon/file mappings.
|
||
def _vma_unmap_range(p: process.Process*, addr: uint64, length: uint64) -> int:
|
||
if length == 0: return 0
|
||
end: uint64 = addr + length
|
||
i: int = 0
|
||
while i < process.MAX_VMAS:
|
||
if p.vma_kind[i] != 0:
|
||
b: uint64 = p.vma_base[i]
|
||
e: uint64 = b + p.vma_size[i]
|
||
if b < end and addr < e:
|
||
s: uint64 = addr
|
||
if b > s: s = b
|
||
t: uint64 = end
|
||
if e < t: t = e
|
||
free_pages: int = 1
|
||
if p.vma_kind[i] == uint8(process.VMA_KIND_DEV): free_pages = 0
|
||
_unmap_pages(p, s, t, free_pages)
|
||
if s <= b and t >= e:
|
||
_vma_del(p, i)
|
||
elif s <= b:
|
||
p.vma_base[i] = t
|
||
p.vma_size[i] = e - t
|
||
elif t >= e:
|
||
p.vma_size[i] = s - b
|
||
else:
|
||
r: int = _vma_add(p, t, e - t, uint32(p.vma_prot[i]),
|
||
int(p.vma_kind[i]), uint32(p.vma_flags[i]))
|
||
if r != 0: return r
|
||
p.vma_size[i] = s - b
|
||
i += 1
|
||
return 0
|
||
|
||
def MMAP(addr: uint64, length: uint64, prot: uint64, flags: uint64, fd: uint64, offset: uint64) -> uint64:
|
||
"""mmap(addr, length, prot, flags, fd, offset) -> base, or MAP_FAILED (-1).
|
||
|
||
· MAP_ANONYMOUS: fresh zeroed pages;
|
||
· FAT32 fd: file contents copied into fresh pages (MAP_PRIVATE semantics);
|
||
· devfs fd: the device physical range is mapped directly (no frame owns it).
|
||
"""
|
||
p: process.Process* = _current_proc()
|
||
if length == 0: return abi.MAP_FAILED
|
||
aligned: uint64 = (length + uint64(0xFFF)) & ~uint64(0xFFF)
|
||
if aligned == 0: return abi.MAP_FAILED
|
||
fixed: int = 0
|
||
if (flags & uint64(abi.MAP_FIXED)) != 0: fixed = 1
|
||
base: uint64 = 0
|
||
if fixed != 0:
|
||
if (addr & uint64(0xFFF)) != 0: return abi.MAP_FAILED
|
||
base = addr
|
||
if base < uint64(0x10000) or base + aligned > uint64(USER_VA_LIMIT):
|
||
return abi.MAP_FAILED
|
||
if p.brk_end > p.brk_start and base < p.brk_end and p.brk_start < base + aligned:
|
||
return abi.MAP_FAILED
|
||
else:
|
||
if p.heap_start == 0: p.heap_start = USER_HEAP_BASE
|
||
base = p.heap_start + p.heap_size
|
||
while True:
|
||
brk_hit: int = 0
|
||
if p.brk_end > p.brk_start and base < p.brk_end and p.brk_start < base + aligned:
|
||
brk_hit = 1
|
||
if _vma_overlap(p, base, aligned) == 0 and brk_hit == 0: break
|
||
p.heap_size = p.heap_size + aligned
|
||
base = p.heap_start + p.heap_size
|
||
if base + aligned > uint64(USER_VA_LIMIT): return abi.MAP_FAILED
|
||
if base + aligned > uint64(USER_VA_LIMIT): return abi.MAP_FAILED
|
||
# classify the mapping source
|
||
kind: int = process.VMA_KIND_ANON
|
||
uncached: int = 0
|
||
dev_phys: uint64 = 0
|
||
dev_size: uint64 = 0
|
||
fp: rawptr = None
|
||
if (flags & uint64(abi.MAP_ANONYMOUS)) != 0:
|
||
kind = process.VMA_KIND_ANON
|
||
elif fd == MMAP_FD_ANON:
|
||
return abi.MAP_FAILED
|
||
else:
|
||
fdi: int = int(fd)
|
||
if fdi < 0 or fdi >= process.PROC_MAX_FDS: return abi.MAP_FAILED
|
||
if not p.fd_used[fdi]: return abi.MAP_FAILED
|
||
if p.fd_kind[fdi] == process.FD_KIND_DEVFS:
|
||
kind = process.VMA_KIND_DEV
|
||
if (offset & uint64(0xFFF)) != 0: return abi.MAP_FAILED
|
||
if devfs.devfs_get_mmap_info(int(uint64(p.fd_table[fdi])) - 1, &dev_phys, &dev_size) != 0:
|
||
return abi.MAP_FAILED
|
||
if devfs.devfs_mmap_uncached(int(uint64(p.fd_table[fdi])) - 1) != 0:
|
||
uncached = 1
|
||
if offset + aligned > dev_size: return abi.MAP_FAILED
|
||
elif p.fd_kind[fdi] == process.FD_KIND_FAT32:
|
||
kind = process.VMA_KIND_FILE
|
||
if (offset & uint64(0xFFF)) != 0: return abi.MAP_FAILED
|
||
if offset > uint64(0xFFFFFFFF): return abi.MAP_FAILED
|
||
fp = p.fd_table[fdi]
|
||
else:
|
||
return abi.MAP_FAILED
|
||
if fixed != 0:
|
||
ur: int = _vma_unmap_range(p, base, aligned)
|
||
if ur != 0: return abi.MAP_FAILED
|
||
pte: uint64 = paging.PTE_PRESENT | paging.PTE_USER
|
||
if (prot & uint64(abi.PROT_WRITE)) != 0:
|
||
pte = pte | paging.PTE_WRITABLE
|
||
if uncached != 0:
|
||
# MMIO (framebuffer). Use PAT write-combining when the CPU exposes it;
|
||
# otherwise leave the mapping write-back (fast sequential blits, and the
|
||
# compositor does not rely on UC for visibility). UC (PTE_PCD) made
|
||
# dragging noticeably slower on VMware.
|
||
if paging.has_pat_wc() != 0:
|
||
pte = pte | paging.PTE_PAT
|
||
# 这一段会连续调用 MapPage,而 MapPage 依赖**全局 paging.pml4**。若中途被
|
||
# 定时器抢占并切换进程,paging.pml4 会被 switch_pid 改成别的进程,后续页就
|
||
# 映射进错误的地址空间(本进程留下未映射空洞;用户写空洞时若命中 identity
|
||
# 大页就会直接改物理内存 → BAD blk / 画面花屏)。整个映射循环必须原子。
|
||
asm.cli()
|
||
va: uint64 = base
|
||
if kind == process.VMA_KIND_FILE:
|
||
fat32.lock()
|
||
oldpos: uint32 = fat32.tell(fp)
|
||
fat32.seek(fp, uint32(offset))
|
||
fat32.unlock()
|
||
done: uint64 = 0
|
||
while va < base + aligned:
|
||
fphys: rawptr = mm.alloc_page()
|
||
if fphys is None:
|
||
fat32.lock()
|
||
fat32.seek(fp, oldpos)
|
||
fat32.unlock()
|
||
_unmap_pages(p, base, va, 1)
|
||
asm.sti()
|
||
return abi.MAP_FAILED
|
||
string.memset(fphys, 0, 4096)
|
||
chunk: uint32 = 4096
|
||
if length - done < uint64(4096): chunk = uint32(length - done)
|
||
if chunk > 0:
|
||
fat32.lock()
|
||
got: int = fat32.read(fp, fphys, chunk)
|
||
fat32.unlock()
|
||
if got > 0:
|
||
done = done + uint64(got)
|
||
paging.MapPage(va, uint64(fphys), pte)
|
||
va = va + uint64(4096)
|
||
fat32.lock()
|
||
fat32.seek(fp, oldpos)
|
||
fat32.unlock()
|
||
else:
|
||
while va < base + aligned:
|
||
if kind == process.VMA_KIND_DEV:
|
||
paging.MapPage(va, dev_phys + offset + (va - base), pte)
|
||
else:
|
||
aphys: rawptr = mm.alloc_page()
|
||
if aphys is None:
|
||
_mf: [128]char
|
||
viperlib.snprintf(&_mf, 128, "[mmap] FAIL pid=%d va=0x%lx len=0x%lx\n",
|
||
p.pid, va, aligned)
|
||
serial.puts(&_mf)
|
||
_unmap_pages(p, base, va, 1)
|
||
asm.sti()
|
||
return abi.MAP_FAILED
|
||
string.memset(aphys, 0, 4096)
|
||
paging.MapPage(va, uint64(aphys), pte)
|
||
va = va + uint64(4096)
|
||
r: int = _vma_add(p, base, aligned, uint32(prot), kind, uint32(flags))
|
||
if r != 0:
|
||
if kind == process.VMA_KIND_DEV:
|
||
_unmap_pages(p, base, base + aligned, 0)
|
||
else:
|
||
_unmap_pages(p, base, base + aligned, 1)
|
||
asm.sti()
|
||
return abi.MAP_FAILED
|
||
if fixed == 0:
|
||
p.heap_size = p.heap_size + aligned
|
||
asm.sti()
|
||
return base
|
||
|
||
def MUNMAP(ptr: uint64, size: uint64) -> int:
|
||
if size == 0: return _err(abi.EINVAL)
|
||
start: uint64 = ptr & ~uint64(0xFFF)
|
||
end: uint64 = (ptr + size + uint64(0xFFF)) & ~uint64(0xFFF)
|
||
if end <= start: return _err(abi.EINVAL)
|
||
if start < uint64(0x10000) or end > uint64(USER_VA_LIMIT): return _err(abi.EINVAL)
|
||
asm.cli()
|
||
_mr: int = _vma_unmap_range(_current_proc(), start, end - start)
|
||
asm.sti()
|
||
return _mr
|
||
|
||
def MPROTECT(addr: uint64, length: uint64, prot: uint64) -> int:
|
||
p: process.Process* = _current_proc()
|
||
if length == 0: return 0
|
||
start: uint64 = addr & ~uint64(0xFFF)
|
||
end: uint64 = (addr + length + uint64(0xFFF)) & ~uint64(0xFFF)
|
||
if end <= start: return _err(abi.EINVAL)
|
||
if start < uint64(0x10000) or end > uint64(USER_VA_LIMIT): return _err(abi.EINVAL)
|
||
va: uint64 = start
|
||
while va < end:
|
||
# Kernel-loaded ELF segments are not in the VMA table; accept any
|
||
# present 4KB page so RELRO mprotect() on the main image still works.
|
||
if _vma_find(p, va) < 0 and paging.GetPagePhys(va) == 0:
|
||
return _err(abi.ENOMEM)
|
||
va = va + uint64(4096)
|
||
# Snapshot the VMAs that overlap: splitting below adds new entries.
|
||
idx: [64]int
|
||
n: int = 0
|
||
for i in range(process.MAX_VMAS):
|
||
if p.vma_kind[i] == 0: continue
|
||
b: uint64 = p.vma_base[i]
|
||
e: uint64 = b + p.vma_size[i]
|
||
if b < end and start < e:
|
||
idx[n] = i
|
||
n += 1
|
||
j: int = 0
|
||
while j < n:
|
||
i: int = idx[j]
|
||
b: uint64 = p.vma_base[i]
|
||
e: uint64 = b + p.vma_size[i]
|
||
s: uint64 = start
|
||
if b > s: s = b
|
||
t: uint64 = end
|
||
if e < t: t = e
|
||
old_prot: uint32 = uint32(p.vma_prot[i])
|
||
kind: int = int(p.vma_kind[i])
|
||
vflags: uint32 = uint32(p.vma_flags[i])
|
||
if s <= b and t >= e:
|
||
p.vma_prot[i] = uint32(prot)
|
||
elif s <= b:
|
||
r: int = _vma_add(p, t, e - t, old_prot, kind, vflags)
|
||
if r != 0: return r
|
||
p.vma_size[i] = t - b
|
||
p.vma_prot[i] = uint32(prot)
|
||
elif t >= e:
|
||
r = _vma_add(p, s, e - s, uint32(prot), kind, vflags)
|
||
if r != 0: return r
|
||
p.vma_size[i] = s - b
|
||
else:
|
||
r = _vma_add(p, s, t - s, uint32(prot), kind, vflags)
|
||
if r != 0: return r
|
||
r = _vma_add(p, t, e - t, old_prot, kind, vflags)
|
||
if r != 0: return r
|
||
p.vma_size[i] = s - b
|
||
j += 1
|
||
pte: uint64 = paging.PTE_PRESENT | paging.PTE_USER
|
||
if (prot & uint64(abi.PROT_WRITE)) != 0:
|
||
pte = pte | paging.PTE_WRITABLE
|
||
va = start
|
||
while va < end:
|
||
if paging.SetPageFlags(va, pte) == 0: return _err(abi.ENOMEM)
|
||
va = va + uint64(4096)
|
||
return 0
|
||
|
||
def BRK(addr: uint64) -> uint64:
|
||
p: process.Process* = _current_proc()
|
||
if p.brk_start == 0:
|
||
p.brk_start = USER_BRK_BASE
|
||
p.brk_end = USER_BRK_BASE
|
||
if addr == 0: return p.brk_end
|
||
if addr < p.brk_start or addr > uint64(USER_VA_LIMIT): return p.brk_end
|
||
old_map: uint64 = (p.brk_end + uint64(0xFFF)) & ~uint64(0xFFF)
|
||
new_map: uint64 = (addr + uint64(0xFFF)) & ~uint64(0xFFF)
|
||
if new_map > old_map:
|
||
asm.cli()
|
||
va: uint64 = old_map
|
||
while va < new_map:
|
||
if _vma_overlap(p, va, uint64(4096)) != 0:
|
||
_unmap_pages(p, old_map, va, 1)
|
||
asm.sti()
|
||
return p.brk_end
|
||
bphys: rawptr = mm.alloc_page()
|
||
if bphys is None:
|
||
_unmap_pages(p, old_map, va, 1)
|
||
asm.sti()
|
||
return p.brk_end
|
||
string.memset(bphys, 0, 4096)
|
||
paging.MapPage(va, uint64(bphys),
|
||
paging.PTE_PRESENT | paging.PTE_WRITABLE | paging.PTE_USER)
|
||
va = va + uint64(4096)
|
||
asm.sti()
|
||
elif new_map < old_map:
|
||
_unmap_pages(p, new_map, old_map, 1)
|
||
p.brk_end = addr
|
||
return p.brk_end
|
||
|
||
def MREMAP(old_addr: uint64, old_size: uint64, new_size: uint64, flags: uint64, new_addr: uint64) -> uint64:
|
||
p: process.Process* = _current_proc()
|
||
if old_size == 0 or new_size == 0: return abi.MAP_FAILED
|
||
if (old_addr & uint64(0xFFF)) != 0: return abi.MAP_FAILED
|
||
old_len: uint64 = (old_size + uint64(0xFFF)) & ~uint64(0xFFF)
|
||
new_len: uint64 = (new_size + uint64(0xFFF)) & ~uint64(0xFFF)
|
||
if old_addr < uint64(0x10000) or old_addr + old_len > uint64(USER_VA_LIMIT):
|
||
return abi.MAP_FAILED
|
||
if _vma_find(p, old_addr) < 0: return abi.MAP_FAILED
|
||
if new_len <= old_len:
|
||
if new_len < old_len:
|
||
_vma_unmap_range(p, old_addr + new_len, old_len - new_len)
|
||
return old_addr
|
||
if (flags & uint64(abi.MREMAP_MAYMOVE)) == 0:
|
||
return abi.MAP_FAILED
|
||
mflags: uint64 = uint64(abi.MAP_PRIVATE | abi.MAP_ANONYMOUS)
|
||
hint: uint64 = 0
|
||
if (flags & uint64(abi.MREMAP_FIXED)) != 0:
|
||
if new_addr < old_addr + old_len and old_addr < new_addr + new_len:
|
||
return abi.MAP_FAILED
|
||
mflags = mflags | uint64(abi.MAP_FIXED)
|
||
hint = new_addr
|
||
nv: uint64 = MMAP(hint, new_len, uint64(abi.PROT_READ | abi.PROT_WRITE),
|
||
mflags, MMAP_FD_ANON, 0)
|
||
if nv == abi.MAP_FAILED: return abi.MAP_FAILED
|
||
string.memcpy(rawptr(nv), rawptr(old_addr), old_len)
|
||
_vma_unmap_range(p, old_addr, old_len)
|
||
return nv
|
||
|
||
def MADVISE(addr: uint64, length: uint64, advice: uint32) -> int:
|
||
# No paging/swapping policy yet; accept the hint.
|
||
return 0
|
||
|
||
# ============================================================================
|
||
# P4/P5/P6: TLS, futex, time, signals, iovec, pipes, reboot
|
||
# ============================================================================
|
||
|
||
# ---- arch_prctl ----
|
||
def ARCH_PRCTL(code: int, addr: uint64) -> int:
|
||
if code == abi.ARCH_SET_FS:
|
||
sched.set_fs_base(addr)
|
||
return 0
|
||
if code == abi.ARCH_GET_FS:
|
||
if addr == 0: return _err(abi.EFAULT)
|
||
dst: uint64* = uint64*(addr)
|
||
dst[0] = sched.get_fs_base()
|
||
return 0
|
||
return _err(abi.ENOSYS)
|
||
|
||
# ---- futex: polling implementation (no sleep queue yet) ----
|
||
def FUTEX(uaddr: uint64, op: uint32, val: uint32, timeout: rawptr) -> int:
|
||
if uaddr == 0: return _err(abi.EFAULT)
|
||
cmd: int = int(op) & 0x7F
|
||
u: uint32* = uint32*(uaddr)
|
||
if cmd == 0 or cmd == 9:
|
||
if u[0] != val: return _err(abi.EAGAIN)
|
||
while u[0] == val:
|
||
sched.Scheduler.yield_now()
|
||
return 0
|
||
if cmd == 1 or cmd == 10:
|
||
return 0
|
||
return _err(abi.ENOSYS)
|
||
|
||
# ---- time ----
|
||
def _rtc_epoch() -> uint64:
|
||
y: int = int(rtc.rtc_get_year())
|
||
mo: int = int(rtc.rtc_get_month())
|
||
d: int = int(rtc.rtc_get_day())
|
||
h: int = int(rtc.rtc_get_hours())
|
||
mi: int = int(rtc.rtc_get_minutes())
|
||
s: int = int(rtc.rtc_get_seconds())
|
||
yy: int = y
|
||
if mo <= 2: yy = yy - 1
|
||
era: int = yy / 400
|
||
yoe: int = yy - era * 400
|
||
mp: int = (mo + 9) % 12
|
||
doy: int = (153 * mp + 2) / 5 + d - 1
|
||
doe: int = yoe * 365 + yoe / 4 - yoe / 100 + doy
|
||
days: int = era * 146097 + doe - 719468
|
||
return uint64(days * 86400 + h * 3600 + mi * 60 + s)
|
||
|
||
def CLOCK_GETTIME(clockid: int, ts_ptr: rawptr) -> int:
|
||
if ts_ptr is None: return _err(abi.EFAULT)
|
||
ts: abi.linux_timespec* = ts_ptr
|
||
if clockid == 0:
|
||
ts.tv_sec = _rtc_epoch()
|
||
ts.tv_nsec = uint64(0)
|
||
else:
|
||
t: uint64 = timer.timer_get_ticks()
|
||
ts.tv_sec = t / uint64(1000)
|
||
ts.tv_nsec = (t % uint64(1000)) * uint64(1000000)
|
||
return 0
|
||
|
||
def CLOCK_GETRES(clockid: int, ts_ptr: rawptr) -> int:
|
||
if ts_ptr is None: return _err(abi.EFAULT)
|
||
ts: abi.linux_timespec* = ts_ptr
|
||
ts.tv_sec = uint64(0)
|
||
ts.tv_nsec = uint64(1000000)
|
||
return 0
|
||
|
||
def GETTIMEOFDAY(tv_ptr: rawptr, tz_ptr: rawptr) -> int:
|
||
if tv_ptr is not None:
|
||
tv: uint64* = uint64*(tv_ptr)
|
||
tv[0] = _rtc_epoch()
|
||
tv[1] = uint64(0)
|
||
if tz_ptr is not None:
|
||
string.memset(tz_ptr, 0, 8)
|
||
return 0
|
||
|
||
def NANOSLEEP(req_ptr: rawptr, rem_ptr: rawptr) -> int:
|
||
if req_ptr is None: return _err(abi.EFAULT)
|
||
ts: abi.linux_timespec* = req_ptr
|
||
ms: uint64 = ts.tv_sec * uint64(1000) + ts.tv_nsec / uint64(1000000)
|
||
if ms > uint64(0x7FFFFFFF): ms = uint64(0x7FFFFFFF)
|
||
if ms > 0:
|
||
timer.timer_msleep(uint32(ms))
|
||
return 0
|
||
|
||
# ---- signals: registration + delivery on syscall return ----
|
||
define SIG_DFL = 0
|
||
define SIG_IGN = 1
|
||
define SA_NODEFER = 0x40000000
|
||
|
||
def RT_SIGACTION(sig: int, act_ptr: rawptr, old_ptr: rawptr, sigsetsize: uint32) -> int:
|
||
if sig < 1 or sig > 64: return _err(abi.EINVAL)
|
||
p: process.Process* = _current_proc()
|
||
if old_ptr is not None:
|
||
o: uint64* = uint64*(old_ptr)
|
||
o[0] = p.sig_handler[sig]
|
||
o[1] = p.sig_actflags[sig]
|
||
o[2] = p.sig_restorer[sig]
|
||
o[3] = uint64(0)
|
||
if act_ptr is not None:
|
||
a: uint64* = uint64*(act_ptr)
|
||
p.sig_handler[sig] = a[0]
|
||
p.sig_actflags[sig] = a[1]
|
||
p.sig_restorer[sig] = a[2]
|
||
return 0
|
||
|
||
def RT_SIGPROCMASK(how: int, set_ptr: rawptr, old_ptr: rawptr, sigsetsize: uint32) -> int:
|
||
p: process.Process* = _current_proc()
|
||
if old_ptr is not None:
|
||
o: uint64* = uint64*(old_ptr)
|
||
o[0] = p.sig_mask
|
||
if set_ptr is not None:
|
||
s: uint64* = uint64*(set_ptr)
|
||
m: uint64 = s[0]
|
||
if how == 0:
|
||
p.sig_mask = p.sig_mask | m
|
||
elif how == 1:
|
||
p.sig_mask = p.sig_mask & ~m
|
||
elif how == 2:
|
||
p.sig_mask = m
|
||
else:
|
||
return _err(abi.EINVAL)
|
||
return 0
|
||
|
||
def KILL(pid: int, sig: int) -> int:
|
||
if sig < 0 or sig > 64: return _err(abi.EINVAL)
|
||
if sig == 0: return 0
|
||
target: process.Process* = None
|
||
if pid == 0 or pid == process.current():
|
||
target = _current_proc()
|
||
elif pid > 0:
|
||
target = process.ProcessManager.get_process(pid)
|
||
if target is None: return _err(abi.ESRCH)
|
||
target.sig_pending = target.sig_pending | (uint64(1) << uint64(sig - 1))
|
||
return 0
|
||
|
||
def RT_SIGRETURN() -> int:
|
||
f: uint64* = uint64*(sched.get_syscall_frame())
|
||
sf: uint64 = f[15] - uint64(8)
|
||
w: uint64* = uint64*(sf)
|
||
i: int = 0
|
||
while i < 12:
|
||
f[i] = w[1 + i]
|
||
i += 1
|
||
f[13] = w[13]
|
||
f[12] = w[14]
|
||
f[15] = w[15]
|
||
p: process.Process* = _current_proc()
|
||
p.sig_mask = w[18]
|
||
return int(w[16])
|
||
|
||
# Deliver one pending signal by rewriting the syscall return frame.
|
||
# Returns 1 if a handler was entered (the syscall must return 0 in RAX).
|
||
def _deliver_signals(ret: uint64) -> int:
|
||
p: process.Process* = _current_proc()
|
||
avail: uint64 = p.sig_pending & ~p.sig_mask
|
||
# SIGKILL(9) / SIGSTOP(19) cannot be blocked
|
||
avail = avail | (p.sig_pending & (uint64(1) << uint64(8)))
|
||
avail = avail | (p.sig_pending & (uint64(1) << uint64(18)))
|
||
if avail == 0: return 0
|
||
sig: int = 1
|
||
while sig <= 64:
|
||
if (avail & (uint64(1) << uint64(sig - 1))) != 0: break
|
||
sig += 1
|
||
if sig > 64: return 0
|
||
bit: uint64 = uint64(1) << uint64(sig - 1)
|
||
p.sig_pending = p.sig_pending & ~bit
|
||
h: uint64 = p.sig_handler[sig]
|
||
if h == uint64(SIG_IGN):
|
||
return _deliver_signals(ret)
|
||
if h == uint64(SIG_DFL):
|
||
if sig == 1 or sig == 2 or sig == 3 or sig == 4 or sig == 5 or sig == 6 or sig == 7 or sig == 8 or sig == 9 or sig == 11 or sig == 13 or sig == 14 or sig == 15:
|
||
p.exit_code = 128 + sig
|
||
sched.Scheduler.exit_process(p.pid)
|
||
return _deliver_signals(ret)
|
||
rest: uint64 = p.sig_restorer[sig]
|
||
if rest == 0:
|
||
return _deliver_signals(ret)
|
||
f: uint64* = uint64*(sched.get_syscall_frame())
|
||
user_rsp: uint64 = f[15]
|
||
user_rip: uint64 = f[13]
|
||
user_flags: uint64 = f[12]
|
||
# The handler is entered with RSP == sf and [sf] == restorer, exactly like a
|
||
# normal call: rsp % 16 == 8 at function entry. rt_sigreturn then runs with
|
||
# rsp == sf + 8 and recovers sf as (user_rsp - 8).
|
||
sf: uint64 = ((user_rsp - uint64(320)) & ~uint64(0xF)) - uint64(8)
|
||
w: uint64* = uint64*(sf)
|
||
w[0] = rest
|
||
i: int = 0
|
||
while i < 12:
|
||
w[1 + i] = f[i]
|
||
i += 1
|
||
w[13] = user_rip
|
||
w[14] = user_flags
|
||
w[15] = user_rsp
|
||
w[16] = ret
|
||
w[17] = uint64(sig)
|
||
w[18] = p.sig_mask
|
||
si: uint64 = sf + uint64(152)
|
||
string.memset(rawptr(si), 0, 128)
|
||
si32: uint32* = uint32*(si)
|
||
si32[0] = uint32(sig) # si_signo
|
||
si32[1] = uint32(0) # si_errno
|
||
si32[2] = uint32(0) # si_code
|
||
# handler(sig, &siginfo, &ucontext)
|
||
f[8] = uint64(sig)
|
||
f[9] = si
|
||
f[10] = sf + uint64(8)
|
||
f[13] = h
|
||
# Enter the handler with [sf] = restorer (normal call-frame shape).
|
||
f[15] = sf
|
||
if (p.sig_actflags[sig] & uint64(SA_NODEFER)) == 0:
|
||
p.sig_mask = p.sig_mask | bit
|
||
return 1
|
||
|
||
# ---- iovec ----
|
||
define IOV_MAX = 1024
|
||
|
||
def READV(fd: int, iov_ptr: rawptr, iovcnt: int) -> int:
|
||
if iov_ptr is None or iovcnt < 0 or iovcnt > IOV_MAX: return _err(abi.EINVAL)
|
||
iov: abi.iovec* = iov_ptr
|
||
total: int = 0
|
||
i: int = 0
|
||
while i < iovcnt:
|
||
want: uint32 = 0xFFFFFFFF
|
||
if iov[i].len < uint64(0xFFFFFFFF): want = uint32(iov[i].len)
|
||
n: int = READ(fd, iov[i].base, want)
|
||
if n < 0:
|
||
if total > 0: return total
|
||
return n
|
||
total = total + n
|
||
if uint64(n) < iov[i].len: break
|
||
i += 1
|
||
return total
|
||
|
||
def WRITEV(fd: int, iov_ptr: rawptr, iovcnt: int) -> int:
|
||
if iov_ptr is None or iovcnt < 0 or iovcnt > IOV_MAX: return _err(abi.EINVAL)
|
||
iov: abi.iovec* = iov_ptr
|
||
total: int = 0
|
||
i: int = 0
|
||
while i < iovcnt:
|
||
want: uint32 = 0xFFFFFFFF
|
||
if iov[i].len < uint64(0xFFFFFFFF): want = uint32(iov[i].len)
|
||
n: int = WRITE(fd, iov[i].base, want)
|
||
if n < 0:
|
||
if total > 0: return total
|
||
return n
|
||
total = total + n
|
||
if uint64(n) < iov[i].len: break
|
||
i += 1
|
||
return total
|
||
|
||
def POLL(fds: rawptr, nfds: uint64, timeout: int) -> int:
|
||
return 0
|
||
|
||
# ---- pipes ----
|
||
define MAX_PIPES = 8
|
||
define PIPE_CAP = 4096
|
||
|
||
_pipe_buf: [MAX_PIPES]rawptr
|
||
_pipe_head: [MAX_PIPES]uint32
|
||
_pipe_tail: [MAX_PIPES]uint32
|
||
_pipe_cnt: [MAX_PIPES]uint32
|
||
_pipe_rd: [MAX_PIPES]int
|
||
_pipe_wr: [MAX_PIPES]int
|
||
_pipe_used: [MAX_PIPES]uint8
|
||
|
||
def _pipe_alloc() -> int:
|
||
for i in range(MAX_PIPES):
|
||
if _pipe_used[i] == 0:
|
||
b: rawptr = mm.malloc(uint64(PIPE_CAP))
|
||
if b is None: return -1
|
||
_pipe_buf[i] = b
|
||
_pipe_head[i] = 0
|
||
_pipe_tail[i] = 0
|
||
_pipe_cnt[i] = 0
|
||
_pipe_rd[i] = 0
|
||
_pipe_wr[i] = 0
|
||
_pipe_used[i] = 1
|
||
return i
|
||
return -1
|
||
|
||
def PIPE2(fds_ptr: rawptr, flags: uint32) -> int:
|
||
if fds_ptr is None: return _err(abi.EFAULT)
|
||
p: process.Process* = _current_proc()
|
||
id: int = _pipe_alloc()
|
||
if id < 0: return _err(abi.EMFILE)
|
||
rfd: int = _fd_alloc(p)
|
||
if rfd < 0:
|
||
mm.free(_pipe_buf[id])
|
||
_pipe_used[id] = 0
|
||
return _err(abi.EMFILE)
|
||
wfd: int = _fd_alloc(p)
|
||
if wfd < 0:
|
||
_fd_free(p, rfd)
|
||
mm.free(_pipe_buf[id])
|
||
_pipe_used[id] = 0
|
||
return _err(abi.EMFILE)
|
||
p.fd_table[rfd] = rawptr(uint64(id * 2 + 1))
|
||
p.fd_kind[rfd] = process.FD_KIND_PIPE
|
||
p.fd_flags[rfd] = flags
|
||
p.fd_table[wfd] = rawptr(uint64(id * 2 + 2))
|
||
p.fd_kind[wfd] = process.FD_KIND_PIPE
|
||
p.fd_flags[wfd] = flags
|
||
_pipe_rd[id] = 1
|
||
_pipe_wr[id] = 1
|
||
fds: int* = int*(fds_ptr)
|
||
fds[0] = rfd
|
||
fds[1] = wfd
|
||
return 0
|
||
|
||
def PIPE(fds_ptr: rawptr) -> int:
|
||
return PIPE2(fds_ptr, uint32(0))
|
||
|
||
def _pipe_read(fp: rawptr, buf_ptr: rawptr, count: uint32) -> int:
|
||
id: int = (int(uint64(fp)) - 1) / 2
|
||
if id < 0 or id >= MAX_PIPES or _pipe_used[id] == 0: return _err(abi.EBADF)
|
||
while _pipe_cnt[id] == 0:
|
||
if _pipe_wr[id] == 0: return 0
|
||
sched.Scheduler.yield_now()
|
||
n: uint32 = count
|
||
if _pipe_cnt[id] < n: n = _pipe_cnt[id]
|
||
dst: uint8* = uint8*(buf_ptr)
|
||
i: uint32 = 0
|
||
while i < n:
|
||
src: uint8* = uint8*(uint64(_pipe_buf[id]) + uint64(_pipe_head[id]))
|
||
dst[i] = src[0]
|
||
_pipe_head[id] = (_pipe_head[id] + uint32(1)) % uint32(PIPE_CAP)
|
||
i += 1
|
||
_pipe_cnt[id] = _pipe_cnt[id] - n
|
||
return int(n)
|
||
|
||
def _pipe_write(fp: rawptr, buf_ptr: rawptr, count: uint32) -> int:
|
||
id: int = (int(uint64(fp)) - 1) / 2
|
||
if id < 0 or id >= MAX_PIPES or _pipe_used[id] == 0: return _err(abi.EBADF)
|
||
if _pipe_rd[id] == 0: return _err(abi.EPIPE)
|
||
while _pipe_cnt[id] >= uint32(PIPE_CAP):
|
||
if _pipe_rd[id] == 0: return _err(abi.EPIPE)
|
||
sched.Scheduler.yield_now()
|
||
n: uint32 = count
|
||
room: uint32 = uint32(PIPE_CAP) - _pipe_cnt[id]
|
||
if room < n: n = room
|
||
src: uint8* = uint8*(buf_ptr)
|
||
i: uint32 = 0
|
||
while i < n:
|
||
dst: uint8* = uint8*(uint64(_pipe_buf[id]) + uint64(_pipe_tail[id]))
|
||
dst[0] = src[i]
|
||
_pipe_tail[id] = (_pipe_tail[id] + uint32(1)) % uint32(PIPE_CAP)
|
||
i += 1
|
||
_pipe_cnt[id] = _pipe_cnt[id] + n
|
||
return int(n)
|
||
|
||
# Count one more reference to a pipe end (dup/dup2/fork inherit).
|
||
def _pipe_add_ref(fp: rawptr):
|
||
v: int = int(uint64(fp)) - 1
|
||
id: int = v / 2
|
||
end: int = v % 2
|
||
if id < 0 or id >= MAX_PIPES: return
|
||
if end == 0: _pipe_rd[id] = _pipe_rd[id] + 1
|
||
else: _pipe_wr[id] = _pipe_wr[id] + 1
|
||
|
||
def _pipe_close(fp: rawptr) -> int:
|
||
v: int = int(uint64(fp)) - 1
|
||
id: int = v / 2
|
||
end: int = v % 2
|
||
if id < 0 or id >= MAX_PIPES: return _err(abi.EBADF)
|
||
if end == 0:
|
||
if _pipe_rd[id] > 0: _pipe_rd[id] = _pipe_rd[id] - 1
|
||
else:
|
||
if _pipe_wr[id] > 0: _pipe_wr[id] = _pipe_wr[id] - 1
|
||
if _pipe_rd[id] == 0 and _pipe_wr[id] == 0:
|
||
if _pipe_buf[id] is not None: mm.free(_pipe_buf[id])
|
||
_pipe_buf[id] = None
|
||
_pipe_used[id] = 0
|
||
return 0
|
||
|
||
# ---- Linux reboot(169) ----
|
||
define LINUX_REBOOT_MAGIC1 = 0xFEE1DEAD
|
||
define LINUX_REBOOT_MAGIC2 = 672274793
|
||
define LINUX_REBOOT_CMD_RESTART = 0x01234567
|
||
define LINUX_REBOOT_CMD_POWER_OFF = 0x4321FEDC
|
||
|
||
def REBOOT_LINUX(magic1: uint32, magic2: uint32, cmd: uint32, arg: rawptr) -> int:
|
||
if magic1 != uint32(LINUX_REBOOT_MAGIC1) or magic2 != uint32(LINUX_REBOOT_MAGIC2):
|
||
return _err(abi.EINVAL)
|
||
if cmd == uint32(LINUX_REBOOT_CMD_RESTART):
|
||
power_reboot()
|
||
return 0
|
||
if cmd == uint32(LINUX_REBOOT_CMD_POWER_OFF):
|
||
power_shutdown()
|
||
return 0
|
||
return _err(abi.EINVAL)
|
||
|
||
# ============================================================================
|
||
# Linux clone()/fork(): resume the child at the caller's post-syscall RIP
|
||
# ============================================================================
|
||
|
||
define CLONE_VM = 0x00000100
|
||
define CLONE_FS = 0x00000200
|
||
define CLONE_FILES = 0x00000400
|
||
define CLONE_SIGHAND = 0x00000800
|
||
define CLONE_THREAD = 0x00010000
|
||
define CLONE_SETTLS = 0x00080000
|
||
define CLONE_PARENT_SETTID = 0x00100000
|
||
define CLONE_CHILD_CLEARTID = 0x00200000
|
||
define CLONE_CHILD_SETTID = 0x01000000
|
||
|
||
class ForkCtx:
|
||
pid: int
|
||
rip: uint64
|
||
rsp: uint64
|
||
flags: uint64
|
||
fs: uint64
|
||
kern_stack: uint64
|
||
regs: [12]uint64
|
||
|
||
def _clone_context(flags: uint64, stack: uint64, tls: uint64) -> ForkCtx*:
|
||
fp: uint64 = sched.get_syscall_frame()
|
||
if fp == 0: return None
|
||
f: uint64* = uint64*(fp)
|
||
ctx: ForkCtx* = ForkCtx*(mm.malloc(uint64(ForkCtx.__sizeof__())))
|
||
if ctx is None: return None
|
||
string.memset(rawptr(ctx), 0, uint64(ForkCtx.__sizeof__()))
|
||
ctx.rip = f[13]
|
||
ctx.flags = f[12]
|
||
ctx.rsp = f[15]
|
||
if stack != 0: ctx.rsp = stack
|
||
ctx.fs = sched.get_fs_base()
|
||
if (flags & uint64(CLONE_SETTLS)) != 0 and tls != 0:
|
||
ctx.fs = tls
|
||
i: int = 0
|
||
while i < 12:
|
||
ctx.regs[i] = f[i]
|
||
i += 1
|
||
return ctx
|
||
|
||
def _fork_child_entry(arg: rawptr) -> int:
|
||
ctx: ForkCtx* = arg
|
||
asm.cli()
|
||
proc.switch_pid(ctx.pid)
|
||
ks: uint64 = ctx.kern_stack
|
||
gdt.set_tss_rsp0(ks)
|
||
cpu.set_kernel_rsp0(ks)
|
||
proc.set_resume(ctx.rip, ctx.rsp, ctx.flags, uint64(0), &ctx.regs[0])
|
||
mm.free(rawptr(ctx))
|
||
proc.drop_to_user_mode_ctx(ks)
|
||
return 0
|
||
|
||
def _clone_thread(flags: uint64, stack: uint64, ptid: uint64, ctid: uint64, tls: uint64) -> int:
|
||
cur: process.Process* = _current_proc()
|
||
ctx: ForkCtx* = _clone_context(flags, stack, tls)
|
||
if ctx is None: return _err(abi.EAGAIN)
|
||
ctx.pid = cur.pid
|
||
th: sched.Thread* = sched.Scheduler.create_thread(&_fork_child_entry, rawptr(ctx), cur.pid)
|
||
if th is None:
|
||
mm.free(rawptr(ctx))
|
||
return _err(abi.EAGAIN)
|
||
th.kern_stack = uint64(th.stack) + th.stack_size
|
||
ctx.kern_stack = th.kern_stack
|
||
if cur.addThread(th.tid) != 0:
|
||
th.state = sched.THREAD_DONE
|
||
mm.free(rawptr(ctx))
|
||
return _err(abi.EAGAIN)
|
||
sched.set_thread_fs_base(th.tid, ctx.fs)
|
||
if (flags & uint64(CLONE_PARENT_SETTID)) != 0 and ptid != 0:
|
||
pt: int* = int*(ptid)
|
||
pt[0] = th.tid
|
||
if (flags & uint64(CLONE_CHILD_SETTID)) != 0 and ctid != 0:
|
||
ct: int* = int*(ctid)
|
||
ct[0] = th.tid
|
||
if (flags & uint64(CLONE_CHILD_CLEARTID)) != 0 and ctid != 0:
|
||
sched.set_clear_child_tid(th.tid, ctid)
|
||
return th.tid
|
||
|
||
def _alloc_page4k() -> rawptr:
|
||
return mm.alloc_page()
|
||
|
||
# Fork/exec inherit the fd table as shared references: neither side may release
|
||
# an object the other still uses. Non-pipe objects have no cross-process
|
||
# refcount yet, so mark BOTH sides shared (they are leaked on close instead of
|
||
# being freed under a live user). Pipes do have counts and stay reclaimable.
|
||
def _copy_fds(dst: process.Process*, src: process.Process*):
|
||
i: int = 0
|
||
while i < process.PROC_MAX_FDS:
|
||
if src.fd_used[i]:
|
||
dst.fd_used[i] = 1
|
||
dst.fd_table[i] = src.fd_table[i]
|
||
dst.fd_kind[i] = src.fd_kind[i]
|
||
dst.fd_flags[i] = src.fd_flags[i]
|
||
if src.fd_kind[i] == process.FD_KIND_PIPE:
|
||
dst.fd_shared[i] = 0
|
||
_pipe_add_ref(src.fd_table[i])
|
||
else:
|
||
dst.fd_shared[i] = 1
|
||
src.fd_shared[i] = 1
|
||
i += 1
|
||
|
||
# Linux execve (placed after the pipe/fork helpers so it can inherit fds).
|
||
def EXECVE(path_ptr: str, argv: rawptr, envp: rawptr) -> int:
|
||
cur: process.Process* = _current_proc()
|
||
old_parent: int = cur.parent_pid
|
||
path: const str = path_ptr
|
||
pid: int = elf.spawn_elf(path, argv, envp)
|
||
if pid < 0:
|
||
return _err(abi.ENOENT)
|
||
np: process.Process* = process.ProcessManager.get_process(pid)
|
||
if np is not None:
|
||
np.parent_pid = old_parent
|
||
string.strcpy(&np.cwd[0], &cur.cwd[0])
|
||
np.sig_mask = cur.sig_mask
|
||
i: int = 0
|
||
while i < 65:
|
||
np.sig_handler[i] = 0
|
||
np.sig_actflags[i] = 0
|
||
np.sig_restorer[i] = 0
|
||
i += 1
|
||
np.sig_pending = 0
|
||
_copy_fds(np, cur)
|
||
cur.exit_code = 0
|
||
sched.Scheduler.exit_process(cur.pid)
|
||
return 0
|
||
|
||
def _fork_process(stack: uint64) -> int:
|
||
cur: process.Process* = _current_proc()
|
||
ctx: ForkCtx* = _clone_context(uint64(0), stack, uint64(0))
|
||
if ctx is None: return _err(abi.EAGAIN)
|
||
np: process.Process* = process.ProcessManager.create_process(&cur.name[0], None)
|
||
if np is None:
|
||
mm.free(rawptr(ctx))
|
||
return _err(abi.EAGAIN)
|
||
new_pml4: uint64 = paging.create_process_page_table()
|
||
if new_pml4 == 0:
|
||
mm.free(rawptr(ctx))
|
||
np.exit()
|
||
return _err(abi.ENOMEM)
|
||
if paging.CopyUserSpace(cur.pml4_root, new_pml4, rawptr(uint64(&_alloc_page4k))) != 0:
|
||
mm.free(rawptr(ctx))
|
||
np.exit()
|
||
return _err(abi.ENOMEM)
|
||
np.pml4_root = new_pml4
|
||
np.elf_base = cur.elf_base
|
||
np.heap_start = cur.heap_start
|
||
np.heap_size = cur.heap_size
|
||
np.brk_start = cur.brk_start
|
||
np.brk_end = cur.brk_end
|
||
np.entry = cur.entry
|
||
np.parent_pid = cur.pid
|
||
string.strcpy(&np.cwd[0], &cur.cwd[0])
|
||
i: int = 0
|
||
while i < process.MAX_VMAS:
|
||
np.vma_base[i] = cur.vma_base[i]
|
||
np.vma_size[i] = cur.vma_size[i]
|
||
np.vma_prot[i] = cur.vma_prot[i]
|
||
np.vma_kind[i] = cur.vma_kind[i]
|
||
np.vma_flags[i] = cur.vma_flags[i]
|
||
i += 1
|
||
i = 0
|
||
while i < 65:
|
||
np.sig_handler[i] = cur.sig_handler[i]
|
||
np.sig_actflags[i] = cur.sig_actflags[i]
|
||
np.sig_restorer[i] = cur.sig_restorer[i]
|
||
i += 1
|
||
np.sig_mask = cur.sig_mask
|
||
np.sig_pending = 0
|
||
# shared fd table (the child must not close the parent's objects)
|
||
_copy_fds(np, cur)
|
||
# create_process allocated a private 64KB stack; the child uses the copy.
|
||
if np.user_stack != 0:
|
||
mm.free(rawptr(np.user_stack - uint64(process.USER_STACK_SIZE)))
|
||
np.user_stack = 0
|
||
ctx.pid = np.pid
|
||
th: sched.Thread* = sched.Scheduler.create_thread(&_fork_child_entry, rawptr(ctx), np.pid)
|
||
if th is None:
|
||
mm.free(rawptr(ctx))
|
||
np.exit()
|
||
return _err(abi.EAGAIN)
|
||
th.kern_stack = np.kern_stack
|
||
ctx.kern_stack = np.kern_stack
|
||
np.addThread(th.tid)
|
||
sched.set_thread_fs_base(th.tid, ctx.fs)
|
||
return np.pid
|
||
|
||
def CLONE(flags: uint64, stack: uint64, ptid: uint64, ctid: uint64, tls: uint64) -> int:
|
||
if (flags & uint64(CLONE_VM)) != 0:
|
||
return _clone_thread(flags, stack, ptid, ctid, tls)
|
||
return _fork_process(stack)
|
||
|
||
def FORK() -> int:
|
||
return _fork_process(uint64(0))
|
||
|
||
# ============================================================================
|
||
# P0: identity / environment / random
|
||
# ============================================================================
|
||
|
||
def GETUID() -> uint64:
|
||
return uint64(0)
|
||
|
||
def GETEUID() -> uint64:
|
||
return uint64(0)
|
||
|
||
def GETGID() -> uint64:
|
||
return uint64(0)
|
||
|
||
def GETEGID() -> uint64:
|
||
return uint64(0)
|
||
|
||
def GETPPID() -> uint64:
|
||
return uint64(_current_proc().parent_pid)
|
||
|
||
def GETTID() -> uint64:
|
||
return uint64(sched.thread_current())
|
||
|
||
def SET_TID_ADDRESS(tid_ptr: rawptr) -> uint64:
|
||
# Single-address-space threads: the scheduler tid is the Linux tid.
|
||
tid: int = sched.thread_current()
|
||
if tid_ptr is not None:
|
||
sched.set_clear_child_tid(tid, uint64(tid_ptr))
|
||
return uint64(tid)
|
||
|
||
def SET_ROBUST_LIST(head: rawptr, len: uint64) -> int:
|
||
# Recorded nowhere yet; futex-based thread exit lands with P4.
|
||
return 0
|
||
|
||
def RSEQ(rseq_ptr: rawptr, len: uint32, flags: uint32) -> int:
|
||
# glibc disables rseq when this returns -ENOSYS.
|
||
return _err(abi.ENOSYS)
|
||
|
||
def UNAME(buf_ptr: rawptr) -> int:
|
||
if buf_ptr is None: return _err(abi.EFAULT)
|
||
u: abi.linux_utsname* = buf_ptr
|
||
string.memset(rawptr(u), 0, uint64(abi.SIZEOF_UTSNAME))
|
||
# Report "Linux" so scripts and build systems do not special-case us;
|
||
# the ViperOS identity stays in the version string.
|
||
string.strcpy(&u.sysname[0], "Linux")
|
||
string.strcpy(&u.nodename[0], "viperos")
|
||
string.strcpy(&u.release[0], "6.1.0-viperos")
|
||
string.strcpy(&u.version[0], "#1 SMP ViperOS")
|
||
string.strcpy(&u.machine[0], "x86_64")
|
||
string.strcpy(&u.domainname[0], "(none)")
|
||
return 0
|
||
|
||
def GETCWD(buf_ptr: rawptr, size: uint64) -> int:
|
||
if buf_ptr is None: return _err(abi.EFAULT)
|
||
p: process.Process* = _current_proc()
|
||
n: int = int(string.strlen(&p.cwd[0]))
|
||
if size < uint64(n + 1): return _err(abi.ERANGE)
|
||
dst: char* = char*(buf_ptr)
|
||
string.strcpy(dst, &p.cwd[0])
|
||
return n + 1
|
||
|
||
def CHDIR(path_ptr: str) -> int:
|
||
path: const str = path_ptr
|
||
buf: [256]char
|
||
r: int = _resolve_path(abi.AT_FDCWD, path, &buf[0])
|
||
if r != 0: return r
|
||
p: process.Process* = _current_proc()
|
||
if buf[0] == 47 and buf[1] == 0:
|
||
string.strcpy(&p.cwd[0], "/")
|
||
return 0
|
||
info: fat32_types.fileinfo
|
||
fat32.lock()
|
||
res: int = int(fat32.stat(&buf[0], &info))
|
||
fat32.unlock()
|
||
if res != 0: return _fr_errno(res)
|
||
if (int(info.attr) & 0x10) == 0:
|
||
return _err(abi.ENOTDIR)
|
||
string.strcpy(&p.cwd[0], &buf[0])
|
||
return 0
|
||
|
||
def FACCESSAT(dirfd: int, path_ptr: str, mode: uint32, flags: uint32) -> int:
|
||
path: const str = path_ptr
|
||
buf: [256]char
|
||
r: int = _resolve_path(dirfd, path, &buf[0])
|
||
if r != 0: return r
|
||
if buf[0] == 47 and buf[1] == 0: return 0
|
||
info: fat32_types.fileinfo
|
||
fat32.lock()
|
||
res: int = int(fat32.stat(&buf[0], &info))
|
||
fat32.unlock()
|
||
if res != 0: return _fr_errno(res)
|
||
return 0
|
||
|
||
def ACCESS(path_ptr: str, mode: uint32) -> int:
|
||
return FACCESSAT(abi.AT_FDCWD, path_ptr, mode, uint32(0))
|
||
|
||
def READLINKAT(dirfd: int, path_ptr: str, buf_ptr: rawptr, bufsiz: uint64) -> int:
|
||
path: const str = path_ptr
|
||
tmp: [256]char
|
||
r: int = _resolve_path(dirfd, path, &tmp[0])
|
||
if r != 0: return r
|
||
info: fat32_types.fileinfo
|
||
fat32.lock()
|
||
res: int = int(fat32.stat(&tmp[0], &info))
|
||
fat32.unlock()
|
||
if res != 0: return _fr_errno(res)
|
||
# FAT32 has no symbolic links; an existing name is simply not a link.
|
||
return _err(abi.EINVAL)
|
||
|
||
def READLINK(path_ptr: str, buf_ptr: rawptr, bufsiz: uint64) -> int:
|
||
return READLINKAT(abi.AT_FDCWD, path_ptr, buf_ptr, bufsiz)
|
||
|
||
def PRLIMIT64(pid: int, resource: uint32, new_ptr: rawptr, old_ptr: rawptr) -> int:
|
||
if pid != 0 and pid != process.current():
|
||
return _err(abi.ESRCH)
|
||
# Setting limits is accepted but not enforced yet.
|
||
if old_ptr is not None:
|
||
rl: abi.linux_rlimit* = old_ptr
|
||
cur: uint64 = abi.RLIM_INFINITY
|
||
mx: uint64 = abi.RLIM_INFINITY
|
||
if resource == uint32(abi.RLIMIT_STACK):
|
||
cur = uint64(process.USER_STACK_SIZE)
|
||
mx = uint64(process.USER_STACK_SIZE)
|
||
elif resource == uint32(abi.RLIMIT_NOFILE):
|
||
cur = uint64(process.PROC_MAX_FDS)
|
||
mx = uint64(process.PROC_MAX_FDS)
|
||
rl.cur = cur
|
||
rl.max = mx
|
||
return 0
|
||
|
||
def GETRANDOM(buf_ptr: rawptr, buflen: uint64, flags: uint32) -> int:
|
||
if buf_ptr is None: return _err(abi.EFAULT)
|
||
if buflen > uint64(0x7FFFFFFF): return _err(abi.EINVAL)
|
||
p: uint8* = uint8*(buf_ptr)
|
||
i: uint64 = 0
|
||
while i < buflen:
|
||
v: uint64 = _rng_next()
|
||
j: int = 0
|
||
while j < 8 and i < buflen:
|
||
p[i] = uint8(v & uint64(0xFF))
|
||
v = v >> 8
|
||
i += 1
|
||
j += 1
|
||
return int(buflen)
|
||
|
||
extern def syscall_entry()
|
||
|
||
def syscall_dispatch(num: uint64, arg1: uint64, arg2: uint64, arg3: uint64, arg4: uint64, arg5: uint64, arg6: uint64) -> uint64:
|
||
n: int = int(num)
|
||
if n == int(Syscall.EXIT):
|
||
return uint64(EXIT(int(arg1)))
|
||
elif n == int(Syscall.OPEN):
|
||
path_ptr: str = str(arg1)
|
||
return uint64(OPEN(path_ptr, uint32(arg2)))
|
||
elif n == int(Syscall.CLOSE):
|
||
return uint64(CLOSE(int(arg1)))
|
||
elif n == int(Syscall.READ):
|
||
buf_ptr: rawptr = rawptr(arg2)
|
||
return uint64(READ(int(arg1), buf_ptr, uint32(arg3)))
|
||
elif n == int(Syscall.WRITE):
|
||
buf_ptr: rawptr = rawptr(arg2)
|
||
return uint64(WRITE(int(arg1), buf_ptr, uint32(arg3)))
|
||
elif n == int(Syscall.IOCTL):
|
||
return uint64(IOCTL(int(arg1), uint32(arg2), rawptr(arg3)))
|
||
elif n == int(Syscall.SEEK):
|
||
return uint64(SEEK(int(arg1), arg2, uint32(arg3)))
|
||
elif n == int(Syscall.MKDIR):
|
||
path_ptr: str = str(arg1)
|
||
return uint64(MKDIR(path_ptr))
|
||
elif n == int(Syscall.REMOVE):
|
||
path_ptr: str = str(arg1)
|
||
return uint64(REMOVE(path_ptr))
|
||
elif n == int(Syscall.STAT):
|
||
path_ptr: str = str(arg1)
|
||
info_ptr: rawptr = rawptr(arg2)
|
||
return uint64(STAT(path_ptr, info_ptr))
|
||
elif n == int(Syscall.REBOOT):
|
||
power_reboot()
|
||
return uint64(0)
|
||
elif n == int(Syscall.SHUTDOWN):
|
||
power_shutdown()
|
||
return uint64(0)
|
||
elif n == int(Syscall.LOAD_SO):
|
||
path_ptr_so: rawptr = rawptr(arg1)
|
||
return LOAD_SO(path_ptr_so)
|
||
elif n == int(Syscall.SO_CALL1):
|
||
sym_ptr: rawptr = rawptr(arg1)
|
||
return uint64(SO_CALL1(sym_ptr, rawptr(arg2)))
|
||
elif n == int(Syscall.GETPID):
|
||
return GETPID()
|
||
elif n == int(Syscall.YIELD):
|
||
# sched_yield 是"主动放弃剩余时间片",必须无条件切换;try_reschedule() 只在
|
||
# 时间片到期时才切,作为 syscall 会让等待外设的进程白等(见 Scheduler.yield_now 注释)。
|
||
sched.Scheduler.yield_now()
|
||
return 0
|
||
elif n == int(Syscall.SPAWN):
|
||
return uint64(EXECVE(str(arg1), rawptr(arg2), rawptr(arg3)))
|
||
elif n == int(Syscall.SPAWN_PROC):
|
||
path_ptr_sp2: rawptr = rawptr(arg1)
|
||
return uint64(SPAWN_PROC(path_ptr_sp2, int(arg2)))
|
||
elif n == int(Syscall.WAIT4):
|
||
return uint64(WAIT4(int(arg1), rawptr(arg2), uint32(arg3), rawptr(arg4)))
|
||
elif n == int(Syscall.MMAP):
|
||
return MMAP(arg1, arg2, arg3, arg4, arg5, arg6)
|
||
elif n == int(Syscall.MUNMAP):
|
||
return uint64(MUNMAP(arg1, arg2))
|
||
elif n == int(Syscall.MPROTECT):
|
||
return uint64(MPROTECT(arg1, arg2, arg3))
|
||
elif n == int(Syscall.BRK):
|
||
return BRK(arg1)
|
||
elif n == int(Syscall.MREMAP):
|
||
return MREMAP(arg1, arg2, arg3, arg4, arg5)
|
||
elif n == int(Syscall.MADVISE):
|
||
return uint64(MADVISE(arg1, arg2, uint32(arg3)))
|
||
elif n == int(Syscall.CLONE):
|
||
return uint64(CLONE(arg1, arg2, arg3, arg4, arg5))
|
||
elif n == int(Syscall.FORK):
|
||
return uint64(FORK())
|
||
elif n == int(Syscall.THREAD_CREATE_OLD):
|
||
func_ptr: rawptr = rawptr(arg1)
|
||
arg_ptr: rawptr = rawptr(arg2)
|
||
cur_pid: int = process.current()
|
||
# app 传进来的是**用户地址**:必须用 create_user_thread(独立用户栈 + 独立内核栈、
|
||
# 走 drop_to_user_mode 进 Ring 3),不能用 create_thread 在 Ring 0 直接 call。
|
||
th: sched.Thread* = sched.Scheduler.create_user_thread(cur_pid, func_ptr, arg_ptr)
|
||
if th is None: return uint64(_err(abi.EAGAIN))
|
||
p: process.Process* = process.ProcessManager.get_process(cur_pid)
|
||
if p:
|
||
p.addThread(th.tid)
|
||
return uint64(th.tid)
|
||
elif n == int(Syscall.THREAD_EXIT):
|
||
# 用户线程显式退出(回收用户栈等资源后让出,永不返回)。
|
||
sched.Scheduler.exit_current_thread()
|
||
return 0
|
||
elif n == int(Syscall.UNAME):
|
||
return uint64(UNAME(rawptr(arg1)))
|
||
elif n == int(Syscall.GETUID):
|
||
return GETUID()
|
||
elif n == int(Syscall.GETEUID):
|
||
return GETEUID()
|
||
elif n == int(Syscall.GETGID):
|
||
return GETGID()
|
||
elif n == int(Syscall.GETEGID):
|
||
return GETEGID()
|
||
elif n == int(Syscall.GETPPID):
|
||
return GETPPID()
|
||
elif n == int(Syscall.GETTID):
|
||
return GETTID()
|
||
elif n == int(Syscall.GETCWD):
|
||
return uint64(GETCWD(rawptr(arg1), arg2))
|
||
elif n == int(Syscall.CHDIR):
|
||
return uint64(CHDIR(str(arg1)))
|
||
elif n == int(Syscall.ACCESS):
|
||
return uint64(ACCESS(str(arg1), uint32(arg2)))
|
||
elif n == int(Syscall.FACCESSAT):
|
||
return uint64(FACCESSAT(int(arg1), str(arg2), uint32(arg3), uint32(arg4)))
|
||
elif n == int(Syscall.READLINK):
|
||
return uint64(READLINK(str(arg1), rawptr(arg2), arg3))
|
||
elif n == int(Syscall.READLINKAT):
|
||
return uint64(READLINKAT(int(arg1), str(arg2), rawptr(arg3), arg4))
|
||
elif n == int(Syscall.SET_TID_ADDRESS):
|
||
return SET_TID_ADDRESS(rawptr(arg1))
|
||
elif n == int(Syscall.SET_ROBUST_LIST):
|
||
return uint64(SET_ROBUST_LIST(rawptr(arg1), arg2))
|
||
elif n == int(Syscall.RSEQ):
|
||
return uint64(RSEQ(rawptr(arg1), uint32(arg2), uint32(arg3)))
|
||
elif n == int(Syscall.PRLIMIT64):
|
||
return uint64(PRLIMIT64(int(arg1), uint32(arg2), rawptr(arg3), rawptr(arg4)))
|
||
elif n == int(Syscall.GETRANDOM):
|
||
return uint64(GETRANDOM(rawptr(arg1), arg2, uint32(arg3)))
|
||
elif n == int(Syscall.FSTAT):
|
||
return uint64(FSTAT(int(arg1), rawptr(arg2)))
|
||
elif n == int(Syscall.NEWFSTATAT):
|
||
return uint64(NEWFSTATAT(int(arg1), str(arg2), rawptr(arg3), uint32(arg4)))
|
||
elif n == int(Syscall.OPENAT):
|
||
return uint64(OPENAT(int(arg1), str(arg2), uint32(arg3), uint32(arg4)))
|
||
elif n == int(Syscall.GETDENTS64):
|
||
return uint64(GETDENTS64(int(arg1), rawptr(arg2), arg3))
|
||
elif n == int(Syscall.DUP):
|
||
return uint64(DUP(int(arg1)))
|
||
elif n == int(Syscall.DUP2):
|
||
return uint64(DUP2(int(arg1), int(arg2)))
|
||
elif n == int(Syscall.DUP3):
|
||
return uint64(DUP3(int(arg1), int(arg2), uint32(arg3)))
|
||
elif n == int(Syscall.FCNTL):
|
||
return uint64(FCNTL(int(arg1), uint32(arg2), arg3))
|
||
elif n == int(Syscall.PREAD64):
|
||
return uint64(PREAD64(int(arg1), rawptr(arg2), uint32(arg3), arg4))
|
||
elif n == int(Syscall.PWRITE64):
|
||
return uint64(PWRITE64(int(arg1), rawptr(arg2), uint32(arg3), arg4))
|
||
elif n == int(Syscall.RENAME):
|
||
return uint64(RENAME(str(arg1), str(arg2)))
|
||
elif n == int(Syscall.RMDIR):
|
||
return uint64(RMDIR(str(arg1)))
|
||
elif n == int(Syscall.UNLINKAT):
|
||
return uint64(UNLINKAT(int(arg1), str(arg2), uint32(arg3)))
|
||
elif n == int(Syscall.ARCH_PRCTL):
|
||
return uint64(ARCH_PRCTL(int(arg1), arg2))
|
||
elif n == int(Syscall.FUTEX):
|
||
return uint64(FUTEX(arg1, uint32(arg2), uint32(arg3), rawptr(arg4)))
|
||
elif n == int(Syscall.CLOCK_GETTIME):
|
||
return uint64(CLOCK_GETTIME(int(arg1), rawptr(arg2)))
|
||
elif n == int(Syscall.CLOCK_GETRES):
|
||
return uint64(CLOCK_GETRES(int(arg1), rawptr(arg2)))
|
||
elif n == int(Syscall.GETTIMEOFDAY):
|
||
return uint64(GETTIMEOFDAY(rawptr(arg1), rawptr(arg2)))
|
||
elif n == int(Syscall.NANOSLEEP):
|
||
return uint64(NANOSLEEP(rawptr(arg1), rawptr(arg2)))
|
||
elif n == int(Syscall.RT_SIGACTION):
|
||
return uint64(RT_SIGACTION(int(arg1), rawptr(arg2), rawptr(arg3), uint32(arg4)))
|
||
elif n == int(Syscall.RT_SIGPROCMASK):
|
||
return uint64(RT_SIGPROCMASK(int(arg1), rawptr(arg2), rawptr(arg3), uint32(arg4)))
|
||
elif n == int(Syscall.RT_SIGRETURN):
|
||
return uint64(RT_SIGRETURN())
|
||
elif n == int(Syscall.KILL):
|
||
return uint64(KILL(int(arg1), int(arg2)))
|
||
elif n == int(Syscall.READV):
|
||
return uint64(READV(int(arg1), rawptr(arg2), int(arg3)))
|
||
elif n == int(Syscall.WRITEV):
|
||
return uint64(WRITEV(int(arg1), rawptr(arg2), int(arg3)))
|
||
elif n == int(Syscall.POLL):
|
||
return uint64(POLL(rawptr(arg1), arg2, int(arg3)))
|
||
elif n == int(Syscall.PIPE):
|
||
return uint64(PIPE(rawptr(arg1)))
|
||
elif n == int(Syscall.PIPE2):
|
||
return uint64(PIPE2(rawptr(arg1), uint32(arg2)))
|
||
elif n == int(Syscall.REBOOT_LINUX):
|
||
return uint64(REBOOT_LINUX(uint32(arg1), uint32(arg2), uint32(arg3), rawptr(arg4)))
|
||
# Unknown/unimplemented Linux numbers return -ENOSYS (never success).
|
||
return uint64(_err(abi.ENOSYS))
|
||
|
||
# Normal C entry: dispatch, then deliver at most one pending signal by
|
||
# rewriting the saved user context. Returning 0 makes the signal handler's
|
||
# entry RAX = 0; the original syscall result is saved in the frame.
|
||
def _syscall_invoke(num: uint64, arg1: uint64, arg2: uint64, arg3: uint64, arg4: uint64, arg5: uint64, arg6: uint64) -> uint64:
|
||
sched.set_syscall_frame(_entry_frame)
|
||
r: uint64 = syscall_dispatch(num, arg1, arg2, arg3, arg4, arg5, arg6)
|
||
if _deliver_signals(r) != 0:
|
||
return uint64(0)
|
||
return r
|
||
|
||
# The compiler-provided syscall_entry calls this symbol. Naked: save the
|
||
# entry frame pointer and tail-jump into _syscall_invoke (its ret returns to
|
||
# the entry's call site).
|
||
@__attribute__(naked)
|
||
export def syscall_handler(num: uint64, arg1: uint64, arg2: uint64, arg3: uint64, arg4: uint64, arg5: uint64, arg6: uint64) -> uint64:
|
||
__asm__("mov qword ptr [rip + _entry_frame], rbp\n jmp qword ptr [rip + _handler_ptr]")
|
||
return 0
|
||
|
||
def init():
|
||
global _handler_ptr
|
||
_handler_ptr = rawptr(uint64(&_syscall_invoke))
|
||
efer: uint64 = rdmsr(IA32_EFER)
|
||
efer = efer | 1
|
||
wrmsr(IA32_EFER, efer)
|
||
star_val: uint64 = (uint64(0x08) << 32) | (uint64(0x1B) << 48)
|
||
wrmsr(IA32_STAR, star_val)
|
||
wrmsr(IA32_LSTAR, uint64(syscall_entry))
|
||
wrmsr(IA32_FMASK, 0x200)
|
||
serial.puts("[syscall] MSR init done\n")
|