implemented a bunch of syscalls
This commit is contained in:
@@ -5,7 +5,7 @@ EMULATOR = ./amach
|
|||||||
ASFLAGS = -march=rv32imac -mabi=ilp32 -nostdlib -s -static -fno-pic -fno-pie -mno-relax
|
ASFLAGS = -march=rv32imac -mabi=ilp32 -nostdlib -s -static -fno-pic -fno-pie -mno-relax
|
||||||
LDFLAGS = -Wl,-Ttext=0x100b0 -Wl,--build-id=none
|
LDFLAGS = -Wl,-Ttext=0x100b0 -Wl,--build-id=none
|
||||||
|
|
||||||
all: rv32imac_tests.bin
|
all: rv32imac_tests.bin rv32imac_syscall_tests.bin
|
||||||
|
|
||||||
rv32imac_tests.elf: rv32imac_tests.s
|
rv32imac_tests.elf: rv32imac_tests.s
|
||||||
$(CC) $(ASFLAGS) $(LDFLAGS) rv32imac_tests.s -o rv32imac_tests.elf
|
$(CC) $(ASFLAGS) $(LDFLAGS) rv32imac_tests.s -o rv32imac_tests.elf
|
||||||
@@ -13,15 +13,25 @@ rv32imac_tests.elf: rv32imac_tests.s
|
|||||||
rv32imac_tests.bin: rv32imac_tests.elf
|
rv32imac_tests.bin: rv32imac_tests.elf
|
||||||
$(OBJCOPY) -O binary rv32imac_tests.elf rv32imac_tests.bin
|
$(OBJCOPY) -O binary rv32imac_tests.elf rv32imac_tests.bin
|
||||||
|
|
||||||
run: rv32imac_tests.bin
|
rv32imac_syscall_tests.elf: rv32imac_syscall_tests.s
|
||||||
|
$(CC) $(ASFLAGS) $(LDFLAGS) rv32imac_syscall_tests.s -o rv32imac_syscall_tests.elf
|
||||||
|
|
||||||
|
rv32imac_syscall_tests.bin: rv32imac_syscall_tests.elf
|
||||||
|
$(OBJCOPY) -O binary rv32imac_syscall_tests.elf rv32imac_syscall_tests.bin
|
||||||
|
|
||||||
|
run: rv32imac_tests.bin rv32imac_syscall_tests.bin
|
||||||
|
@echo "Running CPU instruction tests..."
|
||||||
$(EMULATOR) rv32imac_tests.bin
|
$(EMULATOR) rv32imac_tests.bin
|
||||||
|
@echo "Running syscall tests..."
|
||||||
|
ln -sf test_output_new.txt test_link.txt
|
||||||
|
$(EMULATOR) rv32imac_syscall_tests.bin
|
||||||
@echo "Testing standard ebreak..."
|
@echo "Testing standard ebreak..."
|
||||||
@echo "115 0 16 0" | awk -f amach.awk 2>&1 | grep -q "Fatal: EBREAK"
|
@echo "115 0 16 0" | awk -f amach.awk 2>&1 | grep -q "Fatal: EBREAK"
|
||||||
@echo "Testing compressed c.ebreak..."
|
@echo "Testing compressed c.ebreak..."
|
||||||
@echo "2 144" | awk -f amach.awk 2>&1 | grep -q "Fatal: EBREAK"
|
@echo "2 144" | awk -f amach.awk 2>&1 | grep -q "Fatal: EBREAK"
|
||||||
@echo "All EBREAK tests PASSED!"
|
@echo "All tests PASSED!"
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f rv32imac_tests.elf rv32imac_tests.bin
|
rm -rf rv32imac_tests.elf rv32imac_tests.bin rv32imac_syscall_tests.elf rv32imac_syscall_tests.bin test_dir test_link.txt test_output.txt test_output_new.txt
|
||||||
|
|
||||||
.PHONY: all run clean
|
.PHONY: all run clean
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ A-machine is a simple and straightforward implementation of the RV32I specificat
|
|||||||
- [amach.awk](amach.awk), the emulator core itself that supports .dec files,
|
- [amach.awk](amach.awk), the emulator core itself that supports .dec files,
|
||||||
- and [amach](amach) shell script which is a two-liner that uses POSIX `od` to convert .bin files into .dec files on the fly and then passes them to the emulator core.
|
- and [amach](amach) shell script which is a two-liner that uses POSIX `od` to convert .bin files into .dec files on the fly and then passes them to the emulator core.
|
||||||
|
|
||||||
Additionally, A-machine is shipped with a reference test suite that assumes that you have a full RISC-V toolchain installed under the `riscv64-linux-gnu-` prefix (adjustable in the `Makefile`). Just run `make run` to build and run all tests. In case you don't have any toolchain available, a file called `rv32imac_tests.bin` is also included into the repo.
|
Additionally, A-machine is shipped with a reference test suite that assumes that you have a full RISC-V toolchain installed under the `riscv64-linux-gnu-` prefix (adjustable in the `Makefile`). Just run `make run` to build and run all tests. In case you don't have any toolchain available, two files called `rv32imac_tests.bin` and `rv32imac_syscall_tests.bin` are also included into the repo.
|
||||||
|
|
||||||
The project is to be considered highly experimental and not production-ready in any way.
|
The project is to be considered highly experimental and not production-ready in any way.
|
||||||
|
|
||||||
@@ -32,12 +32,19 @@ POSIXLY_CORRECT=1 awk -f amach.awk [-v LVA=...] -- path/to/program.dec
|
|||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
Currently, A-machine only supports the **unprivileged** RV32IMAC instruction set with a few additional `ecall` syscalls: 63 (sys_read, for fd 0 only), 64 (sys_write, for fd 1 and 2 only) and 93 (sys_exit). For completeness, `fence` instruction is also supported but yields a no-op, and both `ebreak` and `c.ebreak` are supported to halt execution with a debug message.
|
Currently, A-machine only supports the **unprivileged** RV32IMAC instruction set with an extended subset of POSIX/Linux-compatible `ecall` system calls (using standard `asm-generic` numbers):
|
||||||
|
|
||||||
|
- **Process Control**: `sys_exit` (93), `sys_exit_group` (94).
|
||||||
|
- **File & Directory I/O**: `sys_openat` (56), `sys_close` (57), `sys_read` (63), `sys_write` (64), `sys_lseek` (62). Multiple file descriptors (with full path resolution, creation/truncation flags, and offset tracking) are supported for files and directories.
|
||||||
|
- **Directory Operations**: `sys_mkdirat` (34), `sys_unlinkat` (35) (including directory removal), `sys_renameat` (38) / `sys_renameat2` (276), `sys_getdents64` (61) (for listing directory contents).
|
||||||
|
- **File Metadata & Links**: `sys_newfstatat` (79), `sys_fstat` (80), `sys_readlinkat` (78).
|
||||||
|
- **System Time & Entropy**: `sys_clock_gettime` (113), `sys_clock_gettime64` (403), `sys_gettimeofday` (169), and `sys_getrandom` (278).
|
||||||
|
|
||||||
|
For completeness, `fence` instruction is also supported but yields a no-op, and both `ebreak` and `c.ebreak` are supported to halt execution with a debug message.
|
||||||
|
|
||||||
## Plans (from higher to lower priority)
|
## Plans (from higher to lower priority)
|
||||||
|
|
||||||
- Implement a simple RV32IMAC disassembler in POSIX AWK.
|
- Implement a simple RV32IMAC disassembler in POSIX AWK.
|
||||||
- Introduce more popular Linux-compatible syscalls (that make sense to implement in AWK but don't increase the overall project complexity).
|
|
||||||
- Test for RV32EMC compatibility (that the code built for the "embedded" variant runs here).
|
- Test for RV32EMC compatibility (that the code built for the "embedded" variant runs here).
|
||||||
- Implement a privileged instruction set minimum (Zicsr), interrupts and traps.
|
- Implement a privileged instruction set minimum (Zicsr), interrupts and traps.
|
||||||
- If time permits, implement a simple RV32IMAC assembler in POSIX AWK, with full pseudo-instruction support.
|
- If time permits, implement a simple RV32IMAC assembler in POSIX AWK, with full pseudo-instruction support.
|
||||||
|
|||||||
@@ -9,7 +9,12 @@
|
|||||||
# Created by Luxferre in 2026, released into the public domain
|
# Created by Luxferre in 2026, released into the public domain
|
||||||
|
|
||||||
# fatal error reporting function
|
# fatal error reporting function
|
||||||
function trapout(msg) {
|
function trapout(msg, fd) {
|
||||||
|
for(fd in FD_PATH) {
|
||||||
|
if(FD_DIRTY[fd]) {
|
||||||
|
write_fd_to_file(fd)
|
||||||
|
}
|
||||||
|
}
|
||||||
cmd = "cat 1>&2"
|
cmd = "cat 1>&2"
|
||||||
printf("Fatal: %s\n", msg) | cmd
|
printf("Fatal: %s\n", msg) | cmd
|
||||||
close(cmd)
|
close(cmd)
|
||||||
@@ -80,23 +85,476 @@ function write_mem(addr, val, bytes, i) {
|
|||||||
function read_word(addr) {return read_mem(addr, 4, 1)}
|
function read_word(addr) {return read_mem(addr, 4, 1)}
|
||||||
function write_word(addr, val) {write_mem(addr, val, 4)}
|
function write_word(addr, val) {write_mem(addr, val, 4)}
|
||||||
|
|
||||||
|
# Escapes single quotes for shell command safety
|
||||||
|
function qquote(str) {
|
||||||
|
gsub(/'/, "'\\''", str)
|
||||||
|
return "'" str "'"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Converts hex string to decimal
|
||||||
|
function hex_to_dec(hex, dec, i, len, c, val) {
|
||||||
|
dec = 0
|
||||||
|
len = length(hex)
|
||||||
|
for (i = 1; i <= len; i++) {
|
||||||
|
c = tolower(substr(hex, i, 1))
|
||||||
|
if (c ~ /[0-9]/) val = int(c)
|
||||||
|
else if (c == "a") val = 10
|
||||||
|
else if (c == "b") val = 11
|
||||||
|
else if (c == "c") val = 12
|
||||||
|
else if (c == "d") val = 13
|
||||||
|
else if (c == "e") val = 14
|
||||||
|
else if (c == "f") val = 15
|
||||||
|
else continue
|
||||||
|
dec = dec * 16 + val
|
||||||
|
}
|
||||||
|
return dec
|
||||||
|
}
|
||||||
|
|
||||||
|
# Reads a null-terminated string from memory
|
||||||
|
function read_str(addr, c, s) {
|
||||||
|
s = ""
|
||||||
|
addr = uint32(addr)
|
||||||
|
while ((c = MEM[addr++]) != 0) {
|
||||||
|
s = s sprintf("%c", c)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
# Checks if path exists using POSIX test -e or test -L
|
||||||
|
function path_exists(path) {
|
||||||
|
return (system("test -e " qquote(path)) == 0 || system("test -L " qquote(path)) == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Checks if path is a directory using POSIX test -d
|
||||||
|
function is_dir(path) {
|
||||||
|
return (system("test -d " qquote(path)) == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Resolves relative path against dfd directory
|
||||||
|
function resolve_path(dfd, pathname, path) {
|
||||||
|
if (substr(pathname, 1, 1) == "/") {
|
||||||
|
return pathname
|
||||||
|
}
|
||||||
|
if (dfd == -100) { # AT_FDCWD
|
||||||
|
return pathname
|
||||||
|
}
|
||||||
|
if (dfd in FD_PATH) {
|
||||||
|
return FD_PATH[dfd] "/" pathname
|
||||||
|
}
|
||||||
|
return pathname
|
||||||
|
}
|
||||||
|
|
||||||
|
# Populate timespec structure
|
||||||
|
# TV_SEC: 64-bit for clock_gettime64, 32-bit for clock_gettime
|
||||||
|
function get_system_time(time_arr, cmd, line) {
|
||||||
|
cmd = "date +%s%N"
|
||||||
|
if ((cmd | getline line) > 0) {
|
||||||
|
if (length(line) > 9 && line ~ /^[0-9]+$/) {
|
||||||
|
time_arr["sec"] = int(substr(line, 1, length(line) - 9))
|
||||||
|
time_arr["nsec"] = int(substr(line, length(line) - 8))
|
||||||
|
close(cmd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(cmd)
|
||||||
|
|
||||||
|
# Fallback if %N is not supported
|
||||||
|
cmd = "date +%s"
|
||||||
|
if ((cmd | getline line) > 0) {
|
||||||
|
time_arr["sec"] = int(line)
|
||||||
|
} else {
|
||||||
|
time_arr["sec"] = 0
|
||||||
|
}
|
||||||
|
close(cmd)
|
||||||
|
|
||||||
|
if (time_arr["sec"] == LAST_SEC) {
|
||||||
|
VIRT_NSEC += 500000
|
||||||
|
if (VIRT_NSEC >= 1000000000) {
|
||||||
|
VIRT_NSEC = 999999999
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LAST_SEC = time_arr["sec"]
|
||||||
|
VIRT_NSEC = 0
|
||||||
|
}
|
||||||
|
time_arr["nsec"] = VIRT_NSEC
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fills stat structure in VM memory
|
||||||
|
function fill_stat_struct(path, addr, follow, cmd, line, parts, opt, mode_hex, mode_dec, size, atime, mtime, ctime, i) {
|
||||||
|
opt = follow ? "-L " : ""
|
||||||
|
cmd = "stat " opt "-c \"%f %s %X %Y %Z\" " qquote(path)
|
||||||
|
if ((cmd | getline line) > 0) {
|
||||||
|
split(line, parts)
|
||||||
|
mode_hex = parts[1]
|
||||||
|
mode_dec = hex_to_dec(mode_hex)
|
||||||
|
size = int(parts[2])
|
||||||
|
atime = int(parts[3])
|
||||||
|
mtime = int(parts[4])
|
||||||
|
ctime = int(parts[5])
|
||||||
|
|
||||||
|
close(cmd)
|
||||||
|
|
||||||
|
for (i = 0; i < 104; i++) {
|
||||||
|
MEM[addr + i] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
write_mem(addr + 0, 1, 8)
|
||||||
|
write_mem(addr + 8, 1, 8)
|
||||||
|
write_mem(addr + 16, mode_dec, 4)
|
||||||
|
write_mem(addr + 20, 1, 4)
|
||||||
|
write_mem(addr + 24, 1000, 4)
|
||||||
|
write_mem(addr + 28, 1000, 4)
|
||||||
|
write_mem(addr + 32, 0, 8)
|
||||||
|
write_mem(addr + 48, size, 8)
|
||||||
|
write_mem(addr + 56, 4096, 4)
|
||||||
|
write_mem(addr + 64, int((size + 511) / 512), 8)
|
||||||
|
write_mem(addr + 72, atime, 4)
|
||||||
|
write_mem(addr + 80, mtime, 4)
|
||||||
|
write_mem(addr + 88, ctime, 4)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
} else {
|
||||||
|
close(cmd)
|
||||||
|
return -2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Writes file descriptor cached data back to file
|
||||||
|
function write_fd_to_file(fd, path, chunk, i, size) {
|
||||||
|
path = FD_PATH[fd]
|
||||||
|
size = FD_SIZE[fd]
|
||||||
|
printf("") > path
|
||||||
|
close(path)
|
||||||
|
chunk = ""
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
chunk = chunk sprintf("%c", FD_DATA[fd, i])
|
||||||
|
if (length(chunk) >= 1024) {
|
||||||
|
printf("%s", chunk) >> path
|
||||||
|
chunk = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (length(chunk) > 0) {
|
||||||
|
printf("%s", chunk) >> path
|
||||||
|
}
|
||||||
|
close(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function amach_exit(code, fd) {
|
||||||
|
for (fd in FD_PATH) {
|
||||||
|
if (FD_DIRTY[fd]) {
|
||||||
|
write_fd_to_file(fd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit(code)
|
||||||
|
}
|
||||||
|
|
||||||
# syscall emulation
|
# syscall emulation
|
||||||
|
|
||||||
function handle_ecall(callnum, a0, a1, a2, a3, a4, a5, i, l, buf) {
|
function handle_ecall(callnum, a0, a1, a2, a3, a4, a5, i, l, buf, path, pathname, flags, fd, count, now_time, dtype, full_path, reclen, bytes_written, idx, count_limit, buf_addr, name, status, follow, oldpath, newpath, access_mode, is_read, is_write, do_trunc, cmd, line, arr, size, buflen) {
|
||||||
if(callnum == 63) { # sys_read
|
if (callnum == 34) { # sys_mkdirat
|
||||||
if(a0 == 0) {
|
pathname = read_str(a1)
|
||||||
getline buf < "/dev/tty"
|
path = resolve_path(a0, pathname)
|
||||||
l = length(buf); if(l > a2) l = a2
|
if (path_exists(path)) {
|
||||||
for(i=0; i<l; i++) MEM[uint32(a1 + i)] = ord(substr(buf, i+1, 1))
|
setreg(10, -17) # -EEXIST
|
||||||
setreg(10, i)
|
} else {
|
||||||
}
|
status = system("mkdir " qquote(path))
|
||||||
} else if(callnum == 64) { # sys_write
|
setreg(10, (status == 0) ? 0 : -2)
|
||||||
if(a0 == 1 || a0 == 2) {
|
|
||||||
for(i=0; i<a2; i++) printf("%c", MEM[uint32(a1 + i)])
|
|
||||||
setreg(10, i)
|
|
||||||
}
|
}
|
||||||
} else if(callnum == 93) exit(a0) # sys_exit
|
|
||||||
else trapout(sprintf("Unimplemented environment call %d at 0x%X", callnum, pc-4))
|
} else if (callnum == 35) { # sys_unlinkat
|
||||||
|
pathname = read_str(a1)
|
||||||
|
path = resolve_path(a0, pathname)
|
||||||
|
if (!path_exists(path)) {
|
||||||
|
setreg(10, -2) # -ENOENT
|
||||||
|
} else {
|
||||||
|
if (bw_and(a2, 512)) { # AT_REMOVEDIR (0x200)
|
||||||
|
status = system("rmdir " qquote(path))
|
||||||
|
} else {
|
||||||
|
status = system("rm " qquote(path))
|
||||||
|
}
|
||||||
|
setreg(10, (status == 0) ? 0 : -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (callnum == 38 || callnum == 276) { # sys_renameat / sys_renameat2
|
||||||
|
oldpath = resolve_path(a0, read_str(a1))
|
||||||
|
newpath = resolve_path(a2, read_str(a3))
|
||||||
|
status = system("mv " qquote(oldpath) " " qquote(newpath))
|
||||||
|
setreg(10, (status == 0) ? 0 : -1)
|
||||||
|
|
||||||
|
} else if (callnum == 56) { # sys_openat
|
||||||
|
pathname = read_str(a1)
|
||||||
|
path = resolve_path(a0, pathname)
|
||||||
|
flags = a2
|
||||||
|
if (bw_and(flags, 64)) { # O_CREAT (0x40)
|
||||||
|
if (bw_and(flags, 128) && path_exists(path)) { # O_EXCL (0x80)
|
||||||
|
setreg(10, -17) # -EEXIST
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!path_exists(path)) {
|
||||||
|
printf("") > path
|
||||||
|
close(path)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!path_exists(path)) {
|
||||||
|
setreg(10, -2) # -ENOENT
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = NEXT_FD++
|
||||||
|
FD_PATH[fd] = path
|
||||||
|
FD_FLAGS[fd] = flags
|
||||||
|
FD_OFFSET[fd] = 0
|
||||||
|
FD_SIZE[fd] = 0
|
||||||
|
FD_DIRTY[fd] = 0
|
||||||
|
|
||||||
|
if (is_dir(path)) {
|
||||||
|
cmd = "ls -a1 " qquote(path)
|
||||||
|
count = 0
|
||||||
|
while ((cmd | getline line) > 0) {
|
||||||
|
if (line != "") {
|
||||||
|
FD_DIRENTS[fd, count++] = line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(cmd)
|
||||||
|
FD_DIRENT_COUNT[fd] = count
|
||||||
|
} else {
|
||||||
|
access_mode = bw_and(flags, 3)
|
||||||
|
is_read = (access_mode == 0 || access_mode == 2)
|
||||||
|
is_write = (access_mode == 1 || access_mode == 2)
|
||||||
|
do_trunc = (is_write && bw_and(flags, 512)) # O_TRUNC (0x200)
|
||||||
|
|
||||||
|
if (is_read && !do_trunc) {
|
||||||
|
cmd = "od -An -v -tu1 " qquote(path)
|
||||||
|
size = 0
|
||||||
|
while ((cmd | getline line) > 0) {
|
||||||
|
split(line, arr)
|
||||||
|
for (i = 1; i in arr; i++) {
|
||||||
|
FD_DATA[fd, size++] = arr[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(cmd)
|
||||||
|
FD_SIZE[fd] = size
|
||||||
|
} else if (do_trunc) {
|
||||||
|
printf("") > path
|
||||||
|
close(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setreg(10, fd)
|
||||||
|
|
||||||
|
} else if (callnum == 57) { # sys_close
|
||||||
|
fd = a0
|
||||||
|
if (fd >= 3 && (fd in FD_PATH)) {
|
||||||
|
if (FD_DIRTY[fd]) {
|
||||||
|
write_fd_to_file(fd)
|
||||||
|
}
|
||||||
|
delete FD_PATH[fd]
|
||||||
|
delete FD_FLAGS[fd]
|
||||||
|
delete FD_OFFSET[fd]
|
||||||
|
delete FD_SIZE[fd]
|
||||||
|
delete FD_DIRTY[fd]
|
||||||
|
if (fd in FD_DIRENT_COUNT) {
|
||||||
|
for (i = 0; i < FD_DIRENT_COUNT[fd]; i++) {
|
||||||
|
delete FD_DIRENTS[fd, i]
|
||||||
|
}
|
||||||
|
delete FD_DIRENT_COUNT[fd]
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < FD_SIZE[fd]; i++) {
|
||||||
|
delete FD_DATA[fd, i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setreg(10, 0)
|
||||||
|
} else {
|
||||||
|
setreg(10, -9) # -EBADF
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (callnum == 61) { # sys_getdents64
|
||||||
|
fd = a0
|
||||||
|
if (!(fd in FD_DIRENT_COUNT)) {
|
||||||
|
setreg(10, -9) # -EBADF
|
||||||
|
} else {
|
||||||
|
bytes_written = 0
|
||||||
|
idx = FD_OFFSET[fd]
|
||||||
|
count_limit = a2
|
||||||
|
buf_addr = a1
|
||||||
|
while (idx < FD_DIRENT_COUNT[fd]) {
|
||||||
|
name = FD_DIRENTS[fd, idx]
|
||||||
|
reclen = int((20 + length(name) + 7) / 8) * 8
|
||||||
|
if (bytes_written + reclen > count_limit) {
|
||||||
|
if (bytes_written == 0) {
|
||||||
|
setreg(10, -22) # -EINVAL
|
||||||
|
return
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
write_mem(buf_addr + bytes_written, 1, 8)
|
||||||
|
write_mem(buf_addr + bytes_written + 8, idx + 1, 8)
|
||||||
|
write_mem(buf_addr + bytes_written + 16, reclen, 2)
|
||||||
|
full_path = FD_PATH[fd] "/" name
|
||||||
|
dtype = is_dir(full_path) ? 4 : 8
|
||||||
|
write_mem(buf_addr + bytes_written + 18, dtype, 1)
|
||||||
|
for (i = 0; i < length(name); i++) {
|
||||||
|
MEM[buf_addr + bytes_written + 19 + i] = ord(substr(name, i + 1, 1))
|
||||||
|
}
|
||||||
|
MEM[buf_addr + bytes_written + 19 + length(name)] = 0
|
||||||
|
for (i = 19 + length(name) + 1; i < reclen; i++) {
|
||||||
|
MEM[buf_addr + bytes_written + i] = 0
|
||||||
|
}
|
||||||
|
bytes_written += reclen
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
FD_OFFSET[fd] = idx
|
||||||
|
setreg(10, bytes_written)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (callnum == 62) { # sys_lseek
|
||||||
|
fd = a0
|
||||||
|
if (fd >= 3 && (fd in FD_PATH) && !(fd in FD_DIRENT_COUNT)) {
|
||||||
|
idx = FD_OFFSET[fd]
|
||||||
|
size = FD_SIZE[fd]
|
||||||
|
if (a2 == 0) idx = a1 # SEEK_SET
|
||||||
|
else if (a2 == 1) idx = idx + a1 # SEEK_CUR
|
||||||
|
else if (a2 == 2) idx = size + a1 # SEEK_END
|
||||||
|
else {
|
||||||
|
setreg(10, -22) # -EINVAL
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (idx < 0) {
|
||||||
|
setreg(10, -22) # -EINVAL
|
||||||
|
} else {
|
||||||
|
FD_OFFSET[fd] = idx
|
||||||
|
setreg(10, idx)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setreg(10, -9) # -EBADF
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (callnum == 63) { # sys_read
|
||||||
|
fd = a0
|
||||||
|
if (fd == 0) {
|
||||||
|
getline buf < "/dev/tty"
|
||||||
|
l = length(buf); if (l > a2) l = a2
|
||||||
|
for (i = 0; i < l; i++) MEM[uint32(a1 + i)] = ord(substr(buf, i + 1, 1))
|
||||||
|
setreg(10, l)
|
||||||
|
} else if (fd >= 3 && (fd in FD_PATH) && !(fd in FD_DIRENT_COUNT)) {
|
||||||
|
idx = FD_OFFSET[fd]
|
||||||
|
size = FD_SIZE[fd]
|
||||||
|
count = a2
|
||||||
|
if (idx >= size) {
|
||||||
|
setreg(10, 0)
|
||||||
|
} else {
|
||||||
|
if (idx + count > size) count = size - idx
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
MEM[uint32(a1 + i)] = FD_DATA[fd, idx + i]
|
||||||
|
}
|
||||||
|
FD_OFFSET[fd] = idx + count
|
||||||
|
setreg(10, count)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setreg(10, -9) # -EBADF
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (callnum == 64) { # sys_write
|
||||||
|
fd = a0
|
||||||
|
if (fd == 1 || fd == 2) {
|
||||||
|
for (i = 0; i < a2; i++) printf("%c", MEM[uint32(a1 + i)])
|
||||||
|
setreg(10, a2)
|
||||||
|
} else if (fd >= 3 && (fd in FD_PATH) && !(fd in FD_DIRENT_COUNT)) {
|
||||||
|
idx = FD_OFFSET[fd]
|
||||||
|
count = a2
|
||||||
|
if (bw_and(FD_FLAGS[fd], 1024)) { # O_APPEND (0x400)
|
||||||
|
idx = FD_SIZE[fd]
|
||||||
|
}
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
FD_DATA[fd, idx + i] = MEM[uint32(a1 + i)]
|
||||||
|
}
|
||||||
|
FD_OFFSET[fd] = idx + count
|
||||||
|
if (FD_OFFSET[fd] > FD_SIZE[fd]) {
|
||||||
|
FD_SIZE[fd] = FD_OFFSET[fd]
|
||||||
|
}
|
||||||
|
FD_DIRTY[fd] = 1
|
||||||
|
setreg(10, count)
|
||||||
|
} else {
|
||||||
|
setreg(10, -9) # -EBADF
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (callnum == 78) { # sys_readlinkat
|
||||||
|
path = resolve_path(a0, read_str(a1))
|
||||||
|
cmd = "readlink " qquote(path)
|
||||||
|
if ((cmd | getline line) > 0) {
|
||||||
|
close(cmd)
|
||||||
|
l = length(line)
|
||||||
|
if (l > a3) l = a3
|
||||||
|
for (i = 0; i < l; i++) {
|
||||||
|
MEM[uint32(a2 + i)] = ord(substr(line, i + 1, 1))
|
||||||
|
}
|
||||||
|
setreg(10, l)
|
||||||
|
} else {
|
||||||
|
close(cmd)
|
||||||
|
setreg(10, -22) # -EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (callnum == 79) { # sys_newfstatat
|
||||||
|
pathname = read_str(a1)
|
||||||
|
flags = a3
|
||||||
|
if (pathname == "" && bw_and(flags, 4096)) { # AT_EMPTY_PATH
|
||||||
|
if (a0 == -100) path = "."
|
||||||
|
else if (a0 in FD_PATH) path = FD_PATH[a0]
|
||||||
|
else {
|
||||||
|
setreg(10, -9) # -EBADF
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
path = resolve_path(a0, pathname)
|
||||||
|
}
|
||||||
|
follow = (bw_and(flags, 256) == 0) # follow link if AT_SYMLINK_NOFOLLOW (0x100) is NOT set
|
||||||
|
status = fill_stat_struct(path, a2, follow)
|
||||||
|
setreg(10, status)
|
||||||
|
|
||||||
|
} else if (callnum == 80) { # sys_fstat
|
||||||
|
fd = a0
|
||||||
|
if (fd == 0) path = "/dev/stdin"
|
||||||
|
else if (fd == 1) path = "/dev/stdout"
|
||||||
|
else if (fd == 2) path = "/dev/stderr"
|
||||||
|
else if (fd >= 3 && (fd in FD_PATH)) path = FD_PATH[fd]
|
||||||
|
else {
|
||||||
|
setreg(10, -9) # -EBADF
|
||||||
|
return
|
||||||
|
}
|
||||||
|
status = fill_stat_struct(path, a1, 1)
|
||||||
|
setreg(10, status)
|
||||||
|
|
||||||
|
} else if (callnum == 93 || callnum == 94) { # sys_exit / sys_exit_group
|
||||||
|
amach_exit(a0)
|
||||||
|
|
||||||
|
} else if (callnum == 113 || callnum == 403) { # sys_clock_gettime / sys_clock_gettime64
|
||||||
|
get_system_time(now_time)
|
||||||
|
if (callnum == 113) {
|
||||||
|
write_mem(a1, now_time["sec"], 4)
|
||||||
|
write_mem(a1 + 4, now_time["nsec"], 4)
|
||||||
|
} else {
|
||||||
|
write_mem(a1, now_time["sec"], 8)
|
||||||
|
write_mem(a1 + 8, now_time["nsec"], 4)
|
||||||
|
}
|
||||||
|
setreg(10, 0)
|
||||||
|
|
||||||
|
} else if (callnum == 169) { # sys_gettimeofday
|
||||||
|
get_system_time(now_time)
|
||||||
|
write_mem(a0, now_time["sec"], 4)
|
||||||
|
write_mem(a0 + 4, int(now_time["nsec"] / 1000), 4)
|
||||||
|
setreg(10, 0)
|
||||||
|
|
||||||
|
} else if (callnum == 278) { # sys_getrandom
|
||||||
|
buf_addr = a0
|
||||||
|
buflen = a1
|
||||||
|
for (i = 0; i < buflen; i++) {
|
||||||
|
MEM[uint32(buf_addr + i)] = int(rand() * 256)
|
||||||
|
}
|
||||||
|
setreg(10, buflen)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
trapout(sprintf("Unimplemented environment call %d at 0x%X", callnum, pc - 4))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# instruction type executors
|
# instruction type executors
|
||||||
@@ -384,6 +842,10 @@ BEGIN {
|
|||||||
RES_ADDR = -1 # reserved address for atomic ops
|
RES_ADDR = -1 # reserved address for atomic ops
|
||||||
LVA = int(LVA)
|
LVA = int(LVA)
|
||||||
pc = LVA
|
pc = LVA
|
||||||
|
NEXT_FD = 3
|
||||||
|
LAST_SEC = -1
|
||||||
|
VIRT_NSEC = 0
|
||||||
|
srand()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Decimal memory collection section
|
# Decimal memory collection section
|
||||||
@@ -399,4 +861,9 @@ END {
|
|||||||
instr += 65536 * MEM[pc++] + 16777216 * MEM[pc++]
|
instr += 65536 * MEM[pc++] + 16777216 * MEM[pc++]
|
||||||
amach_exec(instr) # decode and execute
|
amach_exec(instr) # decode and execute
|
||||||
}
|
}
|
||||||
|
for(fd in FD_PATH) {
|
||||||
|
if(FD_DIRTY[fd]) {
|
||||||
|
write_fd_to_file(fd)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
BIN
Binary file not shown.
Reference in New Issue
Block a user