xref: /qemu/target/openrisc/mmu.c (revision fc524567087c2537b5103cdfc1d41e4f442892b6)
1 /*
2  * OpenRISC MMU.
3  *
4  * Copyright (c) 2011-2012 Jia Liu <proljc@gmail.com>
5  *                         Zhizhou Zhang <etouzh@gmail.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "qemu/osdep.h"
22 #include "qemu/log.h"
23 #include "cpu.h"
24 #include "exec/cputlb.h"
25 #include "exec/page-protection.h"
26 #include "exec/target_page.h"
27 #include "gdbstub/helpers.h"
28 #include "qemu/host-utils.h"
29 #include "hw/loader.h"
30 
get_phys_nommu(hwaddr * phys_addr,int * prot,target_ulong address)31 static inline void get_phys_nommu(hwaddr *phys_addr, int *prot,
32                                   target_ulong address)
33 {
34     *phys_addr = address;
35     *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
36 }
37 
get_phys_mmu(OpenRISCCPU * cpu,hwaddr * phys_addr,int * prot,target_ulong addr,int need,bool super)38 static int get_phys_mmu(OpenRISCCPU *cpu, hwaddr *phys_addr, int *prot,
39                         target_ulong addr, int need, bool super)
40 {
41     int idx = (addr >> TARGET_PAGE_BITS) & TLB_MASK;
42     uint32_t imr = cpu->env.tlb.itlb[idx].mr;
43     uint32_t itr = cpu->env.tlb.itlb[idx].tr;
44     uint32_t dmr = cpu->env.tlb.dtlb[idx].mr;
45     uint32_t dtr = cpu->env.tlb.dtlb[idx].tr;
46     int right, match, valid;
47 
48     /* If the ITLB and DTLB indexes map to the same page, we want to
49        load all permissions all at once.  If the destination pages do
50        not match, zap the one we don't need.  */
51     if (unlikely((itr ^ dtr) & TARGET_PAGE_MASK)) {
52         if (need & PAGE_EXEC) {
53             dmr = dtr = 0;
54         } else {
55             imr = itr = 0;
56         }
57     }
58 
59     /* Check if either of the entries matches the source address.  */
60     match  = (imr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_EXEC;
61     match |= (dmr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_READ | PAGE_WRITE;
62 
63     /* Check if either of the entries is valid.  */
64     valid  = imr & 1 ? PAGE_EXEC : 0;
65     valid |= dmr & 1 ? PAGE_READ | PAGE_WRITE : 0;
66     valid &= match;
67 
68     /* Collect the permissions from the entries.  */
69     right  = itr & (super ? SXE : UXE) ? PAGE_EXEC : 0;
70     right |= dtr & (super ? SRE : URE) ? PAGE_READ : 0;
71     right |= dtr & (super ? SWE : UWE) ? PAGE_WRITE : 0;
72     right &= valid;
73 
74     /* Note that above we validated that itr and dtr match on page.
75        So oring them together changes nothing without having to
76        check which one we needed.  We also want to store to these
77        variables even on failure, as it avoids compiler warnings.  */
78     *phys_addr = ((itr | dtr) & TARGET_PAGE_MASK) | (addr & ~TARGET_PAGE_MASK);
79     *prot = right;
80 
81     qemu_log_mask(CPU_LOG_MMU,
82                   "MMU lookup: need %d match %d valid %d right %d -> %s\n",
83                   need, match, valid, right, (need & right) ? "OK" : "FAIL");
84 
85     /* Check the collective permissions are present.  */
86     if (likely(need & right)) {
87         return 0;  /* success! */
88     }
89 
90     /* Determine what kind of failure we have.  */
91     if (need & valid) {
92         return need & PAGE_EXEC ? EXCP_IPF : EXCP_DPF;
93     } else {
94         return need & PAGE_EXEC ? EXCP_ITLBMISS : EXCP_DTLBMISS;
95     }
96 }
97 
raise_mmu_exception(OpenRISCCPU * cpu,target_ulong address,int exception)98 static void raise_mmu_exception(OpenRISCCPU *cpu, target_ulong address,
99                                 int exception)
100 {
101     CPUState *cs = CPU(cpu);
102 
103     cs->exception_index = exception;
104     cpu->env.eear = address;
105     cpu->env.lock_addr = -1;
106 }
107 
openrisc_cpu_tlb_fill(CPUState * cs,vaddr addr,int size,MMUAccessType access_type,int mmu_idx,bool probe,uintptr_t retaddr)108 bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
109                            MMUAccessType access_type, int mmu_idx,
110                            bool probe, uintptr_t retaddr)
111 {
112     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
113     int excp = EXCP_DPF;
114     int prot;
115     hwaddr phys_addr;
116 
117     if (mmu_idx == MMU_NOMMU_IDX) {
118         /* The mmu is disabled; lookups never fail.  */
119         get_phys_nommu(&phys_addr, &prot, addr);
120         excp = 0;
121     } else {
122         bool super = mmu_idx == MMU_SUPERVISOR_IDX;
123         int need = (access_type == MMU_INST_FETCH ? PAGE_EXEC
124                     : access_type == MMU_DATA_STORE ? PAGE_WRITE
125                     : PAGE_READ);
126         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, need, super);
127     }
128 
129     if (likely(excp == 0)) {
130         tlb_set_page(cs, addr & TARGET_PAGE_MASK,
131                      phys_addr & TARGET_PAGE_MASK, prot,
132                      mmu_idx, TARGET_PAGE_SIZE);
133         return true;
134     }
135     if (probe) {
136         return false;
137     }
138 
139     raise_mmu_exception(cpu, addr, excp);
140     cpu_loop_exit_restore(cs, retaddr);
141 }
142 
openrisc_cpu_get_phys_page_debug(CPUState * cs,vaddr addr)143 hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
144 {
145     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
146     int prot, excp, sr = cpu->env.sr;
147     hwaddr phys_addr;
148 
149     switch (sr & (SR_DME | SR_IME)) {
150     case SR_DME | SR_IME:
151         /* The mmu is definitely enabled.  */
152         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
153                             PAGE_READ,
154                             (sr & SR_SM) != 0);
155         if (!excp) {
156             return phys_addr;
157         }
158         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
159                             PAGE_EXEC,
160                             (sr & SR_SM) != 0);
161         return excp ? -1 : phys_addr;
162 
163     default:
164         /* The mmu is partially enabled, and we don't really have
165            a "real" access type.  Begin by trying the mmu, but if
166            that fails try again without.  */
167         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
168                             PAGE_EXEC | PAGE_READ | PAGE_WRITE,
169                             (sr & SR_SM) != 0);
170         if (!excp) {
171             return phys_addr;
172         }
173         /* fallthru */
174 
175     case 0:
176         /* The mmu is definitely disabled; lookups never fail.  */
177         get_phys_nommu(&phys_addr, &prot, addr);
178         return phys_addr;
179     }
180 }
181