commit 074dbafb1fd0553fd2e83ff789a6d4b8ee5c71a5 from: Benjamin Stürz date: Sun Feb 18 02:26:23 2024 UTC initial commit commit - /dev/null commit + 074dbafb1fd0553fd2e83ff789a6d4b8ee5c71a5 blob - /dev/null blob + b4943239cdbae64207c5335fedf98807b744e273 (mode 644) Binary files /dev/null and .Makefile.swp differ blob - /dev/null blob + 33c1fb532c831eddaf2d07ce83f3a43d2de493ca (mode 644) Binary files /dev/null and .rvemu.c.swp differ blob - /dev/null blob + e1198275f127c7dbff1e756da68980c4b937adf7 (mode 644) Binary files /dev/null and .test.S.swp differ blob - /dev/null blob + e1dcf57e9f918953e13413b837526d8c5992b7ff (mode 644) --- /dev/null +++ Makefile @@ -0,0 +1,19 @@ +.POSIX: + +CROSS = riscv32-unknown-linux-musl +CFLAGS = -fno-PIC -O3 +LDFLAGS = -s -Wl,--image-base,0x78000000 -no-pie -static -lpthread -lutil + +all: rvemu test.elf + +clean: + rm -f rvemu test.o test.elf *.core + +run: rvemu test.elf + ./rvemu test.elf + +test.o: test.S + ${CROSS}-as -o test.o test.S + +test.elf: test.o test.ld + ${CROSS}-ld -s -T test.ld -o test.elf test.o blob - /dev/null blob + a11261750e131eaf4e04ca1386ecc82b41631c7b (mode 644) --- /dev/null +++ rvemu.c @@ -0,0 +1,1443 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; + +typedef unsigned int uint; + +#define DEBUG 0 + +#if DEBUG +# define eprintf(...) fprintf (stderr, __VA_ARGS__) +#else +# define eprintf(...) +#endif + +struct cpu { + u32 regs[31]; + u32 pc; +}; + +static u32 cpu_get (const struct cpu *cpu, u8 reg) +{ + return reg == 0 ? 0 : cpu->regs[reg - 1]; +} + +static void cpu_set (struct cpu *cpu, u8 reg, u32 val) +{ + if (reg != 0) + cpu->regs[reg - 1] = val; +} + +static u32 cpu_fetch (struct cpu *cpu) +{ + const u32 i = *(const u32 *)(size_t)cpu->pc; + //eprintf ("%08x: %08x\n", cpu->pc, i); + cpu->pc += 4; + return i; +} + +static int map_errno (int err) +{ + const int errnos[] = { + [EPERM] = 1, + [ENOENT] = 2, + [ESRCH] = 3, + [EINTR] = 4, + [EIO] = 5, + [ENXIO] = 6, + [E2BIG] = 7, + [ENOEXEC] = 8, + [EBADF] = 9, + [ECHILD] = 10, + [EAGAIN] = 11, + [ENOMEM] = 12, + [EACCES] = 13, + [EFAULT] = 14, + [ENOTBLK] = 15, + [EBUSY] = 16, + [EEXIST] = 17, + [EXDEV] = 18, + [ENODEV] = 19, + [ENOTDIR] = 20, + [EISDIR] = 21, + [EINVAL] = 22, + [ENFILE] = 23, + [EMFILE] = 24, + [ENOTTY] = 25, + [ETXTBSY] = 26, + [EFBIG] = 27, + [ENOSPC] = 28, + [ESPIPE] = 29, + [EROFS] = 30, + [EMLINK] = 31, + [EPIPE] = 32, + [EDOM] = 33, + [ERANGE] = 34, + [EDEADLK] = 35, + [ENAMETOOLONG] = 36, + [ENOLCK] = 37, + [ENOSYS] = 38, + }; + return errnos[err]; +} + +static int map (int x) { + return x < 0 ? -map_errno (errno) : x; +} + +/* +struct linux_stat { + u64 dev; + u64 ino; + u32 mode; + u32 nlink; + u32 uid; + u32 gid; + u64 rdev; + u64 __pad1; + u32 size; + u32 blksize; + u32 __pad2; + u32 blocks; + u32 atime; + u32 atime_ns; + u32 mtime; + u32 mtime_ns; + u32 ctime; + u32 ctime_ns; + u32 __unused4; + u32 __unused5; +}; +*/ + +struct linux_stat64 { + u64 dev; + u64 ino; + u32 mode; + u32 nlink; + u32 uid; + u32 gid; + u64 rdev; + u64 __pad1; + u64 size; + u32 blksize; + u32 __pad2; + u64 blocks; + u32 atime; + u32 atime_ns; + u32 mtime; + u32 mtime_ns; + u32 ctime; + u32 ctime_ns; + u32 __unused4; + u32 __unused5; +}; + +struct linux_utsname { + char sysname[65]; + char nodename[65]; + char release[65]; + char version[65]; + char machine[65]; +}; + +static void stat_to_linux_stat (struct linux_stat64 *lst, const struct stat *st) +{ + memset (lst, 0, sizeof (*lst)); + lst->dev = st->st_dev; + lst->ino = st->st_dev; + lst->mode = st->st_dev; + lst->nlink = st->st_dev; + lst->uid = st->st_dev; + lst->gid = st->st_dev; + lst->rdev = st->st_dev; + lst->size = st->st_dev; + lst->blksize = st->st_dev; + lst->blocks = st->st_dev; + lst->atime = st->st_atim.tv_sec; + lst->atime_ns = st->st_atim.tv_nsec; + lst->mtime = st->st_mtim.tv_sec; + lst->mtime_ns = st->st_mtim.tv_nsec; + lst->ctime = st->st_ctim.tv_sec; + lst->ctime_ns = st->st_ctim.tv_nsec; +} + +static void utsname_to_linux_utsname (struct linux_utsname *lun, const struct utsname *un) +{ + memset (lun, 0, sizeof (*lun)); + strlcpy (lun->sysname, un->sysname, 65); + strlcpy (lun->nodename, un->nodename, 65); + strlcpy (lun->release, un->release, 65); + strlcpy (lun->version, un->version, 65); + strlcpy (lun->machine, un->machine, 65); +} + +static int enosys (const char *sys) +{ + warnx ("unimplemented syscall: %s", sys); + return -map_errno (ENOSYS); +} + +static int my_execve (const char *path, char **argv, char **envp) +{ + return -enosys ("execve"); +} + +#define ptr(T, x) ((T *)(size_t)(x)) +#define str(x) ptr (const char, x) +static void ecall (struct cpu *cpu) +{ + struct linux_stat64 lst; + struct linux_utsname lun; + struct stat st; + struct utsname un; + void *ptr; + const u32 a0 = cpu_get (cpu, 10); + const u32 a1 = cpu_get (cpu, 11); + const u32 a2 = cpu_get (cpu, 12); + const u32 a3 = cpu_get (cpu, 13); + const u32 a4 = cpu_get (cpu, 14); + const u32 a5 = cpu_get (cpu, 15); + const u32 a7 = cpu_get (cpu, 17); + u32 ret; + switch (a7) { + case 17: // getcwd + ret = enosys ("getcwd"); + break; + case 23: // dup + ret = map (dup ((int)a0)); + break; + case 24: // dup3 + ret = map (dup3 ((int)a0, (int)a1, (int)a2)); + break; + case 25: // fcntl + ret = enosys ("fcntl"); + break; + case 29: // ioctl + ret = enosys ("ioctl"); + break; + case 32: // flock + ret = map (flock ((int)a0, (int)a1)); + break; + case 33: // mknodat + ret = map (mknodat ((int)a0, str (a1), (mode_t)a2, (dev_t)a3)); + + case 34: // mkdirat + ret = map (mkdirat ((int)a0, str (a1), (mode_t)a2)); + break; + case 35: // unlinkat + ret = map (unlinkat ((int)a0, str (a1), (int)a2)); + break; + case 36: // symlinkat + ret = map (symlinkat (str (a0), (int)a1, str (a2))); + break; + case 37: // linkat + ret = map (linkat ((int)a0, str (a1), (int)a2, str (a3), (int)a4)); + break; + case 38: // renameat + ret = map (renameat ((int)a0, str (a1), (int)a2, str (a3))); + break; + case 39: // umount + ret = enosys ("umount"); + break; + case 40: // mount + ret = enosys ("mount"); + break; + case 41: // pivot_root + ret = enosys ("pivot_root"); + break; + case 42: // nfsservctl + ret = enosys ("nfsservctl"); + break; + case 43: // statfs + ret = enosys ("statfs"); + break; + case 44: // fstatfs + ret = enosys ("fstatfs"); + break; + case 45: // truncate + ret = map (truncate (str (a0), (off_t)a1)); + break; + break; + case 46: // ftruncate + ret = map (ftruncate ((int)a0, (off_t)a1)); + break; + case 47: // fallocate + ret = enosys ("fallocate"); + break; + case 48: // faccessat + ret = map (faccessat ((int)a0, str (a1), (int)a2, (int)a3)); + break; + case 49: // chdir + ret = map (chdir (str (a0))); + break; + case 50: // fchdir + ret = map (fchdir ((int)a0)); + break; + case 51: // chroot + ret = map (chroot (str (a0))); + break; + case 52: // fchmod + ret = map (fchmod ((int)a0, (mode_t)a1)); + break; + case 53: // fchmodat + ret = map (fchmodat ((int)a0, str (a1), (mode_t)a2, (int)a3)); + break; + case 54: // fchownat + ret = map (fchownat ((int)a0, str (a1), (uid_t)a2, (gid_t)a3, (int)a4)); + break; + case 55: // fchown + ret = map (fchown ((int)a0, (uid_t)a1, (gid_t)a2)); + break; + case 56: // openat + ret = map (openat ((int)a0, str (a1), (int)a2, (int)a3)); + break; + case 57: // close + ret = map (close ((int)a0)); + break; + case 58: // vhangup + ret = enosys ("vhangup"); + break; + case 59: // pipe2 + ret = map (pipe2 (ptr (int, a0), (int)a1)); + break; + case 60: // quotactl + ret = enosys ("quotactl"); + break; + case 61: // getdents64 + ret = enosys ("getdents64"); + break; + case 62: // lseek + ret = map (lseek ((int)a0, (off_t)a1, (int)a2)); + break; + case 63: // read + ret = map (read ((int)a0, ptr (void, a1), (size_t)a2)); + break; + case 64: // write + ret = map (write ((int)a0, ptr (const void, a1), (size_t)a2)); + break; + case 65: // readv + ret = enosys ("readv"); + break; + case 66: // writev + ret = enosys ("writev"); + break; + case 67: // pread + ret = map (pread ((int)a0, ptr (void, a1), (size_t)a2, (off_t)a3)); + break; + case 68: // pwrite + ret = map (pwrite ((int)a0, ptr (const void, a1), (size_t)a2, (off_t)a3)); + break; + case 69: // preadv + ret = enosys ("preadv"); + break; + case 70: // pwritev + ret = enosys ("pwritev"); + break; + case 71: // pwritev + ret = enosys ("sendfile"); + break; + case 72: // pselect6 + ret = enosys ("pselect6"); + break; + case 73: // ppoll + ret = enosys ("ppoll"); + break; + case 74: // signalfd4 + ret = enosys ("signalfd4"); + break; + case 75: // vmsplice + ret = enosys ("vmsplice"); + break; + case 76: // splice + ret = enosys ("splice"); + break; + case 77: // tee + ret = enosys ("tee"); + break; + case 78: // readlinkat + ret = map (readlinkat ((int)a0, str (a1), ptr (char, a2), (size_t)a3)); + break; + case 79: // fstatat + ret = map (fstatat ((int)a0, str (a1), &st, (int)a3)); + stat_to_linux_stat (&lst, &st); + memcpy (ptr (void, a2), &lst, sizeof (lst)); + break; + case 80: // fstat + ret = map (fstat ((int)a0, &st)); + stat_to_linux_stat (&lst, &st); + memcpy (ptr (void, a1), &lst, sizeof (lst)); + break; + case 81: // sync + sync (); + ret = 0; + break; + case 82: // fsync + ret = map (fsync ((int)a0)); + break; + case 83: // fdatasync + ret = map (fdatasync ((int)a0)); + break; + case 84: // sync_file_range + ret = enosys ("sync_file_range"); + break; + case 85: // timerfd_create + ret = enosys ("timerfd_create"); + break; + case 86: // timerfd_settime + ret = enosys ("timerfd_settime"); + break; + case 87: // timerfd_gettime + ret = enosys ("timerfd_gettime"); + break; + case 88: // utimensat + ret = enosys ("utimensat"); + break; + case 89: // acct + ret = acct (str (a0)); + break; + case 90: // capget + ret = enosys ("capget"); + break; + case 91: // capset + ret = enosys ("capset"); + break; + case 92: // personality + ret = enosys ("personality"); + break; + case 93: // exit + exit (a0); + ret = -1; + break; + case 94: // exit_group + ret = enosys ("exit_group"); + break; + case 95: // waitid + ret = enosys ("waitid"); + break; + case 96: // set_tid_address + ret = enosys ("set_tid_address"); + break; + case 97: // unshare + ret = enosys ("unshare"); + break; + case 98: // futex + ret = enosys ("futex"); + break; + case 99: // set_robust_limit + ret = enosys ("set_robust_limit"); + break; + case 100: // get_robust_limit + ret = enosys ("get_robust_limit"); + break; + case 101: // nanosleep + ret = enosys ("nanosleep"); + break; + case 102: // gettimer + ret = enosys ("gettimer"); + break; + case 103: // settimer + ret = enosys ("settimer"); + break; + case 104: // kexec_load + ret = enosys ("kexec_load"); + break; + case 105: // kexec_init_module + ret = enosys ("kexec_init_module"); + break; + case 106: // kexec_delete_module + ret = enosys ("kexec_delete_module"); + break; + case 107: // timer_create + ret = enosys ("timer_create"); + break; + case 108: // timer_gettime + ret = enosys ("timer_gettime"); + break; + case 109: // timer_getoverrun + ret = enosys ("timer_getoverrun"); + break; + case 110: // timer_settime + ret = enosys ("timer_settime"); + break; + case 111: // timer_delete + ret = enosys ("timer_delete"); + break; + case 112: // clock_settime + ret = enosys ("clock_settime"); + break; + case 113: // clock_gettime + ret = enosys ("clock_gettime"); + break; + case 114: // clock_getres + ret = enosys ("clock_getres"); + break; + case 115: // clock_nanosleep + ret = enosys ("clock_nanosleep"); + break; + case 116: // syslog + ret = enosys ("syslog"); + break; + case 117: // ptrace + ret = enosys ("ptrace"); + break; + case 118: // sched_setparam + ret = enosys ("sched_setparam"); + break; + case 119: // sched_setscheduler + ret = enosys ("sched_setscheduler"); + break; + case 120: // sched_getscheduler + ret = enosys ("sched_getscheduler"); + break; + case 121: // sched_getparam + ret = enosys ("sched_getparam"); + break; + case 122: // sched_setaffinity + ret = enosys ("sched_setaffinity"); + break; + case 123: // sched_getaffinity + ret = enosys ("sched_getaffinity"); + break; + case 124: // sched_yield + ret = map (sched_yield ()); + break; + case 125: // sched_get_priority_max + ret = map (sched_get_priority_max ((int)a0)); + break; + case 126: // sched_get_priority_min + ret = map (sched_get_priority_min ((int)a0)); + break; + case 127: // sched_rr_get_interval + ret = enosys ("sched_rr_get_interval"); + break; + case 128: // restart_syscall + ret = enosys ("restart_syscall"); + break; + case 129: // kill + ret = map (kill ((pid_t)a0, (int)a1)); + break; + case 130: // tkill + ret = enosys ("tkill"); + break; + case 131: // tgkill + ret = enosys ("tgkill"); + break; + case 132: // signalstack + ret = enosys ("signalstack"); + break; + case 133: // sigaction + ret = enosys ("sigaction"); + break; + case 134: // sigaction + ret = enosys ("sigaction"); + break; + case 135: // rt_sigprocmask + ret = enosys ("rt_sigprocmask"); + break; + case 136: // sigpending + ret = enosys ("sigpending"); + break; + case 137: // sigtimedwait + ret = enosys ("sigtimedwait"); + break; + case 138: // sigqueueinfo + ret = enosys ("sigqueueinfo"); + break; + case 139: // sigreturn + ret = enosys ("sigreturn"); + break; + case 140: // setpriority + ret = map (setpriority ((int)a0, (id_t)a1, (int)a2)); + break; + case 141: // getpriority + ret = map (getpriority ((int)a0, (id_t)a1)); + break; + case 142: // reboot + ret = enosys ("reboot"); + break; + case 143: // setregid + ret = map (setregid ((gid_t)a0, (gid_t)a1)); + break; + case 144: // setgid + ret = map (setgid ((gid_t)a0)); + break; + case 145: // setreuid + ret = map (setreuid ((uid_t)a0, (uid_t)a1)); + break; + case 146: // setuid + ret = map (setuid ((uid_t)a0)); + break; + case 147: // setresuid + ret = map (setresuid ((uid_t)a0, (uid_t)a1, (uid_t)a2)); + break; + case 148: // getresuid + ret = map (getresuid (ptr (uid_t, a0), ptr (uid_t, a1), ptr (uid_t, a2))); + break; + case 149: // setresgid + ret = map (setresgid ((gid_t)a0, (gid_t)a1, (gid_t)a2)); + break; + case 150: // getresgid + ret = map (getresgid (ptr (gid_t, a0), ptr (gid_t, a1), ptr (gid_t, a2))); + break; + case 151: // setfsuid + ret = enosys ("setfsuid"); + break; + case 152: // setfsgid + ret = enosys ("setfsgid"); + break; + case 153: // times + ret = enosys ("times"); + break; + case 154: // setpgid + ret = map (setpgid ((pid_t)a0, (pid_t)a1)); + break; + case 155: // getpgid + ret = map (getpgid ((pid_t)a0)); + break; + case 156: // getsid + ret = map (getsid ((pid_t)a0)); + break; + case 158: // setsid + ret = map (setsid ()); + break; + case 159: // setgroups + ret = enosys ("setgroups"); + break; + case 160: // uname + ret = map (uname (&un)); + utsname_to_linux_utsname (&lun, &un); + memcpy (ptr (void, a0), &lun, sizeof (lun)); + break; + case 161: // sethostname + ret = enosys ("sethostname"); + break; + case 162: // setdomainname + ret = enosys ("setdomainname"); + break; + case 163: // getrlimit + ret = enosys ("getrlimit"); + break; + case 164: // setrlimit + ret = enosys ("setrlimit"); + break; + case 165: // getrusage + ret = enosys ("getrusage"); + break; + case 166: // umask + ret = map (umask ((mode_t)a0)); + break; + case 167: // prctl + ret = enosys ("prctl"); + break; + case 168: // getcpu + ret = enosys ("getcpu"); + break; + case 169: // gettimeofday + ret = enosys ("gettimeofday"); + break; + case 170: // settimeofday + ret = enosys ("settimeofday"); + break; + case 171: // adjtimex + ret = enosys ("adjtimex"); + break; + case 172: // getpid + ret = map (getpid ()); + break; + case 174: // getuid + ret = map (getuid ()); + break; + case 175: // geteuid + ret = map (geteuid ()); + break; + case 176: // getgid + ret = map (getgid ()); + break; + case 177: // getegid + ret = map (getegid ()); + break; + case 178: // gettid + ret = enosys ("gettid"); + break; + case 179: // sysinfo + ret = enosys ("sysinfo"); + break; + case 180: + case 181: + case 182: + case 183: + case 184: + case 185: + ret = enosys ("mq_*"); + break; + case 186: // msgget + ret = map (msgget ((key_t)a0, (int)a1)); + break; + case 187: // msgctl + ret = enosys ("msgctl"); + break; + case 188: // msgrcv + ret = map (msgrcv ((int)a0, ptr (void, a1), (size_t)a2, (long)a3, (int)a4)); + break; + case 189: // msgsnd + ret = map (msgsnd ((int)a0, ptr (const void, a1), (size_t)a2, (int)a3)); + break; + case 190: // semget + ret = map (semget ((key_t)a0, (int)a1, (int)a2)); + break; + case 191: // semctl + ret = enosys ("semctl"); + break; + case 192: // semtimedop + ret = enosys ("semtimedop"); + break; + case 193: // semop + ret = enosys ("semop"); + break; + case 194: + case 195: + case 196: + case 197: + ret = enosys ("shm*"); + break; + case 198: + case 199: + case 200: + case 201: + case 202: + case 203: + case 204: + case 205: + case 206: + case 207: + case 208: + case 209: + case 210: + case 211: + case 212: + ret = enosys ("socket api"); + break; + case 213: // readahead + ret = enosys ("readahead"); + break; + case 214: // brk + ret = map (brk ((void *)(size_t)a0)); + break; + case 215: // munmap + ret = map (munmap (ptr (void, a0), (size_t)a1)); + break; + case 216: // mremap + ret = enosys ("mremap"); + break; + case 217: // add_key + ret = enosys ("add_key"); + break; + case 218: // request_key + ret = enosys ("request_key"); + break; + case 219: // keyctl + ret = enosys ("keyctl"); + break; + case 220: // clone + ret = enosys ("clone"); + break; + case 221: // execve + ret = my_execve (str (a0), ptr (char *, a1), ptr (char *, a2)); + break; + case 222: // mmap + ptr = mmap (ptr (void, a0), (size_t)a1, (int)a2, (int)a3, (int)a4, (off_t)a5); + if (ptr == NULL) { + ret = -map_errno (errno); + } else if ((size_t)ptr > INT32_MAX) { + ret = -map_errno (EINVAL); + } else { + ret = (size_t)ptr; + } + break; + case 223: // fadvise64 + ret = enosys ("fadvise64"); + break; + case 224: // swapon + ret = enosys ("swapon"); + break; + case 225: // swapoff + ret = enosys ("swapoff"); + break; + case 226: // mprotect + ret = map (mprotect (ptr (void, a0), (size_t)a1, (int)a2)); + break; + case 227: // msync + ret = map (msync (ptr (void, a0), (size_t)a1, (int)a2)); + break; + case 228: // mlock + ret = map (mlock (ptr (void, a0), (size_t)a1)); + break; + case 229: // munlock + ret = map (munlock (ptr (void, a0), (size_t)a1)); + break; + case 230: // mlockall + ret = map (mlockall ((int)a0)); + break; + case 231: // munlockall + ret = map (munlockall ()); + break; + case 232: // mincore + ret = enosys ("mincore"); + break; + case 233: // madvise + ret = map (madvise (ptr (void, a0), (size_t)a1, (int)a2)); + break; + case 234: // remap_file_pages + ret = enosys ("remap_file_pages"); + break; + case 235: // mbind + ret = enosys ("mbind"); + break; + case 236: // get_mempolicy + ret = enosys ("get_mempolicy"); + break; + case 237: // set_mempolicy + ret = enosys ("set_mempolicy"); + break; + case 238: // migrate_pages + ret = enosys ("migrate_pages"); + break; + case 239: // move_pages + ret = enosys ("move_pages"); + break; + case 240: // rt_tgsigqueueinfo + ret = enosys ("rt_tgsigqueueinfo"); + break; + case 241: // perf_event_open + ret = enosys ("perf_event_open"); + break; + case 242: // accept4 + ret = enosys ("accept4"); + break; + case 243: // recvmsg + ret = enosys ("recvmsg"); + break; + case 244: + case 245: + case 246: + case 247: + case 248: + case 249: + case 250: + case 251: + case 252: + case 253: + case 254: + case 255: + case 256: + case 257: + case 258: + case 259: + ret = enosys ("arch-specific"); + break; + case 260: + ret = enosys ("wait4"); + break; + case 261: // prlimit64 + ret = enosys ("prlimit64"); + break; + case 262: // fanotify_init + ret = enosys ("fanotify_init"); + break; + case 263: // fanotify_mark + ret = enosys ("fanotify_mark"); + break; + case 264: // name_to_handle_at + ret = enosys ("name_to_handle_at"); + break; + case 265: // open_by_handle_at + ret = enosys ("open_by_handle_at"); + break; + case 266: // clock_adjtime + ret = enosys ("clock_adjtime"); + break; + case 267: // syncfs + ret = enosys ("syncfs"); + break; + case 268: // setns + ret = enosys ("setns"); + break; + case 269: // sendmsg + ret = enosys ("sendmsg"); + break; + case 270: // process_vm_readv + ret = enosys ("process_vm_readv"); + break; + case 271: // process_vm_writev + ret = enosys ("process_vm_writev"); + break; + case 272: // kcmp + ret = enosys ("kcmp"); + break; + case 273: // finit_module + ret = enosys ("finit_module"); + break; + case 274: // sched_setattr + ret = enosys ("sched_setattr"); + break; + case 275: // sched_getattr + ret = enosys ("sched_getattr"); + break; + case 276: // renameat2 + ret = enosys ("renameat2"); + break; + case 277: // seccomp + ret = enosys ("seccomp"); + break; + case 278: // getrandom + ret = enosys ("getrandom"); + break; + case 279: // memfd_create + ret = enosys ("seccomp"); + break; + case 280: // bpf + ret = enosys ("bpf"); + break; + case 281: // execveat + ret = enosys ("execveat"); + break; + case 282: // userfaultfd + ret = enosys ("usefaultfd"); + break; + case 283: // membarrier + ret = enosys ("membarrier"); + break; + case 284: // mlock2 + ret = enosys ("mlock2"); + break; + case 285: // copy_file_range + ret = enosys ("copy_file_range"); + break; + case 286: // preadv2 + ret = enosys ("preadv2"); + break; + case 287: // pwritev2 + ret = enosys ("pwritev2"); + break; + case 288: // pkey_mprotect + ret = enosys ("pkey_mprotect"); + break; + case 289: // pkey_alloc + ret = enosys ("pkey_alloc"); + break; + case 290: // pkey_free + ret = enosys ("pkey_free"); + break; + case 291: // statx + ret = enosys ("statx"); + break; + case 292: // io_pgetevents + ret = enosys ("io_pgetevents"); + break; + case 293: // rseq + ret = enosys ("rseq"); + break; + case 294: // kexec_file_load + ret = enosys ("kexec_file_load"); + break; + case 1024: // open + ret = map (open ((const char *)(size_t)a0, (int)a1, (int)a2)); + break; + case 1025: // link + ret = map (link (str (a0), str (a1))); + break; + case 1026: // unlink + ret = map (unlink (str (a0))); + break; + case 1030: // mkdir + ret = map (mkdir (str (a0), (mode_t)a1)); + break; + case 1033: // access + ret = map (access (str (a0), (int)a1)); + break; + case 1038: // stat + ret = map (stat (str (a0), &st)); + stat_to_linux_stat (&lst, &st); + memcpy (ptr (void, a1), &lst, sizeof (lst)); + break; + case 1039: // lstat + ret = map (lstat (str (a0), &st)); + stat_to_linux_stat (&lst, &st); + memcpy (ptr (void, a1), &lst, sizeof (lst)); + break; + case 1062: // time + ret = enosys ("time"); + break; + case 2011: // getmainvars + ret = enosys ("getmainvars"); + break; + case 2048: + fprintf (stderr, "DEBUG: %u\n", a0); + ret = 0; + break; + default: + warnx ("%08x: unimplemented syscall %u", cpu->pc - 4, a7); + ret = -map_errno (ENOSYS); + break; + } + cpu_set (cpu, 10, ret); +} + +static void cpu_exec (struct cpu *cpu, u32 instr) +{ + const char *name; + const u8 rd = (instr >> 7) & 0x1f; + const u8 funct3 = (instr >> 12) & 0x7; + const u8 rs1 = (instr >> 15) & 0x1f; + const u8 rs2 = (instr >> 20) & 0x1f; + const u8 funct7 = instr >> 25; + const u32 imm_i = (i32)instr >> 20; + const u32 imm_s = ((i32)instr >> 25 << 5) | ((instr >> 7) & 0x1f); + const u32 imm_b = (instr >> 31 << 12) + | (((instr >> 25) & 0x3f) << 5) + | (((instr >> 8) & 0xf) << 1) + | (((instr >> 7) & 1) << 11); + const u32 imm_u = instr & 0xfffff000; + const u32 imm_j = ((i32)instr >> 31 << 20) + | (((instr >> 21) & 0x3ff) << 1) + | (((instr >> 20) & 1) << 11) + | (((instr >> 12) & 0xff) << 12); + + u32 a, b, c; + switch (instr & 0x7f) { + case 0b0110111: // lui rd, uimm + eprintf ("lui x%u, %u\n", (uint)rd, imm_u); + cpu_set (cpu, rd, imm_u); + break; + case 0b0010111: // auipc rd, uimm + eprintf ("auipc x%u, %u\n", (uint)rd, imm_u); + cpu_set (cpu, rd, cpu->pc + imm_u - 4); + break; + case 0b1101111: // jal rd, jimm + eprintf ("jal x%u, %d\n", (uint)rd, (int)imm_j); + cpu_set (cpu, rd, cpu->pc); + cpu->pc += imm_j - 4; + break; + case 0b1100111: // jalr rd, rs1, iimm + if (funct3 != 0b000) + goto ud; + eprintf ("jalr x%u, x%u, %d\n", (uint)rd, (uint)rs1, (int)imm_i); + cpu_set (cpu, rd, cpu->pc); + cpu->pc = cpu_get (cpu, rs1); + break; + case 0b1100011: // bcc rs1, rs2, bimm + a = cpu_get (cpu, rs1); + b = cpu_get (cpu, rs2); + switch (funct3) { + case 0b000: + name = "beq"; + c = a == b; + break; + case 0b001: + name = "bne"; + c = a != b; + case 0b100: + name = "blt"; + c = (i32)a < (i32)b; + break; + case 0b101: + name = "bge"; + c = (i32)a >= (i32)b; + break; + case 0b110: + name = "bltu"; + c = a < b; + break; + case 0b111: + name = "bgeu"; + c = a >= b; + break; + default: + goto ud; + } + eprintf ("%s x%u, x%u, %d\n", name, (uint)rs1, (uint)rs2, (int)imm_b); + if (c) + cpu->pc += imm_b - 4; + break; + case 0b0000011: // lx rd, iimm(rs1) + a = cpu_get (cpu, rs1) + imm_i; + switch (funct3) { + case 0b000: // lb + name = "lb"; + b = (i32)*(const i8 *)(size_t)a; + break; + case 0b001: // lh + name = "lh"; + b = (i32)*(const i16 *)(size_t)a; + break; + case 0b010: // lw + name = "lw"; + b = *(const u32 *)(size_t)a; + break; + case 0b100: // lbu + name = "lbu"; + b = (u32)*(const u8 *)(size_t)a; + break; + case 0b101: // lhu + name = "lhu"; + b = (u32)*(const u16 *)(size_t)a; + break; + default: + goto ud; + } + eprintf ("%s x%u, %d(x%u)\n", name, (uint)rd, (int)imm_i, (uint)rs1); + cpu_set (cpu, rd, b); + break; + case 0b0100011: // sx rs2, simm(rs1) + a = cpu_get (cpu, rs2); + b = cpu_get (cpu, rs1) + imm_s; + switch (funct3) { + case 0b000: // sb + name = "sb"; + *(u8 *)(size_t)b = a; + break; + case 0b001: // sh + name = "sh"; + *(u16 *)(size_t)b = a; + break; + case 0b010: // sw + name = "sw"; + *(u32 *)(size_t)b = a; + break; + default: + goto ud; + } + eprintf ("%s x%u, %d(x%u)\n", name, (uint)rs2, (int)imm_s, (uint)rs1); + break; + case 0b0010011: // alui rd, rs1, iimm + a = cpu_get (cpu, rs1); + b = imm_i; + switch (funct3) { + case 0b000: // addi + name = "addi"; + c = a + b; + break; + case 0b001: // slli + name = "slli"; + c = a << (b & 0x1f); + break; + case 0b010: // slti + name = "slti"; + c = (i32)a < (i32)b; + break; + case 0b011: // sltiu + name = "sltiu"; + c = a < b; + break; + case 0b100: // xori + name = "xori"; + c = a ^ b; + break; + case 0b101: // srli/srai + if ((instr >> 30) & 1) { + name = "srai"; + c = (i32)a >> b; + } else { + name = "srli"; + c = a >> b; + } + break; + case 0b110: // ori + name = "ori"; + c = a | b; + break; + case 0b111: // andi + name = "andi"; + c = a & b; + break; + default: + goto ud; + } + eprintf ("%s x%u, x%u, %d\n", name, (uint)rd, (uint)rs1, (int)imm_i); + cpu_set (cpu, rd, c); + break; + case 0b0110011: // alu rd, rs1, rs2 + a = cpu_get (cpu, rs1); + b = cpu_get (cpu, rs2); + c = (instr >> 30) & 1; + switch (funct3) { + case 0b000: // add/sub + if (c) { + name = "sub"; + a -= b; + } else { + name = "add"; + a += b; + } + break; + case 0b001: // sll + name = "sll"; + a <<= b; + break; + case 0b010: // slt + name = "slt"; + a = (i32)a < (i32)b; + break; + case 0b011: // sltu + name = "sltu"; + a = a < b; + break; + case 0b100: // xor + name = "xor"; + a ^= b; + break; + case 0b101: // srl/sra + if (c) { + name = "sra"; + a = (i32)a >> b; + } else { + name = "srl"; + a >>= b; + } + break; + case 0b110: // or + name = "or"; + a |= b; + break; + case 0b111: // and + name = "and"; + a &= b; + break; + } + eprintf ("%s x%u, x%u, x%u\n", name, (uint)rd, (uint)rs1, (uint)rs2); + cpu_set (cpu, rd, a); + break; + case 0b0001111: // fence/fence.tso/pause + eprintf ("fence\n"); + break; + case 0b1110011: // ecall/ebreak + eprintf ( + "ecall a0=%u, a1=%u, a2=%u, a3=%u, a4=%u, a5=%u, a7=%u\n", + cpu_get (cpu, 10), + cpu_get (cpu, 11), + cpu_get (cpu, 12), + cpu_get (cpu, 13), + cpu_get (cpu, 14), + cpu_get (cpu, 15), + cpu_get (cpu, 17) + ); + ecall (cpu); + break; + default: + ud: + errx (1, "%08x: invalid instruction: %08x", cpu->pc - 4, instr); + } +} + +static void load_image (struct cpu *cpu, const char *filename) +{ + Elf32_Ehdr ehdr; + int fd; + + fd = open (filename, O_RDONLY); + if (fd < 0) + err (1, "open('%s')", filename); + + if (read (fd, &ehdr, sizeof (ehdr)) != sizeof (ehdr)) + err (1, "read('%s')", filename); + + if (!IS_ELF (ehdr)) + errx (1, "not ELF"); + + if (ehdr.e_ident[EI_CLASS] != ELFCLASS32) + errx (1, "not 32bit"); + + if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) + errx (1, "not little endian"); + + //if (ehdr.e_ident[EI_OSABI] != ELFOSABI_LINUX) + // errx (1, "not linux"); + + if (ehdr.e_machine != EM_RISCV) + errx (1, "invalid e_machine"); + + cpu->pc = ehdr.e_entry; + + for (unsigned i = 0; i < ehdr.e_phnum; ++i) { + Elf32_Phdr phdr; + u32 flags = 0; + void *ptr; + + if (phdr.p_flags & PF_R) + flags |= PROT_READ; + if (phdr.p_flags & PF_W) + flags |= PROT_WRITE; + if (phdr.p_flags & PF_X) + flags |= PROT_EXEC; + + lseek (fd, ehdr.e_phoff + i * ehdr.e_phentsize, SEEK_SET); + if (read (fd, &phdr, ehdr.e_phentsize) != ehdr.e_phentsize) + err (1, "failed to read program header %u", i); + + switch (phdr.p_type) { + case PT_NULL: + break; + case PT_LOAD: + eprintf ("loading %08x into %08x flags %x memsz %u\n", + phdr.p_offset, phdr.p_vaddr, phdr.p_flags, phdr.p_memsz); + if (phdr.p_filesz == phdr.p_memsz) { + ptr = mmap ( + (void *)(size_t)phdr.p_vaddr, + phdr.p_memsz, + flags, + MAP_PRIVATE | MAP_COPY, + fd, + phdr.p_offset + ); + } else if (phdr.p_filesz == 0) { + ptr = mmap ( + (void *)(size_t)phdr.p_vaddr, + phdr.p_memsz, + flags, + MAP_ANON | MAP_PRIVATE, + -1, + 0 + ); + } else { + errx (1, "%u: p_filesz: %u, p_memsz: %u", + i, phdr.p_filesz, phdr.p_memsz); + } + + if ((size_t)ptr != (size_t)phdr.p_vaddr) + err (1, "%u: failed to map program header", i); + break; + case PT_INTERP: + case PT_DYNAMIC: + errx (1, "shared objects not supported"); + default: + //warnx ("%u: ignoring program header: %x", + // i, (unsigned)phdr.p_type); + break; + } + } + close (fd); +} + +static void cpu_push (struct cpu *cpu, u32 val) +{ + cpu_set (cpu, 2, cpu_get (cpu, 2) - 4); + *(u32 *)(size_t)cpu_get (cpu, 2) = val; +} + +static void cpu_push_str (struct cpu *cpu, const char *ptr) +{ + if ((size_t)ptr > 0x80000000) { + const size_t len = strlen (ptr); + char *nptr = sbrk (len + 1); + memcpy (nptr, ptr, len + 1); + assert ((size_t)nptr < 0x80000000); + ptr = nptr; + } + cpu_push (cpu, (u32)(size_t)ptr); +} + +static void setup_stack ( + struct cpu *cpu, + u32 stack_size, + int argc, + char **argv, + int envc, + char **envp +) { + void *ptr; + void *stack_bottom; + + // set stack pointer + cpu_set (cpu, 2, 0x80000000); + + stack_bottom = (void *)(size_t)(0x80000000 - stack_size); + ptr = mmap ( + stack_bottom, + stack_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_STACK, + -1, + 0 + ); + + if (ptr != stack_bottom) + err (1, "failed to map stack"); + + cpu_push (cpu, 0); + for (int i = envc - 1; i >= 0; --i) + cpu_push_str (cpu, envp[i]); + cpu_push (cpu, 0); + for (int i = argc; i >= 0; --i) + cpu_push_str (cpu, argv[i]); + cpu_push (cpu, argc); +} + +int main (int argc, char *argv[], char *envp[]) +{ + struct cpu cpu; + const char *filename; + struct rlimit rl; + size_t stack_size; + int fd, option, envc; + void *ptr; + + while ((option = getopt (argc, argv, "")) != -1) { + switch (option) { + default: + return 1; + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) + errx (1, "usage: rvemu file"); + + filename = argv[0]; + memset (&cpu, 0, sizeof (cpu)); + load_image (&cpu, filename); + + if (getrlimit (RLIMIT_STACK, &rl) == 0) { + stack_size = rl.rlim_cur; + } else { + stack_size = 1 << 20; + } + + for (envc = 0; envp[envc] != NULL; ++envc); + + setup_stack (&cpu, stack_size, argc, argv, envc, envp); + + while (1) { + const u32 instr = cpu_fetch (&cpu); + cpu_exec (&cpu, instr); + } + + return 0; +} blob - /dev/null blob + 2ebd0fad6472bfc86aec71f22929eb1e170bfa13 (mode 644) --- /dev/null +++ test.S @@ -0,0 +1,66 @@ +.section .text +.global _start +.type _start, %function +_start: + addi sp, sp, -4 + + li t0, 10 + sw t0, 0(sp) + + li a0, 0x42 + jal printhex + + li a0, 1 + mv a1, sp + li a2, 1 + li a7, 64 + ecall + + li a0, 0 + li a7, 93 + ecall + +# printhex(u32 a0) +printhex: + addi sp, sp, -12 + sw s0, 8(sp) + sw s1, 4(sp) + li s0, 28 + mv s1, a0 + +.Loop: + srl t0, s1, s0 + andi t0, t0, 0xf + + li t1, 10 + blt t0, t1, .Ldec + addi t0, t0, 55 + j .Lprint + +.Ldec: + addi t0, t0, 48 + +.Lprint: + sb t0, 0(sp) + li a0, 1 + mv a1, sp + li a2, 2 + li a7, 64 + ecall + + beq s0, zero, .Lret + addi s0, s0, -4 + j .Loop + +.Lret: + lw s1, 4(sp) + lw s0, 8(sp) + addi sp, sp, 12 + ret + + +.section .rodata +str: .string "Hello World\n" + +.section .data +buf: .space 1 blob - /dev/null blob + a5826bdff3660ddabb245f576fe5539b00f2243e (mode 644) --- /dev/null +++ test.ld @@ -0,0 +1,23 @@ +ENTRY(_start) + +SECTIONS { + . = 1M; + + .text : ALIGN(4K) { + *(.text*) + } + + .rodata : ALIGN(4K) { + *(.rodata*) + } + + .data : ALIGN(4K) { + *(.data*) + } + + .bss : ALIGN(4K) { + *(COMMON) + *(.bss*) + } + +}