implemented syscalls open, read, write, close, exit
This commit is contained in:
parent
a483db65c7
commit
5a722c8cf1
46
docs/syscalls.md
Normal file
46
docs/syscalls.md
Normal file
@ -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 typing import Dict, IO
|
||||
import sys
|
||||
|
||||
from .Registers import Registers
|
||||
from .Exceptions import InvalidSyscallException
|
||||
from .helpers import *
|
||||
|
||||
import typing
|
||||
|
||||
@ -8,9 +12,19 @@ if typing.TYPE_CHECKING:
|
||||
from . import CPU
|
||||
|
||||
SYSCALLS = {
|
||||
63: 'read',
|
||||
64: 'write',
|
||||
93: 'exit'
|
||||
63: 'read',
|
||||
64: 'write',
|
||||
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
|
||||
)
|
||||
|
||||
def ret(self, code):
|
||||
self.registers.set('a0', code)
|
||||
|
||||
class SyscallInterface:
|
||||
open_files: Dict[int, IO]
|
||||
next_open_handle: int
|
||||
|
||||
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):
|
||||
getattr(self, scall.name)(scall)
|
||||
else:
|
||||
raise InvalidSyscallException(scall)
|
||||
|
||||
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):
|
||||
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):
|
||||
scall.cpu.exit = True
|
||||
|
@ -43,6 +43,7 @@ def int_from_bytes(bytes, unsigned=False):
|
||||
|
||||
# Colors
|
||||
|
||||
FMT_RED = '\033[31m'
|
||||
FMT_ORANGE = '\033[33m'
|
||||
FMT_GRAY = '\033[37m'
|
||||
FMT_CYAN = '\033[36m'
|
||||
@ -50,4 +51,6 @@ FMT_GREEN = '\033[32m'
|
||||
FMT_BOLD = '\033[1m'
|
||||
FMT_MAGENTA = '\033[35m'
|
||||
FMT_NONE = '\033[0m'
|
||||
FMT_UNDERLINE = '\033[4m'
|
||||
FMT_UNDERLINE = '\033[4m'
|
||||
|
||||
FMT_ERROR = FMT_RED + FMT_BOLD
|
||||
|
Loading…
Reference in New Issue
Block a user