implemented syscalls open, read, write, close, exit

float_support
Anton Lydike 4 years ago
parent a483db65c7
commit 5a722c8cf1

@ -0,0 +1,46 @@
# Syscalls:
Performing a syscall is quite simple:
```risc-v asm
; set syscall code:
addi a7, zero, 93 ; or SCALL_EXIT if syscall symbols are mapped
; set syscall args:
addi a0, zero, 1 ; exit with code 1
; invode syscall handler
scall
```
In order to use the global syscall symbols, run with the `--syscall-symbols` flag (not implemented yet)
## Read (63) `SCALL_READ`
* `a0`: source file descriptor
* `a1`: addr in which to read
* `a2`: number of bytes to read (at most)
* `return: a0` number of bytes read or -1
## Write (64) `SCALL_WRITE`
* `a0`: target file descriptor
* `a1`: addr from which to read
* `a2`: number of bytes to read
* `return: a0`: number of bytes written or -1
## Exit (93) `SCALL_EXIT`
* `a0`: exit code
## Open (1024) `SCALL_OPEN`
* `a0`: open mode:
- `0`: read
- `1`: write (truncate)
- `2`: read/write (no truncate)
- `3`: only create
- `4`: append
* `a1`: addr where path is stored
* `a2`: length of path
* `return: a0`: file descriptor of opened file or -1
Requires flag `--scall-fs` to be set to True
## Close (1025) `SCALL_OPEN`
* `a0`: file descriptor to close
* `return: a0`: 0 if closed correctly or -1

@ -1,6 +1,10 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import Dict, IO
import sys
from .Registers import Registers from .Registers import Registers
from .Exceptions import InvalidSyscallException from .Exceptions import InvalidSyscallException
from .helpers import *
import typing import typing
@ -8,9 +12,19 @@ if typing.TYPE_CHECKING:
from . import CPU from . import CPU
SYSCALLS = { SYSCALLS = {
63: 'read', 63: 'read',
64: 'write', 64: 'write',
93: 'exit' 93: 'exit',
1024: 'open',
1025: 'close',
}
OPEN_MODES = {
0: 'rb',
1: 'wb',
2: 'r+b',
3: 'x',
4: 'ab',
} }
@ -29,19 +43,124 @@ class Syscall:
self.id, self.name self.id, self.name
) )
def ret(self, code):
self.registers.set('a0', code)
class SyscallInterface: class SyscallInterface:
open_files: Dict[int, IO]
next_open_handle: int
def handle_syscall(self, scall: Syscall): def handle_syscall(self, scall: Syscall):
self.next_open_handle = 3
self.open_files = {
0: sys.stdin,
1: sys.stdout,
2: sys.stderr
}
if getattr(self, scall.name): if getattr(self, scall.name):
getattr(self, scall.name)(scall) getattr(self, scall.name)(scall)
else: else:
raise InvalidSyscallException(scall) raise InvalidSyscallException(scall)
def read(self, scall: Syscall): def read(self, scall: Syscall):
pass """
read syscall (63): read from file no a0, into addr a1, at most a2 bytes
on return a0 will be the number of read bytes or -1 if an error occured
"""
fileno = scall.registers.get('a0')
addr = scall.registers.get('a1')
len = scall.registers.get('a2')
if fileno not in self.open_files:
scall.registers.set('a0', -1)
return
chars = self.open_files[fileno].read(len)
try:
data = bytearray(chars, 'ascii')
scall.cpu.mmu.write(addr, len(data), data)
return scall.ret(len(data))
except UnicodeEncodeError:
print(FMT_ERROR + '[Syscall] read: UnicodeError - invalid input "{}"'.format(chars) + FMT_NONE)
return scall.ret(-1)
def write(self, scall: Syscall): def write(self, scall: Syscall):
pass """
write syscall (64): write a2 bytes from addr a1 into fileno a0
on return a0 will hold the number of bytes written or -1 if an error occured
"""
fileno = scall.registers.get('a0')
addr = scall.registers.get('a1')
size = scall.registers.get('a2')
if fileno not in self.open_files:
return scall.ret(-1)
data = scall.cpu.mmu.read(addr, size)
if not isinstance(str, bytearray):
print(FMT_ERROR + '[Syscall] write: writing from .text region not supported.' + FMT_NONE)
return scall.ret(-1)
self.open_files[fileno].write(data.decode('ascii'))
return scall.ret(size)
def open(self, scall: Syscall):
"""
open syscall (1024): read path of a2 bytes from addr a1, in mode a0
returns the file no in a0
modes:
- 0: read
- 1: write (truncate)
- 2: read/write (no truncate)
- 3: only create
- 4: append
Requires running with flag scall-fs
"""
if not scall.cpu.conf.scall_fs:
print(FMT_ERROR + '[Syscall] open: opening files not supported without scall-fs flag!' + FMT_NONE)
return scall.ret(-1)
mode = scall.registers.get('a0')
addr = scall.registers.get('a1')
size = scall.registers.get('a2')
mode_st = OPEN_MODES.get(mode, )
if mode_st == -1:
print(FMT_ERROR + '[Syscall] open: unknown opening mode {}!'.format(mode) + FMT_NONE)
return scall.ret(-1)
path = scall.cpu.mmu.read(addr, size).decode('ascii')
fileno = self.next_open_handle
self.next_open_handle += 1
try:
self.open_files[fileno] = open(path, mode_st)
except OSError as err:
print(FMT_ERROR + '[Syscall] open: encountered error during {}!'.format(err.strerror) + FMT_NONE)
return scall.ret(-1)
print(FMT_CYAN + '[Syscall] open: opened fd {} to {}!'.format(fileno, path) + FMT_NONE)
return scall.ret(fileno)
def close(self, scall: Syscall):
"""
close syscall (1025): closes file no a0
return -1 if an error was encountered, otherwise returns 0
"""
fileno = scall.registers.get('a0')
if fileno not in self.open_files:
print(FMT_ERROR + '[Syscall] close: unknown fileno {}!'.format(fileno) + FMT_NONE)
return scall.ret(-1)
self.open_files[fileno].close()
print(FMT_CYAN + '[Syscall] close: closed fd {}!'.format(fileno) + FMT_NONE)
del self.open_files[fileno]
return scall.ret(0)
def exit(self, scall: Syscall): def exit(self, scall: Syscall):
scall.cpu.exit = True scall.cpu.exit = True

@ -43,6 +43,7 @@ def int_from_bytes(bytes, unsigned=False):
# Colors # Colors
FMT_RED = '\033[31m'
FMT_ORANGE = '\033[33m' FMT_ORANGE = '\033[33m'
FMT_GRAY = '\033[37m' FMT_GRAY = '\033[37m'
FMT_CYAN = '\033[36m' FMT_CYAN = '\033[36m'
@ -50,4 +51,6 @@ FMT_GREEN = '\033[32m'
FMT_BOLD = '\033[1m' FMT_BOLD = '\033[1m'
FMT_MAGENTA = '\033[35m' FMT_MAGENTA = '\033[35m'
FMT_NONE = '\033[0m' FMT_NONE = '\033[0m'
FMT_UNDERLINE = '\033[4m' FMT_UNDERLINE = '\033[4m'
FMT_ERROR = FMT_RED + FMT_BOLD

Loading…
Cancel
Save