xref: /openbmc/qemu/target/openrisc/mmu.c (revision 500eb6db)
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 "cpu.h"
23 #include "exec/exec-all.h"
24 #include "exec/gdbstub.h"
25 #include "qemu/host-utils.h"
26 #ifndef CONFIG_USER_ONLY
27 #include "hw/loader.h"
28 #endif
29 
30 #ifndef CONFIG_USER_ONLY
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 
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 #endif
98 
99 static void raise_mmu_exception(OpenRISCCPU *cpu, target_ulong address,
100                                 int exception)
101 {
102     CPUState *cs = CPU(cpu);
103 
104     cs->exception_index = exception;
105     cpu->env.eear = address;
106     cpu->env.lock_addr = -1;
107 }
108 
109 bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
110                            MMUAccessType access_type, int mmu_idx,
111                            bool probe, uintptr_t retaddr)
112 {
113     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
114     int excp = EXCP_DPF;
115 
116 #ifndef CONFIG_USER_ONLY
117     int prot;
118     hwaddr phys_addr;
119 
120     if (mmu_idx == MMU_NOMMU_IDX) {
121         /* The mmu is disabled; lookups never fail.  */
122         get_phys_nommu(&phys_addr, &prot, addr);
123         excp = 0;
124     } else {
125         bool super = mmu_idx == MMU_SUPERVISOR_IDX;
126         int need = (access_type == MMU_INST_FETCH ? PAGE_EXEC
127                     : access_type == MMU_DATA_STORE ? PAGE_WRITE
128                     : PAGE_READ);
129         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, need, super);
130     }
131 
132     if (likely(excp == 0)) {
133         tlb_set_page(cs, addr & TARGET_PAGE_MASK,
134                      phys_addr & TARGET_PAGE_MASK, prot,
135                      mmu_idx, TARGET_PAGE_SIZE);
136         return true;
137     }
138     if (probe) {
139         return false;
140     }
141 #endif
142 
143     raise_mmu_exception(cpu, addr, excp);
144     cpu_loop_exit_restore(cs, retaddr);
145 }
146 
147 #ifndef CONFIG_USER_ONLY
148 hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
149 {
150     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
151     int prot, excp, sr = cpu->env.sr;
152     hwaddr phys_addr;
153 
154     switch (sr & (SR_DME | SR_IME)) {
155     case SR_DME | SR_IME:
156         /* The mmu is definitely enabled.  */
157         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
158                             PAGE_EXEC | PAGE_READ | PAGE_WRITE,
159                             (sr & SR_SM) != 0);
160         return excp ? -1 : phys_addr;
161 
162     default:
163         /* The mmu is partially enabled, and we don't really have
164            a "real" access type.  Begin by trying the mmu, but if
165            that fails try again without.  */
166         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
167                             PAGE_EXEC | PAGE_READ | PAGE_WRITE,
168                             (sr & SR_SM) != 0);
169         if (!excp) {
170             return phys_addr;
171         }
172         /* fallthru */
173 
174     case 0:
175         /* The mmu is definitely disabled; lookups never fail.  */
176         get_phys_nommu(&phys_addr, &prot, addr);
177         return phys_addr;
178     }
179 }
180 #endif
181