From f6a0d7f9daacd657c5bf5ea837a0f9e91bb36bce Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 23 Apr 2020 21:54:08 +1000 Subject: [PATCH] MMU: Implement data segment interrupts A data segment interrupt (DSegI) occurs when an address to be translated by the MMU is outside the range of the radix tree or the top two bits of the address (the quadrant) are 01 or 10. This is detected in a new state of the MMU state machine, and is sent back to loadstore1 as an error, which sends it on to execute1 to generate an interrupt to the 0x380 vector. Signed-off-by: Paul Mackerras --- common.vhdl | 2 ++ execute1.vhdl | 6 ++++- loadstore1.vhdl | 7 ++++-- mmu.vhdl | 64 ++++++++++++++++++++++++++++++------------------- 4 files changed, 52 insertions(+), 27 deletions(-) diff --git a/common.vhdl b/common.vhdl index d617fa4..07d1a36 100644 --- a/common.vhdl +++ b/common.vhdl @@ -242,6 +242,7 @@ package common is type Loadstore1ToExecute1Type is record exception : std_ulogic; + segment_fault : std_ulogic; end record; type Loadstore1ToDcacheType is record @@ -280,6 +281,7 @@ package common is done : std_ulogic; invalid : std_ulogic; badtree : std_ulogic; + segerr : std_ulogic; sprval : std_ulogic_vector(63 downto 0); end record; diff --git a/execute1.vhdl b/execute1.vhdl index 5e25efc..7181f7f 100644 --- a/execute1.vhdl +++ b/execute1.vhdl @@ -974,7 +974,11 @@ begin -- generate DSI for load/store exceptions if l_in.exception = '1' then - ctrl_tmp.irq_nia <= std_logic_vector(to_unsigned(16#300#, 64)); + if l_in.segment_fault = '0' then + ctrl_tmp.irq_nia <= std_logic_vector(to_unsigned(16#300#, 64)); + else + ctrl_tmp.irq_nia <= std_logic_vector(to_unsigned(16#380#, 64)); + end if; ctrl_tmp.srr1 <= msr_copy(ctrl.msr); v.e.exc_write_enable := '1'; v.e.exc_write_reg := fast_spr_num(SPR_SRR0); diff --git a/loadstore1.vhdl b/loadstore1.vhdl index 03aaa6f..a29564b 100644 --- a/loadstore1.vhdl +++ b/loadstore1.vhdl @@ -372,7 +372,7 @@ begin byte_sel := r.first_bytes; end if; if m_in.done = '1' then - if m_in.invalid = '0' and m_in.badtree = '0' then + if m_in.invalid = '0' and m_in.badtree = '0' and m_in.segerr = '0' then -- retry the request now that the MMU has installed a TLB entry req := '1'; if r.state = MMU_LOOKUP_1ST then @@ -480,9 +480,12 @@ begin -- update exception info back to execute1 e_out.exception <= exception; + e_out.segment_fault <= m_in.segerr; if exception = '1' then v.dar := addr; - v.dsisr := dsisr; + if m_in.segerr = '0' then + v.dsisr := dsisr; + end if; end if; stall_out <= stall; diff --git a/mmu.vhdl b/mmu.vhdl index fe6ad16..293b7a8 100644 --- a/mmu.vhdl +++ b/mmu.vhdl @@ -26,11 +26,11 @@ architecture behave of mmu is type state_t is (IDLE, TLB_WAIT, + SEGMENT_CHECK, RADIX_LOOKUP, RADIX_READ_WAIT, RADIX_LOAD_TLB, - RADIX_NO_TRANS, - RADIX_BAD_TREE + RADIX_ERROR ); type reg_stage_t is record @@ -44,6 +44,9 @@ architecture behave of mmu is mask_size : unsigned(4 downto 0); pgbase : std_ulogic_vector(55 downto 0); pde : std_ulogic_vector(63 downto 0); + invalid : std_ulogic; + badtree : std_ulogic; + segerror : std_ulogic; end record; signal r, rin : reg_stage_t; @@ -155,8 +158,6 @@ begin variable v : reg_stage_t; variable dcreq : std_ulogic; variable done : std_ulogic; - variable invalid : std_ulogic; - variable badtree : std_ulogic; variable tlb_load : std_ulogic; variable tlbie_req : std_ulogic; variable rts : unsigned(5 downto 0); @@ -164,13 +165,15 @@ begin variable pgtable_addr : std_ulogic_vector(63 downto 0); variable pte : std_ulogic_vector(63 downto 0); variable data : std_ulogic_vector(63 downto 0); + variable nonzero : std_ulogic; begin v := r; v.valid := '0'; dcreq := '0'; done := '0'; - invalid := '0'; - badtree := '0'; + v.invalid := '0'; + v.badtree := '0'; + v.segerror := '0'; tlb_load := '0'; tlbie_req := '0'; @@ -183,10 +186,11 @@ begin case r.state is when IDLE => -- rts == radix tree size, # address bits being translated - rts := unsigned('0' & r.pgtbl0(62 downto 61) & r.pgtbl0(7 downto 5)) + (31 - 12); + rts := unsigned('0' & r.pgtbl0(62 downto 61) & r.pgtbl0(7 downto 5)); -- mbits == # address bits to index top level of tree mbits := unsigned('0' & r.pgtbl0(4 downto 0)); - v.shift := rts - mbits; + -- set v.shift to rts so that we can use finalmask for the segment check + v.shift := rts; v.mask_size := mbits(4 downto 0); v.pgbase := r.pgtbl0(55 downto 8) & x"00"; @@ -198,13 +202,12 @@ begin v.state := TLB_WAIT; else v.valid := '1'; - -- for now, take RPDS = 0 to disable radix translation + -- Use RPDS = 0 to disable radix tree walks if mbits = 0 then - v.state := RADIX_NO_TRANS; - elsif mbits < 5 or mbits > 16 or mbits > rts then - v.state := RADIX_BAD_TREE; + v.state := RADIX_ERROR; + v.invalid := '1'; else - v.state := RADIX_LOOKUP; + v.state := SEGMENT_CHECK; end if; end if; end if; @@ -218,6 +221,20 @@ begin v.state := IDLE; end if; + when SEGMENT_CHECK => + mbits := '0' & r.mask_size; + v.shift := r.shift + (31 - 12) - mbits; + nonzero := or(r.addr(61 downto 31) and not finalmask(30 downto 0)); + if r.addr(63) /= r.addr(62) or nonzero = '1' then + v.state := RADIX_ERROR; + v.segerror := '1'; + elsif mbits < 5 or mbits > 16 or mbits > (r.shift + (31 - 12)) then + v.state := RADIX_ERROR; + v.badtree := '1'; + else + v.state := RADIX_LOOKUP; + end if; + when RADIX_LOOKUP => dcreq := '1'; v.state := RADIX_READ_WAIT; @@ -234,7 +251,8 @@ begin else mbits := unsigned('0' & data(4 downto 0)); if mbits < 5 or mbits > 16 or mbits > r.shift then - v.state := RADIX_BAD_TREE; + v.state := RADIX_ERROR; + v.badtree := '1'; else v.shift := v.shift - mbits; v.mask_size := mbits(4 downto 0); @@ -244,10 +262,12 @@ begin end if; else -- non-present PTE, generate a DSI - v.state := RADIX_NO_TRANS; + v.state := RADIX_ERROR; + v.invalid := '1'; end if; else - v.state := RADIX_BAD_TREE; + v.state := RADIX_ERROR; + v.badtree := '1'; end if; end if; @@ -256,15 +276,10 @@ begin dcreq := '1'; v.state := TLB_WAIT; - when RADIX_NO_TRANS => + when RADIX_ERROR => done := '1'; - invalid := '1'; v.state := IDLE; - when RADIX_BAD_TREE => - done := '1'; - badtree := '1'; - v.state := IDLE; end case; pgtable_addr := x"00" & r.pgbase(55 downto 19) & @@ -279,8 +294,9 @@ begin -- drive outputs l_out.done <= done; - l_out.invalid <= invalid; - l_out.badtree <= badtree; + l_out.invalid <= r.invalid; + l_out.badtree <= r.badtree; + l_out.segerr <= r.segerror; d_out.valid <= dcreq; d_out.tlbie <= tlbie_req;