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 <paulus@ozlabs.org>
pull/169/head
Paul Mackerras 5 years ago
parent 42d0fcc511
commit d47fbf88d1

@ -230,12 +230,13 @@ package common is
xerc : xer_common_t; xerc : xer_common_t;
reserve : std_ulogic; -- set for larx/stcx. reserve : std_ulogic; -- set for larx/stcx.
rc : std_ulogic; -- set for 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 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; end record;
constant Execute1ToLoadstore1Init : Execute1ToLoadstore1Type := (valid => '0', op => OP_ILLEGAL, ci => '0', byte_reverse => '0', constant Execute1ToLoadstore1Init : Execute1ToLoadstore1Type := (valid => '0', op => OP_ILLEGAL, ci => '0', byte_reverse => '0',
sign_extend => '0', update => '0', xerc => xerc_init, 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')); spr_num => 0, others => (others => '0'));


type Loadstore1ToExecute1Type is record type Loadstore1ToExecute1Type is record
@ -250,6 +251,7 @@ package common is
nc : std_ulogic; nc : std_ulogic;
reserve : std_ulogic; reserve : std_ulogic;
virt_mode : std_ulogic; virt_mode : std_ulogic;
priv_mode : std_ulogic;
addr : std_ulogic_vector(63 downto 0); addr : std_ulogic_vector(63 downto 0);
data : std_ulogic_vector(63 downto 0); data : std_ulogic_vector(63 downto 0);
byte_sel : std_ulogic_vector(7 downto 0); byte_sel : std_ulogic_vector(7 downto 0);
@ -261,6 +263,8 @@ package common is
store_done : std_ulogic; store_done : std_ulogic;
error : std_ulogic; error : std_ulogic;
tlb_miss : std_ulogic; tlb_miss : std_ulogic;
perm_error : std_ulogic;
rc_error : std_ulogic;
end record; end record;


type Loadstore1ToWritebackType is record type Loadstore1ToWritebackType is record

@ -149,6 +149,30 @@ architecture rtl of dcache is
signal r0 : Loadstore1ToDcacheType; signal r0 : Loadstore1ToDcacheType;
signal r0_valid : std_ulogic; 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 of operation on a "valid" input
type op_t is (OP_NONE, type op_t is (OP_NONE,
OP_LOAD_HIT, -- Cache hit on load OP_LOAD_HIT, -- Cache hit on load
@ -208,7 +232,9 @@ architecture rtl of dcache is


-- Signals to complete with error -- Signals to complete with error
error_done : std_ulogic; 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 -- completion signal for tlbie
tlbie_done : std_ulogic; tlbie_done : std_ulogic;
@ -262,6 +288,9 @@ architecture rtl of dcache is
signal pte : tlb_pte_t; signal pte : tlb_pte_t;
signal ra : std_ulogic_vector(REAL_ADDR_BITS - 1 downto 0); signal ra : std_ulogic_vector(REAL_ADDR_BITS - 1 downto 0);
signal valid_ra : std_ulogic; 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 -- TLB PLRU output interface
type tlb_plru_out_t is array(tlb_index_t) of std_ulogic_vector(TLB_WAY_BITS-1 downto 0); 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 if r0.virt_mode = '1' then
ra <= pte(REAL_ADDR_BITS - 1 downto TLB_LG_PGSZ) & ra <= pte(REAL_ADDR_BITS - 1 downto TLB_LG_PGSZ) &
r0.addr(TLB_LG_PGSZ - 1 downto 0); r0.addr(TLB_LG_PGSZ - 1 downto 0);
perm_attr <= extract_perm_attr(pte);
else else
ra <= r0.addr(REAL_ADDR_BITS - 1 downto 0); ra <= r0.addr(REAL_ADDR_BITS - 1 downto 0);
perm_attr <= real_mode_perm_attr;
end if; end if;
end process; end process;


@ -588,6 +619,7 @@ begin
variable op : op_t; variable op : op_t;
variable opsel : std_ulogic_vector(2 downto 0); variable opsel : std_ulogic_vector(2 downto 0);
variable go : std_ulogic; variable go : std_ulogic;
variable nc : std_ulogic;
variable s_hit : std_ulogic; variable s_hit : std_ulogic;
variable s_tag : cache_tag_t; variable s_tag : cache_tag_t;
variable s_pte : tlb_pte_t; variable s_pte : tlb_pte_t;
@ -655,13 +687,20 @@ begin
-- The way to replace on a miss -- The way to replace on a miss
replace_way <= to_integer(unsigned(plru_victim(req_index))); 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 -- operation needs to be done
-- --
nc := r0.nc or perm_attr.nocache;
op := OP_NONE; op := OP_NONE;
if go = '1' then if go = '1' then
if valid_ra = '1' then if valid_ra = '1' and rc_ok = '1' and perm_ok = '1' then
opsel := r0.load & r0.nc & is_hit; opsel := r0.load & nc & is_hit;
case opsel is case opsel is
when "101" => op := OP_LOAD_HIT; when "101" => op := OP_LOAD_HIT;
when "100" => op := OP_LOAD_MISS; when "100" => op := OP_LOAD_MISS;
@ -742,6 +781,8 @@ begin
d_out.store_done <= '0'; d_out.store_done <= '0';
d_out.error <= '0'; d_out.error <= '0';
d_out.tlb_miss <= '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 -- 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 -- op such as a load miss, a NC load or a store
@ -772,6 +813,8 @@ begin
report "completing ld/st with error"; report "completing ld/st with error";
d_out.error <= '1'; d_out.error <= '1';
d_out.tlb_miss <= r1.tlb_miss; 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'; d_out.valid <= '1';
end if; end if;


@ -918,8 +961,12 @@ begin
end if; end if;


if req_op = OP_BAD then 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.error_done <= '1';
r1.tlb_miss <= not valid_ra; 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 else
r1.error_done <= '0'; r1.error_done <= '0';
end if; end if;

@ -1004,6 +1004,7 @@ begin
lv.ci := '1'; lv.ci := '1';
end if; end if;
lv.virt_mode := ctrl.msr(MSR_DR); lv.virt_mode := ctrl.msr(MSR_DR);
lv.priv_mode := not ctrl.msr(MSR_PR);


-- Update registers -- Update registers
rin <= v; rin <= v;

@ -60,6 +60,7 @@ architecture behave of loadstore1 is
rc : std_ulogic; rc : std_ulogic;
nc : std_ulogic; -- non-cacheable access nc : std_ulogic; -- non-cacheable access
virt_mode : std_ulogic; virt_mode : std_ulogic;
priv_mode : std_ulogic;
state : state_t; state : state_t;
second_bytes : std_ulogic_vector(7 downto 0); second_bytes : std_ulogic_vector(7 downto 0);
dar : std_ulogic_vector(63 downto 0); dar : std_ulogic_vector(63 downto 0);
@ -266,6 +267,7 @@ begin
v.rc := l_in.rc; v.rc := l_in.rc;
v.nc := l_in.ci; v.nc := l_in.ci;
v.virt_mode := l_in.virt_mode; 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 -- XXX Temporary hack. Mark the op as non-cachable if the address
-- is the form 0xc------- for a real-mode access. -- is the form 0xc------- for a real-mode access.
@ -323,6 +325,9 @@ begin
-- dcache will discard the second request -- dcache will discard the second request
exception := '1'; exception := '1';
dsisr(30) := d_in.tlb_miss; 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; v.state := IDLE;
else else
v.state := LAST_ACK_WAIT; v.state := LAST_ACK_WAIT;
@ -343,6 +348,9 @@ begin
end if; end if;
exception := '1'; exception := '1';
dsisr(30) := d_in.tlb_miss; 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; v.state := IDLE;
else else
write_enable := r.load; write_enable := r.load;
@ -376,6 +384,7 @@ begin
d_out.data <= v.store_data; d_out.data <= v.store_data;
d_out.byte_sel <= byte_sel; d_out.byte_sel <= byte_sel;
d_out.virt_mode <= v.virt_mode; d_out.virt_mode <= v.virt_mode;
d_out.priv_mode <= v.priv_mode;


-- Update outputs to writeback -- Update outputs to writeback
-- Multiplex either cache data to the destination GPR or -- Multiplex either cache data to the destination GPR or

Loading…
Cancel
Save