mirror of
https://github.com/antonblanchard/microwatt.git
synced 2026-04-06 21:20:04 +00:00
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>
This commit is contained in:
@@ -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
|
||||
|
||||
55
dcache.vhdl
55
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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user