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..6797fb7d2 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,13 +24,14 @@ #include "signal-common.h" #include "elf.h" + enum { - RV64UME_EXC_ILLEGAL_INSTR = 2, - RV64UME_EXC_BREAKPOINT = 3, - RV64UME_EXC_ECALL = 11, + [# th:each="exc : ${config.excIds}"] + [(${gen_arch_upper})]_EXC_[(${exc.key})] = [(${exc.value})], + [/] }; -void cpu_loop(CPURV64UMEState *env) +void cpu_loop(CPU[(${gen_arch_upper})]State *env) { CPUState *cs = env_cpu(env); int trapnr; @@ -50,45 +51,47 @@ void cpu_loop(CPURV64UMEState *env) case EXCP_ATOMIC: cpu_exec_step_atomic(cs); break; - case RV64UME_EXCP_EXC: - cause = env->arg_exc_cause; + [# th:if="${not #strings.isEmpty(config.excCauseVar)}"] + case [(${gen_arch_upper})]_EXCP_[(${config.syscallException})]: + cause = env->[(${config.excCauseVar})]; switch (cause) { - case RV64UME_EXC_ECALL: - env->pc += 4; - if (env->x[RV64UME_REG_A7] == TARGET_NR_rv64ume_flush_icache) { + case [(${gen_arch_upper})]_EXC_[(${config.syscallInstr})]: + env->[(${pc_reg.name_lower})] += [(${config.insn_width_bytes})]; + [# th:if="${config.hasIcacheFlush}"] + if (env->[(${config.mainRegisterFile})][ [(${config.sysReg})] ] == TARGET_NR_[(${gen_arch_lower})]_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); + env->[(${config.mainRegisterFile})][ [(${config.sysReg})] ], + [# th:each="arg : ${config.args}"] + env->[(${config.mainRegisterFile})][ [(${arg})] ], + [/] + 0, 0); + [# th:if="${config.hasIcacheFlush}"] } + [/] if (ret == -QEMU_ERESTARTSYS) { - env->pc -= 4; + env->[(${pc_reg.name_lower})] -= [(${config.insn_width_bytes})]; } else if (ret != -QEMU_ESIGRETURN) { - env->x[RV64UME_REG_A0] = ret; - } + env->[(${config.mainRegisterFile})][ [(${config.retReg})] ] = ret; } if (cs->singlestep_enabled) { goto gdbstep; } break; - case RV64UME_EXC_ILLEGAL_INSTR: - force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->pc); + + case [(${gen_arch_upper})]_EXC_[(${config.illegalInstrExcName})]: + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->[(${pc_reg.name_lower})]); break; - case RV64UME_EXC_BREAKPOINT: + case [(${gen_arch_upper})]_EXC_[(${config.breakpointExcName})]: case EXCP_DEBUG: -gdbstep: - force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); + gdbstep: + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->[(${pc_reg.name_lower})]); break; default: EXCP_DUMP(env, - "\nqemu: unhandled rv64ume exception cause %#x - aborting\n", + "\nqemu: unhandled [(${gen_arch_lower})] exception cause %#x - aborting\n", cause); exit(EXIT_FAILURE); } @@ -98,6 +101,43 @@ void cpu_loop(CPURV64UMEState *env) trapnr); exit(EXIT_FAILURE); } + [/] + [# th:if="${#strings.isEmpty(config.excCauseVar)}"] + case [(${gen_arch_upper})]_EXC_[(${config.syscallInstr})]: + env->[(${pc_reg.name_lower})] += [(${config.insn_width_bytes})]; + [# th:if="${config.hasIcacheFlush}"] + if (env->[(${config.mainRegisterFile})][ [(${config.sysReg})] ] == TARGET_NR_[(${gen_arch_lower})]_flush_icache) { + /* no-op in QEMU; TB invalidation is automatic */ + ret = 0; + } else { + [/] + ret = do_syscall(env, + env->[(${config.mainRegisterFile})][ [(${config.sysReg})] ], + [# th:each="arg : ${config.args}"] + env->[(${config.mainRegisterFile})][ [(${arg})] ], + [/] + 0, 0); + [# th:if="${config.hasIcacheFlush}"] + } + [/] + if (ret == -QEMU_ERESTARTSYS) { + env->[(${pc_reg.name_lower})] -= [(${config.insn_width_bytes})]; + } else if (ret != -QEMU_ESIGRETURN) { + env->[(${config.mainRegisterFile})][ [(${config.retReg})] ] = ret; } + if (cs->singlestep_enabled) { + goto gdbstep; + } + break; + + case [(${gen_arch_upper})]_EXC_[(${config.illegalInstrExcName})]: + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->[(${pc_reg.name_lower})]); + break; + case [(${gen_arch_upper})]_EXC_[(${config.breakpointExcName})]: + case EXCP_DEBUG: + gdbstep: + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->[(${pc_reg.name_lower})]); + break; + [/] process_pending_signals(env); } @@ -109,8 +149,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_reg.name_lower})] = regs->[(${config.initialPc})]; + env->[(${config.mainRegisterFile})][ [(${config.spReg})] ] = regs->[(${config.initalSp})]; 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..cd085a196 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,5 +1,5 @@ /* - * Emulation of Linux signals for rv64ume user-mode. + * Emulation of Linux signals for [(${gen_arch_lower})] user-mode. */ #include "qemu/osdep.h" @@ -9,13 +9,16 @@ #include "linux-user/trace.h" /* - * Minimal sigcontext matching rv64ume state: + * Minimal sigcontext matching [(${gen_arch_lower})] state: * - pc * - x1..x31 (x0 is always zero and omitted) */ struct target_sigcontext { - abi_long pc; - abi_long gpr[31]; + abi_long [(${pc_reg.name_lower})]; + + [# th:each="tensor : ${config.signalStateTensors}"] + abi_long [(${tensor.name_lower})][ [(${tensor.size})] ]; + [/] }; struct target_ucontext { @@ -33,7 +36,7 @@ struct target_rt_sigframe { }; static abi_ulong get_sigframe(struct target_sigaction *ka, - CPURV64UMEState *regs, size_t framesize) + CPU[(${gen_arch_upper})]State *regs, size_t framesize) { abi_ulong sp = get_sp_from_cpustate(regs); @@ -42,23 +45,25 @@ static abi_ulong get_sigframe(struct target_sigaction *ka, } sp = target_sigsp(sp, ka) - framesize; - sp &= ~0xf; + sp &= ~[(${config.stack_align_mask} ?: '0xf')]; return sp; } -static void setup_sigcontext(struct target_sigcontext *sc, CPURV64UMEState *env) +static void setup_sigcontext(struct target_sigcontext *sc, CPU[(${gen_arch_upper})]State *env) { int i; - __put_user(env->pc, &sc->pc); - for (i = 1; i < 32; i++) { - __put_user(env->x[i], &sc->gpr[i - 1]); - } + __put_user(env->[(${pc_reg.name_lower})], &sc->[(${pc_reg.name_lower})]); + [# th:each="tensor : ${config.signalStateTensors}"] + for (i = 0; i < [(${tensor.size})]; i++) { + __put_user(env->[(${tensor.name_lower})][i], &sc->[(${tensor.name_lower})][i]); + } + [/] } static void setup_ucontext(struct target_ucontext *uc, - CPURV64UMEState *env, target_sigset_t *set) + CPU[(${gen_arch_upper})]State *env, target_sigset_t *set) { int i; @@ -76,7 +81,7 @@ static void setup_ucontext(struct target_ucontext *uc, 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; @@ -91,12 +96,18 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, 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; + env->[(${pc_reg.name_lower})] = ka->_sa_handler; + env->[(${config.mainRegisterFile})][ [(${config.spReg})] ] = frame_addr; + env->[(${config.mainRegisterFile})][ [(${config.args[0]})] ] = sig; + [# th:if="${#lists.size(config.args) > 1}"] + env->[(${config.mainRegisterFile})][ [(${config.args[1]})] ] = frame_addr + offsetof(struct target_rt_sigframe, info); + [/] + [# th:if="${#lists.size(config.args) > 2}"] + env->[(${config.mainRegisterFile})][ [(${config.args[2]})] ] = frame_addr + offsetof(struct target_rt_sigframe, uc); + [/] + [# th:if="${config.raReg != null}"] + env->[(${config.mainRegisterFile})][ [(${config.raReg})] ] = default_rt_sigreturn; + [/] return; @@ -108,17 +119,19 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, force_sig(TARGET_SIGSEGV); } -static void restore_sigcontext(CPURV64UMEState *env, struct target_sigcontext *sc) +static void restore_sigcontext(CPU[(${gen_arch_upper})]State *env, struct target_sigcontext *sc) { int i; - __get_user(env->pc, &sc->pc); - for (i = 1; i < 32; ++i) { - __get_user(env->x[i], &sc->gpr[i - 1]); + __get_user(env->[(${pc_reg.name_lower})], &sc->[(${pc_reg.name_lower})]); + [# th:each="tensor : ${config.signalStateTensors}"] + for (i = 0; i < [(${tensor.size})]; i++) { + __get_user(env->[(${tensor.name_lower})][i], &sc->[(${tensor.name_lower})][i]); } + [/] } -static void restore_ucontext(CPURV64UMEState *env, struct target_ucontext *uc) +static void restore_ucontext(CPU[(${gen_arch_upper})]State *env, struct target_ucontext *uc) { sigset_t blocked; target_sigset_t target_set; @@ -135,12 +148,12 @@ static void restore_ucontext(CPURV64UMEState *env, struct target_ucontext *uc) restore_sigcontext(env, &uc->uc_mcontext); } -long do_rt_sigreturn(CPURV64UMEState *env) +long do_rt_sigreturn(CPU[(${gen_arch_upper})]State *env) { struct target_rt_sigframe *frame; abi_ulong frame_addr; - frame_addr = env->x[RV64UME_REG_SP]; + frame_addr = env->[(${config.mainRegisterFile})][ [(${config.spReg})] ]; trace_user_do_sigreturn(env, frame_addr); if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { goto badframe; @@ -163,8 +176,8 @@ 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 */ + __put_user([(${config.sigtrampLoadSyscallInstr})], tramp + 0); /* load rt_sigreturn syscall number */ + __put_user([(${config.sigtrampTrapInstr})], tramp + 1); /* syscall/trap instruction */ default_rt_sigreturn = sigtramp_page; unlock_user(tramp, sigtramp_page, 8); 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..6f0363942 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 interpolattions 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 = [(${config.raReg})], + [(${gen_arch_upper})]_REG_SP = [(${config.spReg})], + [(${gen_arch_upper})]_REG_TP = [(${config.tpReg})], + + [# th:each="arg, stat : ${config.args}"] + [(${gen_arch_upper})]_REG_ARG[(${stat.index})] = [(${arg})], + [/] }; -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..3860bde86 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,11 +2,11 @@ * 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; @@ -43,7 +43,7 @@ struct target_pt_regs { 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/vadl/iss/passes/UmeHardcodedRiscvDefinitionPass.java b/vadl/main/vadl/iss/passes/UmeHardcodedRiscvDefinitionPass.java new file mode 100644 index 000000000..a81cf8b5c --- /dev/null +++ b/vadl/main/vadl/iss/passes/UmeHardcodedRiscvDefinitionPass.java @@ -0,0 +1,47 @@ +// 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 javax.annotation.CheckForNull; +import vadl.configuration.IssConfiguration; +import vadl.pass.PassName; +import vadl.pass.PassResults; +import vadl.viam.Specification; +import vadl.viam.UserModeEmulation; + +/** + * 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"); + } + + @CheckForNull + @Override + public Object execute(PassResults passResults, Specification viam) throws IOException { + UserModeEmulation ume = UserModeEmulation.createDummySolution(); + viam.add(ume); + return null; + } +} diff --git a/vadl/main/vadl/iss/passes/UmeTemplateRenderingPass.java b/vadl/main/vadl/iss/passes/UmeTemplateRenderingPass.java new file mode 100644 index 000000000..87712d3fe --- /dev/null +++ b/vadl/main/vadl/iss/passes/UmeTemplateRenderingPass.java @@ -0,0 +1,100 @@ +// 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.Map; +import vadl.configuration.IssConfiguration; +import vadl.iss.template.IssTemplateRenderingPass; +import vadl.lcb.templateUtils.RegisterUtils; +import vadl.pass.PassName; +import vadl.pass.PassResults; +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")); + + vars.put("config", Map.ofEntries( + Map.entry("sysReg", ume.getSysReg().index()), + Map.entry("retReg", ume.getRetReg().index()), + Map.entry("spReg", ume.getSpReg().index()), + Map.entry("raReg", ume.getRaReg().index()), + Map.entry("tpReg", ume.getTpReg().index()), + Map.entry("args", ume.getArgs().stream() + .map(RegisterUtils.Register::index) + .toList()), + Map.entry("excIds", ume.getExcIds()), + Map.entry("syscallInstr", ume.getSyscallInstr().simpleName()), + Map.entry("syscallException", ume.getSyscallException().simpleName()), + Map.entry("breakpointExcName", ume.getBreakpointExcName().simpleName()), + Map.entry("illegalInstrExcName", ume.getIllegalInstrExcName().simpleName()), + Map.entry("initialPc", ume.getInitialPc().simpleName()), + Map.entry("initialSp", ume.getInitialSp().simpleName()), + Map.entry("excCauseVar", ume.getExcCauseVar() != null + ? ume.getExcCauseVar().simpleName() + : ""), + Map.entry("hasIcacheFlush", ume.hasIcacheFlush()), + Map.entry("insn_width_bytes", ume.getInsnWidthBytes()), + Map.entry("stack_align_mask", ume.getStackAlignMask()), + Map.entry("sigtrampLoadSyscallInstr", ume.getSigtrampLoadSyscallInstr()), + Map.entry("sigtrampTrapInstr", ume.getSigtrampTrapInstr()), + Map.entry("mainRegisterFile", ume.getMainRegisterFile().simpleName().toLowerCase()), + Map.entry("mainRegFileSize", ume.getMainRegisterFile().outermostDim().size()), + Map.entry("signalStateTensors", ume.getSignalStateTensors().stream() + .map(t -> Map.of( + "name_lower", t.simpleName().toLowerCase(), + "size", t.outermostDim().size() + )) + .toList()) + )); + + return vars; + } +} diff --git a/vadl/main/vadl/pass/PassOrders.java b/vadl/main/vadl/pass/PassOrders.java index 4af85df7d..1bcccc777 100644 --- a/vadl/main/vadl/pass/PassOrders.java +++ b/vadl/main/vadl/pass/PassOrders.java @@ -70,6 +70,8 @@ import vadl.iss.passes.IssSelectLoweringPass; import vadl.iss.passes.IssTcgSchedulingPass; import vadl.iss.passes.IssTcgVAllocationPass; +import vadl.iss.passes.UmeHardcodedRiscvDefinitionPass; +import vadl.iss.passes.UmeTemplateRenderingPass; import vadl.iss.passes.IssTensorAssignmentToForallPass; import vadl.iss.passes.opDecomposition.IssOpDecompositionPass; import vadl.iss.passes.safeResourceRead.IssSafeResourceReadPass; @@ -707,6 +709,8 @@ private static void addUserModeEmitPasses(PassOrder order, IssConfiguration conf var inputPath = config.inputPath(); if (inputPath != null && inputPath.getFileName().endsWith("rv64ume.vadl")) { order + .add(new UmeHardcodedRiscvDefinitionPass(config)) + .add(new UmeTemplateRenderingPass(config, "cpu_loop.c")) .add(issDefault("/configs/targets/gen-arch-linux-user.mak", config)) .add(issDefault("/linux-user/meson.build", config)) @@ -714,12 +718,13 @@ private static void addUserModeEmitPasses(PassOrder order, IssConfiguration conf .add(issDefault("/linux-user/syscall_defs.h", config)) .add(issDefault("/linux-user/gen-arch/meson.build", config)) - .add(issDefault("/linux-user/gen-arch/cpu_loop.c", config)) - .add(issDefault("/linux-user/gen-arch/signal.c", 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(issDefault("/linux-user/gen-arch/target_cpu.h", 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)) diff --git a/vadl/main/vadl/viam/DefinitionVisitor.java b/vadl/main/vadl/viam/DefinitionVisitor.java index b8981a40c..699209e5f 100644 --- a/vadl/main/vadl/viam/DefinitionVisitor.java +++ b/vadl/main/vadl/viam/DefinitionVisitor.java @@ -70,6 +70,8 @@ public interface DefinitionVisitor { void visit(Abi abi); + void visit(UserModeEmulation userModeEmulation); + void visit(Processor processor); void visit(MicroArchitecture microArchitecture); @@ -301,6 +303,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); @@ -538,6 +546,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..2109cd05c --- /dev/null +++ b/vadl/main/vadl/viam/UserModeEmulation.java @@ -0,0 +1,333 @@ +// 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.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.IntStream; +import vadl.lcb.templateUtils.RegisterUtils; +import vadl.types.BitsType; +import vadl.types.Type; +import vadl.utils.Pair; +import vadl.utils.SourceLocation; +import vadl.viam.graph.Graph; + +/** + * 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 Identifier excCauseVar; + private final RegisterTensor mainRegisterFile; + private final RegisterUtils.Register sysReg; + private final RegisterUtils.Register retReg; + private final RegisterUtils.Register spReg; + private final RegisterUtils.Register raReg; + private final RegisterUtils.Register tpReg; + private final List signalStateTensors; + private final List args; + private final Map excIds; + private final Instruction syscallInstr; + private final ExceptionDef syscallException; + private final ExceptionDef breakpointExcName; + private final ExceptionDef illegalInstrExcName; + private final Identifier initialPc; + private final Identifier initialSp; + private final boolean hasIcacheFlush; + private final int insnWidthBytes; + private final int stackAlignMask; + private final int sigtrampLoadSyscallInstr; + private final int sigtrampTrapInstr; + + /** + * Constructs a UserModeEmulation configuration. + */ + public UserModeEmulation( + Identifier identifier, ExceptionDef syscallException, + RegisterTensor mainRegisterFile, + RegisterUtils.Register sysReg, RegisterUtils.Register retReg, + RegisterUtils.Register spReg, RegisterUtils.Register raReg, RegisterUtils.Register tpReg, + List signalStateTensors, + List args, Map excIds, + Instruction syscallInstr, ExceptionDef breakpointExcName, + ExceptionDef illegalInstrExcName, + Identifier initialPc, Identifier initialSp, Identifier excCauseVar, boolean hasIcacheFlush, + int insnWidthBytes, int stackAlignMask, int sigtrampLoadSyscallInstr, + int sigtrampTrapInstr + ) { + + super(identifier); + this.syscallException = syscallException; + this.mainRegisterFile = mainRegisterFile; + this.signalStateTensors = signalStateTensors; + if (args == null || args.isEmpty()) { + throw new IllegalArgumentException("args must not be null/empty"); + } + + if (excIds == null || excIds.isEmpty()) { + throw new IllegalArgumentException("excIds must not be null/empty"); + } + + this.sysReg = sysReg; + this.retReg = retReg; + this.spReg = spReg; + this.raReg = raReg; + this.tpReg = tpReg; + this.args = args; + this.excIds = excIds; + this.syscallInstr = syscallInstr; + this.breakpointExcName = breakpointExcName; + this.illegalInstrExcName = illegalInstrExcName; + this.initialPc = initialPc; + this.initialSp = initialSp; + this.excCauseVar = excCauseVar; + this.hasIcacheFlush = hasIcacheFlush; + this.insnWidthBytes = insnWidthBytes; + this.stackAlignMask = stackAlignMask; + this.sigtrampLoadSyscallInstr = sigtrampLoadSyscallInstr; + this.sigtrampTrapInstr = sigtrampTrapInstr; + } + + /** + * 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() { + Identifier identifier = new Identifier(new String[]{"ume"}, + SourceLocation.INVALID_SOURCE_LOCATION); + + RegisterTensor.Dimension regDim = new RegisterTensor.Dimension( + 0, + Type.bits(5), + 32 + ); + + RegisterTensor.Dimension dummyDim = new RegisterTensor.Dimension( + 1, + Type.bits(1), + 1 + ); + + List dimensions = List.of(regDim, dummyDim); + RegisterTensor mainFile = new RegisterTensor( + new Identifier(new String[]{"x"}, + SourceLocation.INVALID_SOURCE_LOCATION), + dimensions + ); + + var dummyMap = new HashMap, List>(); + + RegisterUtils.RegisterClass gprClass = RegisterUtils.getRegisterClass(mainFile, dummyMap); + + RegisterUtils.Register sp = gprClass.registers().get(2); + RegisterUtils.Register ra = gprClass.registers().get(1); + RegisterUtils.Register tp = gprClass.registers().get(4); + RegisterUtils.Register sys = gprClass.registers().get(17); + RegisterUtils.Register ret = gprClass.registers().get(10); + + List args = IntStream.range(10, 16) + .mapToObj(i -> gprClass.registers().get(i)) + .toList(); + + Parameter[] emptyParams = new Parameter[0]; + Graph emptyGraph = new Graph("empty_graph"); + + ExceptionDef mockSyscallExc = new ExceptionDef( + new Identifier(new String[]{"EXC"}, + SourceLocation.INVALID_SOURCE_LOCATION), + emptyParams, + emptyGraph, + ExceptionDef.Kind.DECLARED + ); + + ExceptionDef mockBreakpointExc = new ExceptionDef( + new Identifier(new String[]{"BREAKPOINT"}, + SourceLocation.INVALID_SOURCE_LOCATION), + emptyParams, + emptyGraph, + ExceptionDef.Kind.DECLARED + ); + + ExceptionDef mockIllegalExc = new ExceptionDef( + new Identifier(new String[]{"ILLEGAL_INSTR"}, + SourceLocation.INVALID_SOURCE_LOCATION), + emptyParams, + emptyGraph, + ExceptionDef.Kind.DECLARED + ); + + 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 + ); + + + Identifier riscvCauseVar = new Identifier(new String[]{"arg_exc_cause"}, + SourceLocation.INVALID_SOURCE_LOCATION); + + Map excIds = Map.of( + "ILLEGAL_INSTR", 2, + "BREAKPOINT", 3, + "ECALL", 11 + ); + + Identifier ptRegsPcField = new Identifier(new String[]{"sepc"}, + SourceLocation.INVALID_SOURCE_LOCATION); + Identifier ptRegsSpField = new Identifier(new String[]{"sp"}, + SourceLocation.INVALID_SOURCE_LOCATION); + + List signalStateTensors = List.of(mainFile); + + return new UserModeEmulation( + identifier, + mockSyscallExc, + mainFile, + sys, ret, sp, ra, tp, + signalStateTensors, args, + excIds, + mockSyscallInsn, + mockBreakpointExc, + mockIllegalExc, + ptRegsPcField, ptRegsSpField, riscvCauseVar, + true, 4, 0xf, + 0x08b00893, 0x00000073); + } + + public int getSigtrampLoadSyscallInstr() { + return sigtrampLoadSyscallInstr; + } + + public int getSigtrampTrapInstr() { + return sigtrampTrapInstr; + } + + public int getStackAlignMask() { + return stackAlignMask; + } + + public int getInsnWidthBytes() { + return insnWidthBytes; + } + + public Identifier getInitialPc() { + return initialPc; + } + + public Identifier getInitialSp() { + return initialSp; + } + + public Identifier getExcCauseVar() { + return excCauseVar; + } + + public boolean hasIcacheFlush() { + return hasIcacheFlush; + } + + public Instruction getSyscallInstr() { + return syscallInstr; + } + + public ExceptionDef getBreakpointExcName() { + return breakpointExcName; + } + + public ExceptionDef getIllegalInstrExcName() { + return illegalInstrExcName; + } + + public RegisterUtils.Register getSysReg() { + return sysReg; + } + + public RegisterUtils.Register getRetReg() { + return retReg; + } + + public RegisterUtils.Register getSpReg() { + return spReg; + } + + public RegisterUtils.Register getRaReg() { + return raReg; + } + + public RegisterUtils.Register getTpReg() { + return tpReg; + } + + public List getArgs() { + return args; + } + + public Map getExcIds() { + return excIds; + } + + public ExceptionDef getSyscallException() { + return syscallException; + } + + public RegisterTensor getMainRegisterFile() { + return mainRegisterFile; + } + + @Override + public void accept(DefinitionVisitor visitor) { + visitor.visit(this); + } + + @Override + public String toString() { + return simpleName() + " [sysReg=" + sysReg + ", retReg=" + retReg + ", spReg=" + spReg + + ", raReg=" + raReg + ", tpReg=" + tpReg + ", args=" + args + ", excIds=" + excIds + "]"; + } + + public List getSignalStateTensors() { + return signalStateTensors; + } +}