From: Paul Mackerras Date: Mon, 20 Apr 2020 02:43:06 +0000 (+1000) Subject: Implement access permission checks X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=d47fbf88d14f2ca90d6b37378d698b9133e66631;p=microwatt.git Implement access permission checks This adds logic to the dcache to check the permissions encoded in the PTE that it gets from the dTLB. The bits that are checked are: R must be 1 C must be 1 for a store EAA(0) - if this is 1, MSR[PR] must be 0 EAA(2) must be 1 for a store EAA(1) | EAA(2) must be 1 for a load In addition, ATT(0) is used to indicate a cache-inhibited access. This now implements DSISR bits 36, 38 and 45. (Bit numbers above correspond to the ISA, i.e. using big-endian numbering.) MSR[PR] is now conveyed to loadstore1 for use in permission checking. Signed-off-by: Paul Mackerras --- diff --git a/common.vhdl b/common.vhdl index 59b3744..e8ec19e 100644 --- a/common.vhdl +++ b/common.vhdl @@ -230,12 +230,13 @@ package common is xerc : xer_common_t; reserve : std_ulogic; -- set for larx/stcx. rc : std_ulogic; -- set for stcx. - spr_num : spr_num_t; -- SPR number for mfspr/mtspr virt_mode : std_ulogic; -- do translation through TLB + priv_mode : std_ulogic; -- privileged mode (MSR[PR] = 0) + spr_num : spr_num_t; -- SPR number for mfspr/mtspr end record; constant Execute1ToLoadstore1Init : Execute1ToLoadstore1Type := (valid => '0', op => OP_ILLEGAL, ci => '0', byte_reverse => '0', sign_extend => '0', update => '0', xerc => xerc_init, - reserve => '0', rc => '0', virt_mode => '0', + reserve => '0', rc => '0', virt_mode => '0', priv_mode => '0', spr_num => 0, others => (others => '0')); type Loadstore1ToExecute1Type is record @@ -250,6 +251,7 @@ package common is nc : std_ulogic; reserve : std_ulogic; virt_mode : std_ulogic; + priv_mode : std_ulogic; addr : std_ulogic_vector(63 downto 0); data : std_ulogic_vector(63 downto 0); byte_sel : std_ulogic_vector(7 downto 0); @@ -261,6 +263,8 @@ package common is store_done : std_ulogic; error : std_ulogic; tlb_miss : std_ulogic; + perm_error : std_ulogic; + rc_error : std_ulogic; end record; type Loadstore1ToWritebackType is record diff --git a/dcache.vhdl b/dcache.vhdl index 7895877..03b3886 100644 --- a/dcache.vhdl +++ b/dcache.vhdl @@ -149,6 +149,30 @@ architecture rtl of dcache is signal r0 : Loadstore1ToDcacheType; signal r0_valid : std_ulogic; + -- Record for storing permission, attribute, etc. bits from a PTE + type perm_attr_t is record + reference : std_ulogic; + changed : std_ulogic; + nocache : std_ulogic; + priv : std_ulogic; + rd_perm : std_ulogic; + wr_perm : std_ulogic; + end record; + + function extract_perm_attr(pte : std_ulogic_vector(TLB_PTE_BITS - 1 downto 0)) return perm_attr_t is + variable pa : perm_attr_t; + begin + pa.reference := pte(8); + pa.changed := pte(7); + pa.nocache := pte(5); + pa.priv := pte(3); + pa.rd_perm := pte(2); + pa.wr_perm := pte(1); + return pa; + end; + + constant real_mode_perm_attr : perm_attr_t := (nocache => '0', others => '1'); + -- Type of operation on a "valid" input type op_t is (OP_NONE, OP_LOAD_HIT, -- Cache hit on load @@ -208,7 +232,9 @@ architecture rtl of dcache is -- Signals to complete with error error_done : std_ulogic; - tlb_miss : std_ulogic; + tlb_miss : std_ulogic; -- No entry found in TLB + perm_error : std_ulogic; -- Permissions don't allow access + rc_error : std_ulogic; -- Reference or change bit clear -- completion signal for tlbie tlbie_done : std_ulogic; @@ -262,6 +288,9 @@ architecture rtl of dcache is signal pte : tlb_pte_t; signal ra : std_ulogic_vector(REAL_ADDR_BITS - 1 downto 0); signal valid_ra : std_ulogic; + signal perm_attr : perm_attr_t; + signal rc_ok : std_ulogic; + signal perm_ok : std_ulogic; -- TLB PLRU output interface type tlb_plru_out_t is array(tlb_index_t) of std_ulogic_vector(TLB_WAY_BITS-1 downto 0); @@ -490,8 +519,10 @@ begin if r0.virt_mode = '1' then ra <= pte(REAL_ADDR_BITS - 1 downto TLB_LG_PGSZ) & r0.addr(TLB_LG_PGSZ - 1 downto 0); + perm_attr <= extract_perm_attr(pte); else ra <= r0.addr(REAL_ADDR_BITS - 1 downto 0); + perm_attr <= real_mode_perm_attr; end if; end process; @@ -588,6 +619,7 @@ begin variable op : op_t; variable opsel : std_ulogic_vector(2 downto 0); variable go : std_ulogic; + variable nc : std_ulogic; variable s_hit : std_ulogic; variable s_tag : cache_tag_t; variable s_pte : tlb_pte_t; @@ -655,13 +687,20 @@ begin -- The way to replace on a miss replace_way <= to_integer(unsigned(plru_victim(req_index))); - -- Combine the request and cache his status to decide what + -- work out whether we have permission for this access + -- NB we don't yet implement AMR, thus no KUAP + rc_ok <= perm_attr.reference and (r0.load or perm_attr.changed); + perm_ok <= (r0.priv_mode or not perm_attr.priv) and + (perm_attr.wr_perm or (r0.load and perm_attr.rd_perm)); + + -- Combine the request and cache hit status to decide what -- operation needs to be done -- + nc := r0.nc or perm_attr.nocache; op := OP_NONE; if go = '1' then - if valid_ra = '1' then - opsel := r0.load & r0.nc & is_hit; + if valid_ra = '1' and rc_ok = '1' and perm_ok = '1' then + opsel := r0.load & nc & is_hit; case opsel is when "101" => op := OP_LOAD_HIT; when "100" => op := OP_LOAD_MISS; @@ -742,6 +781,8 @@ begin d_out.store_done <= '0'; d_out.error <= '0'; d_out.tlb_miss <= '0'; + d_out.perm_error <= '0'; + d_out.rc_error <= '0'; -- We have a valid load or store hit or we just completed a slow -- op such as a load miss, a NC load or a store @@ -772,6 +813,8 @@ begin report "completing ld/st with error"; d_out.error <= '1'; d_out.tlb_miss <= r1.tlb_miss; + d_out.perm_error <= r1.perm_error; + d_out.rc_error <= r1.rc_error; d_out.valid <= '1'; end if; @@ -918,8 +961,12 @@ begin end if; if req_op = OP_BAD then + report "Signalling ld/st error valid_ra=" & " rc_ok=" & std_ulogic'image(rc_ok) & + " perm_ok=" & std_ulogic'image(perm_ok); r1.error_done <= '1'; r1.tlb_miss <= not valid_ra; + r1.perm_error <= valid_ra and not perm_ok; + r1.rc_error <= valid_ra and perm_ok and not rc_ok; else r1.error_done <= '0'; end if; diff --git a/execute1.vhdl b/execute1.vhdl index e2cb651..5e25efc 100644 --- a/execute1.vhdl +++ b/execute1.vhdl @@ -1004,6 +1004,7 @@ begin lv.ci := '1'; end if; lv.virt_mode := ctrl.msr(MSR_DR); + lv.priv_mode := not ctrl.msr(MSR_PR); -- Update registers rin <= v; diff --git a/loadstore1.vhdl b/loadstore1.vhdl index 6ab18f5..c54e47b 100644 --- a/loadstore1.vhdl +++ b/loadstore1.vhdl @@ -60,6 +60,7 @@ architecture behave of loadstore1 is rc : std_ulogic; nc : std_ulogic; -- non-cacheable access virt_mode : std_ulogic; + priv_mode : std_ulogic; state : state_t; second_bytes : std_ulogic_vector(7 downto 0); dar : std_ulogic_vector(63 downto 0); @@ -266,6 +267,7 @@ begin v.rc := l_in.rc; v.nc := l_in.ci; v.virt_mode := l_in.virt_mode; + v.priv_mode := l_in.priv_mode; -- XXX Temporary hack. Mark the op as non-cachable if the address -- is the form 0xc------- for a real-mode access. @@ -323,6 +325,9 @@ begin -- dcache will discard the second request exception := '1'; dsisr(30) := d_in.tlb_miss; + dsisr(63 - 36) := d_in.perm_error; + dsisr(63 - 38) := not r.load; + dsisr(63 - 45) := d_in.rc_error; v.state := IDLE; else v.state := LAST_ACK_WAIT; @@ -343,6 +348,9 @@ begin end if; exception := '1'; dsisr(30) := d_in.tlb_miss; + dsisr(63 - 36) := d_in.perm_error; + dsisr(63 - 38) := not r.load; + dsisr(63 - 45) := d_in.rc_error; v.state := IDLE; else write_enable := r.load; @@ -376,6 +384,7 @@ begin d_out.data <= v.store_data; d_out.byte_sel <= byte_sel; d_out.virt_mode <= v.virt_mode; + d_out.priv_mode <= v.priv_mode; -- Update outputs to writeback -- Multiplex either cache data to the destination GPR or