diff --git a/riscemu/syscall.py b/riscemu/syscall.py index 7ac95d6..bd64f00 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', @@ -56,7 +71,7 @@ class Syscall: self.id, self.name ) - def ret(self, code): + def ret(self, code: int | Int32): self.cpu.regs.set('a0', Int32(code)) @@ -66,10 +81,14 @@ def get_syscall_symbols(): :return: dictionary of all syscall symbols (SCALL_ -> id) """ - return { + items: Dict[str, int] = { ('SCALL_' + name.upper()): num for num, name in SYSCALLS.items() } + items.update(ADDITIONAL_SYMBOLS) + + return items + class SyscallInterface: """ @@ -198,6 +217,57 @@ 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__,