From 3204331568531ab8e4d8b8e45e3496b9563eb6eb Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Thu, 25 Apr 2024 08:39:53 -0700 Subject: [PATCH] draft: support for Hexagon VM Status: boots linux kernel up to setup_command_line() before panic Hacks: * vmnewmap call not implemented (does get invoked by linux) * vmyield call not implemented (does get invoked by linux) * trap0 not quite right (not _yet_ invoked by linux) Signed-off-by: Brian Cain --- docs/system/hexagon/hvm.rst | 186 +++++++++++++ hw/hexagon/hexagon_testboard.c | 1 + target/hexagon/cpu.c | 7 + target/hexagon/cpu.h | 1 + target/hexagon/gen_tcg.h | 2 + target/hexagon/gen_tcg_sys.h | 5 + target/hexagon/genptr.c | 98 +++++++ target/hexagon/helper.h | 1 + target/hexagon/hex_vm.c.inc | 321 +++++++++++++++++++++++ target/hexagon/hexswi.c | 26 ++ target/hexagon/op_helper.c | 6 + target/hexagon/reg_fields_def.h.inc | 4 + target/hexagon/sys_macros.h | 2 +- target/hexagon/translate.c | 4 + target/hexagon/translate.h | 1 + tests/tcg/hexagon/system/vmtest.c | 390 ++++++++++++++++++++++++++++ 16 files changed, 1054 insertions(+), 1 deletion(-) create mode 100644 docs/system/hexagon/hvm.rst create mode 100644 target/hexagon/hex_vm.c.inc create mode 100644 tests/tcg/hexagon/system/vmtest.c diff --git a/docs/system/hexagon/hvm.rst b/docs/system/hexagon/hvm.rst new file mode 100644 index 0000000000000..648a518f77304 --- /dev/null +++ b/docs/system/hexagon/hvm.rst @@ -0,0 +1,186 @@ +Hexagon Virtual Machine +======================= + +The hexagon virtual machine is a hypervisor that can partition a single +Hexagon DSP among multiple guest operating systems, and abstracts the +specific details of a DSP architectural revision for the sake of consistency +among generations. + +The ``virt`` machine has Hexagon VM emulation enabled by default. + +Events +------ + +The guest operating system should register the Guest Event Vector Base +via the ``vmsetvec`` virtual instruction at system startup. The vector table +and handlers are determined by the guest OS. + +Guests return from event handlers with ``vmrte``. This instruction will restore +the mode (user versus guest), interrupt enable state, PC, SP. + +.. list-table:: Event types + :header-rows: 1 + + * - Number + - Name + - Description + - Maskable + - Detail + * - 0 + - Reserved + - + - + - + * - 1 + - Machine check event + - unrecoverable VM state + - No + - execution terminates if unhandled + * - 2 + - General exception + - internal hardware or software exception + - No + - + * - 3-4 + - Reserved + - + - + - + * - 5 + - ``trap0`` + - ``trap0`` instruction + - No + - + * - 6 + - Reserved + - + - + - + * - 7 + - Interrupt + - external interrupts + - Yes + - increasing interrupt numbers have descending priority + +Startup +------- +In order to transition to user-mode, the guest OS must set the ``UM`` bit in +the guest status register and specify the address to start executing in +user mode in the guest event link register. + +Virtual Instructions +-------------------- + +.. list-table:: Virtual Instructions + :header-rows: 1 + + * - Instruction + - Behavior + - Operand + - Input + - Output + * - vmversion + - returns the VM version + - 0x0 + - requested VM version + - provided VM version + * - vmrte + - return from event + - 0x1 + - Event info in g3:0 + - N/A + * - vmsetvec + - set event vector + - 0x2 + - r0 is set to vector table addr + - r0 is 0 on success, 1 otherwise + * - vmsetie + - set interrupt enabled + - 0x3 + - r0 is set to 1 to enable, 0 to disable + - previous IE bit is stored as LSB of r0 + * - vmgetie + - get interrupt enabled + - 0x4 + - N/A + - current IE bit is stored as LSB of r0 + * - vmintop + - interrupt operation + - 0x5 + - r0 = Interrupt Op, r1-r4: Depends on Op + - r0 - value depends on operation + * - vmclrmap + - clear virtual memory map + - 0xa + - r0 = Interrupt Op, r1-r4: Depends on Op + - r0 - value depends on operation + * - vmnewmap + - set new virtual memory map + - 0xb + - r0 contains logical address of new segment table, r1 =Type of translations + - r0 contains 0 on success, otherwise negative error code + * - vmcache + - VM cache control: not modeled + - 0xd + - r0 contains the operation to be performed, r1 = Starting virtual address, r2 contains the length in bytes + - r0 contains 0 on success, otherwise -1. Cache behavior is not modeled so this operation always succeeds. + * - vmgettime + - Get virtual machine time + - 0xe + - N/A + - r0 contains the least significant 32 bits of timestamp, r1 contains the most significant 32 bits of timestamp + * - vmsettime + - Set virtual machine time + - 0xf + - r0 contains the least significant 32 bits of timestamp, r1 contains the most significant 32 bits of timestamp + - N/A + * - vmwait + - wait for interrupt + - 0x10 + - N/A + - r0 contains the interrupt number of the interrupt waking the guest + * - vmyield + - voluntarily yield VM task + - 0x11 + - N/A + - N/A + * - vmstart + - Create new virtual processor instance + - 0x12 + - r0 contains the starting execution address, r1 contains the starting stack pointer + - r0 contains the Virtual processor number of new virtual processor on success, otherwise -1 + * - vmstop + - terminate current virtual processor instance + - 0x13 + - N/A + - N/A + * - vmvpid + - get the virtual processor ID + - 0x14 + - N/A + - r0 contains the virtual processor number of virtual processor executing the instruction + * - vmsetregs + - Set guest registers + - 0x15 + - r0-3 hold g0-3 values + - N/A + * - vmgetregs + - Get guest registers + - 0x16 + - N/A + - r0-3 hold g0-3 values + * - vmtimerop + - perform an operation on a system timer + - 0x18 + - getfreq = 0 + getres = 1 + gettime = 2 + gettimeout = 3 + settimeout = 4 + deltatimeout = 5 + - TBD + * - vmgetinfo + - Get system info + - 0x1a + - Index of the system info parameter: FIXME another table + - value of the indicated system info parameter diff --git a/hw/hexagon/hexagon_testboard.c b/hw/hexagon/hexagon_testboard.c index 9bc079f8b1c4f..cb8c9ac7ea7dc 100644 --- a/hw/hexagon/hexagon_testboard.c +++ b/hw/hexagon/hexagon_testboard.c @@ -180,6 +180,7 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev, CPUHexagonState *env = &cpu->env; qemu_register_reset(do_cpu_reset, cpu); + qdev_prop_set_bit(DEVICE(cpu), "hexagon-vm", true); qdev_prop_set_uint32(DEVICE(cpu), "thread-count", machine->smp.cpus); qdev_prop_set_uint32(DEVICE(cpu), "config-table-addr", cfgExtensions->cfgbase); diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 30c7096b7787f..0a333047e1d2f 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -127,6 +127,7 @@ static Property hexagon_cpu_properties[] = { DEFINE_PROP_BOOL("isdben-dfd-enable", HexagonCPU, isdben_dfd_enable, false), DEFINE_PROP_BOOL("isdben-trusted", HexagonCPU, isdben_trusted, false), DEFINE_PROP_BOOL("isdben-secure", HexagonCPU, isdben_secure, false), + DEFINE_PROP_BOOL("hexagon-vm", HexagonCPU, hexagon_vm, false), DEFINE_PROP_STRING("dump-json-reg-file", HexagonCPU, dump_json_file), #endif DEFINE_PROP_UINT32("dsp-rev", HexagonCPU, rev_reg, 0), @@ -573,11 +574,17 @@ static void hexagon_restore_state_to_opc(CPUState *cs, #if !defined(CONFIG_USER_ONLY) void hexagon_cpu_soft_reset(CPUHexagonState *env) { + CPUState *cs = env_cpu(env); + HexagonCPU *cpu = HEXAGON_CPU(cs); ARCH_SET_SYSTEM_REG(env, HEX_SREG_SSR, 0); hexagon_ssr_set_cause(env, HEX_CAUSE_RESET); target_ulong evb = ARCH_GET_SYSTEM_REG(env, HEX_SREG_EVB); ARCH_SET_THREAD_REG(env, HEX_REG_PC, evb); + + if (cpu->hexagon_vm) { + ARCH_SET_SYSTEM_REG(env, HEX_SREG_IMASK, 0xffffffff); + } } #endif diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 3135086087695..af0356f35cec9 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -458,6 +458,7 @@ struct ArchCPU { bool isdben_dfd_enable; bool isdben_trusted; bool isdben_secure; + bool hexagon_vm; #endif uint32_t rev_reg; bool lldb_compat; diff --git a/target/hexagon/gen_tcg.h b/target/hexagon/gen_tcg.h index 3b341fd1220b6..84c11cde81b78 100644 --- a/target/hexagon/gen_tcg.h +++ b/target/hexagon/gen_tcg.h @@ -1437,6 +1437,8 @@ do { RsV = RsV; } while (0) #define fGEN_TCG_Y2_icinva(SHORTCODE) \ do { RsV = RsV; } while (0) +#define fGEN_TCG_J2_trap1(SHORTCODE) \ + do { (void) uiV; } while (0) #else /* data/insn cache ops can raise exceptions */ #define fGEN_TCG_CACHEOP(HELPER) \ diff --git a/target/hexagon/gen_tcg_sys.h b/target/hexagon/gen_tcg_sys.h index 054d1536c95d5..aa93f88bdcd92 100644 --- a/target/hexagon/gen_tcg_sys.h +++ b/target/hexagon/gen_tcg_sys.h @@ -135,4 +135,9 @@ tcg_gen_extrh_i64_i32(ctx->t_sreg_new_value[HEX_SREG_SGP1], tmp); \ } while (0) +#define fGEN_TCG_J2_trap1(SHORTCODE) \ + do { \ + gen_vminst(ctx, uiV); \ + } while (0) + #endif diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index efaa9757acbe0..af15e1c625b3a 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -1862,5 +1862,103 @@ static void gen_vtcm_memcpy(DisasContext *ctx, TCGv dst, TCGv src, TCGv size) gen_set_label(finish); } +#if !defined(CONFIG_USER_ONLY) +enum hex_virt_opc { + HEX_VIRT_VMVERSION = 0x00, + HEX_VIRT_VMRETURN = 0x01, + HEX_VIRT_VMSETVEC = 0x02, + HEX_VIRT_VMSETIE = 0x03, + HEX_VIRT_VMGETIE = 0x04, + HEX_VIRT_VMINTOP = 0x05, + HEX_VIRT_VMCLRMAP = 0x0A, + HEX_VIRT_VMNEWMAP = 0x0B, + HEX_VIRT_VMCACHECTL = 0x0D, + HEX_VIRT_VMGETTIME = 0x0E, + HEX_VIRT_VMSETTIME = 0x0F, + HEX_VIRT_VMWAIT = 0x10, + HEX_VIRT_VMYIELD = 0x11, + HEX_VIRT_VMSTART = 0x12, + HEX_VIRT_VMSTOP = 0x13, + HEX_VIRT_VMVMPID = 0x14, + HEX_VIRT_VMSETREGS = 0x15, + HEX_VIRT_VMGETREGS = 0x16, + HEX_VIRT_VMTIMEROP = 0x18, + HEX_VIRT_VMPMUCTRL = 0x19, + HEX_VIRT_VMGETINFO = 0x1A, +}; + +#include "hex_vm.c.inc" + +static void gen_vminst(DisasContext *ctx, int operand) +{ + if (!ctx->has_hexagon_vm) { + /* FIXME: raise an exception instead? */ + return; + } + + /* TODO: when swapping guest/user, must exchange GOSP, R29... */ + switch (operand) { + case HEX_VIRT_VMVERSION: + gen_vmversion(); + break; + case HEX_VIRT_VMSETREGS: + gen_vmsetregs(); + break; + case HEX_VIRT_VMGETREGS: + gen_vmgetregs(); + break; + case HEX_VIRT_VMSETIE: + gen_vmsetie(); + break; + case HEX_VIRT_VMGETIE: + gen_vmgetie(); + break; + case HEX_VIRT_VMVMPID: + gen_vmvpid(); + break; + case HEX_VIRT_VMCACHECTL: + gen_vmcache(); + break; + case HEX_VIRT_VMSTOP: + gen_vmstop(); + break; + case HEX_VIRT_VMYIELD: + gen_vmyield(); + break; + case HEX_VIRT_VMGETTIME: + gen_vmgettime(); + break; + case HEX_VIRT_VMRETURN: + gen_vmrte(); + break; + case HEX_VIRT_VMCLRMAP: + gen_vmclrmap(); + break; + case HEX_VIRT_VMNEWMAP: + gen_vmnewmap(); + break; + case HEX_VIRT_VMSETVEC: + gen_vmsetvec(); + break; + case HEX_VIRT_VMINTOP: + gen_vmintop(); + break; + case HEX_VIRT_VMGETINFO: + gen_vmgetinfo(); + break; + case HEX_VIRT_VMTIMEROP: + gen_vmtimerop(); + break; + + + default: + /* FIXME: Invalid packet exception? */ + fprintf(stderr, "Unknown VM instruction 0x%08x\n", operand); + g_assert_not_reached(); + break; + } +} +#endif + #include "tcg_funcs_generated.c.inc" #include "tcg_func_table_generated.c.inc" diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h index f7b6bb136feb6..9da5bb0f8cad6 100644 --- a/target/hexagon/helper.h +++ b/target/hexagon/helper.h @@ -148,6 +148,7 @@ DEF_HELPER_2(greg_read, i32, env, i32) DEF_HELPER_2(greg_read_pair, i64, env, i32) DEF_HELPER_1(inc_gcycle_xt, void, env) DEF_HELPER_3(modify_ssr, void, env, i32, i32) +DEF_HELPER_2(modify_syscfg, void, env, i32) DEF_HELPER_1(pending_interrupt, void, env) DEF_HELPER_3(raise_stack_overflow, void, env, i32, i32) DEF_HELPER_1(resched, void, env) diff --git a/target/hexagon/hex_vm.c.inc b/target/hexagon/hex_vm.c.inc new file mode 100644 index 0000000000000..d542c002d9719 --- /dev/null +++ b/target/hexagon/hex_vm.c.inc @@ -0,0 +1,321 @@ +/* + * Copyright(c) 2024 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * 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 2 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 . + */ + +#include "hex_regs.h" +#include "reg_fields.h" + +static inline void gen_vmsetregs(void) +{ + tcg_gen_mov_tl(hex_g_sreg[HEX_GREG_G0], hex_gpr[HEX_REG_R00]); + tcg_gen_mov_tl(hex_g_sreg[HEX_GREG_G1], hex_gpr[HEX_REG_R01]); + tcg_gen_mov_tl(hex_g_sreg[HEX_GREG_G2], hex_gpr[HEX_REG_R02]); + tcg_gen_mov_tl(hex_g_sreg[HEX_GREG_G3], hex_gpr[HEX_REG_R03]); +} + +static inline void gen_vmgetregs(void) +{ + tcg_gen_mov_tl(hex_gpr[HEX_REG_R00], hex_g_sreg[HEX_GREG_G0]); + tcg_gen_mov_tl(hex_gpr[HEX_REG_R01], hex_g_sreg[HEX_GREG_G1]); + tcg_gen_mov_tl(hex_gpr[HEX_REG_R02], hex_g_sreg[HEX_GREG_G2]); + tcg_gen_mov_tl(hex_gpr[HEX_REG_R03], hex_g_sreg[HEX_GREG_G3]); +} + +static inline void gen_vmsetie(void) +{ + TCGv old_ie = tcg_temp_new(); + tcg_gen_extract_tl(old_ie, hex_g_sreg[HEX_SREG_SYSCFG], + reg_field_info[SYSCFG_GIE].offset, + reg_field_info[SYSCFG_GIE].width); + + TCGv new_syscfg = tcg_temp_new(); + tcg_gen_deposit_tl(new_syscfg, hex_g_sreg[HEX_SREG_SYSCFG], + hex_gpr[HEX_REG_R00], + reg_field_info[SYSCFG_GIE].offset, + reg_field_info[SYSCFG_GIE].width); + gen_helper_modify_syscfg(tcg_env, new_syscfg); + tcg_gen_mov_tl(hex_gpr[HEX_REG_R00], old_ie); +} + +static inline void gen_vmgetie(void) +{ + tcg_gen_extract_tl(hex_gpr[HEX_REG_R00], hex_g_sreg[HEX_SREG_SYSCFG], + reg_field_info[SYSCFG_GIE].offset, + reg_field_info[SYSCFG_GIE].width); +} + +static void gen_vmvpid(void) +{ + tcg_gen_mov_tl(hex_gpr[HEX_REG_R00], hex_t_sreg[HEX_SREG_HTID]); +} + +static void gen_vmgettime(void) +{ + /* + * For now we'll take only the cycles from this vCPU, which seems + * reasonable for this use case. + */ + tcg_gen_extr_i64_i32(hex_gpr[HEX_REG_R00], hex_gpr[HEX_REG_R01], + hex_cycle_count); +} + +static void gen_vmcache(void) +{ + tcg_gen_movi_tl(hex_gpr[HEX_REG_R00], 0); +} + +static inline void gen_vmversion(void) +{ + tcg_gen_movi_tl(hex_gpr[HEX_REG_R00], 0x800); +} + +static inline void gen_vmstop(void) +{ + gen_helper_stop(tcg_env); +} +static inline void gen_vmstart(void) +{ + g_assert_not_reached(); +} +static inline void gen_vmyield(void) +{ + /* gen_exception(EXCP_YIELD, ctx->next_PC); ?? */ +} +static inline void gen_vmnewmap(void) +{ + /* g_assert_not_reached(); HACK FIXME */ +} +static inline void gen_vmclrmap(void) +{ + g_assert_not_reached(); +} + +typedef enum VmIntOp { + VMINTOP_NOP = 0, + VMINTOP_GLOBEN = 1, + VMINTOP_GLOBDIS = 2, + VMINTOP_LOCEN = 3, + VMINTOP_LOCDIS = 4, + VMINTOP_AFFINITY = 5, + VMINTOP_GET = 6, + VMINTOP_PEEK = 7, + VMINTOP_STATUS = 8, + VMINTOP_POST = 9, + VMINTOP_CLEAR = 10, +} VmIntOp; + +static inline void gen_vmintop(void) +{ + TCGLabel *global_en = gen_new_label(); + TCGLabel *global_dis = gen_new_label(); + TCGLabel *local_en = gen_new_label(); + TCGLabel *local_dis = gen_new_label(); + TCGLabel *affinity = gen_new_label(); + TCGLabel *get = gen_new_label(); + TCGLabel *peek = gen_new_label(); + TCGLabel *status = gen_new_label(); + TCGLabel *post = gen_new_label(); + TCGLabel *clear = gen_new_label(); + TCGLabel *exit_ = gen_new_label(); + + TCGv disable_irq = tcg_temp_new(); + tcg_gen_movi_tl(disable_irq, false); + + tcg_gen_brcondi_tl(TCG_COND_EQ, hex_gpr[HEX_REG_R00], VMINTOP_GLOBEN, + global_en); + tcg_gen_brcondi_tl(TCG_COND_EQ, hex_gpr[HEX_REG_R00], VMINTOP_GLOBDIS, + global_dis); + tcg_gen_brcondi_tl(TCG_COND_EQ, hex_gpr[HEX_REG_R00], VMINTOP_LOCEN, + local_en); + tcg_gen_brcondi_tl(TCG_COND_EQ, hex_gpr[HEX_REG_R00], VMINTOP_LOCDIS, + local_dis); + tcg_gen_brcondi_tl(TCG_COND_EQ, hex_gpr[HEX_REG_R00], VMINTOP_GET, + get); + tcg_gen_brcondi_tl(TCG_COND_EQ, hex_gpr[HEX_REG_R00], VMINTOP_AFFINITY, + affinity); + tcg_gen_brcondi_tl(TCG_COND_EQ, hex_gpr[HEX_REG_R00], VMINTOP_PEEK, + peek); + tcg_gen_brcondi_tl(TCG_COND_EQ, hex_gpr[HEX_REG_R00], VMINTOP_STATUS, + status); + tcg_gen_brcondi_tl(TCG_COND_EQ, hex_gpr[HEX_REG_R00], VMINTOP_POST, + post); + tcg_gen_brcondi_tl(TCG_COND_EQ, hex_gpr[HEX_REG_R00], VMINTOP_CLEAR, + clear); + + TCGv irq_mask = tcg_temp_new(); + TCGv set_mask = tcg_temp_new(); + TCGv clear_mask = tcg_temp_new(); + + gen_set_label(global_en); + tcg_gen_shl_tl(irq_mask, tcg_constant_tl(1), hex_gpr[HEX_REG_R01]); + gen_helper_fbrev(irq_mask, irq_mask); + gen_helper_ciad(tcg_env, irq_mask); + tcg_gen_movi_tl(hex_gpr[HEX_REG_R00], 0); + tcg_gen_br(exit_); + + gen_set_label(global_dis); + tcg_gen_movi_tl(hex_gpr[HEX_REG_R00], -1); + tcg_gen_br(exit_); + + gen_set_label(local_dis); + tcg_gen_movi_tl(disable_irq, true); + gen_set_label(local_en); + + /* + * set_mask = 1 << r1 + * clear_mask = brev(imask) & ~set_mask + * set_mask = brev(imask) | set_mask + * imask = brev(disable_irq ? set_mask : clear_mask) + */ + tcg_gen_shl_tl(set_mask, tcg_constant_tl(1), hex_gpr[HEX_REG_R01]); + gen_helper_fbrev(irq_mask, hex_t_sreg[HEX_SREG_IMASK]); + tcg_gen_neg_tl(clear_mask, set_mask); + tcg_gen_and_tl(clear_mask, irq_mask, clear_mask); + tcg_gen_or_tl(set_mask, irq_mask, set_mask); + tcg_gen_movcond_tl(TCG_COND_EQ, irq_mask, disable_irq, + tcg_constant_tl(true), set_mask, clear_mask); + gen_helper_fbrev(hex_t_sreg[HEX_SREG_IMASK], irq_mask); + tcg_gen_movi_tl(hex_gpr[HEX_REG_R00], 0); + tcg_gen_br(exit_); + + gen_set_label(affinity); + tcg_gen_shl_tl(clear_mask, tcg_constant_tl(1), hex_gpr[HEX_REG_R02]); + tcg_gen_neg_tl(clear_mask, clear_mask); + /* + * FIXME do we need `r10 = combine(r1.l,r10.l)` ? + */ + gen_helper_iassignw(tcg_env, clear_mask); + tcg_gen_movi_tl(hex_gpr[HEX_REG_R00], 0); + tcg_gen_br(exit_); + + gen_set_label(get); + /* FIXME implement */ + tcg_gen_movi_tl(hex_gpr[HEX_REG_R00], -1); + tcg_gen_br(exit_); + + gen_set_label(peek); + TCGv ipend = tcg_temp_new(); + TCGv high_irq = tcg_temp_new(); + tcg_gen_extract_tl(ipend, hex_g_sreg[HEX_SREG_IPENDAD], + reg_field_info[IPENDAD_IPEND].offset, + reg_field_info[IPENDAD_IPEND].width); + tcg_gen_clzi_tl(high_irq, ipend, 32); /* FIXME is that right? */ + const int MAX_IRQ = reg_field_info[IPENDAD_IPEND].width; + tcg_gen_movcond_tl(TCG_COND_GE, hex_gpr[HEX_REG_R00], high_irq, + tcg_constant_tl(MAX_IRQ), tcg_constant_tl(-1), high_irq); + tcg_gen_br(exit_); + + gen_set_label(status); + /* + * result: bitmask, bit 0: pending for IRQ # (IPEND) + * bit 1: locally enabled for IRQ # (~IAD) + * bit 2: globally enabled for IRQ # (~IMASK) + * failure: -1 (all 32 bits set) + * + * r0 = (cpuint_pending >> irq) & 1; + * r0 |= (((cpuint_enabled >> irq) & 1) << 2); + * ^^ but what about bit #2? + */ + TCGv iad = tcg_temp_new(); + tcg_gen_extract_tl(iad, hex_g_sreg[HEX_SREG_IPENDAD], + reg_field_info[IPENDAD_IAD].offset, + reg_field_info[IPENDAD_IAD].width); + tcg_gen_extract_tl(ipend, hex_g_sreg[HEX_SREG_IPENDAD], + reg_field_info[IPENDAD_IPEND].offset, + reg_field_info[IPENDAD_IPEND].width); + TCGv tmp = tcg_temp_new(); + + tcg_gen_shr_tl(tmp, ipend, hex_gpr[HEX_REG_R01]); + tcg_gen_andi_tl(tmp, tmp, 0x1); + + tcg_gen_shr_tl(hex_gpr[HEX_REG_R00], iad, hex_gpr[HEX_REG_R01]); + tcg_gen_andi_tl(hex_gpr[HEX_REG_R00], hex_gpr[HEX_REG_R00], 0x1); + tcg_gen_shli_tl(hex_gpr[HEX_REG_R00], hex_gpr[HEX_REG_R00], 1); + + tcg_gen_or_tl(hex_gpr[HEX_REG_R00], hex_gpr[HEX_REG_R00], tmp); + + tcg_gen_shr_tl(tmp, hex_t_sreg[HEX_SREG_IMASK], hex_gpr[HEX_REG_R01]); + tcg_gen_andi_tl(tmp, tmp, 0x1); + tcg_gen_shli_tl(tmp, tmp, 2); + + /* Invert the sense of disabled, masked bits: 0b0110 */ + tcg_gen_or_tl(hex_gpr[HEX_REG_R00], hex_gpr[HEX_REG_R00], tmp); + tcg_gen_xori_tl(hex_gpr[HEX_REG_R00], hex_gpr[HEX_REG_R00], 0x06); + tcg_gen_br(exit_); + + gen_set_label(post); + tcg_gen_shl_tl(irq_mask, tcg_constant_tl(1), hex_gpr[HEX_REG_R01]); + gen_helper_fbrev(irq_mask, irq_mask); + gen_helper_swi(tcg_env, irq_mask); + tcg_gen_movi_tl(hex_gpr[HEX_REG_R00], 0); + tcg_gen_br(exit_); + + gen_set_label(clear); + tcg_gen_shl_tl(irq_mask, tcg_constant_tl(1), hex_gpr[HEX_REG_R01]); + gen_helper_fbrev(irq_mask, irq_mask); + gen_helper_cswi(tcg_env, irq_mask); + tcg_gen_movi_tl(hex_gpr[HEX_REG_R00], 1); /* FIXME why 1? */ + tcg_gen_br(exit_); + + gen_set_label(exit_); +} + +static inline void gen_vmsetvec(void) +{ + tcg_gen_mov_tl(hex_t_sreg[HEX_SREG_GEVB], hex_gpr[HEX_REG_R00]); + tcg_gen_movi_tl(hex_gpr[HEX_REG_R00], 0); +} +static inline void gen_vmrte(void) +{ + TCGv gsr_ie = tcg_temp_new(); + /* SYSCFG.GIE = GSR.IE */ + tcg_gen_extract_tl(gsr_ie, hex_t_sreg[HEX_GREG_GSR], + reg_field_info[GSR_IE].offset, + reg_field_info[GSR_IE].width); + tcg_gen_deposit_tl(hex_g_sreg[HEX_SREG_SYSCFG], hex_g_sreg[HEX_SREG_SYSCFG], + gsr_ie, + reg_field_info[SYSCFG_GIE].offset, + reg_field_info[SYSCFG_GIE].width); + + TCGv gsr_um = tcg_temp_new(); + tcg_gen_extract_tl(gsr_um, hex_t_sreg[HEX_GREG_GSR], + reg_field_info[GSR_UM].offset, + reg_field_info[GSR_UM].width); + TCGLabel *is_not_user_mode = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, gsr_um, 0, is_not_user_mode); + + /* if (GSR.UM) { Swap GOSP, r29 } */ + TCGv tmp = tcg_temp_new(); + tcg_gen_mov_tl(tmp, hex_t_sreg[HEX_GREG_GOSP]); + tcg_gen_mov_tl(hex_t_sreg[HEX_GREG_GOSP], hex_gpr[HEX_REG_R29]); + tcg_gen_mov_tl(hex_gpr[HEX_REG_R29], tmp); + + gen_set_label(is_not_user_mode); + + tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], hex_t_sreg[HEX_GREG_GELR]); + tcg_gen_movi_tl(hex_gpr[HEX_REG_R00], 0); +} + +static inline void gen_vmgetinfo(void) +{ + /* FIXME */ + tcg_gen_movi_tl(hex_gpr[HEX_REG_R00], 0); +} +static inline void gen_vmtimerop(void) +{ + /* FIXME */ + tcg_gen_movi_tl(hex_gpr[HEX_REG_R00], 1); +} diff --git a/target/hexagon/hexswi.c b/target/hexagon/hexswi.c index c3517d7a7f26e..89e80fba7c26c 100644 --- a/target/hexagon/hexswi.c +++ b/target/hexagon/hexswi.c @@ -201,6 +201,7 @@ static int sim_handle_trap_functional(CPUHexagonState *env) char c = swi_info; FILE *fp = stdout; + fprintf(stderr, "\twritecreg '%c'\n", c); fprintf(fp, "%c", c); fflush(stdout); } @@ -1026,6 +1027,15 @@ static void set_addresses(CPUHexagonState *env, target_ulong pc_offset, target_ulong exception_index) { + CPUState *cs = env_cpu(env); + HexagonCPU *cpu = HEXAGON_CPU(cs); + + if (cpu->hexagon_vm) { + ARCH_SET_SYSTEM_REG(env, HEX_GREG_GELR, + ARCH_GET_THREAD_REG(env, HEX_REG_PC) + pc_offset); + + g_assert(ARCH_GET_THREAD_REG(env, HEX_REG_PC) > 0x200); + } ARCH_SET_SYSTEM_REG(env, HEX_SREG_ELR, ARCH_GET_THREAD_REG(env, HEX_REG_PC) + pc_offset); ARCH_SET_THREAD_REG(env, HEX_REG_PC, @@ -1096,6 +1106,7 @@ void hexagon_cpu_do_interrupt(CPUState *cs) env->cause_code); if (env->cause_code == 0) { + fprintf(stderr, "\tTRAP0 cause code 0\n"); sim_handle_trap(env); } @@ -1301,6 +1312,8 @@ void hexagon_cpu_do_interrupt(CPUState *cs) void register_trap_exception(CPUHexagonState *env, int traptype, int imm, target_ulong PC) { + QEMU_IOTHREAD_LOCK_GUARD(); + HEX_DEBUG_LOG("%s:\n\ttid = %d, pc = 0x%" PRIx32 ", traptype %d, " "imm %d\n", @@ -1309,6 +1322,19 @@ void register_trap_exception(CPUHexagonState *env, int traptype, int imm, traptype, imm); CPUState *cs = env_cpu(env); + HexagonCPU *cpu = HEXAGON_CPU(cs); + + if (cpu->hexagon_vm) { + ASSERT_DIRECT_TO_GUEST_UNSET(env, cs->exception_index); + SET_SYSTEM_FIELD(env, HEX_GREG_GSR, GSR_CAUSE, imm); + /* + ARCH_SET_SYSTEM_REG(env, HEX_GREG_GELR, env->gpr[HEX_REG_PC] + sizeof(int32_t)); + uint32_t pc = ARCH_GET_SYSTEM_REG(env, HEX_SREG_GEVB) + (imm * sizeof(int32_t)); + */ + env->gpr[HEX_REG_PC] = PC + 4; /* HACK FIXME */ + return; + } + /* assert(cs->exception_index == HEX_EVENT_NONE); */ cs->exception_index = (traptype == 0) ? HEX_EVENT_TRAP0 : HEX_EVENT_TRAP1; diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index deab424964d81..b74dd2d830b97 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -1688,6 +1688,12 @@ void HELPER(modify_ssr)(CPUHexagonState *env, uint32_t new, uint32_t old) { hexagon_modify_ssr(env, new, old); } + +static void modify_syscfg(CPUHexagonState *env, uint32_t val); +void HELPER(modify_syscfg)(CPUHexagonState *env, uint32_t new) +{ + modify_syscfg(env, new); +} #endif diff --git a/target/hexagon/reg_fields_def.h.inc b/target/hexagon/reg_fields_def.h.inc index afb88c7e38bd1..1ec73ba4b2263 100644 --- a/target/hexagon/reg_fields_def.h.inc +++ b/target/hexagon/reg_fields_def.h.inc @@ -207,3 +207,7 @@ DEF_REG_FIELD(PMUCFG_CNT5_MSB, 10, 2) DEF_REG_FIELD(PMUCFG_CNT6_MSB, 12, 2) DEF_REG_FIELD(PMUCFG_CNT7_MSB, 14, 2) DEF_REG_FIELD(PMUCFG_THMASK, 16, 3) + +DEF_REG_FIELD(GSR_UM, 31, 1) +DEF_REG_FIELD(GSR_IE, 30, 1) +DEF_REG_FIELD(GSR_CAUSE, 0, 16) diff --git a/target/hexagon/sys_macros.h b/target/hexagon/sys_macros.h index 2b2b13ac8bc68..6da95a9910a2e 100644 --- a/target/hexagon/sys_macros.h +++ b/target/hexagon/sys_macros.h @@ -145,7 +145,7 @@ #define fVIRTINSN_RTE(IMM, REG) #define fGRE_ENABLED() GET_FIELD(CCR_GRE, READ_SREG(HEX_SREG_CCR)) #define fTRAP1_VIRTINSN(IMM) \ - (fGRE_ENABLED() && \ + (g_assert_not_reached() && fGRE_ENABLED() && \ (((IMM) == 1) || ((IMM) == 3) || ((IMM) == 4) || ((IMM) == 6))) #define fICINVIDX(REG) diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 8585c7b6167cc..f3efde8011f85 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -559,6 +559,9 @@ static bool need_next_PC(DisasContext *ctx) GET_ATTRIB(opcode, A_HWLOOP1_END)) { return true; } + if (ctx->has_hexagon_vm && opcode == J2_trap1) { + return true; + } } } /* @@ -1690,6 +1693,7 @@ static void hexagon_tr_init_disas_context(DisasContextBase *dcbase, ctx->pcycle_enabled = FIELD_EX32(hex_flags, TB_FLAGS, PCYCLE_ENABLED); ctx->gen_cacheop_exceptions = hex_cpu->cacheop_exceptions; ctx->pmu_enabled = FIELD_EX32(hex_flags, TB_FLAGS, PMU_ENABLED); + ctx->has_hexagon_vm = hex_cpu->hexagon_vm; #endif ctx->pcycle_enabled = FIELD_EX32(hex_flags, TB_FLAGS, PCYCLE_ENABLED); ctx->hvx_coproc_enabled = FIELD_EX32(hex_flags, TB_FLAGS, HVX_COPROC_ENABLED); diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h index 0e13b7c8314b8..d92602f378b1f 100644 --- a/target/hexagon/translate.h +++ b/target/hexagon/translate.h @@ -67,6 +67,7 @@ typedef struct DisasContext { int sreg_log_idx; bool need_cpu_limit; bool pmu_enabled; + bool has_hexagon_vm; TCGv t_sreg_new_value[NUM_SREGS]; TCGv greg_new_value[NUM_GREGS]; #endif diff --git a/tests/tcg/hexagon/system/vmtest.c b/tests/tcg/hexagon/system/vmtest.c new file mode 100644 index 0000000000000..9fc722ea4533e --- /dev/null +++ b/tests/tcg/hexagon/system/vmtest.c @@ -0,0 +1,390 @@ + +#include +#include +#include + +typedef struct { + union { + struct { + uint32_t r0; + uint32_t r1; + }; + uint64_t r0100; + }; + union { + struct { + uint32_t r2; + uint32_t r3; + }; + uint64_t r0302; + }; + uint32_t gevb; + uint32_t gelr; + uint32_t gssr; + uint32_t gosp; + uint32_t gbadva; + uint32_t vmstatus; + uint64_t totalcycles; + union { + uint32_t raw; + } id; +} VM_thread_context; + +static void FAIL(const char *err) +{ + puts(err); + exit(1); +} + +#define VMINST(TRAP_NUM) asm volatile("trap1(#" # TRAP_NUM ")\n\t") + +static void vmvpid(VM_thread_context *a) +{ + VMINST(0x11); +} +static void vmyield(VM_thread_context *a) +{ + VMINST(0x11); +} +static void vmgetie(VM_thread_context *a) +{ + VMINST(4); +} +static void vmsetregs(VM_thread_context *a) +{ + VMINST(0x15); +} +static void vmsetie(VM_thread_context *a) +{ + VMINST(3); +} +static void vmgettime(VM_thread_context *a) +{ + VMINST(0xe); +} +static void vmsettime(VM_thread_context *a) +{ + VMINST(0xf); +} +static void vmsetvec(VM_thread_context *a) +{ + VMINST(2); +} + +static void vmversion(VM_thread_context *a) +{ + VMINST(0); +} + +VM_thread_context a; + +int main() +{ +/* + asm volatile(GLOBAL_REG_STR " = %0 " : : "r"(&test_kg)); +*/ + uint32_t TH_expected_enable; + uint32_t TH_expected_disable; + uint32_t TH_enable_ret; + uint32_t TH_disable_ret; + uint32_t TH_expected_yield; + + + /* VMVERSION */ + vmversion(&a); + static const int EXPECTED_VERSION = 0x800; + if ((a.r0) != EXPECTED_VERSION) { + FAIL("vmversion"); + } + + /* VMSETVEC */ + a.r0 = 0xabcdef00; + a.gevb = 0; + vmsetvec(&a); + if (a.r0 != 0) { + FAIL("setvec/return"); + } + if (a.gevb != 0xabcdef00U) { + FAIL("setvec/vec"); + } + + /* VMSETIE */ + a.r0 = 0; + TH_disable_ret = a.vmstatus = 0; + TH_expected_disable = 1; + vmsetie(&a); + if (a.r0 != 0) { + FAIL("setie/0/0/ret"); + } + TH_expected_disable = 0; /* could be inlined */ + if (a.vmstatus != 0) { + FAIL("setie/0/0/vmstatus"); + } + + a.r0 = 0; + TH_disable_ret = 1; + /* a.vmstatus = vmSTATUS_IE; */ + TH_expected_disable = 1; + vmsetie(&a); + if (a.r0 != 1) { + FAIL("setie/0/1/ret"); + } + TH_expected_disable = 0; /* could be inlined */ + if (a.vmstatus != 0) { + FAIL("setie/0/1/vmstatus"); + } + + a.r0 = 1; + TH_enable_ret = 1; + /* a.vmstatus = vmSTATUS_IE; */ + TH_expected_enable = 1; + vmsetie(&a); + if (a.r0 != 1) { + FAIL("setie/1/1/ret"); + } + /* if (TH_expected_enable) { FAIL("Enable not called, maybe that's OK?"); } + */ + TH_expected_enable = 0; +/* + if (a.vmstatus != vmSTATUS_IE) { + FAIL("setie/1/1/vmstatus"); + } +*/ + + a.r0 = 1; + TH_enable_ret = a.vmstatus = 0; + TH_expected_enable = 1; + vmsetie(&a); + if (a.r0 != 0) { + FAIL("setie/1/0/ret"); + } + if (TH_expected_enable) { + FAIL("Enable not called. Probably bad"); + } + TH_expected_enable = 0; +/* + if (a.vmstatus != vmSTATUS_IE) { + FAIL("setie/1/0/vmstatus"); + } +*/ + + + /* VMGETIE */ + a.vmstatus = 0; + vmgetie(&a); + if (a.r0 != 0) { + FAIL("getie/0"); + } + + a.vmstatus = 0xff; + vmgetie(&a); + if (a.r0 != 1) { + FAIL("getie/f->1"); + } + + /* GETPCYCLES */ + a.totalcycles = 0x8000000000000000ULL; + vmgettime(&a); + if (a.r0100 <= 0x8000000000000000ULL) { + FAIL("get totalcycles"); + } + + /* SETPCYCLES */ + a.r0100 = 0; + vmsettime(&a); + if (a.r0100 != 0x0ULL) { + FAIL("set totalcycles"); + } + + /* YIELD */ + a.r0 = 0x123; + TH_expected_yield = 1; + vmyield(&a); + if (a.r0 != 0) { + FAIL("yield/ret"); + } + if (TH_expected_yield) { + FAIL("yield"); + } + + +#if 0 + /* STOP */ + a.r0 = 0x123; + TH_expected_stop = 1; + if (setjmp(env) == 0) { + vmstop(&a); + } + if (TH_expected_stop) { + FAIL("stop"); + } +#endif + + /* PID */ + a.id.raw = 0x1234abcd; + vmvpid(&a); + if (a.r0 != 0x1234abcd) { + FAIL("vmvpid"); + } + + /* SETREGS */ + a.gelr = a.gbadva = a.gssr = a.gosp = 0xdeadbeef; + a.r0100 = 0x0123456789abcdefULL; + a.r0302 = 0xfedcba9876543210ULL; + /* test_gregs_restore(&a); */ + vmsetregs(&a); + /* test_gregs_save(&a); */ + if (a.gelr != 0x89abcdef) { + FAIL("set/gelr"); + } + if (a.gssr != 0x01234567) { + FAIL("set/gssr"); + } + if (a.gosp != 0x76543210) { + FAIL("set/gosp"); + } + if (a.gbadva != 0xfedcba98) { + FAIL("set/gbadva"); + } + +#if 0 + /* GETREGS */ + a.r0302 = a.r0100 = 0xdeadbeefdeadbeefULL; + a.gbadva_gosp = 0x0123456789abcdefULL; + a.gssr_gelr = 0xfedcba9876543210ULL; + test_gregs_restore(&a); + vmgetregs(&a); + test_gregs_save(&a); + if (a.r0302 != 0x0123456789abcdefULL) { + FAIL("get/r3:2"); + } + if (a.r0100 != 0xfedcba9876543210ULL) { + FAIL("get/r1:0"); + } + + /* START */ + TH_expected_create = 1; + a.r0 = 0x10203040; + a.r1 = 0x50607080; + a.base_prio = 4; + a.vmblock = (void *)&a; + TH_create_ret = 0x0a0b0c0d; + vmstart(&a); + if (a.r0 != 0x0a0b0c0d) { + FAIL("start/ret"); + } + + /* WAIT */ + + TH_expected_work = 1; + TH_work_ret = 1; + a.r0 = 0; + vmwait(&a); + if (TH_expected_work != 0) { + FAIL("no work call"); + } + if (a.r0 != 1) { + FAIL("didn't return intno"); + } + + TH_expected_work = 1; + TH_work_ret = -1; + a.r0 = -1; + a.vmblock = &av; + av.waiting_cpus = 0; + a.id.cpuidx = 5; + test_kg.runlist[0] = &a; + test_kg.runlist_prios[0] = 4; + a.status = 0; + TH_expected_dosched = 1; + if (setjmp(env) == 0) { + vmwait(&a); + } + if (TH_expected_dosched != 0) { + FAIL("didn't dosched"); + } + if (a.r0 != -1) { + FAIL("r0 clobbered"); + } + if (av.waiting_cpus != (1 << 5)) { + FAIL("wrong waiting cpus"); + } + if (a.status != test_STATUS_VMWAIT) { + FAIL("wrong status"); + } + if (test_kg.runlist[0] != NULL) { + FAIL("runlist"); + } + if (test_kg.runlist_prios[0] != -1) { + FAIL("runlist_prios"); + } + + /* RETURN */ + + a.gelr = 0xCCCCCCCC; + a.gssr = 0x00000000; + a.gosp = 0xdeadbeef; + a.r29 = 0x44444444; + test_set_elr(0xDEAD0000); + a.r0100 = 0x1111222233334444ULL; + a.ssr = ((1 << SSR_GUEST_BIT) | (1 << SSR_SS_BIT)); + test_gregs_restore(&a); + vmreturn(&a); + test_gregs_save(&a); + if (test_get_elr() != 0xCCCCCCCC) { + FAIL("return/elr"); + } + if (a.r0100 != 0x1111222233334444ULL) { + FAIL("return/r0100 clobbered"); + } + if ((a.ssr & (1 << SSR_SS_BIT)) != 0) { + FAIL("SS set"); + } + if ((a.ssr & (1 << SSR_GUEST_BIT)) == 0) { + FAIL("GUEST not set"); + } + if (a.gosp != 0xdeadbeef) { + FAIL("swapped sp"); + } + if (a.r29 != 0x44444444) { + FAIL("swapped sp"); + } + + a.gelr = 0xCCCCCCCC; + a.gssr = 0xE0000000; + a.gosp = 0xdeadbeef; + a.r29 = 0x44444444; + test_set_elr(0xDEAD0000); + a.r0100 = 0x1111222233334444ULL; + a.ssr = ((1 << SSR_GUEST_BIT) | (0 << SSR_SS_BIT)); + test_gregs_restore(&a); + TH_expected_enable = 1; + vmreturn(&a); + test_gregs_save(&a); + if (TH_expected_enable == 1) { + FAIL("no enable call"); + } + if (test_get_elr() != 0xCCCCCCCC) { + FAIL("return/elr"); + } + if (a.r0100 != 0x1111222233334444ULL) { + FAIL("return/r0100 clobbered"); + } + if ((a.ssr & (1 << SSR_SS_BIT)) == 0) { + FAIL("SS not set"); + } + if ((a.ssr & (1 << SSR_GUEST_BIT)) != 0) { + FAIL("GUEST set"); + } + if (a.r29 != 0xdeadbeef) { + FAIL("didn't swap sp"); + } + if (a.gosp != 0x44444444) { + FAIL("didn't swap sp"); + } + +#endif + + puts("TEST PASSED"); + return 0; +}