xref: /openbmc/qemu/target/openrisc/mmu.c (revision 873f9ca3857cfeeef45441b116c91156736d529c)
1fcf5ef2aSThomas Huth /*
2fcf5ef2aSThomas Huth  * OpenRISC MMU.
3fcf5ef2aSThomas Huth  *
4fcf5ef2aSThomas Huth  * Copyright (c) 2011-2012 Jia Liu <proljc@gmail.com>
5fcf5ef2aSThomas Huth  *                         Zhizhou Zhang <etouzh@gmail.com>
6fcf5ef2aSThomas Huth  *
7fcf5ef2aSThomas Huth  * This library is free software; you can redistribute it and/or
8fcf5ef2aSThomas Huth  * modify it under the terms of the GNU Lesser General Public
9fcf5ef2aSThomas Huth  * License as published by the Free Software Foundation; either
10198a2d21SThomas Huth  * version 2.1 of the License, or (at your option) any later version.
11fcf5ef2aSThomas Huth  *
12fcf5ef2aSThomas Huth  * This library is distributed in the hope that it will be useful,
13fcf5ef2aSThomas Huth  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14fcf5ef2aSThomas Huth  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15fcf5ef2aSThomas Huth  * Lesser General Public License for more details.
16fcf5ef2aSThomas Huth  *
17fcf5ef2aSThomas Huth  * You should have received a copy of the GNU Lesser General Public
18fcf5ef2aSThomas Huth  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19fcf5ef2aSThomas Huth  */
20fcf5ef2aSThomas Huth 
21fcf5ef2aSThomas Huth #include "qemu/osdep.h"
22cd617484SPhilippe Mathieu-Daudé #include "qemu/log.h"
23fcf5ef2aSThomas Huth #include "cpu.h"
24fcf5ef2aSThomas Huth #include "exec/exec-all.h"
25*74781c08SPhilippe Mathieu-Daudé #include "exec/page-protection.h"
264ea5fe99SAlex Bennée #include "gdbstub/helpers.h"
27fcf5ef2aSThomas Huth #include "qemu/host-utils.h"
28fcf5ef2aSThomas Huth #include "hw/loader.h"
29fcf5ef2aSThomas Huth 
get_phys_nommu(hwaddr * phys_addr,int * prot,target_ulong address)30f0655423SRichard Henderson static inline void get_phys_nommu(hwaddr *phys_addr, int *prot,
3123d45ebdSRichard Henderson                                   target_ulong address)
32fcf5ef2aSThomas Huth {
33f0655423SRichard Henderson     *phys_addr = address;
34fcf5ef2aSThomas Huth     *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
35fcf5ef2aSThomas Huth }
36fcf5ef2aSThomas Huth 
get_phys_mmu(OpenRISCCPU * cpu,hwaddr * phys_addr,int * prot,target_ulong addr,int need,bool super)37f0655423SRichard Henderson static int get_phys_mmu(OpenRISCCPU *cpu, hwaddr *phys_addr, int *prot,
38f0655423SRichard Henderson                         target_ulong addr, int need, bool super)
39fcf5ef2aSThomas Huth {
40f0655423SRichard Henderson     int idx = (addr >> TARGET_PAGE_BITS) & TLB_MASK;
41f0655423SRichard Henderson     uint32_t imr = cpu->env.tlb.itlb[idx].mr;
42f0655423SRichard Henderson     uint32_t itr = cpu->env.tlb.itlb[idx].tr;
43f0655423SRichard Henderson     uint32_t dmr = cpu->env.tlb.dtlb[idx].mr;
44f0655423SRichard Henderson     uint32_t dtr = cpu->env.tlb.dtlb[idx].tr;
45f0655423SRichard Henderson     int right, match, valid;
46fcf5ef2aSThomas Huth 
47f0655423SRichard Henderson     /* If the ITLB and DTLB indexes map to the same page, we want to
48f0655423SRichard Henderson        load all permissions all at once.  If the destination pages do
49f0655423SRichard Henderson        not match, zap the one we don't need.  */
50f0655423SRichard Henderson     if (unlikely((itr ^ dtr) & TARGET_PAGE_MASK)) {
51f0655423SRichard Henderson         if (need & PAGE_EXEC) {
52f0655423SRichard Henderson             dmr = dtr = 0;
53fcf5ef2aSThomas Huth         } else {
54f0655423SRichard Henderson             imr = itr = 0;
55fcf5ef2aSThomas Huth         }
56fcf5ef2aSThomas Huth     }
57fcf5ef2aSThomas Huth 
58f0655423SRichard Henderson     /* Check if either of the entries matches the source address.  */
59f0655423SRichard Henderson     match  = (imr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_EXEC;
60f0655423SRichard Henderson     match |= (dmr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_READ | PAGE_WRITE;
61f0655423SRichard Henderson 
62f0655423SRichard Henderson     /* Check if either of the entries is valid.  */
63f0655423SRichard Henderson     valid  = imr & 1 ? PAGE_EXEC : 0;
64f0655423SRichard Henderson     valid |= dmr & 1 ? PAGE_READ | PAGE_WRITE : 0;
65f0655423SRichard Henderson     valid &= match;
66f0655423SRichard Henderson 
67f0655423SRichard Henderson     /* Collect the permissions from the entries.  */
68f0655423SRichard Henderson     right  = itr & (super ? SXE : UXE) ? PAGE_EXEC : 0;
69f0655423SRichard Henderson     right |= dtr & (super ? SRE : URE) ? PAGE_READ : 0;
70f0655423SRichard Henderson     right |= dtr & (super ? SWE : UWE) ? PAGE_WRITE : 0;
71f0655423SRichard Henderson     right &= valid;
72f0655423SRichard Henderson 
73f0655423SRichard Henderson     /* Note that above we validated that itr and dtr match on page.
74f0655423SRichard Henderson        So oring them together changes nothing without having to
75f0655423SRichard Henderson        check which one we needed.  We also want to store to these
76f0655423SRichard Henderson        variables even on failure, as it avoids compiler warnings.  */
77f0655423SRichard Henderson     *phys_addr = ((itr | dtr) & TARGET_PAGE_MASK) | (addr & ~TARGET_PAGE_MASK);
78fcf5ef2aSThomas Huth     *prot = right;
79f0655423SRichard Henderson 
80f0655423SRichard Henderson     qemu_log_mask(CPU_LOG_MMU,
81f0655423SRichard Henderson                   "MMU lookup: need %d match %d valid %d right %d -> %s\n",
82f0655423SRichard Henderson                   need, match, valid, right, (need & right) ? "OK" : "FAIL");
83f0655423SRichard Henderson 
84f0655423SRichard Henderson     /* Check the collective permissions are present.  */
85f0655423SRichard Henderson     if (likely(need & right)) {
86f0655423SRichard Henderson         return 0;  /* success! */
87fcf5ef2aSThomas Huth     }
88fcf5ef2aSThomas Huth 
89f0655423SRichard Henderson     /* Determine what kind of failure we have.  */
90f0655423SRichard Henderson     if (need & valid) {
91f0655423SRichard Henderson         return need & PAGE_EXEC ? EXCP_IPF : EXCP_DPF;
92fcf5ef2aSThomas Huth     } else {
93f0655423SRichard Henderson         return need & PAGE_EXEC ? EXCP_ITLBMISS : EXCP_DTLBMISS;
94fcf5ef2aSThomas Huth     }
95fcf5ef2aSThomas Huth }
96fcf5ef2aSThomas Huth 
raise_mmu_exception(OpenRISCCPU * cpu,target_ulong address,int exception)97f0655423SRichard Henderson static void raise_mmu_exception(OpenRISCCPU *cpu, target_ulong address,
98f0655423SRichard Henderson                                 int exception)
99fcf5ef2aSThomas Huth {
100fcf5ef2aSThomas Huth     CPUState *cs = CPU(cpu);
101fcf5ef2aSThomas Huth 
102fcf5ef2aSThomas Huth     cs->exception_index = exception;
103fcf5ef2aSThomas Huth     cpu->env.eear = address;
104930c3d00SRichard Henderson     cpu->env.lock_addr = -1;
105fcf5ef2aSThomas Huth }
106fcf5ef2aSThomas Huth 
openrisc_cpu_tlb_fill(CPUState * cs,vaddr addr,int size,MMUAccessType access_type,int mmu_idx,bool probe,uintptr_t retaddr)10735e911aeSRichard Henderson bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
10835e911aeSRichard Henderson                            MMUAccessType access_type, int mmu_idx,
10935e911aeSRichard Henderson                            bool probe, uintptr_t retaddr)
110fcf5ef2aSThomas Huth {
111fcf5ef2aSThomas Huth     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
11235e911aeSRichard Henderson     int excp = EXCP_DPF;
11335e911aeSRichard Henderson     int prot;
11435e911aeSRichard Henderson     hwaddr phys_addr;
11535e911aeSRichard Henderson 
11635e911aeSRichard Henderson     if (mmu_idx == MMU_NOMMU_IDX) {
11735e911aeSRichard Henderson         /* The mmu is disabled; lookups never fail.  */
11835e911aeSRichard Henderson         get_phys_nommu(&phys_addr, &prot, addr);
11935e911aeSRichard Henderson         excp = 0;
12035e911aeSRichard Henderson     } else {
12135e911aeSRichard Henderson         bool super = mmu_idx == MMU_SUPERVISOR_IDX;
12235e911aeSRichard Henderson         int need = (access_type == MMU_INST_FETCH ? PAGE_EXEC
12335e911aeSRichard Henderson                     : access_type == MMU_DATA_STORE ? PAGE_WRITE
12435e911aeSRichard Henderson                     : PAGE_READ);
12535e911aeSRichard Henderson         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, need, super);
12635e911aeSRichard Henderson     }
12735e911aeSRichard Henderson 
12835e911aeSRichard Henderson     if (likely(excp == 0)) {
12935e911aeSRichard Henderson         tlb_set_page(cs, addr & TARGET_PAGE_MASK,
13035e911aeSRichard Henderson                      phys_addr & TARGET_PAGE_MASK, prot,
13135e911aeSRichard Henderson                      mmu_idx, TARGET_PAGE_SIZE);
13235e911aeSRichard Henderson         return true;
13335e911aeSRichard Henderson     }
13435e911aeSRichard Henderson     if (probe) {
13535e911aeSRichard Henderson         return false;
13635e911aeSRichard Henderson     }
13735e911aeSRichard Henderson 
13835e911aeSRichard Henderson     raise_mmu_exception(cpu, addr, excp);
13935e911aeSRichard Henderson     cpu_loop_exit_restore(cs, retaddr);
1405ce5dad3SRichard Henderson }
141fcf5ef2aSThomas Huth 
openrisc_cpu_get_phys_page_debug(CPUState * cs,vaddr addr)142fcf5ef2aSThomas Huth hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
143fcf5ef2aSThomas Huth {
144fcf5ef2aSThomas Huth     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
145f0655423SRichard Henderson     int prot, excp, sr = cpu->env.sr;
146fcf5ef2aSThomas Huth     hwaddr phys_addr;
147fcf5ef2aSThomas Huth 
148f0655423SRichard Henderson     switch (sr & (SR_DME | SR_IME)) {
149f0655423SRichard Henderson     case SR_DME | SR_IME:
150f0655423SRichard Henderson         /* The mmu is definitely enabled.  */
151f0655423SRichard Henderson         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
1520fd8a106SStafford Horne                             PAGE_READ,
1530fd8a106SStafford Horne                             (sr & SR_SM) != 0);
1540fd8a106SStafford Horne         if (!excp) {
1550fd8a106SStafford Horne             return phys_addr;
1560fd8a106SStafford Horne         }
1570fd8a106SStafford Horne         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
1580fd8a106SStafford Horne                             PAGE_EXEC,
159f0655423SRichard Henderson                             (sr & SR_SM) != 0);
160f0655423SRichard Henderson         return excp ? -1 : phys_addr;
161461a4b94SStafford Horne 
162f0655423SRichard Henderson     default:
163f0655423SRichard Henderson         /* The mmu is partially enabled, and we don't really have
164f0655423SRichard Henderson            a "real" access type.  Begin by trying the mmu, but if
165f0655423SRichard Henderson            that fails try again without.  */
166f0655423SRichard Henderson         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
167f0655423SRichard Henderson                             PAGE_EXEC | PAGE_READ | PAGE_WRITE,
168f0655423SRichard Henderson                             (sr & SR_SM) != 0);
169f0655423SRichard Henderson         if (!excp) {
170f0655423SRichard Henderson             return phys_addr;
171fcf5ef2aSThomas Huth         }
172f0655423SRichard Henderson         /* fallthru */
173fcf5ef2aSThomas Huth 
174f0655423SRichard Henderson     case 0:
175f0655423SRichard Henderson         /* The mmu is definitely disabled; lookups never fail.  */
176f0655423SRichard Henderson         get_phys_nommu(&phys_addr, &prot, addr);
177fcf5ef2aSThomas Huth         return phys_addr;
178fcf5ef2aSThomas Huth     }
179461a4b94SStafford Horne }
180