xref: /openbmc/qemu/target/openrisc/mmu.c (revision 795c40b8)
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 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 "qemu-common.h"
25 #include "exec/gdbstub.h"
26 #include "qemu/host-utils.h"
27 #ifndef CONFIG_USER_ONLY
28 #include "hw/loader.h"
29 #endif
30 
31 #ifndef CONFIG_USER_ONLY
32 int cpu_openrisc_get_phys_nommu(OpenRISCCPU *cpu,
33                                 hwaddr *physical,
34                                 int *prot, target_ulong address, int rw)
35 {
36     *physical = address;
37     *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
38     return TLBRET_MATCH;
39 }
40 
41 int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu,
42                                hwaddr *physical,
43                                int *prot, target_ulong address, int rw)
44 {
45     int vpn = address >> TARGET_PAGE_BITS;
46     int idx = vpn & ITLB_MASK;
47     int right = 0;
48 
49     if ((cpu->env.tlb->itlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) {
50         return TLBRET_NOMATCH;
51     }
52     if (!(cpu->env.tlb->itlb[0][idx].mr & 1)) {
53         return TLBRET_INVALID;
54     }
55 
56     if (cpu->env.sr & SR_SM) { /* supervisor mode */
57         if (cpu->env.tlb->itlb[0][idx].tr & SXE) {
58             right |= PAGE_EXEC;
59         }
60     } else {
61         if (cpu->env.tlb->itlb[0][idx].tr & UXE) {
62             right |= PAGE_EXEC;
63         }
64     }
65 
66     if ((rw & 2) && ((right & PAGE_EXEC) == 0)) {
67         return TLBRET_BADADDR;
68     }
69 
70     *physical = (cpu->env.tlb->itlb[0][idx].tr & TARGET_PAGE_MASK) |
71                 (address & (TARGET_PAGE_SIZE-1));
72     *prot = right;
73     return TLBRET_MATCH;
74 }
75 
76 int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu,
77                                hwaddr *physical,
78                                int *prot, target_ulong address, int rw)
79 {
80     int vpn = address >> TARGET_PAGE_BITS;
81     int idx = vpn & DTLB_MASK;
82     int right = 0;
83 
84     if ((cpu->env.tlb->dtlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) {
85         return TLBRET_NOMATCH;
86     }
87     if (!(cpu->env.tlb->dtlb[0][idx].mr & 1)) {
88         return TLBRET_INVALID;
89     }
90 
91     if (cpu->env.sr & SR_SM) { /* supervisor mode */
92         if (cpu->env.tlb->dtlb[0][idx].tr & SRE) {
93             right |= PAGE_READ;
94         }
95         if (cpu->env.tlb->dtlb[0][idx].tr & SWE) {
96             right |= PAGE_WRITE;
97         }
98     } else {
99         if (cpu->env.tlb->dtlb[0][idx].tr & URE) {
100             right |= PAGE_READ;
101         }
102         if (cpu->env.tlb->dtlb[0][idx].tr & UWE) {
103             right |= PAGE_WRITE;
104         }
105     }
106 
107     if (!(rw & 1) && ((right & PAGE_READ) == 0)) {
108         return TLBRET_BADADDR;
109     }
110     if ((rw & 1) && ((right & PAGE_WRITE) == 0)) {
111         return TLBRET_BADADDR;
112     }
113 
114     *physical = (cpu->env.tlb->dtlb[0][idx].tr & TARGET_PAGE_MASK) |
115                 (address & (TARGET_PAGE_SIZE-1));
116     *prot = right;
117     return TLBRET_MATCH;
118 }
119 
120 static int cpu_openrisc_get_phys_addr(OpenRISCCPU *cpu,
121                                       hwaddr *physical,
122                                       int *prot, target_ulong address,
123                                       int rw)
124 {
125     int ret = TLBRET_MATCH;
126 
127     if (rw == MMU_INST_FETCH) {    /* ITLB */
128        *physical = 0;
129         ret = cpu->env.tlb->cpu_openrisc_map_address_code(cpu, physical,
130                                                           prot, address, rw);
131     } else {          /* DTLB */
132         ret = cpu->env.tlb->cpu_openrisc_map_address_data(cpu, physical,
133                                                           prot, address, rw);
134     }
135 
136     return ret;
137 }
138 #endif
139 
140 static void cpu_openrisc_raise_mmu_exception(OpenRISCCPU *cpu,
141                                              target_ulong address,
142                                              int rw, int tlb_error)
143 {
144     CPUState *cs = CPU(cpu);
145     int exception = 0;
146 
147     switch (tlb_error) {
148     default:
149         if (rw == 2) {
150             exception = EXCP_IPF;
151         } else {
152             exception = EXCP_DPF;
153         }
154         break;
155 #ifndef CONFIG_USER_ONLY
156     case TLBRET_BADADDR:
157         if (rw == 2) {
158             exception = EXCP_IPF;
159         } else {
160             exception = EXCP_DPF;
161         }
162         break;
163     case TLBRET_INVALID:
164     case TLBRET_NOMATCH:
165         /* No TLB match for a mapped address */
166         if (rw == 2) {
167             exception = EXCP_ITLBMISS;
168         } else {
169             exception = EXCP_DTLBMISS;
170         }
171         break;
172 #endif
173     }
174 
175     cs->exception_index = exception;
176     cpu->env.eear = address;
177     cpu->env.lock_addr = -1;
178 }
179 
180 #ifndef CONFIG_USER_ONLY
181 int openrisc_cpu_handle_mmu_fault(CPUState *cs,
182                                   vaddr address, int rw, int mmu_idx)
183 {
184     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
185     int ret = 0;
186     hwaddr physical = 0;
187     int prot = 0;
188 
189     ret = cpu_openrisc_get_phys_addr(cpu, &physical, &prot,
190                                      address, rw);
191 
192     if (ret == TLBRET_MATCH) {
193         tlb_set_page(cs, address & TARGET_PAGE_MASK,
194                      physical & TARGET_PAGE_MASK, prot,
195                      mmu_idx, TARGET_PAGE_SIZE);
196         ret = 0;
197     } else if (ret < 0) {
198         cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret);
199         ret = 1;
200     }
201 
202     return ret;
203 }
204 #else
205 int openrisc_cpu_handle_mmu_fault(CPUState *cs,
206                                   vaddr address, int rw, int mmu_idx)
207 {
208     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
209     int ret = 0;
210 
211     cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret);
212     ret = 1;
213 
214     return ret;
215 }
216 #endif
217 
218 #ifndef CONFIG_USER_ONLY
219 hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
220 {
221     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
222     hwaddr phys_addr;
223     int prot;
224     int miss;
225 
226     /* Check memory for any kind of address, since during debug the
227        gdb can ask for anything, check data tlb for address */
228     miss = cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, 0);
229 
230     /* Check instruction tlb */
231     if (miss) {
232         miss = cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr,
233                                           MMU_INST_FETCH);
234     }
235 
236     /* Last, fall back to a plain address */
237     if (miss) {
238         miss = cpu_openrisc_get_phys_nommu(cpu, &phys_addr, &prot, addr, 0);
239     }
240 
241     if (miss) {
242         return -1;
243     } else {
244         return phys_addr;
245     }
246 }
247 
248 void cpu_openrisc_mmu_init(OpenRISCCPU *cpu)
249 {
250     cpu->env.tlb = g_malloc0(sizeof(CPUOpenRISCTLBContext));
251 
252     cpu->env.tlb->cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu;
253     cpu->env.tlb->cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu;
254 }
255 #endif
256