diff --git a/riscemu/syscall.py b/riscemu/syscall.py index d9d2a7b..35028f4 100644 --- a/riscemu/syscall.py +++ b/riscemu/syscall.py @@ -6,8 +6,10 @@ SPDX-License-Identifier: MIT import sys from dataclasses import dataclass +from math import log2, ceil from typing import Dict, IO +from .types import BinaryDataMemorySection, MemoryFlags from .colors import FMT_SYSCALL, FMT_NONE from .types import Int32, CPU from .types.exceptions import InvalidSyscallException @@ -16,6 +18,7 @@ SYSCALLS = { 63: "read", 64: "write", 93: "exit", + 192: "mmap2", 1024: "open", 1025: "close", } @@ -27,6 +30,18 @@ dictionary and implement a method with the same name on the SyscallInterface class. """ +ADDITIONAL_SYMBOLS = { + 'MAP_PRIVATE': 1<<0, + 'MAP_SHARED': 1<<1, + 'MAP_ANON': 1<<2, + 'MAP_ANONYMOUS': 1<<2, + 'PROT_READ': 1<<0, + 'PROT_WRITE': 1<<1, +} +""" +A set of additional symbols that are used by various syscalls. +""" + OPEN_MODES = { 0: "rb", 1: "wb", @@ -55,7 +70,7 @@ class Syscall: def __repr__(self): return "Syscall(id={}, name={})".format(self.id, self.name) - def ret(self, code): + def ret(self, code: int | Int32): self.cpu.regs.set("a0", Int32(code)) @@ -65,7 +80,11 @@ def get_syscall_symbols(): :return: dictionary of all syscall symbols (SCALL_ -> id) """ - return {("SCALL_" + name.upper()): num for num, name in SYSCALLS.items()} + items: Dict[str, int] = {("SCALL_" + name.upper()): num for num, name in SYSCALLS.items()} + + items.update(ADDITIONAL_SYMBOLS) + + return items class SyscallInterface: @@ -222,5 +241,53 @@ class SyscallInterface: scall.cpu.halted = True scall.cpu.exit_code = scall.cpu.regs.get("a0").value + def mmap2(self, scall: Syscall): + """ + mmap2 syscall: + + void *mmap(void *addr, size_t length, int prot, int flags, + int fd, off_t offset); + + Only supported modes: + addr = + prot = either PROT_READ or PROT_READ | PROT_WRITE + flags = MAP_PRIVATE | MAP_ANONYMOUS + fd = + off_t = + """ + addr = scall.cpu.regs.get('a0').unsigned_value + size = scall.cpu.regs.get('a1').unsigned_value + prot = scall.cpu.regs.get('a2').unsigned_value + flags = scall.cpu.regs.get('a3').unsigned_value + + # error out if prot is not 1 or 3: + # 1 = PROT_READ + # 3 = PROT_READ | PROT_WRITE + if prot != 1 and prot != 3: + return scall.ret(-1) + + # round size up to multiple of 4096 + size = 4096 * ceil(size / 4096) + section = BinaryDataMemorySection( + bytearray(size), + '.data.runtime-allocated', + None, + 'system', + base=addr, + flags=MemoryFlags(read_only=prot != 3, executable=False) + ) + + # try to insert section + if scall.cpu.mmu.load_section(section, addr != 0): + return scall.ret(section.base) + # if that failed, and we tried to force an address, + # try again at any address + elif addr != 0: + section.base = 0 + if scall.cpu.mmu.load_section(section): + return scall.ret(section.base) + # if that didn't work, return error + return scall.ret(-1) + def __repr__(self): return "{}(\n\tfiles={}\n)".format(self.__class__.__name__, self.open_files)