diff mbox series

[for-7.1,v6,39/51] target/nios2: Implement Misaligned destination exception

Message ID 20220317050538.924111-40-richard.henderson@linaro.org
State Superseded
Headers show
Series target/nios2: Shadow register set, EIC and VIC | expand

Commit Message

Richard Henderson March 17, 2022, 5:05 a.m. UTC
Indirect branches, plus eret and bret optionally raise
an exception when branching to a misaligned address.
The exception is required when an mmu is enabled, but
enable it always because the fallback behaviour is not
documented (though presumably it discards low bits).

For the purposes of the linux-user cpu loop, if EXCP_UNALIGN
(misaligned data) were to arrive, it would be treated the
same as EXCP_UNALIGND (misaligned destination).  See the
!defined(CONFIG_NIOS2_ALIGNMENT_TRAP) block in kernel/traps.c.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 linux-user/nios2/cpu_loop.c |  6 ++++++
 target/nios2/op_helper.c    |  9 ++++++++-
 target/nios2/translate.c    | 15 ++++++++++++++-
 3 files changed, 28 insertions(+), 2 deletions(-)

Comments

Peter Maydell March 17, 2022, 4:37 p.m. UTC | #1
On Thu, 17 Mar 2022 at 05:36, Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> Indirect branches, plus eret and bret optionally raise
> an exception when branching to a misaligned address.
> The exception is required when an mmu is enabled, but
> enable it always because the fallback behaviour is not
> documented (though presumably it discards low bits).
>
> For the purposes of the linux-user cpu loop, if EXCP_UNALIGN
> (misaligned data) were to arrive, it would be treated the
> same as EXCP_UNALIGND (misaligned destination).  See the
> !defined(CONFIG_NIOS2_ALIGNMENT_TRAP) block in kernel/traps.c.
>
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> ---
>  linux-user/nios2/cpu_loop.c |  6 ++++++
>  target/nios2/op_helper.c    |  9 ++++++++-
>  target/nios2/translate.c    | 15 ++++++++++++++-
>  3 files changed, 28 insertions(+), 2 deletions(-)


> @@ -64,6 +64,13 @@ uint32_t helper_divu(CPUNios2State *env, uint32_t num, uint32_t den)
>  void helper_eret(CPUNios2State *env, uint32_t new_status, uint32_t new_pc)
>  {
>      Nios2CPU *cpu = env_archcpu(env);
> +    CPUState *cs = env_cpu(env);
> +
> +    if (unlikely(new_pc & 3)) {
> +        env->ctrl[CR_BADADDR] = new_pc;
> +        cs->exception_index = EXCP_UNALIGND;
> +        cpu_loop_exit_restore(cs, GETPC());
> +    }
>
>      /*
>       * Both estatus and bstatus have no constraints on write;
> @@ -74,6 +81,6 @@ void helper_eret(CPUNios2State *env, uint32_t new_status, uint32_t new_pc)
>
>      env->ctrl[CR_STATUS] = new_status;
>      env->pc = new_pc;
> -    cpu_loop_exit(env_cpu(env));
> +    cpu_loop_exit(cs);
>  }

The spec isn't clear about whether an unaligned-destination on
an eret is handled as "do the eret (ie restore status), then take
the exception when trying to set the new PC" or "take the exception
immediately" (ie whether it's always a nested exception, effectively).
I guess this is as good a guess as any.

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

thanks
-- PMM
Richard Henderson March 17, 2022, 5:41 p.m. UTC | #2
On 3/17/22 09:37, Peter Maydell wrote:
> The spec isn't clear about whether an unaligned-destination on
> an eret is handled as "do the eret (ie restore status), then take
> the exception when trying to set the new PC" or "take the exception
> immediately" (ie whether it's always a nested exception, effectively).
> I guess this is as good a guess as any.

The way I read the spec the first time is that the pc as reported to the exception points 
to the eret, and it's only badaddr that contains the misaligned address.  I simply assumed 
that would mean that the status change hadn't happened either.

I also see that I've failed to update gen_bxx for this.  Also unspecified is if the trap 
for e.g. beq happens *whenever* the low two bits of imm16 are set, or only if the equality 
holds.  I'm tempted to do the former -- other architectures would treat that case as an 
illegal instruction.


r~
diff mbox series

Patch

diff --git a/linux-user/nios2/cpu_loop.c b/linux-user/nios2/cpu_loop.c
index ea364b7d1f..67220128aa 100644
--- a/linux-user/nios2/cpu_loop.c
+++ b/linux-user/nios2/cpu_loop.c
@@ -43,6 +43,12 @@  void cpu_loop(CPUNios2State *env)
             force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTDIV, env->pc);
             break;
 
+        case EXCP_UNALIGN:
+        case EXCP_UNALIGND:
+            force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN,
+                            env->ctrl[CR_BADADDR]);
+            break;
+
         case EXCP_TRAP:
             switch (env->error_code) {
             case 0:
diff --git a/target/nios2/op_helper.c b/target/nios2/op_helper.c
index c93b66c9aa..849867becd 100644
--- a/target/nios2/op_helper.c
+++ b/target/nios2/op_helper.c
@@ -64,6 +64,13 @@  uint32_t helper_divu(CPUNios2State *env, uint32_t num, uint32_t den)
 void helper_eret(CPUNios2State *env, uint32_t new_status, uint32_t new_pc)
 {
     Nios2CPU *cpu = env_archcpu(env);
+    CPUState *cs = env_cpu(env);
+
+    if (unlikely(new_pc & 3)) {
+        env->ctrl[CR_BADADDR] = new_pc;
+        cs->exception_index = EXCP_UNALIGND;
+        cpu_loop_exit_restore(cs, GETPC());
+    }
 
     /*
      * Both estatus and bstatus have no constraints on write;
@@ -74,6 +81,6 @@  void helper_eret(CPUNios2State *env, uint32_t new_status, uint32_t new_pc)
 
     env->ctrl[CR_STATUS] = new_status;
     env->pc = new_pc;
-    cpu_loop_exit(env_cpu(env));
+    cpu_loop_exit(cs);
 }
 #endif /* !CONFIG_USER_ONLY */
diff --git a/target/nios2/translate.c b/target/nios2/translate.c
index f7bab0908b..1e784c8a37 100644
--- a/target/nios2/translate.c
+++ b/target/nios2/translate.c
@@ -170,11 +170,24 @@  static void gen_goto_tb(DisasContext *dc, int n, uint32_t dest)
 
 static void gen_jumpr(DisasContext *dc, int regno, bool is_call)
 {
-    tcg_gen_mov_tl(cpu_pc, load_gpr(dc, regno));
+    TCGLabel *l = gen_new_label();
+    TCGv test = tcg_temp_new();
+    TCGv dest = load_gpr(dc, regno);
+
+    tcg_gen_andi_tl(test, dest, 3);
+    tcg_gen_brcondi_tl(TCG_COND_NE, test, 0, l);
+    tcg_temp_free(test);
+
+    tcg_gen_mov_tl(cpu_pc, dest);
     if (is_call) {
         tcg_gen_movi_tl(dest_gpr(dc, R_RA), dc->base.pc_next);
     }
     tcg_gen_lookup_and_goto_ptr();
+
+    gen_set_label(l);
+    tcg_gen_st_tl(dest, cpu_env, offsetof(CPUNios2State, ctrl[CR_BADADDR]));
+    t_gen_helper_raise_exception(dc, EXCP_UNALIGND);
+
     dc->base.is_jmp = DISAS_NORETURN;
 }