From 939c7e39ddbd0f9ffce5da65729d375334bd46c0 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 9 Aug 2022 12:30:48 +1000 Subject: [PATCH] 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: 813e2317bf1f ("execute1: Restructure to separate out execution of side effects", 2022-06-18) Reported-by: Anton Blanchard Signed-off-by: Paul Mackerras --- execute1.vhdl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/execute1.vhdl b/execute1.vhdl index d77b16f..fd20c01 100644 --- a/execute1.vhdl +++ b/execute1.vhdl @@ -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