diff --git a/sys/risc-v/rv64ume.vadl b/sys/risc-v/rv64ume.vadl index 14653d305..6829c7039 100644 --- a/sys/risc-v/rv64ume.vadl +++ b/sys/risc-v/rv64ume.vadl @@ -4,6 +4,42 @@ import rv64csr::{ RV64IMZicsr } instruction set architecture RV64UME extending RV64IMZicsr = {} +application binary interface ABI for RV64UME = { + alias register a0 = X(10) + alias register a1 = X(11) + alias register a2 = X(12) + alias register a3 = X(13) + alias register a4 = X(14) + alias register a5 = X(15) + alias register a6 = X(16) + alias register a7 = X(17) + alias register sp = X(2) + alias register ra = X(1) + alias register gp = X(3) + alias register fp = X(8) + + return address = ra + stack pointer = sp + return value = a0 + frame pointer = fp + global pointer = gp + function argument = [ a{0..7} ] + caller saved = [ a{0..7} ] + callee saved = [ ra, fp, + X(9), X(18), X(19), X(20), X(21), + X(22), X(23), X(24), X(25), X(26), X(27) ] + + special absolute address load instruction = LA + special local address load instruction = LLA + + special return instruction = RET + special call instruction = CALL +} + +enumeration LinuxSyscall : Bits<64> = { + sys_riscv_hwprobe = 258 + , sys_riscv_flush_icache = 259 +} // This is the system-mode entry point. // Once we have a user-mode definition, this should be removed. diff --git a/vadl/main/resources/templates/iss/linux-user/gen-arch/cpu_loop.c b/vadl/main/resources/templates/iss/linux-user/gen-arch/cpu_loop.c index 008d05752..1ec5598b5 100644 --- a/vadl/main/resources/templates/iss/linux-user/gen-arch/cpu_loop.c +++ b/vadl/main/resources/templates/iss/linux-user/gen-arch/cpu_loop.c @@ -24,17 +24,10 @@ #include "signal-common.h" #include "elf.h" -enum { - RV64UME_EXC_ILLEGAL_INSTR = 2, - RV64UME_EXC_BREAKPOINT = 3, - RV64UME_EXC_ECALL = 11, -}; - -void cpu_loop(CPURV64UMEState *env) +void cpu_loop(CPU[(${gen_arch_upper})]State *env) { CPUState *cs = env_cpu(env); int trapnr; - uint32_t cause; target_ulong ret; for (;;) { @@ -50,55 +43,31 @@ void cpu_loop(CPURV64UMEState *env) case EXCP_ATOMIC: cpu_exec_step_atomic(cs); break; - case RV64UME_EXCP_EXC: - cause = env->arg_exc_cause; - switch (cause) { - case RV64UME_EXC_ECALL: - env->pc += 4; - if (env->x[RV64UME_REG_A7] == TARGET_NR_rv64ume_flush_icache) { - /* no-op in QEMU; TB invalidation is automatic */ - ret = 0; - } else { - ret = do_syscall(env, - env->x[RV64UME_REG_A7], - env->x[RV64UME_REG_A0], - env->x[RV64UME_REG_A1], - env->x[RV64UME_REG_A2], - env->x[RV64UME_REG_A3], - env->x[RV64UME_REG_A4], - env->x[RV64UME_REG_A5], - 0, 0); - } - if (ret == -QEMU_ERESTARTSYS) { - env->pc -= 4; - } else if (ret != -QEMU_ESIGRETURN) { - env->x[RV64UME_REG_A0] = ret; - } - if (cs->singlestep_enabled) { - goto gdbstep; - } - break; - case RV64UME_EXC_ILLEGAL_INSTR: - force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->pc); - break; - case RV64UME_EXC_BREAKPOINT: - case EXCP_DEBUG: -gdbstep: - force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); - break; - default: - EXCP_DUMP(env, - "\nqemu: unhandled rv64ume exception cause %#x - aborting\n", - cause); - exit(EXIT_FAILURE); + case [(${gen_arch_upper})]_EXCP_SYSCALL: + env->[(${pc_info.accessor})] += [(${config.insn_width_bytes})]; + ret = do_syscall(env, + env->[(${config.sysRegFile})][ [(${config.sysReg})] ], + [# th:each="arg : ${config.args}"] + env->[(${arg.file})][ [(${arg.index})] ], + [/] + 0, 0); + if (ret == -QEMU_ERESTARTSYS) { + env->[(${pc_info.accessor})] -= [(${config.insn_width_bytes})]; + } else if (ret != -QEMU_ESIGRETURN) { + env->[(${config.retRegFile})][ [(${config.retReg})] ] = ret; } + if (cs->singlestep_enabled) { + goto gdbstep; } break; + case EXCP_DEBUG: + gdbstep: + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->[(${pc_info.accessor})]); + break; default: EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n", trapnr); exit(EXIT_FAILURE); } - process_pending_signals(env); } } @@ -109,8 +78,8 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) TaskState *ts = get_task_state(cpu); struct image_info *info = ts->info; - env->pc = regs->sepc; - env->x[RV64UME_REG_SP] = regs->sp; + env->[(${pc_info.accessor})] = regs->sepc; + env->[(${config.spRegFile})][ [(${config.spReg})] ] = regs->[(${config.spRegName})]; ts->stack_base = info->start_stack; } diff --git a/vadl/main/resources/templates/iss/linux-user/gen-arch/meson.build b/vadl/main/resources/templates/iss/linux-user/gen-arch/meson.build index a37e8fc3c..404ac04fe 100644 --- a/vadl/main/resources/templates/iss/linux-user/gen-arch/meson.build +++ b/vadl/main/resources/templates/iss/linux-user/gen-arch/meson.build @@ -1,5 +1,5 @@ syscall_nr_generators += { - 'rv64ume': generator(sh, + '[(${gen_arch_lower})]': generator(sh, arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], output: '@BASENAME@_nr.h') } diff --git a/vadl/main/resources/templates/iss/linux-user/gen-arch/signal.c b/vadl/main/resources/templates/iss/linux-user/gen-arch/signal.c index a6a1bc14c..63d649e7a 100644 --- a/vadl/main/resources/templates/iss/linux-user/gen-arch/signal.c +++ b/vadl/main/resources/templates/iss/linux-user/gen-arch/signal.c @@ -1,171 +1,33 @@ -/* - * Emulation of Linux signals for rv64ume user-mode. - */ - #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" #include "signal-common.h" -#include "linux-user/trace.h" - -/* - * Minimal sigcontext matching rv64ume state: - * - pc - * - x1..x31 (x0 is always zero and omitted) - */ -struct target_sigcontext { - abi_long pc; - abi_long gpr[31]; -}; - -struct target_ucontext { - abi_ulong uc_flags; - abi_ptr uc_link; - target_stack_t uc_stack; - target_sigset_t uc_sigmask; - uint8_t __unused[1024 / 8 - sizeof(target_sigset_t)]; - struct target_sigcontext uc_mcontext QEMU_ALIGNED(16); -}; - -struct target_rt_sigframe { - struct target_siginfo info; - struct target_ucontext uc; -}; - -static abi_ulong get_sigframe(struct target_sigaction *ka, - CPURV64UMEState *regs, size_t framesize) -{ - abi_ulong sp = get_sp_from_cpustate(regs); - - if (on_sig_stack(sp) && !likely(on_sig_stack(sp - framesize))) { - return -1L; - } - - sp = target_sigsp(sp, ka) - framesize; - sp &= ~0xf; - - return sp; -} - -static void setup_sigcontext(struct target_sigcontext *sc, CPURV64UMEState *env) -{ - int i; - - __put_user(env->pc, &sc->pc); - for (i = 1; i < 32; i++) { - __put_user(env->x[i], &sc->gpr[i - 1]); - } -} - -static void setup_ucontext(struct target_ucontext *uc, - CPURV64UMEState *env, target_sigset_t *set) -{ - int i; - - __put_user(0, &(uc->uc_flags)); - __put_user(0, &(uc->uc_link)); - - target_save_altstack(&uc->uc_stack, env); - - for (i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &(uc->uc_sigmask.sig[i])); - } - - setup_sigcontext(&uc->uc_mcontext, env); -} void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, - target_sigset_t *set, CPURV64UMEState *env) + target_sigset_t *set, CPU[(${gen_arch_upper})]State *env) { - abi_ulong frame_addr; - struct target_rt_sigframe *frame; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_rt_frame(env, frame_addr); + (void)ka; + (void)info; + (void)set; + (void)env; - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto badframe; - } - - setup_ucontext(&frame->uc, env, set); - frame->info = *info; - - env->pc = ka->_sa_handler; - env->x[RV64UME_REG_SP] = frame_addr; - env->x[RV64UME_REG_A0] = sig; - env->x[RV64UME_REG_A1] = frame_addr + offsetof(struct target_rt_sigframe, info); - env->x[RV64UME_REG_A2] = frame_addr + offsetof(struct target_rt_sigframe, uc); - env->x[RV64UME_REG_RA] = default_rt_sigreturn; - - return; - -badframe: - unlock_user_struct(frame, frame_addr, 1); if (sig == TARGET_SIGSEGV) { - ka->_sa_handler = TARGET_SIG_DFL; + force_sig(TARGET_SIGSEGV); + } else { + force_sig(sig); } - force_sig(TARGET_SIGSEGV); } -static void restore_sigcontext(CPURV64UMEState *env, struct target_sigcontext *sc) +long do_rt_sigreturn(CPU[(${gen_arch_upper})]State *env) { - int i; - - __get_user(env->pc, &sc->pc); - for (i = 1; i < 32; ++i) { - __get_user(env->x[i], &sc->gpr[i - 1]); - } -} - -static void restore_ucontext(CPURV64UMEState *env, struct target_ucontext *uc) -{ - sigset_t blocked; - target_sigset_t target_set; - int i; - - target_sigemptyset(&target_set); - for (i = 0; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &(uc->uc_sigmask.sig[i])); - } + (void)env; - target_to_host_sigset_internal(&blocked, &target_set); - set_sigmask(&blocked); - - restore_sigcontext(env, &uc->uc_mcontext); -} - -long do_rt_sigreturn(CPURV64UMEState *env) -{ - struct target_rt_sigframe *frame; - abi_ulong frame_addr; - - frame_addr = env->x[RV64UME_REG_SP]; - trace_user_do_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - restore_ucontext(env, &frame->uc); - target_restore_altstack(&frame->uc.uc_stack, env); - - unlock_user_struct(frame, frame_addr, 0); - return -QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); force_sig(TARGET_SIGSEGV); return 0; } void setup_sigtramp(abi_ulong sigtramp_page) { - uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 8, 0); - assert(tramp != NULL); - - __put_user(0x08b00893, tramp + 0); /* li a7, 139 = __NR_rt_sigreturn */ - __put_user(0x00000073, tramp + 1); /* ecall */ - - default_rt_sigreturn = sigtramp_page; - unlock_user(tramp, sigtramp_page, 8); -} + (void)sigtramp_page; +} \ No newline at end of file diff --git a/vadl/main/resources/templates/iss/linux-user/gen-arch/syscall.tbl b/vadl/main/resources/templates/iss/linux-user/gen-arch/syscall.tbl index 84eb280d3..249da1518 100644 --- a/vadl/main/resources/templates/iss/linux-user/gen-arch/syscall.tbl +++ b/vadl/main/resources/templates/iss/linux-user/gen-arch/syscall.tbl @@ -302,8 +302,8 @@ 244 or1k or1k_atomic sys_or1k_atomic -258 rv64ume rv64ume_hwprobe sys_rv64ume_hwprobe -259 rv64ume rv64ume_flush_icache sys_rv64ume_flush_icache +258 [(${gen_arch_lower})] [(${gen_arch_lower})]_hwprobe sys_[(${gen_arch_lower})]_hwprobe +259 [(${gen_arch_lower})] [(${gen_arch_lower})]_flush_icache sys_[(${gen_arch_lower})]_flush_icache 260 time32 wait4 sys_wait4 compat_sys_wait4 260 64 wait4 sys_wait4 diff --git a/vadl/main/resources/templates/iss/linux-user/gen-arch/target_cpu.h b/vadl/main/resources/templates/iss/linux-user/gen-arch/target_cpu.h index d06911d34..1a68d4074 100644 --- a/vadl/main/resources/templates/iss/linux-user/gen-arch/target_cpu.h +++ b/vadl/main/resources/templates/iss/linux-user/gen-arch/target_cpu.h @@ -1,40 +1,38 @@ -#ifndef RV64UME_TARGET_CPU_H -#define RV64UME_TARGET_CPU_H +#ifndef [(${gen_arch_upper})]_TARGET_CPU_H +#define [(${gen_arch_upper})]_TARGET_CPU_H +//TODO: check for correct interpolations for regs & values enum { - RV64UME_REG_RA = 1, - RV64UME_REG_SP = 2, - RV64UME_REG_TP = 4, - RV64UME_REG_A0 = 10, - RV64UME_REG_A1 = 11, - RV64UME_REG_A2 = 12, - RV64UME_REG_A3 = 13, - RV64UME_REG_A4 = 14, - RV64UME_REG_A5 = 15, - RV64UME_REG_A7 = 17, + [(${gen_arch_upper})]_REG_RA = 1, + [(${gen_arch_upper})]_REG_SP = [(${config.spReg})], + [(${gen_arch_upper})]_REG_TP = 4, + + [# th:each="arg, stat : ${config.args}"] + [(${gen_arch_upper})]_REG_ARG[(${stat.index})] = [(${arg.index})], + [/] }; -static inline void cpu_clone_regs_child(CPURV64UMEState *env, target_ulong newsp, +static inline void cpu_clone_regs_child(CPU[(${gen_arch_upper})]State *env, target_ulong newsp, unsigned flags) { if (newsp) { - env->x[RV64UME_REG_SP] = newsp; + env->[(${register_tensors[0].name_lower})][ [(${gen_arch_upper})]_REG_SP] = newsp; } - env->x[RV64UME_REG_A0] = 0; + env->[(${register_tensors[0].name_lower})][ [(${config.retReg})] ] = 0; } -static inline void cpu_clone_regs_parent(CPURV64UMEState *env, unsigned flags) +static inline void cpu_clone_regs_parent(CPU[(${gen_arch_upper})]State *env, unsigned flags) { } -static inline void cpu_set_tls(CPURV64UMEState *env, target_ulong newtls) +static inline void cpu_set_tls(CPU[(${gen_arch_upper})]State *env, target_ulong newtls) { - env->x[RV64UME_REG_TP] = newtls; + env->[(${register_tensors[0].name_lower})][ [(${gen_arch_upper})]_REG_TP] = newtls; } -static inline abi_ulong get_sp_from_cpustate(CPURV64UMEState *state) +static inline abi_ulong get_sp_from_cpustate(CPU[(${gen_arch_upper})]State *state) { - return state->x[RV64UME_REG_SP]; + return state->[(${register_tensors[0].name_lower})][ [(${gen_arch_upper})]_REG_SP]; } #endif diff --git a/vadl/main/resources/templates/iss/linux-user/gen-arch/target_elf.h b/vadl/main/resources/templates/iss/linux-user/gen-arch/target_elf.h index a316ece3f..37e3965bf 100644 --- a/vadl/main/resources/templates/iss/linux-user/gen-arch/target_elf.h +++ b/vadl/main/resources/templates/iss/linux-user/gen-arch/target_elf.h @@ -5,8 +5,8 @@ * later version. See the COPYING file in the top-level directory. */ -#ifndef RV64UME_TARGET_ELF_H -#define RV64UME_TARGET_ELF_H +#ifndef [(${gen_arch_upper})]_TARGET_ELF_H +#define [(${gen_arch_upper})]_TARGET_ELF_H static inline const char *cpu_get_model(uint32_t eflags) { return "max"; diff --git a/vadl/main/resources/templates/iss/linux-user/gen-arch/target_errno_defs.h b/vadl/main/resources/templates/iss/linux-user/gen-arch/target_errno_defs.h index 07f1e5f33..b797200bc 100644 --- a/vadl/main/resources/templates/iss/linux-user/gen-arch/target_errno_defs.h +++ b/vadl/main/resources/templates/iss/linux-user/gen-arch/target_errno_defs.h @@ -1,5 +1,5 @@ -#ifndef RV64UME_TARGET_ERRNO_DEFS_H -#define RV64UME_TARGET_ERRNO_DEFS_H +#ifndef [(${gen_arch_upper})]_TARGET_ERRNO_DEFS_H +#define [(${gen_arch_upper})]_TARGET_ERRNO_DEFS_H /* Target uses generic errno */ #include "../generic/target_errno_defs.h" diff --git a/vadl/main/resources/templates/iss/linux-user/gen-arch/target_fcntl.h b/vadl/main/resources/templates/iss/linux-user/gen-arch/target_fcntl.h index 10bec4603..afc18d959 100644 --- a/vadl/main/resources/templates/iss/linux-user/gen-arch/target_fcntl.h +++ b/vadl/main/resources/templates/iss/linux-user/gen-arch/target_fcntl.h @@ -5,7 +5,7 @@ * later version. See the COPYING file in the top-level directory. */ -#ifndef RV64UME_TARGET_FCNTL_H -#define RV64UME_TARGET_FCNTL_H +#ifndef [(${gen_arch_upper})]_TARGET_FCNTL_H +#define [(${gen_arch_upper})]_TARGET_FCNTL_H #include "../generic/fcntl.h" #endif diff --git a/vadl/main/resources/templates/iss/linux-user/gen-arch/target_mman.h b/vadl/main/resources/templates/iss/linux-user/gen-arch/target_mman.h index 97a04f176..abf0ffde1 100644 --- a/vadl/main/resources/templates/iss/linux-user/gen-arch/target_mman.h +++ b/vadl/main/resources/templates/iss/linux-user/gen-arch/target_mman.h @@ -5,7 +5,7 @@ #define TASK_UNMAPPED_BASE \ TARGET_PAGE_ALIGN((1ull << (TARGET_VIRT_ADDR_SPACE_BITS - 1)) / 3) -/* arch/rv64ume/include/asm/elf.h */ +/* arch/[(${gen_arch_lower})]/include/asm/elf.h */ #define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE * 2) #include "../generic/target_mman.h" diff --git a/vadl/main/resources/templates/iss/linux-user/gen-arch/target_proc.h b/vadl/main/resources/templates/iss/linux-user/gen-arch/target_proc.h index 752050f26..07eb9eff4 100644 --- a/vadl/main/resources/templates/iss/linux-user/gen-arch/target_proc.h +++ b/vadl/main/resources/templates/iss/linux-user/gen-arch/target_proc.h @@ -3,8 +3,8 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef RV64UME_TARGET_PROC_H -#define RV64UME_TARGET_PROC_H +#ifndef [(${gen_arch_upper})]_TARGET_PROC_H +#define [(${gen_arch_upper})]_TARGET_PROC_H static int open_cpuinfo(CPUArchState *cpu_env, int fd) { @@ -12,7 +12,7 @@ static int open_cpuinfo(CPUArchState *cpu_env, int fd) dprintf(fd, "processor\t: 0\n"); dprintf(fd, "hart\t\t: 0\n"); - dprintf(fd, "isa\t\t: rv64im\n"); + dprintf(fd, "isa\t\t: [(${gen_arch_lower})]\n"); dprintf(fd, "mmu\t\t: none\n"); dprintf(fd, "uarch\t\t: qemu\n\n"); @@ -20,4 +20,4 @@ static int open_cpuinfo(CPUArchState *cpu_env, int fd) } #define HAVE_ARCH_PROC_CPUINFO -#endif /* RV64UME_TARGET_PROC_H */ +#endif /* [(${gen_arch_upper})]_TARGET_PROC_H */ diff --git a/vadl/main/resources/templates/iss/linux-user/gen-arch/target_signal.h b/vadl/main/resources/templates/iss/linux-user/gen-arch/target_signal.h index cb50d749f..b936e162f 100644 --- a/vadl/main/resources/templates/iss/linux-user/gen-arch/target_signal.h +++ b/vadl/main/resources/templates/iss/linux-user/gen-arch/target_signal.h @@ -1,6 +1,6 @@ -#ifndef RV64UME_TARGET_SIGNAL_H -#define RV64UME_TARGET_SIGNAL_H +#ifndef [(${gen_arch_upper})]_TARGET_SIGNAL_H +#define [(${gen_arch_upper})]_TARGET_SIGNAL_H #include "../generic/signal.h" -#endif /* RV64UME_TARGET_SIGNAL_H */ +#endif /* [(${gen_arch_upper})]_TARGET_SIGNAL_H */ diff --git a/vadl/main/resources/templates/iss/linux-user/gen-arch/target_syscall.h b/vadl/main/resources/templates/iss/linux-user/gen-arch/target_syscall.h index 77fa2385d..80e26fbbd 100644 --- a/vadl/main/resources/templates/iss/linux-user/gen-arch/target_syscall.h +++ b/vadl/main/resources/templates/iss/linux-user/gen-arch/target_syscall.h @@ -2,48 +2,19 @@ * This struct defines the way the registers are stored on the * stack during a system call. * - * Reference: linux/arch/rv64ume/include/uapi/asm/ptrace.h + * Reference: linux/arch/[(${gen_arch_lower})]/include/uapi/asm/ptrace.h */ -#ifndef LINUX_USER_RV64UME_TARGET_SYSCALL_H -#define LINUX_USER_RV64UME_TARGET_SYSCALL_H +#ifndef LINUX_USER_[(${gen_arch_upper})]_TARGET_SYSCALL_H +#define LINUX_USER_[(${gen_arch_upper})]_TARGET_SYSCALL_H struct target_pt_regs { abi_long sepc; - abi_long ra; + abi_long pc; abi_long sp; - abi_long gp; - abi_long tp; - abi_long t0; - abi_long t1; - abi_long t2; - abi_long s0; - abi_long s1; - abi_long a0; - abi_long a1; - abi_long a2; - abi_long a3; - abi_long a4; - abi_long a5; - abi_long a6; - abi_long a7; - abi_long s2; - abi_long s3; - abi_long s4; - abi_long s5; - abi_long s6; - abi_long s7; - abi_long s8; - abi_long s9; - abi_long s10; - abi_long s11; - abi_long t3; - abi_long t4; - abi_long t5; - abi_long t6; }; -#define UNAME_MACHINE "rv64ume" +#define UNAME_MACHINE "[(${gen_arch_lower})]" #define UNAME_MINIMUM_RELEASE "4.15.0" #define TARGET_MCL_CURRENT 1 diff --git a/vadl/main/resources/templates/iss/linux-user/meson.build b/vadl/main/resources/templates/iss/linux-user/meson.build index 33ef639c0..679c26e9a 100644 --- a/vadl/main/resources/templates/iss/linux-user/meson.build +++ b/vadl/main/resources/templates/iss/linux-user/meson.build @@ -56,7 +56,7 @@ subdir('x86_64') subdir('xtensa') # VADL generated target [# th:if="${gen_arch_upper == 'RV64UME'}"] -subdir('rv64ume') +subdir('[(${gen_arch_lower})]') [/] specific_ss.add_all(when: 'CONFIG_LINUX_USER', if_true: linux_user_ss) diff --git a/vadl/main/resources/templates/iss/target/gen-arch/cpu-bits.h b/vadl/main/resources/templates/iss/target/gen-arch/cpu-bits.h index 04e8efa19..1ae19fd13 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/cpu-bits.h +++ b/vadl/main/resources/templates/iss/target/gen-arch/cpu-bits.h @@ -7,6 +7,7 @@ typedef enum [(${gen_arch_upper})]Exception { [# th:each="exc, iterState : ${exc_info.exceptions}"] [[${exc.enum_name}]], [/] + [(${gen_arch_upper})]_EXCP_SYSCALL = 0x10000, } [(${gen_arch_upper})]Exception; #endif //[(${gen_arch_upper})]_CPU_BITS_H diff --git a/vadl/main/resources/templates/iss/target/gen-arch/helper.c b/vadl/main/resources/templates/iss/target/gen-arch/helper.c index b0df5e67a..fff71fe2b 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/helper.c +++ b/vadl/main/resources/templates/iss/target/gen-arch/helper.c @@ -24,6 +24,15 @@ void helper_unsupported(CPU[(${gen_arch_upper})]State *env) { cpu_loop_exit(cs); } +void helper_raise_syscall(CPU[(${gen_arch_upper})]State *env) +{ + CPUState *cs = env_cpu(env); + + cs->exception_index = EXCP_SYSCALL; + + cpu_loop_exit(cs); +} + [# th:each="exc : ${exc_info.exceptions}"] [(${exc.helper_impl})] [/] diff --git a/vadl/main/resources/templates/iss/target/gen-arch/helper.h b/vadl/main/resources/templates/iss/target/gen-arch/helper.h index 107003b8d..47dbb6ef2 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/helper.h +++ b/vadl/main/resources/templates/iss/target/gen-arch/helper.h @@ -6,6 +6,8 @@ DEF_HELPER_1(unsupported, noreturn, env) [(${exc.helper_def})] [/] +DEF_HELPER_1(raise_syscall, void, env) + // helper definitions for instructions [# th:each="instr : ${instr_helper_defs}"] [(${instr})] diff --git a/vadl/main/resources/templates/iss/target/gen-arch/translate.c b/vadl/main/resources/templates/iss/target/gen-arch/translate.c index 37763c2ce..40a441750 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/translate.c +++ b/vadl/main/resources/templates/iss/target/gen-arch/translate.c @@ -150,6 +150,14 @@ static void gen_update_pc_diff(DisasContext *ctx, target_long diff) { gen_update_pc(ctx, dest); } +static void gen_raise_syscall(DisasContext *ctx) { + gen_update_pc(ctx, ctx->pc_curr); + + gen_helper_raise_syscall(tcg_env); + + ctx->base.is_jmp = DISAS_NORETURN; +} + /* * Jumps to the given target_pc and sets is_jmp to NORETURN. n indicates the jump slot @@ -215,7 +223,7 @@ static void translate(DisasContext *ctx) return; } - error_report("[[(${gen_arch_upper})]] translate, illegal instr, pc: 0x%04llx , insn: 0x%04x\n", ctx->base.pc_next, insn); + error_report("[[(${gen_arch_upper})]] translate, illegal instr, pc: 0x%04 , insn: 0x%04x\n", ctx->base.pc_next, insn); gen_update_pc_diff(ctx, 0); gen_helper_unsupported(tcg_env); diff --git a/vadl/main/vadl/ast/vadl.ATG b/vadl/main/vadl/ast/vadl.ATG index 35946b1ee..765a38af2 100644 --- a/vadl/main/vadl/ast/vadl.ATG +++ b/vadl/main/vadl/ast/vadl.ATG @@ -1199,7 +1199,7 @@ The #miaDefinition production rule lists all micro architecture language element \listing{lst_mia_definitions, Example micro architecture definition} ~~~{.vadl} micro architecture FiveStage implements RV32IM = { - + logic [forwarding] bypass stage FETCH -> ( fr : FetchResult ) = { diff --git a/vadl/main/vadl/iss/passes/UmeHardcodedRiscvDefinitionPass.java b/vadl/main/vadl/iss/passes/UmeHardcodedRiscvDefinitionPass.java new file mode 100644 index 000000000..772567821 --- /dev/null +++ b/vadl/main/vadl/iss/passes/UmeHardcodedRiscvDefinitionPass.java @@ -0,0 +1,145 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.List; +import java.util.stream.IntStream; +import javax.annotation.Nullable; +import vadl.configuration.IssConfiguration; +import vadl.pass.PassName; +import vadl.pass.PassResults; +import vadl.types.BitsType; +import vadl.types.DataType; +import vadl.types.Type; +import vadl.utils.SourceLocation; +import vadl.viam.Abi; +import vadl.viam.Assembly; +import vadl.viam.Constant; +import vadl.viam.Encoding; +import vadl.viam.Format; +import vadl.viam.Function; +import vadl.viam.Identifier; +import vadl.viam.Instruction; +import vadl.viam.InstructionSetArchitecture; +import vadl.viam.Parameter; +import vadl.viam.RegisterRef; +import vadl.viam.Specification; +import vadl.viam.UserModeEmulation; +import vadl.viam.graph.Graph; + +/** + * A specialized hardcoded rendering pass for QEMU User-Mode Emulation (UME) source files. + */ +public class UmeHardcodedRiscvDefinitionPass extends AbstractIssPass { + public UmeHardcodedRiscvDefinitionPass(IssConfiguration configuration) { + super(configuration); + } + + @Override + public PassName getName() { + return PassName.of("UME Hardcoded RISC-V Definition"); + } + + @Nullable + @Override + public Object execute(PassResults passResults, Specification viam) throws IOException { + if (!"rv64ume".equals(viam.simpleName())) { + return null; + } + + InstructionSetArchitecture isa = viam.isa() + .orElseThrow(() -> new IllegalStateException("ISA not found in " + viam.simpleName())); + + Abi abi = viam.abi() + .orElseThrow(() -> new IllegalStateException("ABI not found in " + viam.simpleName())); + + UserModeEmulation ume = createDummySolution(abi, isa); + viam.add(ume); + return null; + } + + /** + * Creates a default {@link UserModeEmulation} configuration, + * pre-configured for the RISC-V architecture. + * * @return a standard RISC-V user-mode emulation setup. + */ + public static UserModeEmulation createDummySolution(Abi abi, InstructionSetArchitecture isa) { + Identifier identifier = new Identifier(new String[]{"ume"}, + SourceLocation.INVALID_SOURCE_LOCATION); + + var registerResource = abi.stackPointer().registerFile(); + + List args = IntStream.rangeClosed(10, 15) + .mapToObj(i -> new RegisterRef( + registerResource, + List.of(Constant.Value.of(BigInteger.valueOf(i).toByteArray(), DataType.bits(5))), + SourceLocation.INVALID_SOURCE_LOCATION + )) + .toList(); + + Graph emptyGraph = new Graph("empty_graph"); + + BitsType mockType = BitsType.bits(32); + + Function mockFunc = new Function( + new Identifier(new String[]{"dummy_function"}, + SourceLocation.INVALID_SOURCE_LOCATION), + new Parameter[0], + Type.string(), + emptyGraph + ); + + Assembly emptyAssembly = new Assembly(new Identifier(new String[]{"dummy_assembly"}, + SourceLocation.INVALID_SOURCE_LOCATION), mockFunc); + + Format dummyFormat = new Format(new Identifier(new String[]{"dummy_format"}, + SourceLocation.INVALID_SOURCE_LOCATION), mockType); + + Encoding emptyEncoding = new Encoding( + new Identifier(new String[]{"dummy_encoding"}, + SourceLocation.INVALID_SOURCE_LOCATION), + dummyFormat, new Encoding.Field[0]); + + Instruction mockSyscallInsn = new Instruction( + new Identifier(new String[]{"ECALL"}, + SourceLocation.INVALID_SOURCE_LOCATION), + emptyGraph, emptyAssembly, emptyEncoding + ); + + var syscallNrRef = new RegisterRef( + registerResource, + List.of(Constant.Value.of(BigInteger.valueOf(17).toByteArray(), DataType.bits(5))), + SourceLocation.INVALID_SOURCE_LOCATION + ); + + var syscallReturnRef = new RegisterRef( + registerResource, + List.of(Constant.Value.of(BigInteger.valueOf(10).toByteArray(), DataType.bits(5))), + SourceLocation.INVALID_SOURCE_LOCATION + ); + + return new UserModeEmulation( + identifier, + isa, abi, args, + mockSyscallInsn, + syscallNrRef, + syscallReturnRef); + } + +} diff --git a/vadl/main/vadl/iss/passes/UmeTemplateRenderingPass.java b/vadl/main/vadl/iss/passes/UmeTemplateRenderingPass.java new file mode 100644 index 000000000..4749aa428 --- /dev/null +++ b/vadl/main/vadl/iss/passes/UmeTemplateRenderingPass.java @@ -0,0 +1,93 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes; + +import java.util.List; +import java.util.Map; +import vadl.configuration.IssConfiguration; +import vadl.iss.template.IssTemplateRenderingPass; +import vadl.pass.PassName; +import vadl.pass.PassResults; +import vadl.utils.Pair; +import vadl.viam.Abi; +import vadl.viam.Specification; +import vadl.viam.UserModeEmulation; + +/** + * A specialized template rendering pass for QEMU User-Mode Emulation (UME) source files. + *

+ * This pass populates the template context with architecture-specific configurations + * required for Linux user-mode emulation, such as register mappings for system calls, + * stack alignment, and exception handling indices. + *

+ */ +public class UmeTemplateRenderingPass extends IssTemplateRenderingPass { + + private final String templateFilename; + + public UmeTemplateRenderingPass(IssConfiguration configuration, String templateFilename) { + super(configuration); + this.templateFilename = templateFilename; + } + + @Override + protected String issTemplatePath() { + return "linux-user/gen-arch/" + templateFilename; + } + + @Override + public PassName getName() { + return PassName.of("Rendering UME template: " + + templateFilename); + } + + @Override + protected Map createVariables(PassResults passResults, + Specification specification) { + var vars = super.createVariables(passResults, specification); + + UserModeEmulation ume = specification.userModeEmulation() + .orElseThrow(() -> new IllegalStateException("No UserModeEmulation defined")); + + Abi abi = ume.abi(); + + vars.put("config", Map.ofEntries( + Map.entry("sysReg", ume.getSyscallNumber().singleIndex()), + Map.entry("sysRegFile", ume.getSyscallNumber().resource().simpleName().toLowerCase()), + Map.entry("retReg", ume.getSyscallReturn().singleIndex()), + Map.entry("retRegFile", ume.getSyscallReturn().resource().simpleName().toLowerCase()), + Map.entry("spReg", abi.stackPointer().addr()), + Map.entry("spRegFile", abi.stackPointer().registerFile().simpleName().toLowerCase()), + Map.entry("spRegName", abi.aliases() + .getOrDefault( + Pair.of(abi.stackPointer().registerFile(), abi.stackPointer().addr()), + List.of(new Abi.RegisterAlias("sp")) + ) + .getFirst().value()), + Map.entry("args", ume.args().stream() + .map(ref -> Map.of( + "index", ref.singleIndex(), + "file", ref.resource().simpleName().toLowerCase() + )) + .toList()), + Map.entry("syscallInstr", ume.syscallInstr().simpleName()), + Map.entry("insn_width_bytes", ume.syscallInstr().format().type().bitWidth() / 8) + )); + + return vars; + } +} diff --git a/vadl/main/vadl/pass/order/IssPassOrder.java b/vadl/main/vadl/pass/order/IssPassOrder.java index 4ddf6298c..d08d09bca 100644 --- a/vadl/main/vadl/pass/order/IssPassOrder.java +++ b/vadl/main/vadl/pass/order/IssPassOrder.java @@ -24,6 +24,8 @@ import vadl.configuration.IssConfiguration; import vadl.dump.DumpIssInstructionGraphsPass; import vadl.gcb.passes.RenamingConflictingRegistersPass; +import vadl.iss.passes.UmeHardcodedRiscvDefinitionPass; +import vadl.iss.passes.UmeTemplateRenderingPass; import vadl.iss.passes.common.IssApplyMemoryEndiannessPass; import vadl.iss.passes.common.IssBitfieldWriteLoweringPass; import vadl.iss.passes.common.IssBuiltInArgTruncOptPass; @@ -52,7 +54,6 @@ import vadl.iss.passes.tcg.lowering.TcgBranchLoweringPass; import vadl.iss.passes.tcg.lowering.TcgOpLoweringPass; import vadl.iss.passes.vector.IssDirectGvecLoweringPass; -import vadl.iss.template.IssDefaultRenderingPass; import vadl.iss.template.gdb_xml.EmitIssGdbXmlPass; import vadl.iss.template.hw.EmitIssHwMachineCPass; import vadl.iss.template.target.EmitIssCpuHeaderPass; @@ -215,37 +216,37 @@ private static void addIssTemplatePasses(PassOrder order, IssConfiguration confi } private static void addUserModeEmitPasses(PassOrder order, IssConfiguration config) { + // right now, we only emit those passes if we generate the ISS for RV64UME var inputPath = config.inputPath(); if (inputPath != null && inputPath.getFileName().endsWith("rv64ume.vadl")) { order - .add(IssDefaultRenderingPass.issDefault("/configs/targets/gen-arch-linux-user.mak", - config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/meson.build", config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/elfload.c", config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/syscall_defs.h", config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/meson.build", config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/cpu_loop.c", config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/signal.c", config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/sockbits.h", config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/syscall.tbl", config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/syscallhdr.sh", true, - config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/target_cpu.h", config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/target_elf.h", config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/target_errno_defs.h", - config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/target_fcntl.h", config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/target_mman.h", config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/target_prctl.h", config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/target_proc.h", config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/target_resource.h", - config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/target_signal.h", config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/target_structs.h", - config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/target_syscall.h", - config)) - .add(IssDefaultRenderingPass.issDefault("/linux-user/gen-arch/termbits.h", config)); + .add(new UmeHardcodedRiscvDefinitionPass(config)) + .add(issDefault("/configs/targets/gen-arch-linux-user.mak", config)) + + .add(issDefault("/linux-user/meson.build", config)) + .add(issDefault("/linux-user/elfload.c", config)) + .add(issDefault("/linux-user/syscall_defs.h", config)) + + .add(issDefault("/linux-user/gen-arch/meson.build", config)) + .add(new UmeTemplateRenderingPass(config, "cpu_loop.c")) + .add(new UmeTemplateRenderingPass(config, "signal.c")) + .add(issDefault("/linux-user/gen-arch/sockbits.h", config)) + .add(issDefault("/linux-user/gen-arch/syscall.tbl", config)) + .add(issDefault("/linux-user/gen-arch/syscallhdr.sh", true, config)) + .add(new UmeTemplateRenderingPass(config, "target_cpu.h")) + + .add(issDefault("/linux-user/gen-arch/target_elf.h", config)) + .add(issDefault("/linux-user/gen-arch/target_errno_defs.h", config)) + .add(issDefault("/linux-user/gen-arch/target_fcntl.h", config)) + .add(issDefault("/linux-user/gen-arch/target_mman.h", config)) + .add(issDefault("/linux-user/gen-arch/target_prctl.h", config)) + .add(issDefault("/linux-user/gen-arch/target_proc.h", config)) + .add(issDefault("/linux-user/gen-arch/target_resource.h", config)) + .add(issDefault("/linux-user/gen-arch/target_signal.h", config)) + .add(issDefault("/linux-user/gen-arch/target_structs.h", config)) + .add(issDefault("/linux-user/gen-arch/target_syscall.h", config)) + .add(issDefault("/linux-user/gen-arch/termbits.h", config)) + ; } } diff --git a/vadl/main/vadl/viam/DefinitionVisitor.java b/vadl/main/vadl/viam/DefinitionVisitor.java index b5d0a6755..601100120 100644 --- a/vadl/main/vadl/viam/DefinitionVisitor.java +++ b/vadl/main/vadl/viam/DefinitionVisitor.java @@ -72,6 +72,8 @@ public interface DefinitionVisitor { void visit(Abi abi); + void visit(UserModeEmulation userModeEmulation); + void visit(Processor processor); void visit(MicroArchitecture microArchitecture); @@ -313,6 +315,12 @@ public void visit(Abi abi) { afterTraversal(abi); } + @Override + public void visit(UserModeEmulation userModeEmulation) { + beforeTraversal(userModeEmulation); + afterTraversal(userModeEmulation); + } + @Override public void visit(Processor processor) { beforeTraversal(processor); @@ -555,6 +563,11 @@ public void visit(Abi abi) { } + @Override + public void visit(UserModeEmulation userModeEmulation) { + + } + @Override public void visit(Processor processor) { diff --git a/vadl/main/vadl/viam/Specification.java b/vadl/main/vadl/viam/Specification.java index 8fd70f8c5..e52b3f1cb 100644 --- a/vadl/main/vadl/viam/Specification.java +++ b/vadl/main/vadl/viam/Specification.java @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText : © 2025 TU Wien +// SPDX-FileCopyrightText : © 2025-2026 TU Wien // SPDX-License-Identifier: GPL-3.0-or-later // // This program is free software: you can redistribute it and/or modify @@ -137,6 +137,16 @@ public Stream findAllFormats() { return Stream.concat(formats(), innerFormats); } + /** + * Returns the ume of the specification. + */ + public Optional userModeEmulation() { + return definitions() + .filter(UserModeEmulation.class::isInstance) + .map(UserModeEmulation.class::cast) + .findFirst(); + } + public void add(Definition definition) { definitions.add(definition); } diff --git a/vadl/main/vadl/viam/UserModeEmulation.java b/vadl/main/vadl/viam/UserModeEmulation.java new file mode 100644 index 000000000..56175f6c2 --- /dev/null +++ b/vadl/main/vadl/viam/UserModeEmulation.java @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.viam; + +import java.util.List; + +/** + * Represents the configuration for QEMU user-mode emulation. + *

+ * This class defines architecture-specific parameters required for Linux-user emulation, + * including system call register mappings, signal trampoline instructions, stack + * alignment requirements, and exception IDs. + *

+ */ +public class UserModeEmulation extends Definition { + + private final InstructionSetArchitecture isa; + private final Abi abi; + + private final List args; + private final Instruction syscallInstr; + private final RegisterRef syscallNumber; + private final RegisterRef syscallReturn; + + /** + * Constructs a UserModeEmulation configuration. + */ + public UserModeEmulation( + Identifier identifier, InstructionSetArchitecture isa, Abi abi, + List args, + Instruction syscallInstr, RegisterRef syscallNumber, RegisterRef syscallReturn + ) { + + super(identifier); + this.isa = isa; + this.abi = abi; + this.args = args; + this.syscallInstr = syscallInstr; + this.syscallNumber = syscallNumber; + this.syscallReturn = syscallReturn; + } + + public Instruction syscallInstr() { + return syscallInstr; + } + + public RegisterRef getSyscallNumber() { + return syscallNumber; + } + + public RegisterRef getSyscallReturn() { + return syscallReturn; + } + + public List args() { + return args; + } + + public InstructionSetArchitecture isa() { + return isa; + } + + public Abi abi() { + return abi; + } + + + @Override + public void accept(DefinitionVisitor visitor) { + visitor.visit(this); + } + + @Override + public String toString() { + return simpleName() + " [args=" + args + "]"; + } + +}