From b0caa63ea1c666188a11264ce8b4e6d90eb9a1e5 Mon Sep 17 00:00:00 2001 From: Luxferre Date: Thu, 18 Jun 2026 20:10:23 +0300 Subject: [PATCH] optimized some syscalls --- amach.awk | 308 +++++++++++++------------------------ rv32imac_syscall_tests.bin | Bin 1429 -> 0 bytes rv32imac_syscall_tests.s | 302 ++++++++++++++++++++++++++++++++++++ rv32imac_tests.bin | Bin 17940 -> 0 bytes 4 files changed, 413 insertions(+), 197 deletions(-) delete mode 100755 rv32imac_syscall_tests.bin create mode 100644 rv32imac_syscall_tests.s delete mode 100755 rv32imac_tests.bin diff --git a/amach.awk b/amach.awk index f554170..c007251 100644 --- a/amach.awk +++ b/amach.awk @@ -10,11 +10,7 @@ # fatal error reporting function function trapout(msg, fd) { - for(fd in FD_PATH) { - if(FD_DIRTY[fd]) { - write_fd_to_file(fd) - } - } + for(fd in FD_PATH) if(FD_DIRTY[fd]) write_fd_to_file(fd) cmd = "cat 1>&2" printf("Fatal: %s\n", msg) | cmd close(cmd) @@ -95,15 +91,15 @@ function qquote(str) { function hex_to_dec(hex, dec, i, len, c, val) { dec = 0 len = length(hex) - for (i = 1; i <= len; i++) { + 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 + 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 } @@ -114,9 +110,7 @@ function hex_to_dec(hex, dec, i, len, c, val) { function read_str(addr, c, s) { s = "" addr = uint32(addr) - while ((c = MEM[addr++]) != 0) { - s = s sprintf("%c", c) - } + while((c = MEM[addr++]) != 0) s = s sprintf("%c", c) return s } @@ -126,21 +120,13 @@ function path_exists(path) { } # Checks if path is a directory using POSIX test -d -function is_dir(path) { - return (system("test -d " qquote(path)) == 0) -} +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 - } + if(substr(pathname, 1, 1) == "/") return pathname + if(dfd == -100) return pathname + if(dfd in FD_PATH) return FD_PATH[dfd] "/" pathname return pathname } @@ -148,8 +134,8 @@ function resolve_path(dfd, pathname, path) { # 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]+$/) { + 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) @@ -160,16 +146,13 @@ function get_system_time(time_arr, cmd, line) { # Fallback if %N is not supported cmd = "date +%s" - if ((cmd | getline line) > 0) { + if((cmd | getline line) > 0) { time_arr["sec"] = int(line) - } else { - time_arr["sec"] = 0 - } + } else time_arr["sec"] = 0 close(cmd) - - if (time_arr["sec"] == LAST_SEC) { + if(time_arr["sec"] == LAST_SEC) { VIRT_NSEC += 500000 - if (VIRT_NSEC >= 1000000000) { + if(VIRT_NSEC >= 1000000000) { VIRT_NSEC = 999999999 } } else { @@ -183,7 +166,7 @@ function get_system_time(time_arr, cmd, line) { 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) { + if((cmd | getline line) > 0) { split(line, parts) mode_hex = parts[1] mode_dec = hex_to_dec(mode_hex) @@ -191,12 +174,9 @@ function fill_stat_struct(path, addr, follow, cmd, line, parts, opt, mode_hex, atime = int(parts[3]) mtime = int(parts[4]) ctime = int(parts[5]) - close(cmd) - for (i = 0; i < 104; i++) { - MEM[addr + i] = 0 - } + for(i = 0; i < 104; i++) MEM[addr + i] = 0 write_mem(addr + 0, 1, 8) write_mem(addr + 8, 1, 8) @@ -226,96 +206,80 @@ function write_fd_to_file(fd, path, chunk, i, size) { printf("") > path close(path) chunk = "" - for (i = 0; i < size; i++) { + for(i = 0; i < size; i++) { chunk = chunk sprintf("%c", FD_DATA[fd, i]) - if (length(chunk) >= 1024) { + if(length(chunk) >= 1024) { printf("%s", chunk) >> path chunk = "" } } - if (length(chunk) > 0) { - printf("%s", chunk) >> path - } + 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) - } - } + for(fd in FD_PATH) if(FD_DIRTY[fd]) write_fd_to_file(fd) exit(code) } # 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) { - if (callnum == 34) { # sys_mkdirat + if(callnum == 34) { # sys_mkdirat pathname = read_str(a1) path = resolve_path(a0, pathname) - if (path_exists(path)) { - setreg(10, -17) # -EEXIST - } else { + if(path_exists(path)) setreg(10, -17) # -EEXIST + else { status = system("mkdir " qquote(path)) setreg(10, (status == 0) ? 0 : -2) } - - } else if (callnum == 35) { # sys_unlinkat + } else if(callnum == 35) { # sys_unlinkat pathname = read_str(a1) path = resolve_path(a0, pathname) - if (!path_exists(path)) { + if(!path_exists(path)) { setreg(10, -2) # -ENOENT } else { - if (bw_and(a2, 512)) { # AT_REMOVEDIR (0x200) + if(bw_and(a2, 512)) # AT_REMOVEDIR (0x200) status = system("rmdir " qquote(path)) - } else { + else status = system("rm " qquote(path)) - } setreg(10, (status == 0) ? 0 : -1) - } - - } else if (callnum == 38 || callnum == 276) { # sys_renameat / sys_renameat2 + } + } 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 + } 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) + 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)) { + if(!path_exists(path)) { printf("") > path close(path) } } else { - if (!path_exists(path)) { + 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)) { + if(is_dir(path)) { cmd = "ls -a1 " qquote(path) count = 0 - while ((cmd | getline line) > 0) { - if (line != "") { - FD_DIRENTS[fd, count++] = line - } - } + while((cmd | getline line) > 0) + if(line != "") FD_DIRENTS[fd, count++] = line close(cmd) FD_DIRENT_COUNT[fd] = count } else { @@ -324,64 +288,51 @@ function handle_ecall(callnum, a0, a1, a2, a3, a4, a5, i, l, buf, path, pathnam is_write = (access_mode == 1 || access_mode == 2) do_trunc = (is_write && bw_and(flags, 512)) # O_TRUNC (0x200) - if (is_read && !do_trunc) { + if(is_read && !do_trunc) { cmd = "od -An -v -tu1 " qquote(path) size = 0 - while ((cmd | getline line) > 0) { + while((cmd | getline line) > 0) { split(line, arr) - for (i = 1; i in arr; i++) { + for(i = 1; i in arr; i++) FD_DATA[fd, size++] = arr[i] - } } close(cmd) FD_SIZE[fd] = size - } else if (do_trunc) { + } else if(do_trunc) { printf("") > path close(path) } } setreg(10, fd) - } else if (callnum == 57) { # sys_close + } else if(callnum == 57) { # sys_close fd = a0 - if (fd >= 3 && (fd in FD_PATH)) { - if (FD_DIRTY[fd]) { - write_fd_to_file(fd) - } + 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] - } + 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] - } - } + } 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 + } else setreg(10, -9) # -EBADF + } else if(callnum == 61) { # sys_getdents64 fd = a0 - if (!(fd in FD_DIRENT_COUNT)) { - setreg(10, -9) # -EBADF - } else { + 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]) { + 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) { + if(bytes_written + reclen > count_limit) { + if(bytes_written == 0) { setreg(10, -22) # -EINVAL return } @@ -393,143 +344,116 @@ function handle_ecall(callnum, a0, a1, a2, a3, a4, a5, i, l, buf, path, pathnam 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++) { + 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++) { + 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 + } else if(callnum == 62) { # sys_lseek fd = a0 - if (fd >= 3 && (fd in FD_PATH) && !(fd in FD_DIRENT_COUNT)) { + 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(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 { + 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 + } else setreg(10, -9) # -EBADF + } else if(callnum == 63) { # sys_read fd = a0 - if (fd == 0) { + 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)) + 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)) { + } 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++) { + 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 + } 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)]) + 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)) { + } 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++) { + 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] - } + 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 + } 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) { + if((cmd | getline line) > 0) { close(cmd) l = length(line) - if (l > a3) l = a3 - for (i = 0; i < l; i++) { + 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 + } 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] + 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) - } + } 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 + 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] + 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 + } 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 + } else if(callnum == 113 || callnum == 403) { # sys_clock_gettime / sys_clock_gettime64 get_system_time(now_time) - if (callnum == 113) { + if(callnum == 113) { write_mem(a1, now_time["sec"], 4) write_mem(a1 + 4, now_time["nsec"], 4) } else { @@ -537,24 +461,18 @@ function handle_ecall(callnum, a0, a1, a2, a3, a4, a5, i, l, buf, path, pathnam write_mem(a1 + 8, now_time["nsec"], 4) } setreg(10, 0) - - } else if (callnum == 169) { # sys_gettimeofday + } 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 + } else if(callnum == 278) { # sys_getrandom buf_addr = a0 buflen = a1 - for (i = 0; i < buflen; i++) { + 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)) - } + } else trapout(sprintf("Unimplemented environment call %d at 0x%X", callnum, pc - 4)) } # instruction type executors @@ -861,9 +779,5 @@ END { instr += 65536 * MEM[pc++] + 16777216 * MEM[pc++] amach_exec(instr) # decode and execute } - for(fd in FD_PATH) { - if(FD_DIRTY[fd]) { - write_fd_to_file(fd) - } - } + for(fd in FD_PATH) if(FD_DIRTY[fd]) write_fd_to_file(fd) } diff --git a/rv32imac_syscall_tests.bin b/rv32imac_syscall_tests.bin deleted file mode 100755 index 2488bbba6c0aba64e60aee2cb4cd505f0d98b944..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1429 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 z