From ce40bb9a288c6480cd05c0f2adb5fbdfcbae352f Mon Sep 17 00:00:00 2001 From: Luxferre Date: Thu, 18 Jun 2026 20:15:48 +0300 Subject: [PATCH] optimized ecall handling --- README.md | 2 +- amach.awk | 312 ++++++++++++++++++++++++------------------------------ 2 files changed, 138 insertions(+), 176 deletions(-) diff --git a/README.md b/README.md index 62c4227..a323cb5 100644 --- a/README.md +++ b/README.md @@ -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, - 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, two files called `rv32imac_tests.bin` and `rv32imac_syscall_tests.bin` are 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. The project is to be considered highly experimental and not production-ready in any way. diff --git a/amach.awk b/amach.awk index c007251..bc4087b 100644 --- a/amach.awk +++ b/amach.awk @@ -224,254 +224,216 @@ function amach_exit(code, fd) { # syscall emulation -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) { +function handle_ecall(callnum, a0, a1, a2, a3, a4, a5, i, reclen, cmd, line, arr) { if(callnum == 34) { # sys_mkdirat - pathname = read_str(a1) - path = resolve_path(a0, pathname) - if(path_exists(path)) setreg(10, -17) # -EEXIST - else { - status = system("mkdir " qquote(path)) - setreg(10, (status == 0) ? 0 : -2) - } + a1 = resolve_path(a0, read_str(a1)) + if(path_exists(a1)) setreg(10, -17) # -EEXIST + else setreg(10, (system("mkdir " qquote(a1)) == 0) ? 0 : -2) } 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) + a1 = resolve_path(a0, read_str(a1)) + if(!path_exists(a1)) setreg(10, -2) # -ENOENT + else { + a2 = bw_and(a2, 512) ? "rmdir " : "rm " + setreg(10, (system(a2 qquote(a1)) == 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) + a1 = resolve_path(a0, read_str(a1)) + a3 = resolve_path(a2, read_str(a3)) + setreg(10, (system("mv " qquote(a1) " " qquote(a3)) == 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) + a1 = resolve_path(a0, read_str(a1)) + if(bw_and(a2, 64)) { # O_CREAT (0x40) + if(bw_and(a2, 128) && path_exists(a1)) { # 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 + if(!path_exists(a1)) { + printf("") > a1 + close(a1) } + } else if(!path_exists(a1)) { + 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 + a3 = NEXT_FD++ + FD_PATH[a3] = a1 + FD_FLAGS[a3] = a2 + FD_OFFSET[a3] = 0 + FD_SIZE[a3] = 0 + FD_DIRTY[a3] = 0 + if(is_dir(a1)) { + cmd = "ls -a1 " qquote(a1) + a4 = 0 while((cmd | getline line) > 0) - if(line != "") FD_DIRENTS[fd, count++] = line + if(line != "") FD_DIRENTS[a3, a4++] = line close(cmd) - FD_DIRENT_COUNT[fd] = count + FD_DIRENT_COUNT[a3] = a4 } 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 + a4 = bw_and(a2, 3) # access_mode + a5 = (a4 != 0) && bw_and(a2, 512) # do_trunc + if(a4 != 1 && !a5) { # is_read && !do_trunc + cmd = "od -An -v -tu1 " qquote(a1) + a0 = 0 # reuse a0 for size while((cmd | getline line) > 0) { split(line, arr) for(i = 1; i in arr; i++) - FD_DATA[fd, size++] = arr[i] + FD_DATA[a3, a0++] = arr[i] } close(cmd) - FD_SIZE[fd] = size - } else if(do_trunc) { - printf("") > path - close(path) + FD_SIZE[a3] = a0 + } else if(a5) { + printf("") > a1 + close(a1) } } - setreg(10, fd) - + setreg(10, a3) } 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] + if(a0 >= 3 && (a0 in FD_PATH)) { + if(FD_DIRTY[a0]) write_fd_to_file(a0) + delete FD_PATH[a0] + delete FD_FLAGS[a0] + delete FD_OFFSET[a0] + delete FD_SIZE[a0] + delete FD_DIRTY[a0] + if(a0 in FD_DIRENT_COUNT) { + for(i = 0; i < FD_DIRENT_COUNT[a0]; i++) delete FD_DIRENTS[a0, i] + delete FD_DIRENT_COUNT[a0] + } else for(i = 0; i < FD_SIZE[a0]; i++) delete FD_DATA[a0, 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 + if(!(a0 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) { + a3 = 0 # bytes_written + a4 = FD_OFFSET[a0] # idx + while(a4 < FD_DIRENT_COUNT[a0]) { + a5 = FD_DIRENTS[a0, a4] # name + reclen = int((20 + length(a5) + 7) / 8) * 8 + if(a3 + reclen > a2) { # count_limit + if(a3 == 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++ + write_mem(a1 + a3, 1, 8) + write_mem(a1 + a3 + 8, a4 + 1, 8) + write_mem(a1 + a3 + 16, reclen, 2) + write_mem(a1 + a3 + 18, is_dir(FD_PATH[a0] "/" a5) ? 4 : 8, 1) # dtype + for(i = 0; i < length(a5); i++) + MEM[a1 + a3 + 19 + i] = ord(substr(a5, i + 1, 1)) + MEM[a1 + a3 + 19 + length(a5)] = 0 + for(i = 19 + length(a5) + 1; i < reclen; i++) + MEM[a1 + a3 + i] = 0 + a3 += reclen + a4++ } - FD_OFFSET[fd] = idx - setreg(10, bytes_written) + FD_OFFSET[a0] = a4 + setreg(10, a3) } } 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 + if(a0 >= 3 && (a0 in FD_PATH) && !(a0 in FD_DIRENT_COUNT)) { + if(a2 == 0) {} # SEEK_SET, a1 is unchanged + else if(a2 == 1) a1 += FD_OFFSET[a0] + else if(a2 == 2) a1 += FD_SIZE[a0] else { setreg(10, -22) # -EINVAL return } - if(idx < 0) setreg(10, -22) # -EINVAL + if(a1 < 0) setreg(10, -22) # -EINVAL else { - FD_OFFSET[fd] = idx - setreg(10, idx) + FD_OFFSET[a0] = a1 + setreg(10, a1) } } 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) + if(a0 == 0) { + getline a3 < "/dev/tty" + a4 = length(a3); if(a4 > a2) a4 = a2 + for(i = 0; i < a4; i++) MEM[uint32(a1 + i)] = ord(substr(a3, i + 1, 1)) + setreg(10, a4) + } else if(a0 >= 3 && (a0 in FD_PATH) && !(a0 in FD_DIRENT_COUNT)) { + a3 = FD_OFFSET[a0] # idx + a4 = FD_SIZE[a0] # size + if(a3 >= a4) 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) + if(a3 + a2 > a4) a2 = a4 - a3 # count + for(i = 0; i < a2; i++) + MEM[uint32(a1 + i)] = FD_DATA[a0, a3 + i] + FD_OFFSET[a0] = a3 + a2 + setreg(10, a2) } } else setreg(10, -9) # -EBADF } else if(callnum == 64) { # sys_write - fd = a0 - if(fd == 1 || fd == 2) { + if(a0 == 1 || a0 == 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)) idx = FD_SIZE[fd] # O_APPEND (0x400) - 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 if(a0 >= 3 && (a0 in FD_PATH) && !(a0 in FD_DIRENT_COUNT)) { + a3 = bw_and(FD_FLAGS[a0], 1024) ? FD_SIZE[a0] : FD_OFFSET[a0] # idx + for(i = 0; i < a2; i++) + FD_DATA[a0, a3 + i] = MEM[uint32(a1 + i)] + FD_OFFSET[a0] = a3 + a2 + if(FD_OFFSET[a0] > FD_SIZE[a0]) FD_SIZE[a0] = FD_OFFSET[a0] + FD_DIRTY[a0] = 1 + setreg(10, a2) } 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++) + a0 = resolve_path(a0, read_str(a1)) + a1 = "readlink " qquote(a0) + if((a1 | getline line) > 0) { + close(a1) + a4 = length(line) + if(a4 > a3) a4 = a3 + for(i = 0; i < a4; i++) MEM[uint32(a2 + i)] = ord(substr(line, i + 1, 1)) - setreg(10, l) + setreg(10, a4) } else { - close(cmd) + close(a1) 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] + a1 = read_str(a1) + if(a1 == "" && bw_and(a3, 4096)) { # AT_EMPTY_PATH + if(a0 == -100) a0 = "." + else if(a0 in FD_PATH) a0 = 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 a0 = resolve_path(a0, a1) + a4 = (bw_and(a3, 256) == 0) # follow link if AT_SYMLINK_NOFOLLOW is NOT set + setreg(10, fill_stat_struct(a0, a2, a4)) } 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] + if(a0 == 0) a0 = "/dev/stdin" + else if(a0 == 1) a0 = "/dev/stdout" + else if(a0 == 2) a0 = "/dev/stderr" + else if(a0 >= 3 && (a0 in FD_PATH)) a0 = FD_PATH[a0] else { setreg(10, -9) # -EBADF return } - status = fill_stat_struct(path, a1, 1) - setreg(10, status) + setreg(10, fill_stat_struct(a0, a1, 1)) } 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) + get_system_time(arr) if(callnum == 113) { - write_mem(a1, now_time["sec"], 4) - write_mem(a1 + 4, now_time["nsec"], 4) + write_mem(a1, arr["sec"], 4) + write_mem(a1 + 4, arr["nsec"], 4) } else { - write_mem(a1, now_time["sec"], 8) - write_mem(a1 + 8, now_time["nsec"], 4) + write_mem(a1, arr["sec"], 8) + write_mem(a1 + 8, arr["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) + get_system_time(arr) + write_mem(a0, arr["sec"], 4) + write_mem(a0 + 4, int(arr["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) + for(i = 0; i < a1; i++) + MEM[uint32(a0 + i)] = int(rand() * 256) + setreg(10, a1) } else trapout(sprintf("Unimplemented environment call %d at 0x%X", callnum, pc - 4)) }