commit
						7c4dab7eb0
					
				@ -0,0 +1,473 @@
 | 
				
			|||||||
 | 
					library ieee;
 | 
				
			||||||
 | 
					use ieee.std_logic_1164.all;
 | 
				
			||||||
 | 
					use ieee.numeric_std.all;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					library work;
 | 
				
			||||||
 | 
					use work.common.all;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- Radix MMU
 | 
				
			||||||
 | 
					-- Supports 4-level trees as in arch 3.0B, but not the two-step translation for
 | 
				
			||||||
 | 
					-- guests under a hypervisor (i.e. there is no gRA -> hRA translation).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					entity mmu is
 | 
				
			||||||
 | 
					    port (
 | 
				
			||||||
 | 
					        clk   : in std_ulogic;
 | 
				
			||||||
 | 
					        rst   : in std_ulogic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        l_in  : in Loadstore1ToMmuType;
 | 
				
			||||||
 | 
					        l_out : out MmuToLoadstore1Type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        d_out : out MmuToDcacheType;
 | 
				
			||||||
 | 
					        d_in  : in DcacheToMmuType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        i_out : out MmuToIcacheType
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					end mmu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					architecture behave of mmu is
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    type state_t is (IDLE,
 | 
				
			||||||
 | 
					                     TLB_WAIT,
 | 
				
			||||||
 | 
					                     PROC_TBL_READ,
 | 
				
			||||||
 | 
					                     PROC_TBL_WAIT,
 | 
				
			||||||
 | 
					                     SEGMENT_CHECK,
 | 
				
			||||||
 | 
					                     RADIX_LOOKUP,
 | 
				
			||||||
 | 
					                     RADIX_READ_WAIT,
 | 
				
			||||||
 | 
					                     RADIX_LOAD_TLB,
 | 
				
			||||||
 | 
					                     RADIX_ERROR
 | 
				
			||||||
 | 
					                     );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    type reg_stage_t is record
 | 
				
			||||||
 | 
					        -- latched request from loadstore1
 | 
				
			||||||
 | 
					        valid     : std_ulogic;
 | 
				
			||||||
 | 
					        iside     : std_ulogic;
 | 
				
			||||||
 | 
					        store     : std_ulogic;
 | 
				
			||||||
 | 
					        priv      : std_ulogic;
 | 
				
			||||||
 | 
					        addr      : std_ulogic_vector(63 downto 0);
 | 
				
			||||||
 | 
					        -- config SPRs
 | 
				
			||||||
 | 
					        prtbl     : std_ulogic_vector(63 downto 0);
 | 
				
			||||||
 | 
					        pid       : std_ulogic_vector(31 downto 0);
 | 
				
			||||||
 | 
					        -- internal state
 | 
				
			||||||
 | 
					        state     : state_t;
 | 
				
			||||||
 | 
					        pgtbl0    : std_ulogic_vector(63 downto 0);
 | 
				
			||||||
 | 
					        pt0_valid : std_ulogic;
 | 
				
			||||||
 | 
					        pgtbl3    : std_ulogic_vector(63 downto 0);
 | 
				
			||||||
 | 
					        pt3_valid : std_ulogic;
 | 
				
			||||||
 | 
					        shift     : unsigned(5 downto 0);
 | 
				
			||||||
 | 
					        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;
 | 
				
			||||||
 | 
					        perm_err  : std_ulogic;
 | 
				
			||||||
 | 
					        rc_error  : std_ulogic;
 | 
				
			||||||
 | 
					    end record;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    signal r, rin : reg_stage_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    signal addrsh  : std_ulogic_vector(15 downto 0);
 | 
				
			||||||
 | 
					    signal mask    : std_ulogic_vector(15 downto 0);
 | 
				
			||||||
 | 
					    signal finalmask : std_ulogic_vector(43 downto 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					begin
 | 
				
			||||||
 | 
					    -- Multiplex internal SPR values back to loadstore1, selected
 | 
				
			||||||
 | 
					    -- by l_in.sprn.
 | 
				
			||||||
 | 
					    l_out.sprval <= r.prtbl when l_in.sprn(9) = '1' else x"00000000" & r.pid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mmu_0: process(clk)
 | 
				
			||||||
 | 
					    begin
 | 
				
			||||||
 | 
					        if rising_edge(clk) then
 | 
				
			||||||
 | 
					            if rst = '1' then
 | 
				
			||||||
 | 
					                r.state <= IDLE;
 | 
				
			||||||
 | 
					                r.valid <= '0';
 | 
				
			||||||
 | 
					                r.pt0_valid <= '0';
 | 
				
			||||||
 | 
					                r.pt3_valid <= '0';
 | 
				
			||||||
 | 
					                r.prtbl <= (others => '0');
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                if rin.valid = '1' then
 | 
				
			||||||
 | 
					                    report "MMU got tlb miss for " & to_hstring(rin.addr);
 | 
				
			||||||
 | 
					                end if;
 | 
				
			||||||
 | 
					                if l_out.done = '1' then
 | 
				
			||||||
 | 
					                    report "MMU completing op with invalid=" & std_ulogic'image(l_out.invalid) &
 | 
				
			||||||
 | 
					                        " badtree=" & std_ulogic'image(l_out.badtree);
 | 
				
			||||||
 | 
					                end if;
 | 
				
			||||||
 | 
					                if rin.state = RADIX_LOOKUP then
 | 
				
			||||||
 | 
					                    report "radix lookup shift=" & integer'image(to_integer(rin.shift)) &
 | 
				
			||||||
 | 
					                        " msize=" & integer'image(to_integer(rin.mask_size));
 | 
				
			||||||
 | 
					                end if;
 | 
				
			||||||
 | 
					                if r.state = RADIX_LOOKUP then
 | 
				
			||||||
 | 
					                    report "send load addr=" & to_hstring(d_out.addr) &
 | 
				
			||||||
 | 
					                        " addrsh=" & to_hstring(addrsh) & " mask=" & to_hstring(mask);
 | 
				
			||||||
 | 
					                end if;
 | 
				
			||||||
 | 
					                r <= rin;
 | 
				
			||||||
 | 
					            end if;
 | 
				
			||||||
 | 
					        end if;
 | 
				
			||||||
 | 
					    end process;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -- Shift address bits 61--12 right by 0--47 bits and
 | 
				
			||||||
 | 
					    -- supply the least significant 16 bits of the result.
 | 
				
			||||||
 | 
					    addrshifter: process(all)
 | 
				
			||||||
 | 
					        variable sh1 : std_ulogic_vector(30 downto 0);
 | 
				
			||||||
 | 
					        variable sh2 : std_ulogic_vector(18 downto 0);
 | 
				
			||||||
 | 
					        variable result : std_ulogic_vector(15 downto 0);
 | 
				
			||||||
 | 
					    begin
 | 
				
			||||||
 | 
					        case r.shift(5 downto 4) is
 | 
				
			||||||
 | 
					            when "00" =>
 | 
				
			||||||
 | 
					                sh1 := r.addr(42 downto 12);
 | 
				
			||||||
 | 
					            when "01" =>
 | 
				
			||||||
 | 
					                sh1 := r.addr(58 downto 28);
 | 
				
			||||||
 | 
					            when others =>
 | 
				
			||||||
 | 
					                sh1 := "0000000000000" & r.addr(61 downto 44);
 | 
				
			||||||
 | 
					        end case;
 | 
				
			||||||
 | 
					        case r.shift(3 downto 2) is
 | 
				
			||||||
 | 
					            when "00" =>
 | 
				
			||||||
 | 
					                sh2 := sh1(18 downto 0);
 | 
				
			||||||
 | 
					            when "01" =>
 | 
				
			||||||
 | 
					                sh2 := sh1(22 downto 4);
 | 
				
			||||||
 | 
					            when "10" =>
 | 
				
			||||||
 | 
					                sh2 := sh1(26 downto 8);
 | 
				
			||||||
 | 
					            when others =>
 | 
				
			||||||
 | 
					                sh2 := sh1(30 downto 12);
 | 
				
			||||||
 | 
					        end case;
 | 
				
			||||||
 | 
					        case r.shift(1 downto 0) is
 | 
				
			||||||
 | 
					            when "00" =>
 | 
				
			||||||
 | 
					                result := sh2(15 downto 0);
 | 
				
			||||||
 | 
					            when "01" =>
 | 
				
			||||||
 | 
					                result := sh2(16 downto 1);
 | 
				
			||||||
 | 
					            when "10" =>
 | 
				
			||||||
 | 
					                result := sh2(17 downto 2);
 | 
				
			||||||
 | 
					            when others =>
 | 
				
			||||||
 | 
					                result := sh2(18 downto 3);
 | 
				
			||||||
 | 
					        end case;
 | 
				
			||||||
 | 
					        addrsh <= result;
 | 
				
			||||||
 | 
					    end process;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -- generate mask for extracting address fields for PTE address generation
 | 
				
			||||||
 | 
					    addrmaskgen: process(all)
 | 
				
			||||||
 | 
					        variable m : std_ulogic_vector(15 downto 0);
 | 
				
			||||||
 | 
					    begin
 | 
				
			||||||
 | 
					        -- mask_count has to be >= 5
 | 
				
			||||||
 | 
					        m := x"001f";
 | 
				
			||||||
 | 
					        for i in 5 to 15 loop
 | 
				
			||||||
 | 
					            if i < to_integer(r.mask_size) then
 | 
				
			||||||
 | 
					                m(i) := '1';
 | 
				
			||||||
 | 
					            end if;
 | 
				
			||||||
 | 
					        end loop;
 | 
				
			||||||
 | 
					        mask <= m;
 | 
				
			||||||
 | 
					    end process;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -- generate mask for extracting address bits to go in TLB entry
 | 
				
			||||||
 | 
					    -- in order to support pages > 4kB
 | 
				
			||||||
 | 
					    finalmaskgen: process(all)
 | 
				
			||||||
 | 
					        variable m : std_ulogic_vector(43 downto 0);
 | 
				
			||||||
 | 
					    begin
 | 
				
			||||||
 | 
					        m := (others => '0');
 | 
				
			||||||
 | 
					        for i in 0 to 43 loop
 | 
				
			||||||
 | 
					            if i < to_integer(r.shift) then
 | 
				
			||||||
 | 
					                m(i) := '1';
 | 
				
			||||||
 | 
					            end if;
 | 
				
			||||||
 | 
					        end loop;
 | 
				
			||||||
 | 
					        finalmask <= m;
 | 
				
			||||||
 | 
					    end process;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mmu_1: process(all)
 | 
				
			||||||
 | 
					        variable v : reg_stage_t;
 | 
				
			||||||
 | 
					        variable dcreq : std_ulogic;
 | 
				
			||||||
 | 
					        variable done : std_ulogic;
 | 
				
			||||||
 | 
					        variable tlb_load : std_ulogic;
 | 
				
			||||||
 | 
					        variable itlb_load : std_ulogic;
 | 
				
			||||||
 | 
					        variable tlbie_req : std_ulogic;
 | 
				
			||||||
 | 
					        variable inval_all : std_ulogic;
 | 
				
			||||||
 | 
					        variable prtbl_rd : std_ulogic;
 | 
				
			||||||
 | 
					        variable pt_valid : std_ulogic;
 | 
				
			||||||
 | 
					        variable effpid : std_ulogic_vector(31 downto 0);
 | 
				
			||||||
 | 
					        variable prtable_addr : std_ulogic_vector(63 downto 0);
 | 
				
			||||||
 | 
					        variable rts : unsigned(5 downto 0);
 | 
				
			||||||
 | 
					        variable mbits : unsigned(5 downto 0);
 | 
				
			||||||
 | 
					        variable pgtable_addr : std_ulogic_vector(63 downto 0);
 | 
				
			||||||
 | 
					        variable pte : std_ulogic_vector(63 downto 0);
 | 
				
			||||||
 | 
					        variable tlb_data : std_ulogic_vector(63 downto 0);
 | 
				
			||||||
 | 
					        variable nonzero : std_ulogic;
 | 
				
			||||||
 | 
					        variable pgtbl : std_ulogic_vector(63 downto 0);
 | 
				
			||||||
 | 
					        variable perm_ok : std_ulogic;
 | 
				
			||||||
 | 
					        variable rc_ok : std_ulogic;
 | 
				
			||||||
 | 
					        variable addr : std_ulogic_vector(63 downto 0);
 | 
				
			||||||
 | 
					        variable data : std_ulogic_vector(63 downto 0);
 | 
				
			||||||
 | 
					    begin
 | 
				
			||||||
 | 
					        v := r;
 | 
				
			||||||
 | 
					        v.valid := '0';
 | 
				
			||||||
 | 
					        dcreq := '0';
 | 
				
			||||||
 | 
					        done := '0';
 | 
				
			||||||
 | 
					        v.invalid := '0';
 | 
				
			||||||
 | 
					        v.badtree := '0';
 | 
				
			||||||
 | 
					        v.segerror := '0';
 | 
				
			||||||
 | 
					        v.perm_err := '0';
 | 
				
			||||||
 | 
					        v.rc_error := '0';
 | 
				
			||||||
 | 
					        tlb_load := '0';
 | 
				
			||||||
 | 
					        itlb_load := '0';
 | 
				
			||||||
 | 
					        tlbie_req := '0';
 | 
				
			||||||
 | 
					        inval_all := '0';
 | 
				
			||||||
 | 
					        prtbl_rd := '0';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        -- Radix tree data structures in memory are big-endian,
 | 
				
			||||||
 | 
					        -- so we need to byte-swap them
 | 
				
			||||||
 | 
					        for i in 0 to 7 loop
 | 
				
			||||||
 | 
					            data(i * 8 + 7 downto i * 8) := d_in.data((7 - i) * 8 + 7 downto (7 - i) * 8);
 | 
				
			||||||
 | 
					        end loop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case r.state is
 | 
				
			||||||
 | 
					        when IDLE =>
 | 
				
			||||||
 | 
					            if l_in.addr(63) = '0' then
 | 
				
			||||||
 | 
					                pgtbl := r.pgtbl0;
 | 
				
			||||||
 | 
					                pt_valid := r.pt0_valid;
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                pgtbl := r.pgtbl3;
 | 
				
			||||||
 | 
					                pt_valid := r.pt3_valid;
 | 
				
			||||||
 | 
					            end if;
 | 
				
			||||||
 | 
					            -- rts == radix tree size, # address bits being translated
 | 
				
			||||||
 | 
					            rts := unsigned('0' & pgtbl(62 downto 61) & pgtbl(7 downto 5));
 | 
				
			||||||
 | 
					            -- mbits == # address bits to index top level of tree
 | 
				
			||||||
 | 
					            mbits := unsigned('0' & pgtbl(4 downto 0));
 | 
				
			||||||
 | 
					            -- 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 := pgtbl(55 downto 8) & x"00";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if l_in.valid = '1' then
 | 
				
			||||||
 | 
					                v.addr := l_in.addr;
 | 
				
			||||||
 | 
					                v.iside := l_in.iside;
 | 
				
			||||||
 | 
					                v.store := not (l_in.load or l_in.iside);
 | 
				
			||||||
 | 
					                v.priv := l_in.priv;
 | 
				
			||||||
 | 
					                if l_in.tlbie = '1' then
 | 
				
			||||||
 | 
					                    dcreq := '1';
 | 
				
			||||||
 | 
					                    tlbie_req := '1';
 | 
				
			||||||
 | 
					                    -- Invalidate all iTLB/dTLB entries for tlbie with
 | 
				
			||||||
 | 
					                    -- RB[IS] != 0 or RB[AP] != 0, or for slbia
 | 
				
			||||||
 | 
					                    inval_all := l_in.slbia or l_in.addr(11) or l_in.addr(10) or
 | 
				
			||||||
 | 
					                                 l_in.addr(7) or l_in.addr(6) or l_in.addr(5);
 | 
				
			||||||
 | 
					                    -- The RIC field of the tlbie instruction comes across on the
 | 
				
			||||||
 | 
					                    -- sprn bus as bits 2--3.  RIC=2 flushes process table caches.
 | 
				
			||||||
 | 
					                    if l_in.sprn(3) = '1' then
 | 
				
			||||||
 | 
					                        v.pt0_valid := '0';
 | 
				
			||||||
 | 
					                        v.pt3_valid := '0';
 | 
				
			||||||
 | 
					                    end if;
 | 
				
			||||||
 | 
					                    v.state := TLB_WAIT;
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    v.valid := '1';
 | 
				
			||||||
 | 
					                    if pt_valid = '0' then
 | 
				
			||||||
 | 
					                        -- need to fetch process table entry
 | 
				
			||||||
 | 
					                        -- set v.shift so we can use finalmask for generating
 | 
				
			||||||
 | 
					                        -- the process table entry address
 | 
				
			||||||
 | 
					                        v.shift := unsigned('0' & r.prtbl(4 downto 0));
 | 
				
			||||||
 | 
					                        v.state := PROC_TBL_READ;
 | 
				
			||||||
 | 
					                    elsif mbits = 0 then
 | 
				
			||||||
 | 
					                        -- Use RPDS = 0 to disable radix tree walks
 | 
				
			||||||
 | 
					                        v.state := RADIX_ERROR;
 | 
				
			||||||
 | 
					                        v.invalid := '1';
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                        v.state := SEGMENT_CHECK;
 | 
				
			||||||
 | 
					                    end if;
 | 
				
			||||||
 | 
					                end if;
 | 
				
			||||||
 | 
					            end if;
 | 
				
			||||||
 | 
					            if l_in.mtspr = '1' then
 | 
				
			||||||
 | 
					                -- Move to PID needs to invalidate L1 TLBs and cached
 | 
				
			||||||
 | 
					                -- pgtbl0 value.  Move to PRTBL does that plus
 | 
				
			||||||
 | 
					                -- invalidating the cached pgtbl3 value as well.
 | 
				
			||||||
 | 
					                if l_in.sprn(9) = '0' then
 | 
				
			||||||
 | 
					                    v.pid := l_in.rs(31 downto 0);
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    v.prtbl := l_in.rs;
 | 
				
			||||||
 | 
					                    v.pt3_valid := '0';
 | 
				
			||||||
 | 
					                end if;
 | 
				
			||||||
 | 
					                v.pt0_valid := '0';
 | 
				
			||||||
 | 
					                dcreq := '1';
 | 
				
			||||||
 | 
					                tlbie_req := '1';
 | 
				
			||||||
 | 
					                inval_all := '1';
 | 
				
			||||||
 | 
					                v.state := TLB_WAIT;
 | 
				
			||||||
 | 
					            end if;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when TLB_WAIT =>
 | 
				
			||||||
 | 
					            if d_in.done = '1' then
 | 
				
			||||||
 | 
					                done := '1';
 | 
				
			||||||
 | 
					                v.state := IDLE;
 | 
				
			||||||
 | 
					            end if;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when PROC_TBL_READ =>
 | 
				
			||||||
 | 
					            dcreq := '1';
 | 
				
			||||||
 | 
					            prtbl_rd := '1';
 | 
				
			||||||
 | 
					            v.state := PROC_TBL_WAIT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when PROC_TBL_WAIT =>
 | 
				
			||||||
 | 
					            if d_in.done = '1' then
 | 
				
			||||||
 | 
					                if d_in.err = '0' then
 | 
				
			||||||
 | 
					                    if r.addr(63) = '1' then
 | 
				
			||||||
 | 
					                        v.pgtbl3 := data;
 | 
				
			||||||
 | 
					                        v.pt3_valid := '1';
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                        v.pgtbl0 := data;
 | 
				
			||||||
 | 
					                        v.pt0_valid := '1';
 | 
				
			||||||
 | 
					                    end if;
 | 
				
			||||||
 | 
					                    -- rts == radix tree size, # address bits being translated
 | 
				
			||||||
 | 
					                    rts := unsigned('0' & data(62 downto 61) & data(7 downto 5));
 | 
				
			||||||
 | 
					                    -- mbits == # address bits to index top level of tree
 | 
				
			||||||
 | 
					                    mbits := unsigned('0' & data(4 downto 0));
 | 
				
			||||||
 | 
					                    -- 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 := data(55 downto 8) & x"00";
 | 
				
			||||||
 | 
					                    if mbits = 0 then
 | 
				
			||||||
 | 
					                        v.state := RADIX_ERROR;
 | 
				
			||||||
 | 
					                        v.invalid := '1';
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                        v.state := SEGMENT_CHECK;
 | 
				
			||||||
 | 
					                    end if;
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    v.state := RADIX_ERROR;
 | 
				
			||||||
 | 
					                    v.badtree := '1';
 | 
				
			||||||
 | 
					                end if;
 | 
				
			||||||
 | 
					            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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when RADIX_READ_WAIT =>
 | 
				
			||||||
 | 
					            if d_in.done = '1' then
 | 
				
			||||||
 | 
					                if d_in.err = '0' then
 | 
				
			||||||
 | 
					                    v.pde := data;
 | 
				
			||||||
 | 
					                    -- test valid bit
 | 
				
			||||||
 | 
					                    if data(63) = '1' then
 | 
				
			||||||
 | 
					                        -- test leaf bit
 | 
				
			||||||
 | 
					                        if data(62) = '1' then
 | 
				
			||||||
 | 
					                            -- check permissions and RC bits
 | 
				
			||||||
 | 
					                            perm_ok := '0';
 | 
				
			||||||
 | 
					                            if r.priv = '1' or data(3) = '0' then
 | 
				
			||||||
 | 
					                                if r.iside = '0' then
 | 
				
			||||||
 | 
					                                    perm_ok := data(1) or (data(2) and not r.store);
 | 
				
			||||||
 | 
					                                else
 | 
				
			||||||
 | 
					                                    -- no IAMR, so no KUEP support for now
 | 
				
			||||||
 | 
					                                    -- deny execute permission if cache inhibited
 | 
				
			||||||
 | 
					                                    perm_ok := data(0) and not data(5);
 | 
				
			||||||
 | 
					                                end if;
 | 
				
			||||||
 | 
					                            end if;
 | 
				
			||||||
 | 
					                            rc_ok := data(8) and (data(7) or not r.store);
 | 
				
			||||||
 | 
					                            if perm_ok = '1' and rc_ok = '1' then
 | 
				
			||||||
 | 
					                                v.state := RADIX_LOAD_TLB;
 | 
				
			||||||
 | 
					                            else
 | 
				
			||||||
 | 
					                                v.state := RADIX_ERROR;
 | 
				
			||||||
 | 
					                                v.perm_err := not perm_ok;
 | 
				
			||||||
 | 
					                                -- permission error takes precedence over RC error
 | 
				
			||||||
 | 
					                                v.rc_error := perm_ok;
 | 
				
			||||||
 | 
					                            end if;
 | 
				
			||||||
 | 
					                        else
 | 
				
			||||||
 | 
					                            mbits := unsigned('0' & data(4 downto 0));
 | 
				
			||||||
 | 
					                            if mbits < 5 or mbits > 16 or mbits > r.shift then
 | 
				
			||||||
 | 
					                                v.state := RADIX_ERROR;
 | 
				
			||||||
 | 
					                                v.badtree := '1';
 | 
				
			||||||
 | 
					                            else
 | 
				
			||||||
 | 
					                                v.shift := v.shift - mbits;
 | 
				
			||||||
 | 
					                                v.mask_size := mbits(4 downto 0);
 | 
				
			||||||
 | 
					                                v.pgbase := data(55 downto 8) & x"00";
 | 
				
			||||||
 | 
					                                v.state := RADIX_LOOKUP;
 | 
				
			||||||
 | 
					                            end if;
 | 
				
			||||||
 | 
					                        end if;
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                        -- non-present PTE, generate a DSI
 | 
				
			||||||
 | 
					                        v.state := RADIX_ERROR;
 | 
				
			||||||
 | 
					                        v.invalid := '1';
 | 
				
			||||||
 | 
					                    end if;
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    v.state := RADIX_ERROR;
 | 
				
			||||||
 | 
					                    v.badtree := '1';
 | 
				
			||||||
 | 
					                end if;
 | 
				
			||||||
 | 
					            end if;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when RADIX_LOAD_TLB =>
 | 
				
			||||||
 | 
					            tlb_load := '1';
 | 
				
			||||||
 | 
					            if r.iside = '0' then
 | 
				
			||||||
 | 
					                dcreq := '1';
 | 
				
			||||||
 | 
					                v.state := TLB_WAIT;
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                itlb_load := '1';
 | 
				
			||||||
 | 
					                done := '1';
 | 
				
			||||||
 | 
					                v.state := IDLE;
 | 
				
			||||||
 | 
					            end if;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when RADIX_ERROR =>
 | 
				
			||||||
 | 
					            done := '1';
 | 
				
			||||||
 | 
					            v.state := IDLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        end case;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if r.addr(63) = '1' then
 | 
				
			||||||
 | 
					            effpid := x"00000000";
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            effpid := r.pid;
 | 
				
			||||||
 | 
					        end if;
 | 
				
			||||||
 | 
					        prtable_addr := x"00" & r.prtbl(55 downto 36) &
 | 
				
			||||||
 | 
					                        ((r.prtbl(35 downto 12) and not finalmask(23 downto 0)) or
 | 
				
			||||||
 | 
					                         (effpid(31 downto 8) and finalmask(23 downto 0))) &
 | 
				
			||||||
 | 
					                        effpid(7 downto 0) & "0000";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        pgtable_addr := x"00" & r.pgbase(55 downto 19) &
 | 
				
			||||||
 | 
					                        ((r.pgbase(18 downto 3) and not mask) or (addrsh and mask)) &
 | 
				
			||||||
 | 
					                        "000";
 | 
				
			||||||
 | 
					        pte := x"00" &
 | 
				
			||||||
 | 
					               ((r.pde(55 downto 12) and not finalmask) or (r.addr(55 downto 12) and finalmask))
 | 
				
			||||||
 | 
					               & r.pde(11 downto 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        -- update registers
 | 
				
			||||||
 | 
					        rin <= v;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        -- drive outputs
 | 
				
			||||||
 | 
					        if tlbie_req = '1' then
 | 
				
			||||||
 | 
					            addr := l_in.addr;
 | 
				
			||||||
 | 
					            tlb_data := l_in.rs;
 | 
				
			||||||
 | 
					        elsif tlb_load = '1' then
 | 
				
			||||||
 | 
					            addr := r.addr(63 downto 12) & x"000";
 | 
				
			||||||
 | 
					            tlb_data := pte;
 | 
				
			||||||
 | 
					        elsif prtbl_rd = '1' then
 | 
				
			||||||
 | 
					            addr := prtable_addr;
 | 
				
			||||||
 | 
					            tlb_data := (others => '0');
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            addr := pgtable_addr;
 | 
				
			||||||
 | 
					            tlb_data := (others => '0');
 | 
				
			||||||
 | 
					        end if;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        l_out.done <= done;
 | 
				
			||||||
 | 
					        l_out.invalid <= r.invalid;
 | 
				
			||||||
 | 
					        l_out.badtree <= r.badtree;
 | 
				
			||||||
 | 
					        l_out.segerr <= r.segerror;
 | 
				
			||||||
 | 
					        l_out.perm_error <= r.perm_err;
 | 
				
			||||||
 | 
					        l_out.rc_error <= r.rc_error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        d_out.valid <= dcreq;
 | 
				
			||||||
 | 
					        d_out.tlbie <= tlbie_req;
 | 
				
			||||||
 | 
					        d_out.doall <= inval_all;
 | 
				
			||||||
 | 
					        d_out.tlbld <= tlb_load;
 | 
				
			||||||
 | 
					        d_out.addr <= addr;
 | 
				
			||||||
 | 
					        d_out.pte <= tlb_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        i_out.tlbld <= itlb_load;
 | 
				
			||||||
 | 
					        i_out.tlbie <= tlbie_req;
 | 
				
			||||||
 | 
					        i_out.doall <= inval_all;
 | 
				
			||||||
 | 
					        i_out.addr <= addr;
 | 
				
			||||||
 | 
					        i_out.pte <= tlb_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    end process;
 | 
				
			||||||
 | 
					end;
 | 
				
			||||||
@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					TEST=mmu
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					include ../Makefile.test
 | 
				
			||||||
@ -0,0 +1,179 @@
 | 
				
			|||||||
 | 
					/* Copyright 2013-2014 IBM Corp.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 	http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
				
			||||||
 | 
					 * implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Load an immediate 64-bit value into a register */
 | 
				
			||||||
 | 
					#define LOAD_IMM64(r, e)			\
 | 
				
			||||||
 | 
						lis     r,(e)@highest;			\
 | 
				
			||||||
 | 
						ori     r,r,(e)@higher;			\
 | 
				
			||||||
 | 
						rldicr  r,r, 32, 31;			\
 | 
				
			||||||
 | 
						oris    r,r, (e)@h;			\
 | 
				
			||||||
 | 
						ori     r,r, (e)@l;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.section ".head","ax"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Microwatt currently enters in LE mode at 0x0, so we don't need to
 | 
				
			||||||
 | 
						 * do any endian fix ups
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						. = 0
 | 
				
			||||||
 | 
					.global _start
 | 
				
			||||||
 | 
					_start:
 | 
				
			||||||
 | 
						LOAD_IMM64(%r10,__bss_start)
 | 
				
			||||||
 | 
						LOAD_IMM64(%r11,__bss_end)
 | 
				
			||||||
 | 
						subf	%r11,%r10,%r11
 | 
				
			||||||
 | 
						addi	%r11,%r11,63
 | 
				
			||||||
 | 
						srdi.	%r11,%r11,6
 | 
				
			||||||
 | 
						beq	2f
 | 
				
			||||||
 | 
						mtctr	%r11
 | 
				
			||||||
 | 
					1:	dcbz	0,%r10
 | 
				
			||||||
 | 
						addi	%r10,%r10,64
 | 
				
			||||||
 | 
						bdnz	1b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2:	LOAD_IMM64(%r1,__stack_top)
 | 
				
			||||||
 | 
						li	%r0,0
 | 
				
			||||||
 | 
						stdu	%r0,-16(%r1)
 | 
				
			||||||
 | 
						LOAD_IMM64(%r12, main)
 | 
				
			||||||
 | 
						mtctr	%r12
 | 
				
			||||||
 | 
						bctrl
 | 
				
			||||||
 | 
						attn // terminate on exit
 | 
				
			||||||
 | 
						b .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Read a location with translation on */
 | 
				
			||||||
 | 
						.globl	test_read
 | 
				
			||||||
 | 
					test_read:
 | 
				
			||||||
 | 
						mfmsr	%r9
 | 
				
			||||||
 | 
						ori	%r8,%r9,0x10	/* set MSR_DR */
 | 
				
			||||||
 | 
						mtmsrd	%r8,0
 | 
				
			||||||
 | 
						mr	%r6,%r3
 | 
				
			||||||
 | 
						li	%r3,0
 | 
				
			||||||
 | 
						ld	%r5,0(%r6)
 | 
				
			||||||
 | 
						li	%r3,1
 | 
				
			||||||
 | 
						/* land here if DSI occurred */
 | 
				
			||||||
 | 
						mtmsrd	%r9,0
 | 
				
			||||||
 | 
						std	%r5,0(%r4)
 | 
				
			||||||
 | 
						blr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Write a location with translation on */
 | 
				
			||||||
 | 
						.globl	test_write
 | 
				
			||||||
 | 
					test_write:
 | 
				
			||||||
 | 
						mfmsr	%r9
 | 
				
			||||||
 | 
						ori	%r8,%r9,0x10	/* set MSR_DR */
 | 
				
			||||||
 | 
						mtmsrd	%r8,0
 | 
				
			||||||
 | 
						mr	%r6,%r3
 | 
				
			||||||
 | 
						li	%r3,0
 | 
				
			||||||
 | 
						std	%r4,0(%r6)
 | 
				
			||||||
 | 
						li	%r3,1
 | 
				
			||||||
 | 
						/* land here if DSI occurred */
 | 
				
			||||||
 | 
						mtmsrd	%r9,0
 | 
				
			||||||
 | 
						blr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Do a dcbz with translation on */
 | 
				
			||||||
 | 
						.globl	test_dcbz
 | 
				
			||||||
 | 
					test_dcbz:
 | 
				
			||||||
 | 
						mfmsr	%r9
 | 
				
			||||||
 | 
						ori	%r8,%r9,0x10	/* set MSR_DR */
 | 
				
			||||||
 | 
						mtmsrd	%r8,0
 | 
				
			||||||
 | 
						mr	%r6,%r3
 | 
				
			||||||
 | 
						li	%r3,0
 | 
				
			||||||
 | 
						dcbz	0,%r6
 | 
				
			||||||
 | 
						li	%r3,1
 | 
				
			||||||
 | 
						/* land here if DSI occurred */
 | 
				
			||||||
 | 
						mtmsrd	%r9,0
 | 
				
			||||||
 | 
						blr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.globl	test_exec
 | 
				
			||||||
 | 
					test_exec:
 | 
				
			||||||
 | 
						mtsrr0	%r4
 | 
				
			||||||
 | 
						mtsrr1	%r5
 | 
				
			||||||
 | 
						rfid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define EXCEPTION(nr)		\
 | 
				
			||||||
 | 
						.= nr			;\
 | 
				
			||||||
 | 
						attn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* DSI vector - skip the failing instruction + the next one */
 | 
				
			||||||
 | 
						. = 0x300
 | 
				
			||||||
 | 
						mtsprg0	%r10
 | 
				
			||||||
 | 
						mfsrr0	%r10
 | 
				
			||||||
 | 
						addi	%r10,%r10,8
 | 
				
			||||||
 | 
						mtsrr0	%r10
 | 
				
			||||||
 | 
						rfid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						EXCEPTION(0x380)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * ISI vector - jump to LR to return from the test,
 | 
				
			||||||
 | 
						 * with r3 cleared
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						. = 0x400
 | 
				
			||||||
 | 
						li	%r3,0
 | 
				
			||||||
 | 
						blr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* More exception stubs */
 | 
				
			||||||
 | 
						EXCEPTION(0x480)
 | 
				
			||||||
 | 
						EXCEPTION(0x500)
 | 
				
			||||||
 | 
						EXCEPTION(0x600)
 | 
				
			||||||
 | 
						EXCEPTION(0x700)
 | 
				
			||||||
 | 
						EXCEPTION(0x800)
 | 
				
			||||||
 | 
						EXCEPTION(0x900)
 | 
				
			||||||
 | 
						EXCEPTION(0x980)
 | 
				
			||||||
 | 
						EXCEPTION(0xa00)
 | 
				
			||||||
 | 
						EXCEPTION(0xb00)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * System call - used to exit from tests where MSR[PR]
 | 
				
			||||||
 | 
						 * may have been set.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						. = 0xc00
 | 
				
			||||||
 | 
						blr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						EXCEPTION(0xd00)
 | 
				
			||||||
 | 
						EXCEPTION(0xe00)
 | 
				
			||||||
 | 
						EXCEPTION(0xe20)
 | 
				
			||||||
 | 
						EXCEPTION(0xe40)
 | 
				
			||||||
 | 
						EXCEPTION(0xe60)
 | 
				
			||||||
 | 
						EXCEPTION(0xe80)
 | 
				
			||||||
 | 
						EXCEPTION(0xf00)
 | 
				
			||||||
 | 
						EXCEPTION(0xf20)
 | 
				
			||||||
 | 
						EXCEPTION(0xf40)
 | 
				
			||||||
 | 
						EXCEPTION(0xf60)
 | 
				
			||||||
 | 
						EXCEPTION(0xf80)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						. = 0x1000
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * This page gets mapped at various locations and
 | 
				
			||||||
 | 
						 * the tests try to execute from it.
 | 
				
			||||||
 | 
						 * r3 contains the test number.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						.globl	test_start
 | 
				
			||||||
 | 
					test_start:
 | 
				
			||||||
 | 
						nop
 | 
				
			||||||
 | 
						nop
 | 
				
			||||||
 | 
						cmpdi	%r3,1
 | 
				
			||||||
 | 
						beq	test_1
 | 
				
			||||||
 | 
						cmpdi	%r3,2
 | 
				
			||||||
 | 
						beq	test_2
 | 
				
			||||||
 | 
					test_return:
 | 
				
			||||||
 | 
						li	%r3,1
 | 
				
			||||||
 | 
						sc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						. = 0x1ff8
 | 
				
			||||||
 | 
						/* test a branch near the end of a page */
 | 
				
			||||||
 | 
					test_1:	b	test_return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* test flowing from one page to the next */
 | 
				
			||||||
 | 
					test_2:	nop
 | 
				
			||||||
 | 
						b	test_return
 | 
				
			||||||
@ -0,0 +1,688 @@
 | 
				
			|||||||
 | 
					#include <stddef.h>
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#include <stdbool.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "console.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MSR_DR	0x10
 | 
				
			||||||
 | 
					#define MSR_IR	0x20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern int test_read(long *addr, long *ret, long init);
 | 
				
			||||||
 | 
					extern int test_write(long *addr, long val);
 | 
				
			||||||
 | 
					extern int test_dcbz(long *addr);
 | 
				
			||||||
 | 
					extern int test_exec(int testno, unsigned long pc, unsigned long msr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void do_tlbie(unsigned long rb, unsigned long rs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						__asm__ volatile("tlbie %0,%1" : : "r" (rb), "r" (rs) : "memory");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DSISR	18
 | 
				
			||||||
 | 
					#define DAR	19
 | 
				
			||||||
 | 
					#define SRR0	26
 | 
				
			||||||
 | 
					#define SRR1	27
 | 
				
			||||||
 | 
					#define PID	48
 | 
				
			||||||
 | 
					#define PRTBL	720
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline unsigned long mfspr(int sprnum)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						long val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						__asm__ volatile("mfspr %0,%1" : "=r" (val) : "i" (sprnum));
 | 
				
			||||||
 | 
						return val;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void mtspr(int sprnum, unsigned long val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						__asm__ volatile("mtspr %0,%1" : : "i" (sprnum), "r" (val));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void store_pte(unsigned long *p, unsigned long pte)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						__asm__ volatile("stdbrx %1,0,%0" : : "r" (p), "r" (pte) : "memory");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void print_string(const char *str)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for (; *str; ++str)
 | 
				
			||||||
 | 
							putchar(*str);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void print_hex(unsigned long val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i, x;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 60; i >= 0; i -= 4) {
 | 
				
			||||||
 | 
							x = (val >> i) & 0xf;
 | 
				
			||||||
 | 
							if (x >= 10)
 | 
				
			||||||
 | 
								putchar(x + 'a' - 10);
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								putchar(x + '0');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// i < 100
 | 
				
			||||||
 | 
					void print_test_number(int i)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						print_string("test ");
 | 
				
			||||||
 | 
						putchar(48 + i/10);
 | 
				
			||||||
 | 
						putchar(48 + i%10);
 | 
				
			||||||
 | 
						putchar(':');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CACHE_LINE_SIZE	64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void zero_memory(void *ptr, unsigned long nbytes)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long nb, i, nl;
 | 
				
			||||||
 | 
						void *p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (; nbytes != 0; nbytes -= nb, ptr += nb) {
 | 
				
			||||||
 | 
							nb = -((unsigned long)ptr) & (CACHE_LINE_SIZE - 1);
 | 
				
			||||||
 | 
							if (nb == 0 && nbytes >= CACHE_LINE_SIZE) {
 | 
				
			||||||
 | 
								nl = nbytes / CACHE_LINE_SIZE;
 | 
				
			||||||
 | 
								p = ptr;
 | 
				
			||||||
 | 
								for (i = 0; i < nl; ++i) {
 | 
				
			||||||
 | 
									__asm__ volatile("dcbz 0,%0" : : "r" (p) : "memory");
 | 
				
			||||||
 | 
									p += CACHE_LINE_SIZE;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								nb = nl * CACHE_LINE_SIZE;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if (nb > nbytes)
 | 
				
			||||||
 | 
									nb = nbytes;
 | 
				
			||||||
 | 
								for (i = 0; i < nb; ++i)
 | 
				
			||||||
 | 
									((unsigned char *)ptr)[i] = 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PERM_EX		0x001
 | 
				
			||||||
 | 
					#define PERM_WR		0x002
 | 
				
			||||||
 | 
					#define PERM_RD		0x004
 | 
				
			||||||
 | 
					#define PERM_PRIV	0x008
 | 
				
			||||||
 | 
					#define ATTR_NC		0x020
 | 
				
			||||||
 | 
					#define CHG		0x080
 | 
				
			||||||
 | 
					#define REF		0x100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DFLT_PERM	(PERM_WR | PERM_RD | REF | CHG)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Set up an MMU translation tree using memory starting at the 64k point.
 | 
				
			||||||
 | 
					 * We use 2 levels, mapping 2GB (the minimum size possible), with a
 | 
				
			||||||
 | 
					 * 8kB PGD level pointing to 4kB PTE pages.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					unsigned long *pgdir = (unsigned long *) 0x10000;
 | 
				
			||||||
 | 
					unsigned long *proc_tbl = (unsigned long *) 0x12000;
 | 
				
			||||||
 | 
					unsigned long free_ptr = 0x13000;
 | 
				
			||||||
 | 
					void *eas_mapped[4];
 | 
				
			||||||
 | 
					int neas_mapped;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void init_mmu(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* set up process table */
 | 
				
			||||||
 | 
						zero_memory(proc_tbl, 512 * sizeof(unsigned long));
 | 
				
			||||||
 | 
						mtspr(PRTBL, (unsigned long)proc_tbl);
 | 
				
			||||||
 | 
						mtspr(PID, 1);
 | 
				
			||||||
 | 
						zero_memory(pgdir, 1024 * sizeof(unsigned long));
 | 
				
			||||||
 | 
						/* RTS = 0 (2GB address space), RPDS = 10 (1024-entry top level) */
 | 
				
			||||||
 | 
						store_pte(&proc_tbl[2 * 1], (unsigned long) pgdir | 10);
 | 
				
			||||||
 | 
						do_tlbie(0xc00, 0);	/* invalidate all TLB entries */
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static unsigned long *read_pgd(unsigned long i)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						__asm__ volatile("ldbrx %0,%1,%2" : "=r" (ret) : "b" (pgdir),
 | 
				
			||||||
 | 
								 "r" (i * sizeof(unsigned long)));
 | 
				
			||||||
 | 
						return (unsigned long *) (ret & 0x00ffffffffffff00);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void map(void *ea, void *pa, unsigned long perm_attr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long epn = (unsigned long) ea >> 12;
 | 
				
			||||||
 | 
						unsigned long i, j;
 | 
				
			||||||
 | 
						unsigned long *ptep;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						i = (epn >> 9) & 0x3ff;
 | 
				
			||||||
 | 
						j = epn & 0x1ff;
 | 
				
			||||||
 | 
						if (pgdir[i] == 0) {
 | 
				
			||||||
 | 
							zero_memory((void *)free_ptr, 512 * sizeof(unsigned long));
 | 
				
			||||||
 | 
							store_pte(&pgdir[i], 0x8000000000000000 | free_ptr | 9);
 | 
				
			||||||
 | 
							free_ptr += 512 * sizeof(unsigned long);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ptep = read_pgd(i);
 | 
				
			||||||
 | 
						store_pte(&ptep[j], 0xc000000000000000 | ((unsigned long)pa & 0x00fffffffffff000) | perm_attr);
 | 
				
			||||||
 | 
						eas_mapped[neas_mapped++] = ea;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void unmap(void *ea)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long epn = (unsigned long) ea >> 12;
 | 
				
			||||||
 | 
						unsigned long i, j;
 | 
				
			||||||
 | 
						unsigned long *ptep;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						i = (epn >> 9) & 0x3ff;
 | 
				
			||||||
 | 
						j = epn & 0x1ff;
 | 
				
			||||||
 | 
						if (pgdir[i] == 0)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						ptep = read_pgd(i);
 | 
				
			||||||
 | 
						ptep[j] = 0;
 | 
				
			||||||
 | 
						do_tlbie(((unsigned long)ea & ~0xfff), 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void unmap_all(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < neas_mapped; ++i)
 | 
				
			||||||
 | 
							unmap(eas_mapped[i]);
 | 
				
			||||||
 | 
						neas_mapped = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_1(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						long *ptr = (long *) 0x123000;
 | 
				
			||||||
 | 
						long val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* this should fail */
 | 
				
			||||||
 | 
						if (test_read(ptr, &val, 0xdeadbeefd00d))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* dest reg of load should be unchanged */
 | 
				
			||||||
 | 
						if (val != 0xdeadbeefd00d)
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						/* DAR and DSISR should be set correctly */
 | 
				
			||||||
 | 
						if (mfspr(DAR) != (long) ptr || mfspr(DSISR) != 0x40000000)
 | 
				
			||||||
 | 
							return 3;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_2(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						long *mem = (long *) 0x8000;
 | 
				
			||||||
 | 
						long *ptr = (long *) 0x124000;
 | 
				
			||||||
 | 
						long *ptr2 = (long *) 0x1124000;
 | 
				
			||||||
 | 
						long val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* create PTE */
 | 
				
			||||||
 | 
						map(ptr, mem, DFLT_PERM);
 | 
				
			||||||
 | 
						/* initialize the memory content */
 | 
				
			||||||
 | 
						mem[33] = 0xbadc0ffee;
 | 
				
			||||||
 | 
						/* this should succeed and be a cache miss */
 | 
				
			||||||
 | 
						if (!test_read(&ptr[33], &val, 0xdeadbeefd00d))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* dest reg of load should have the value written */
 | 
				
			||||||
 | 
						if (val != 0xbadc0ffee)
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						/* load a second TLB entry in the same set as the first */
 | 
				
			||||||
 | 
						map(ptr2, mem, DFLT_PERM);
 | 
				
			||||||
 | 
						/* this should succeed and be a cache hit */
 | 
				
			||||||
 | 
						if (!test_read(&ptr2[33], &val, 0xdeadbeefd00d))
 | 
				
			||||||
 | 
							return 3;
 | 
				
			||||||
 | 
						/* dest reg of load should have the value written */
 | 
				
			||||||
 | 
						if (val != 0xbadc0ffee)
 | 
				
			||||||
 | 
							return 4;
 | 
				
			||||||
 | 
						/* check that the first entry still works */
 | 
				
			||||||
 | 
						if (!test_read(&ptr[33], &val, 0xdeadbeefd00d))
 | 
				
			||||||
 | 
							return 5;
 | 
				
			||||||
 | 
						if (val != 0xbadc0ffee)
 | 
				
			||||||
 | 
							return 6;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_3(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						long *mem = (long *) 0x9000;
 | 
				
			||||||
 | 
						long *ptr = (long *) 0x14a000;
 | 
				
			||||||
 | 
						long val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* create PTE */
 | 
				
			||||||
 | 
						map(ptr, mem, DFLT_PERM);
 | 
				
			||||||
 | 
						/* initialize the memory content */
 | 
				
			||||||
 | 
						mem[45] = 0xfee1800d4ea;
 | 
				
			||||||
 | 
						/* this should succeed and be a cache miss */
 | 
				
			||||||
 | 
						if (!test_read(&ptr[45], &val, 0xdeadbeefd0d0))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* dest reg of load should have the value written */
 | 
				
			||||||
 | 
						if (val != 0xfee1800d4ea)
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						/* remove the PTE */
 | 
				
			||||||
 | 
						unmap(ptr);
 | 
				
			||||||
 | 
						/* this should fail */
 | 
				
			||||||
 | 
						if (test_read(&ptr[45], &val, 0xdeadbeefd0d0))
 | 
				
			||||||
 | 
							return 3;
 | 
				
			||||||
 | 
						/* dest reg of load should be unchanged */
 | 
				
			||||||
 | 
						if (val != 0xdeadbeefd0d0)
 | 
				
			||||||
 | 
							return 4;
 | 
				
			||||||
 | 
						/* DAR and DSISR should be set correctly */
 | 
				
			||||||
 | 
						if (mfspr(DAR) != (long) &ptr[45] || mfspr(DSISR) != 0x40000000)
 | 
				
			||||||
 | 
							return 5;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_4(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						long *mem = (long *) 0xa000;
 | 
				
			||||||
 | 
						long *ptr = (long *) 0x10b000;
 | 
				
			||||||
 | 
						long *ptr2 = (long *) 0x110b000;
 | 
				
			||||||
 | 
						long val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* create PTE */
 | 
				
			||||||
 | 
						map(ptr, mem, DFLT_PERM);
 | 
				
			||||||
 | 
						/* initialize the memory content */
 | 
				
			||||||
 | 
						mem[27] = 0xf00f00f00f00;
 | 
				
			||||||
 | 
						/* this should succeed and be a cache miss */
 | 
				
			||||||
 | 
						if (!test_write(&ptr[27], 0xe44badc0ffee))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* memory should now have the value written */
 | 
				
			||||||
 | 
						if (mem[27] != 0xe44badc0ffee)
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						/* load a second TLB entry in the same set as the first */
 | 
				
			||||||
 | 
						map(ptr2, mem, DFLT_PERM);
 | 
				
			||||||
 | 
						/* this should succeed and be a cache hit */
 | 
				
			||||||
 | 
						if (!test_write(&ptr2[27], 0x6e11ae))
 | 
				
			||||||
 | 
							return 3;
 | 
				
			||||||
 | 
						/* memory should have the value written */
 | 
				
			||||||
 | 
						if (mem[27] != 0x6e11ae)
 | 
				
			||||||
 | 
							return 4;
 | 
				
			||||||
 | 
						/* check that the first entry still exists */
 | 
				
			||||||
 | 
						/* (assumes TLB is 2-way associative or more) */
 | 
				
			||||||
 | 
						if (!test_read(&ptr[27], &val, 0xdeadbeefd00d))
 | 
				
			||||||
 | 
							return 5;
 | 
				
			||||||
 | 
						if (val != 0x6e11ae)
 | 
				
			||||||
 | 
							return 6;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_5(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						long *mem = (long *) 0xbffd;
 | 
				
			||||||
 | 
						long *ptr = (long *) 0x39fffd;
 | 
				
			||||||
 | 
						long val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* create PTE */
 | 
				
			||||||
 | 
						map(ptr, mem, DFLT_PERM);
 | 
				
			||||||
 | 
						/* this should fail */
 | 
				
			||||||
 | 
						if (test_read(ptr, &val, 0xdeadbeef0dd0))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* dest reg of load should be unchanged */
 | 
				
			||||||
 | 
						if (val != 0xdeadbeef0dd0)
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						/* DAR and DSISR should be set correctly */
 | 
				
			||||||
 | 
						if (mfspr(DAR) != ((long)ptr & ~0xfff) + 0x1000 || mfspr(DSISR) != 0x40000000)
 | 
				
			||||||
 | 
							return 3;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_6(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						long *mem = (long *) 0xbffd;
 | 
				
			||||||
 | 
						long *ptr = (long *) 0x39fffd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* create PTE */
 | 
				
			||||||
 | 
						map(ptr, mem, DFLT_PERM);
 | 
				
			||||||
 | 
						/* initialize memory */
 | 
				
			||||||
 | 
						*mem = 0x123456789abcdef0;
 | 
				
			||||||
 | 
						/* this should fail */
 | 
				
			||||||
 | 
						if (test_write(ptr, 0xdeadbeef0dd0))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* DAR and DSISR should be set correctly */
 | 
				
			||||||
 | 
						if (mfspr(DAR) != ((long)ptr & ~0xfff) + 0x1000 || mfspr(DSISR) != 0x42000000)
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_7(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						long *mem = (long *) 0x8000;
 | 
				
			||||||
 | 
						long *ptr = (long *) 0x124000;
 | 
				
			||||||
 | 
						long val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*mem = 0x123456789abcdef0;
 | 
				
			||||||
 | 
						/* create PTE without R or C */
 | 
				
			||||||
 | 
						map(ptr, mem, PERM_RD | PERM_WR);
 | 
				
			||||||
 | 
						/* this should fail */
 | 
				
			||||||
 | 
						if (test_read(ptr, &val, 0xdeadd00dbeef))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* dest reg of load should be unchanged */
 | 
				
			||||||
 | 
						if (val != 0xdeadd00dbeef)
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						/* DAR and DSISR should be set correctly */
 | 
				
			||||||
 | 
						if (mfspr(DAR) != (long) ptr || mfspr(DSISR) != 0x00040000)
 | 
				
			||||||
 | 
							return 3;
 | 
				
			||||||
 | 
						/* this should fail */
 | 
				
			||||||
 | 
						if (test_write(ptr, 0xdeadbeef0dd0))
 | 
				
			||||||
 | 
							return 4;
 | 
				
			||||||
 | 
						/* DAR and DSISR should be set correctly */
 | 
				
			||||||
 | 
						if (mfspr(DAR) != (long)ptr || mfspr(DSISR) != 0x02040000)
 | 
				
			||||||
 | 
							return 5;
 | 
				
			||||||
 | 
						/* memory should be unchanged */
 | 
				
			||||||
 | 
						if (*mem != 0x123456789abcdef0)
 | 
				
			||||||
 | 
							return 6;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_8(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						long *mem = (long *) 0x8000;
 | 
				
			||||||
 | 
						long *ptr = (long *) 0x124000;
 | 
				
			||||||
 | 
						long val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*mem = 0x123456789abcdef0;
 | 
				
			||||||
 | 
						/* create PTE with R but not C */
 | 
				
			||||||
 | 
						map(ptr, mem, REF | PERM_RD | PERM_WR);
 | 
				
			||||||
 | 
						/* this should succeed */
 | 
				
			||||||
 | 
						if (!test_read(ptr, &val, 0xdeadd00dbeef))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* this should fail */
 | 
				
			||||||
 | 
						if (test_write(ptr, 0xdeadbeef0dd1))
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						/* DAR and DSISR should be set correctly */
 | 
				
			||||||
 | 
						if (mfspr(DAR) != (long)ptr || mfspr(DSISR) != 0x02040000)
 | 
				
			||||||
 | 
							return 3;
 | 
				
			||||||
 | 
						/* memory should be unchanged */
 | 
				
			||||||
 | 
						if (*mem != 0x123456789abcdef0)
 | 
				
			||||||
 | 
							return 4;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_9(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						long *mem = (long *) 0x8000;
 | 
				
			||||||
 | 
						long *ptr = (long *) 0x124000;
 | 
				
			||||||
 | 
						long val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*mem = 0x123456789abcdef0;
 | 
				
			||||||
 | 
						/* create PTE without read or write permission */
 | 
				
			||||||
 | 
						map(ptr, mem, REF);
 | 
				
			||||||
 | 
						/* this should fail */
 | 
				
			||||||
 | 
						if (test_read(ptr, &val, 0xdeadd00dbeef))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* dest reg of load should be unchanged */
 | 
				
			||||||
 | 
						if (val != 0xdeadd00dbeef)
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						/* DAR and DSISR should be set correctly */
 | 
				
			||||||
 | 
						if (mfspr(DAR) != (long) ptr || mfspr(DSISR) != 0x08000000)
 | 
				
			||||||
 | 
							return 3;
 | 
				
			||||||
 | 
						/* this should fail */
 | 
				
			||||||
 | 
						if (test_write(ptr, 0xdeadbeef0dd1))
 | 
				
			||||||
 | 
							return 4;
 | 
				
			||||||
 | 
						/* DAR and DSISR should be set correctly */
 | 
				
			||||||
 | 
						if (mfspr(DAR) != (long)ptr || mfspr(DSISR) != 0x0a000000)
 | 
				
			||||||
 | 
							return 5;
 | 
				
			||||||
 | 
						/* memory should be unchanged */
 | 
				
			||||||
 | 
						if (*mem != 0x123456789abcdef0)
 | 
				
			||||||
 | 
							return 6;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_10(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						long *mem = (long *) 0x8000;
 | 
				
			||||||
 | 
						long *ptr = (long *) 0x124000;
 | 
				
			||||||
 | 
						long val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*mem = 0x123456789abcdef0;
 | 
				
			||||||
 | 
						/* create PTE with read but not write permission */
 | 
				
			||||||
 | 
						map(ptr, mem, REF | PERM_RD);
 | 
				
			||||||
 | 
						/* this should succeed */
 | 
				
			||||||
 | 
						if (!test_read(ptr, &val, 0xdeadd00dbeef))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* this should fail */
 | 
				
			||||||
 | 
						if (test_write(ptr, 0xdeadbeef0dd1))
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						/* DAR and DSISR should be set correctly */
 | 
				
			||||||
 | 
						if (mfspr(DAR) != (long)ptr || mfspr(DSISR) != 0x0a000000)
 | 
				
			||||||
 | 
							return 3;
 | 
				
			||||||
 | 
						/* memory should be unchanged */
 | 
				
			||||||
 | 
						if (*mem != 0x123456789abcdef0)
 | 
				
			||||||
 | 
							return 4;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_11(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long ptr = 0x523000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* this should fail */
 | 
				
			||||||
 | 
						if (test_exec(0, ptr, MSR_IR))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* SRR0 and SRR1 should be set correctly */
 | 
				
			||||||
 | 
						if (mfspr(SRR0) != (long) ptr || mfspr(SRR1) != 0x40000020)
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_12(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long mem = 0x1000;
 | 
				
			||||||
 | 
						unsigned long ptr = 0x324000;
 | 
				
			||||||
 | 
						unsigned long ptr2 = 0x1324000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* create PTE */
 | 
				
			||||||
 | 
						map((void *)ptr, (void *)mem, PERM_EX | REF);
 | 
				
			||||||
 | 
						/* this should succeed and be a cache miss */
 | 
				
			||||||
 | 
						if (!test_exec(0, ptr, MSR_IR))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* create a second PTE */
 | 
				
			||||||
 | 
						map((void *)ptr2, (void *)mem, PERM_EX | REF);
 | 
				
			||||||
 | 
						/* this should succeed and be a cache hit */
 | 
				
			||||||
 | 
						if (!test_exec(0, ptr2, MSR_IR))
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_13(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long mem = 0x1000;
 | 
				
			||||||
 | 
						unsigned long ptr = 0x349000;
 | 
				
			||||||
 | 
						unsigned long ptr2 = 0x34a000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* create a PTE */
 | 
				
			||||||
 | 
						map((void *)ptr, (void *)mem, PERM_EX | REF);
 | 
				
			||||||
 | 
						/* this should succeed */
 | 
				
			||||||
 | 
						if (!test_exec(1, ptr, MSR_IR))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* invalidate the PTE */
 | 
				
			||||||
 | 
						unmap((void *)ptr);
 | 
				
			||||||
 | 
						/* install a second PTE */
 | 
				
			||||||
 | 
						map((void *)ptr2, (void *)mem, PERM_EX | REF);
 | 
				
			||||||
 | 
						/* this should fail */
 | 
				
			||||||
 | 
						if (test_exec(1, ptr, MSR_IR))
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						/* SRR0 and SRR1 should be set correctly */
 | 
				
			||||||
 | 
						if (mfspr(SRR0) != (long) ptr || mfspr(SRR1) != 0x40000020)
 | 
				
			||||||
 | 
							return 3;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_14(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long mem = 0x1000;
 | 
				
			||||||
 | 
						unsigned long mem2 = 0x2000;
 | 
				
			||||||
 | 
						unsigned long ptr = 0x30a000;
 | 
				
			||||||
 | 
						unsigned long ptr2 = 0x30b000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* create a PTE */
 | 
				
			||||||
 | 
						map((void *)ptr, (void *)mem, PERM_EX | REF);
 | 
				
			||||||
 | 
						/* this should fail due to second page not being mapped */
 | 
				
			||||||
 | 
						if (test_exec(2, ptr, MSR_IR))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* SRR0 and SRR1 should be set correctly */
 | 
				
			||||||
 | 
						if (mfspr(SRR0) != ptr2 || mfspr(SRR1) != 0x40000020)
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						/* create a PTE for the second page */
 | 
				
			||||||
 | 
						map((void *)ptr2, (void *)mem2, PERM_EX | REF);
 | 
				
			||||||
 | 
						/* this should succeed */
 | 
				
			||||||
 | 
						if (!test_exec(2, ptr, MSR_IR))
 | 
				
			||||||
 | 
							return 3;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_15(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long mem = 0x1000;
 | 
				
			||||||
 | 
						unsigned long ptr = 0x324000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* create a PTE without execute permission */
 | 
				
			||||||
 | 
						map((void *)ptr, (void *)mem, DFLT_PERM);
 | 
				
			||||||
 | 
						/* this should fail */
 | 
				
			||||||
 | 
						if (test_exec(0, ptr, MSR_IR))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* SRR0 and SRR1 should be set correctly */
 | 
				
			||||||
 | 
						if (mfspr(SRR0) != ptr || mfspr(SRR1) != 0x10000020)
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_16(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long mem = 0x1000;
 | 
				
			||||||
 | 
						unsigned long mem2 = 0x2000;
 | 
				
			||||||
 | 
						unsigned long ptr = 0x30a000;
 | 
				
			||||||
 | 
						unsigned long ptr2 = 0x30b000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* create a PTE */
 | 
				
			||||||
 | 
						map((void *)ptr, (void *)mem, PERM_EX | REF);
 | 
				
			||||||
 | 
						/* create a PTE for the second page without execute permission */
 | 
				
			||||||
 | 
						map((void *)ptr2, (void *)mem2, PERM_RD | REF);
 | 
				
			||||||
 | 
						/* this should fail due to second page being no-execute */
 | 
				
			||||||
 | 
						if (test_exec(2, ptr, MSR_IR))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* SRR0 and SRR1 should be set correctly */
 | 
				
			||||||
 | 
						if (mfspr(SRR0) != ptr2 || mfspr(SRR1) != 0x10000020)
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						/* create a PTE for the second page with execute permission */
 | 
				
			||||||
 | 
						map((void *)ptr2, (void *)mem2, PERM_RD | PERM_EX | REF);
 | 
				
			||||||
 | 
						/* this should succeed */
 | 
				
			||||||
 | 
						if (!test_exec(2, ptr, MSR_IR))
 | 
				
			||||||
 | 
							return 3;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_17(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long mem = 0x1000;
 | 
				
			||||||
 | 
						unsigned long ptr = 0x349000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* create a PTE without the ref bit set */
 | 
				
			||||||
 | 
						map((void *)ptr, (void *)mem, PERM_EX);
 | 
				
			||||||
 | 
						/* this should fail */
 | 
				
			||||||
 | 
						if (test_exec(2, ptr, MSR_IR))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* SRR0 and SRR1 should be set correctly */
 | 
				
			||||||
 | 
						if (mfspr(SRR0) != (long) ptr || mfspr(SRR1) != 0x00040020)
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						/* create a PTE without ref or execute permission */
 | 
				
			||||||
 | 
						unmap((void *)ptr);
 | 
				
			||||||
 | 
						map((void *)ptr, (void *)mem, 0);
 | 
				
			||||||
 | 
						/* this should fail */
 | 
				
			||||||
 | 
						if (test_exec(2, ptr, MSR_IR))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* SRR0 and SRR1 should be set correctly */
 | 
				
			||||||
 | 
						/* RC update fail bit should not be set */
 | 
				
			||||||
 | 
						if (mfspr(SRR0) != (long) ptr || mfspr(SRR1) != 0x10000020)
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_18(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						long *mem = (long *) 0x8000;
 | 
				
			||||||
 | 
						long *ptr = (long *) 0x124000;
 | 
				
			||||||
 | 
						long *ptr2 = (long *) 0x1124000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* create PTE */
 | 
				
			||||||
 | 
						map(ptr, mem, DFLT_PERM);
 | 
				
			||||||
 | 
						/* this should succeed and be a cache miss */
 | 
				
			||||||
 | 
						if (!test_dcbz(&ptr[129]))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* create a second PTE */
 | 
				
			||||||
 | 
						map(ptr2, mem, DFLT_PERM);
 | 
				
			||||||
 | 
						/* this should succeed and be a cache hit */
 | 
				
			||||||
 | 
						if (!test_dcbz(&ptr2[130]))
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mmu_test_19(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						long *mem = (long *) 0x8000;
 | 
				
			||||||
 | 
						long *ptr = (long *) 0x124000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*mem = 0x123456789abcdef0;
 | 
				
			||||||
 | 
						/* create PTE with read but not write permission */
 | 
				
			||||||
 | 
						map(ptr, mem, REF | PERM_RD);
 | 
				
			||||||
 | 
						/* this should fail and create a TLB entry */
 | 
				
			||||||
 | 
						if (test_write(ptr, 0xdeadbeef0dd1))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						/* DAR and DSISR should be set correctly */
 | 
				
			||||||
 | 
						if (mfspr(DAR) != (long)ptr || mfspr(DSISR) != 0x0a000000)
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						/* Update the PTE to have write permission */
 | 
				
			||||||
 | 
						map(ptr, mem, REF | CHG | PERM_RD | PERM_WR);
 | 
				
			||||||
 | 
						/* this should succeed */
 | 
				
			||||||
 | 
						if (!test_write(ptr, 0xdeadbeef0dd1))
 | 
				
			||||||
 | 
							return 3;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int fail = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void do_test(int num, int (*test)(void))
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mtspr(DSISR, 0);
 | 
				
			||||||
 | 
						mtspr(DAR, 0);
 | 
				
			||||||
 | 
						unmap_all();
 | 
				
			||||||
 | 
						print_test_number(num);
 | 
				
			||||||
 | 
						ret = test();
 | 
				
			||||||
 | 
						if (ret == 0) {
 | 
				
			||||||
 | 
							print_string("PASS\r\n");
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							fail = 1;
 | 
				
			||||||
 | 
							print_string("FAIL ");
 | 
				
			||||||
 | 
							putchar(ret + '0');
 | 
				
			||||||
 | 
							if (num <= 10 || num == 19) {
 | 
				
			||||||
 | 
								print_string(" DAR=");
 | 
				
			||||||
 | 
								print_hex(mfspr(DAR));
 | 
				
			||||||
 | 
								print_string(" DSISR=");
 | 
				
			||||||
 | 
								print_hex(mfspr(DSISR));
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								print_string(" SRR0=");
 | 
				
			||||||
 | 
								print_hex(mfspr(SRR0));
 | 
				
			||||||
 | 
								print_string(" SRR1=");
 | 
				
			||||||
 | 
								print_hex(mfspr(SRR1));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							print_string("\r\n");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						potato_uart_init();
 | 
				
			||||||
 | 
						init_mmu();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						do_test(1, mmu_test_1);
 | 
				
			||||||
 | 
						do_test(2, mmu_test_2);
 | 
				
			||||||
 | 
						do_test(3, mmu_test_3);
 | 
				
			||||||
 | 
						do_test(4, mmu_test_4);
 | 
				
			||||||
 | 
						do_test(5, mmu_test_5);
 | 
				
			||||||
 | 
						do_test(6, mmu_test_6);
 | 
				
			||||||
 | 
						do_test(7, mmu_test_7);
 | 
				
			||||||
 | 
						do_test(8, mmu_test_8);
 | 
				
			||||||
 | 
						do_test(9, mmu_test_9);
 | 
				
			||||||
 | 
						do_test(10, mmu_test_10);
 | 
				
			||||||
 | 
						do_test(11, mmu_test_11);
 | 
				
			||||||
 | 
						do_test(12, mmu_test_12);
 | 
				
			||||||
 | 
						do_test(13, mmu_test_13);
 | 
				
			||||||
 | 
						do_test(14, mmu_test_14);
 | 
				
			||||||
 | 
						do_test(15, mmu_test_15);
 | 
				
			||||||
 | 
						do_test(16, mmu_test_16);
 | 
				
			||||||
 | 
						do_test(17, mmu_test_17);
 | 
				
			||||||
 | 
						do_test(18, mmu_test_18);
 | 
				
			||||||
 | 
						do_test(19, mmu_test_19);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return fail;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					SECTIONS
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						. = 0;
 | 
				
			||||||
 | 
						_start = .;
 | 
				
			||||||
 | 
						.head : {
 | 
				
			||||||
 | 
							KEEP(*(.head))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						. = ALIGN(0x1000);
 | 
				
			||||||
 | 
						.text : { *(.text) *(.text.*) *(.rodata) *(.rodata.*) }
 | 
				
			||||||
 | 
						. = ALIGN(0x1000);
 | 
				
			||||||
 | 
						.data : { *(.data) *(.data.*) *(.got) *(.toc) }
 | 
				
			||||||
 | 
						. = ALIGN(0x80);
 | 
				
			||||||
 | 
						__bss_start = .;
 | 
				
			||||||
 | 
						.bss : {
 | 
				
			||||||
 | 
							*(.dynsbss)
 | 
				
			||||||
 | 
							*(.sbss)
 | 
				
			||||||
 | 
							*(.scommon)
 | 
				
			||||||
 | 
							*(.dynbss)
 | 
				
			||||||
 | 
							*(.bss)
 | 
				
			||||||
 | 
							*(.common)
 | 
				
			||||||
 | 
							*(.bss.*)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						. = ALIGN(0x80);
 | 
				
			||||||
 | 
						__bss_end = .;
 | 
				
			||||||
 | 
						. = . + 0x4000;
 | 
				
			||||||
 | 
						__stack_top = .;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
											
												Binary file not shown.
											
										
									
								@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					test 01:PASS
 | 
				
			||||||
 | 
					test 02:PASS
 | 
				
			||||||
 | 
					test 03:PASS
 | 
				
			||||||
 | 
					test 04:PASS
 | 
				
			||||||
 | 
					test 05:PASS
 | 
				
			||||||
 | 
					test 06:PASS
 | 
				
			||||||
 | 
					test 07:PASS
 | 
				
			||||||
 | 
					test 08:PASS
 | 
				
			||||||
 | 
					test 09:PASS
 | 
				
			||||||
 | 
					test 10:PASS
 | 
				
			||||||
 | 
					test 11:PASS
 | 
				
			||||||
 | 
					test 12:PASS
 | 
				
			||||||
 | 
					test 13:PASS
 | 
				
			||||||
 | 
					test 14:PASS
 | 
				
			||||||
 | 
					test 15:PASS
 | 
				
			||||||
 | 
					test 16:PASS
 | 
				
			||||||
 | 
					test 17:PASS
 | 
				
			||||||
 | 
					test 18:PASS
 | 
				
			||||||
 | 
					test 19:PASS
 | 
				
			||||||
											
												Binary file not shown.
											
										
									
								
					Loading…
					
					
				
		Reference in New Issue