diff --git a/docs/system/hexagon/hvm.rst b/docs/system/hexagon/hvm.rst
new file mode 100644
index 0000000000000..20aeabbd4b06e
--- /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: 0 indicates a logical address of a zero-terminated linear list, 1 indicates a set of page tables.
+ - 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 dd5d54609540f..9207f79a6d22d 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 709b17485196d..0d793327442fb 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -125,6 +125,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),
@@ -571,11 +572,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 adc8c89397175..2321ac3b87d7f 100644
--- a/target/hexagon/genptr.c
+++ b/target/hexagon/genptr.c
@@ -1870,5 +1870,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..e14f774284873 100644
--- a/target/hexagon/helper.h
+++ b/target/hexagon/helper.h
@@ -51,6 +51,9 @@ DEF_HELPER_2(nmi, void, env, i32)
DEF_HELPER_3(setprio, void, env, i32, i32)
DEF_HELPER_2(start, void, env, i32)
DEF_HELPER_1(stop, void, env)
+
+DEF_HELPER_4(vmnewmap, void, env, i32, i32, i32)
+DEF_HELPER_2(vmgetinfo, i32, env, i32)
#endif
DEF_HELPER_5(check_vtcm_memcpy, void, env, i32, i32, i32, i32)
@@ -148,6 +151,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..e0ad9bea27fa1
--- /dev/null
+++ b/target/hexagon/hex_vm.c.inc
@@ -0,0 +1,315 @@
+/*
+ * 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)
+{
+ gen_helper_vmnewmap(tcg_env, hex_gpr[HEX_REG_R01], hex_gpr[HEX_REG_R00],
+ hex_gpr[HEX_REG_R02]);
+}
+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)
+{
+ gen_helper_vmgetinfo(hex_gpr[HEX_REG_R00], tcg_env, hex_gpr[HEX_REG_R00]);
+}
+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..10886f544798e 100644
--- a/target/hexagon/hexswi.c
+++ b/target/hexagon/hexswi.c
@@ -1026,6 +1026,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 +1105,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 +1311,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 +1321,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 d3e54932ed673..aca84c3b702d2 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
@@ -2702,6 +2708,222 @@ void HELPER(check_vtcm_memcpy)(CPUHexagonState *env, uint32_t dst, uint32_t src,
}
}
+
+#if !defined(CONFIG_USER_ONLY)
+typedef enum {
+ HEXVM_ENTRY_DIRECTORY,
+ HEXVM_ENTRY_TABLE,
+ HEXVM_ENTRY_INVALID,
+} HexVMPTEEntryType;
+
+typedef struct {
+ HexVMPTEEntryType entry_type;
+ uint32_t page_size_bytes;
+ uint32_t l2_entries;
+ uint32_t addr_start_bit;
+ uint32_t addr_bit_count;
+} l1_pte_types;
+
+
+#define COPY_FIELD(dest, src, FIELD) \
+ dest = deposit64(dest, reg_field_info[FIELD].offset, \
+ reg_field_info[FIELD].width, \
+ extract32(src, reg_field_info[VM##FIELD].offset, \
+ reg_field_info[VM##FIELD].width))
+
+void HELPER(vmnewmap)(CPUHexagonState *env, uint32_t map_type, uint32_t input,
+ uint32_t tlb_inval)
+{
+ CPUState *cs = env_cpu(env);
+ static const l1_pte_types PTE_TYPES[] = {
+ /* FIXME: off-by-one bit start/count? */
+ { HEXVM_ENTRY_DIRECTORY, 4 * 1024, 1024, 12, 20, },
+ { HEXVM_ENTRY_DIRECTORY, 16 * 1024, 256, 10, 22, },
+ { HEXVM_ENTRY_DIRECTORY, 64 * 1024, 64, 8, 24, },
+ { HEXVM_ENTRY_DIRECTORY, 256 * 1024, 16, 6, 26, },
+ { HEXVM_ENTRY_DIRECTORY, 1024 * 1024, 4, 4, 28, },
+ { HEXVM_ENTRY_TABLE, 4 * 1024 * 1024, 0, 0, 0, },
+ { HEXVM_ENTRY_TABLE, 16 * 1024 * 1024, 0, 0, 0, },
+ { HEXVM_ENTRY_INVALID, 0, 0, 0, 0, },
+ };
+ static const int TYPE_MASK = 0x07;
+
+ bool fail = false;
+
+ /*
+ * LARGE PARTS OF THE CODE BELOW APPEARS TO BE WRONG. IT DOES
+ * NOT WORK AS EXPECTED.
+ */
+
+ switch (map_type) {
+ case 0:
+ /* FIXME: linear list... */
+ g_assert_not_reached();
+ break;
+ case 1:
+ ;
+ const l1_pte_types e = PTE_TYPES[input & TYPE_MASK];
+ switch (e.entry_type) {
+ case HEXVM_ENTRY_DIRECTORY:
+ ;
+ uint32_t l2_table_la = input & ~(0x0f);
+ /* FIXME check the table addr alignment, should match table size */
+ uint32_t i;
+ for (i = 0; i < e.l2_entries; i++) {
+ /* L2 entries are 32-bits long */
+ uint32_t entry;
+ uint32_t offset = i * sizeof(entry);
+ address_space_read(cs->as, l2_table_la + offset,
+ MEMTXATTRS_UNSPECIFIED, &entry,
+ sizeof(entry));
+
+ /*
+ * How to know what terminates the list?
+ * linear lists are null-terminated.
+ */
+ if (entry == 0) {
+ break;
+ }
+ uint64_t phys_entry = 0;
+ deposit64(phys_entry, reg_field_info[PTE_V].offset,
+ reg_field_info[PTE_V].width, true);
+
+ COPY_FIELD(phys_entry, entry, PTE_R);
+ COPY_FIELD(phys_entry, entry, PTE_W);
+ COPY_FIELD(phys_entry, entry, PTE_X);
+ COPY_FIELD(phys_entry, entry, PTE_U);
+ COPY_FIELD(phys_entry, entry, PTE_C);
+ uint32_t logical_page_num =
+ extract32(entry, e.addr_start_bit, e.addr_bit_count);
+ /* COPY_FIELD(phys_entry, entry, PTE_VPN); */
+ phys_entry =
+ deposit64(phys_entry, reg_field_info[PTE_PPD].offset,
+ reg_field_info[PTE_PPD].width, logical_page_num);
+ phys_entry =
+ deposit64(phys_entry, reg_field_info[PTE_VPN].offset,
+ reg_field_info[PTE_VPN].width, logical_page_num);
+
+ bool r = extract32(entry, reg_field_info[VMPTE_R].offset,
+ reg_field_info[VMPTE_R].width);
+ bool w = extract32(entry, reg_field_info[VMPTE_W].offset,
+ reg_field_info[VMPTE_W].width);
+ bool x = extract32(entry, reg_field_info[VMPTE_X].offset,
+ reg_field_info[VMPTE_X].width);
+ g_assert(r || w || x);
+
+ switch (e.page_size_bytes) {
+ case 4 * 1024:
+ phys_entry = deposit64(phys_entry, 0, 1, true);
+ break;
+ case 16 * 1024:
+ phys_entry = deposit64(
+ phys_entry, reg_field_info[PTE_PPD].offset, 1, true);
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+
+ /*
+ * How to know which base TLB index to use?
+ * Keep some state for where the last added one was?
+ */
+ hex_tlbw(env, i, phys_entry);
+ }
+ g_assert_not_reached();
+
+ break;
+ case HEXVM_ENTRY_TABLE:
+ g_assert_not_reached();
+ break;
+ case HEXVM_ENTRY_INVALID:
+ default:
+ /* FIXME raise protection violation exception event */
+ fail = true;
+ break;
+ }
+ break;
+ default:
+ fail = true;
+ break;
+ }
+ env->gpr[HEX_REG_R00] = fail ? -1 : 0;
+}
+
+typedef enum {
+ HEX_VM_INFO_BUILD_ID,
+ HEX_VM_INFO_BOOT_FLAGS,
+ HEX_VM_INFO_STLB,
+ HEX_VM_INFO_SYSCFG,
+ HEX_VM_INFO_LIVELOCK,
+ HEX_VM_INFO_REV,
+ HEX_VM_INFO_SSBASE,
+ HEX_VM_INFO_TLB_FREE,
+ HEX_VM_INFO_TLB_SIZE,
+ HEX_VM_INFO_PHYSADDR,
+ HEX_VM_INFO_TCM_BASE,
+ HEX_VM_INFO_L2MEM_SIZE_BYTES,
+ HEX_VM_INFO_TCM_SIZE,
+ HEX_VM_INFO_H2K_PGSIZE,
+ HEX_VM_INFO_H2K_NPAGES,
+ HEX_VM_INFO_L2VIC_BASE,
+ HEX_VM_INFO_TIMER_BASE,
+ HEX_VM_INFO_TIMER_INT,
+ HEX_VM_INFO_ERROR,
+ HEX_VM_INFO_HTHREADS,
+ HEX_VM_INFO_L2TAG_SIZE,
+ HEX_VM_INFO_L2CFG_BASE,
+ HEX_VM_INFO_RESERVED_00,
+ HEX_VM_INFO_CFGBASE,
+ HEX_VM_INFO_HVX_VLENGTH,
+ HEX_VM_INFO_HVX_CONTEXTS,
+ HEX_VM_INFO_HVX_SWITCH,
+ HEX_VM_INFO_MAX,
+} HexVmInfoType;
+
+
+uint32_t HELPER(vmgetinfo)(CPUHexagonState *env, uint32_t info_type)
+{
+ HexagonCPU *cpu = env_archcpu(env);
+ hwaddr cfgtable_mem;
+ uint32_t cfg_val;
+
+ switch (info_type) {
+ case HEX_VM_INFO_BUILD_ID:
+ return 0x0001;
+ case HEX_VM_INFO_BOOT_FLAGS:
+ /* TODO: USE_TCM */
+ return 0;
+ case HEX_VM_INFO_STLB:
+ /* TODO: sets/ways/size/etc */
+ return 0;
+ case HEX_VM_INFO_SYSCFG:
+ /* TODO: host syscfg reg? */
+ return 0;
+ case HEX_VM_INFO_REV:
+ return cpu->rev_reg;
+ case HEX_VM_INFO_L2MEM_SIZE_BYTES:
+ /* location of l2size_kb entry: */
+ cfgtable_mem = cpu->config_table_addr + 0x44;
+ cpu_physical_memory_write(cfgtable_mem, &cfg_val, sizeof(cfg_val));
+ return cfg_val * 1024;
+ case HEX_VM_INFO_TCM_SIZE:
+ /* TODO: derive from l2 tags? */
+ return 0;
+ case HEX_VM_INFO_TCM_BASE:
+ cfgtable_mem = cpu->config_table_addr + 0;
+ cpu_physical_memory_write(cfgtable_mem, &cfg_val, sizeof(cfg_val));
+ return cfg_val << 16;
+ case HEX_VM_INFO_L2TAG_SIZE:
+ cfgtable_mem = cpu->config_table_addr + 0x40;
+ cpu_physical_memory_write(cfgtable_mem, &cfg_val, sizeof(cfg_val));
+ return cfg_val;
+ default:
+ return (uint32_t) -1;
+ }
+}
+#endif
+
/* These macros can be referenced in the generated helper functions */
#define warn(...) /* Nothing */
#define fatal(...) g_assert_not_reached();
diff --git a/target/hexagon/reg_fields_def.h.inc b/target/hexagon/reg_fields_def.h.inc
index afb88c7e38bd1..06c56d8653c0d 100644
--- a/target/hexagon/reg_fields_def.h.inc
+++ b/target/hexagon/reg_fields_def.h.inc
@@ -207,3 +207,18 @@ 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)
+
+/* Hexagon VM L2 Page table entries */
+
+/* VPN aka LPN: */
+DEF_REG_FIELD(VMPTE_VPN, 12, 20)
+DEF_REG_FIELD(VMPTE_X, 11, 1)
+DEF_REG_FIELD(VMPTE_W, 10, 1)
+DEF_REG_FIELD(VMPTE_R, 9, 1)
+DEF_REG_FIELD(VMPTE_C, 6, 3)
+DEF_REG_FIELD(VMPTE_U, 5, 1)
+DEF_REG_FIELD(VMPTE_TRUSTED, 4, 1)
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..46faa9e9ab0e5 100644
--- a/target/hexagon/translate.c
+++ b/target/hexagon/translate.c
@@ -559,6 +559,11 @@ static bool need_next_PC(DisasContext *ctx)
GET_ATTRIB(opcode, A_HWLOOP1_END)) {
return true;
}
+#if !defined(CONFIG_USER_ONLY)
+ if (ctx->has_hexagon_vm && opcode == J2_trap1) {
+ return true;
+ }
+#endif
}
}
/*
@@ -1690,6 +1695,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;
+}