execute1: Fix trace interrupt on sc instruction

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: 813e2317bf ("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>
pull/389/head
Paul Mackerras 2 years ago
parent 2641e6d5cd
commit 939c7e39dd

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

Loading…
Cancel
Save