aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--target-arm/cpu.h29
-rw-r--r--target-arm/helper.h1
-rw-r--r--target-arm/op_helper.c18
-rw-r--r--target-arm/translate-a64.c11
-rw-r--r--target-arm/translate.c11
5 files changed, 66 insertions, 4 deletions
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index ab57f55..cc3d0ac 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -811,14 +811,29 @@ static inline int arm_current_pl(CPUARMState *env)
typedef struct ARMCPRegInfo ARMCPRegInfo;
-/* Access functions for coprocessor registers. These should return
- * 0 on success, or one of the EXCP_* constants if access should cause
- * an exception (in which case *value is not written).
- */
+typedef enum CPAccessResult {
+ /* Access is permitted */
+ CP_ACCESS_OK = 0,
+ /* Access fails due to a configurable trap or enable which would
+ * result in a categorized exception syndrome giving information about
+ * the failing instruction (ie syndrome category 0x3, 0x4, 0x5, 0x6,
+ * 0xc or 0x18).
+ */
+ CP_ACCESS_TRAP = 1,
+ /* Access fails and results in an exception syndrome 0x0 ("uncategorized").
+ * Note that this is not a catch-all case -- the set of cases which may
+ * result in this failure is specifically defined by the architecture.
+ */
+ CP_ACCESS_TRAP_UNCATEGORIZED = 2,
+} CPAccessResult;
+
+/* Access functions for coprocessor registers. These should always succeed. */
typedef int CPReadFn(CPUARMState *env, const ARMCPRegInfo *opaque,
uint64_t *value);
typedef int CPWriteFn(CPUARMState *env, const ARMCPRegInfo *opaque,
uint64_t value);
+/* Access permission check functions for coprocessor registers. */
+typedef CPAccessResult CPAccessFn(CPUARMState *env, const ARMCPRegInfo *opaque);
/* Hook function for register reset */
typedef void CPResetFn(CPUARMState *env, const ARMCPRegInfo *opaque);
@@ -872,6 +887,12 @@ struct ARMCPRegInfo {
* 2. both readfn and writefn are specified
*/
ptrdiff_t fieldoffset; /* offsetof(CPUARMState, field) */
+ /* Function for making any access checks for this register in addition to
+ * those specified by the 'access' permissions bits. If NULL, no extra
+ * checks required. The access check is performed at runtime, not at
+ * translate time.
+ */
+ CPAccessFn *accessfn;
/* Function for handling reads of this register. If NULL, then reads
* will be done by loading from the offset into CPUARMState specified
* by fieldoffset.
diff --git a/target-arm/helper.h b/target-arm/helper.h
index 7c60121..19bd620 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -57,6 +57,7 @@ DEF_HELPER_1(cpsr_read, i32, env)
DEF_HELPER_3(v7m_msr, void, env, i32, i32)
DEF_HELPER_2(v7m_mrs, i32, env, i32)
+DEF_HELPER_2(access_check_cp_reg, void, env, ptr)
DEF_HELPER_3(set_cp_reg, void, env, ptr, i32)
DEF_HELPER_2(get_cp_reg, i32, env, ptr)
DEF_HELPER_3(set_cp_reg64, void, env, ptr, i64)
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
index a918e5b..34c5e7f 100644
--- a/target-arm/op_helper.c
+++ b/target-arm/op_helper.c
@@ -273,6 +273,24 @@ void HELPER(set_user_reg)(CPUARMState *env, uint32_t regno, uint32_t val)
}
}
+void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip)
+{
+ const ARMCPRegInfo *ri = rip;
+ switch (ri->accessfn(env, ri)) {
+ case CP_ACCESS_OK:
+ return;
+ case CP_ACCESS_TRAP:
+ case CP_ACCESS_TRAP_UNCATEGORIZED:
+ /* These cases will eventually need to generate different
+ * syndrome information.
+ */
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ raise_exception(env, EXCP_UDEF);
+}
+
void HELPER(set_cp_reg)(CPUARMState *env, void *rip, uint32_t value)
{
const ARMCPRegInfo *ri = rip;
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index f6500e5..e70d14f 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -1193,6 +1193,17 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
return;
}
+ if (ri->accessfn) {
+ /* Emit code to perform further access permissions checks at
+ * runtime; this may result in an exception.
+ */
+ TCGv_ptr tmpptr;
+ gen_a64_set_pc_im(s->pc - 4);
+ tmpptr = tcg_const_ptr(ri);
+ gen_helper_access_check_cp_reg(cpu_env, tmpptr);
+ tcg_temp_free_ptr(tmpptr);
+ }
+
/* Handle special cases first */
switch (ri->type & ~(ARM_CP_FLAG_MASK & ~ARM_CP_SPECIAL)) {
case ARM_CP_NOP:
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 6d822c6..0805053 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -6837,6 +6837,17 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
return 1;
}
+ if (ri->accessfn) {
+ /* Emit code to perform further access permissions checks at
+ * runtime; this may result in an exception.
+ */
+ TCGv_ptr tmpptr;
+ gen_set_pc_im(s, s->pc);
+ tmpptr = tcg_const_ptr(ri);
+ gen_helper_access_check_cp_reg(cpu_env, tmpptr);
+ tcg_temp_free_ptr(tmpptr);
+ }
+
/* Handle special cases first */
switch (ri->type & ~(ARM_CP_FLAG_MASK & ~ARM_CP_SPECIAL)) {
case ARM_CP_NOP: