execute1: Fix trace interrupt on sc instruction
authorPaul Mackerras <paulus@ozlabs.org>
Tue, 9 Aug 2022 02:30:48 +0000 (12:30 +1000)
committerPaul Mackerras <paulus@ozlabs.org>
Tue, 9 Aug 2022 02:30:48 +0000 (12:30 +1000)
This fixes a bug which causes a trace interrupt to store the wrong
value in SRR0 in the case where the instruction that has just
completed is followed by a sc (system call) instruction.  What happens
is that first the traced instruction sets ex1.trace_next.  Then, when
the sc instruction following it comes in, the execute1_actions process
sets v.e.last_nia to next_nia because it is an sc instruction, even
though it is not going to be executed -- we are going to take the
trace interrupt instead.  Then when the trace interrupt is taken, we
incorrectly set SRR0 to the incremented address (the address of the
instruction following the sc).

To fix this, we have execute1_actions set a new flag if the current
instruction is sc, and only set v.e.last_nia to next_nia if we
actually execute the sc (in the "if go = '1'" case).

Fixes: 813e2317bf1f ("execute1: Restructure to separate out execution of side effects", 2022-06-18)
Reported-by: Anton Blanchard <anton@linux.ibm.com>
Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
execute1.vhdl

index d77b16f889a26c28c5681608cb1c0ebe8f441446..fd20c01b395e3bd16b5922d2579d492b7720aa0f 100644 (file)
@@ -94,6 +94,7 @@ architecture behaviour of execute1 is
         complete : std_ulogic;
         exception : std_ulogic;
         trap : std_ulogic;
+        advance_nia : std_ulogic;
         new_msr : std_ulogic_vector(63 downto 0);
         take_branch : std_ulogic;
         direct_branch : std_ulogic;
@@ -1030,8 +1031,8 @@ begin
                 -- 0 would mean scv, so generate an illegal instruction interrupt
                 if e_in.insn(1) = '1' then
                     v.trap := '1';
+                    v.advance_nia := '1';
                     v.e.intr_vec := 16#C00#;
-                    v.e.last_nia := next_nia;
                     if e_in.valid = '1' then
                         report "sc";
                     end if;
@@ -1460,6 +1461,9 @@ begin
             v.div_in_progress := actions.start_div;
             v.br_mispredict := v.e.redirect and actions.direct_branch;
             exception := actions.trap;
+            if actions.advance_nia = '1' then
+                v.e.last_nia := next_nia;
+            end if;
 
             -- Go busy while division is happening because the
             -- divider is not pipelined.  Also go busy while a