OSDN Git Service

a4613e9ae4628487b11f7ec042f3870cd504d02a
[qmiga/qemu.git] / target / openrisc / mmu.c
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 static inline int get_phys_nommu(hwaddr *physical, int *prot,
33                                  target_ulong address)
34 {
35     *physical = address;
36     *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
37     return TLBRET_MATCH;
38 }
39
40 static int get_phys_code(OpenRISCCPU *cpu, hwaddr *physical, int *prot,
41                          target_ulong address, int rw, bool supervisor)
42 {
43     int vpn = address >> TARGET_PAGE_BITS;
44     int idx = vpn & TLB_MASK;
45     int right = 0;
46     uint32_t mr = cpu->env.tlb.itlb[idx].mr;
47     uint32_t tr = cpu->env.tlb.itlb[idx].tr;
48
49     if ((mr >> TARGET_PAGE_BITS) != vpn) {
50         return TLBRET_NOMATCH;
51     }
52     if (!(mr & 1)) {
53         return TLBRET_INVALID;
54     }
55     if (supervisor) {
56         if (tr & SXE) {
57             right |= PAGE_EXEC;
58         }
59     } else {
60         if (tr & UXE) {
61             right |= PAGE_EXEC;
62         }
63     }
64     if ((rw & 2) && ((right & PAGE_EXEC) == 0)) {
65         return TLBRET_BADADDR;
66     }
67
68     *physical = (tr & TARGET_PAGE_MASK) | (address & ~TARGET_PAGE_MASK);
69     *prot = right;
70     return TLBRET_MATCH;
71 }
72
73 static int get_phys_data(OpenRISCCPU *cpu, hwaddr *physical, int *prot,
74                          target_ulong address, int rw, bool supervisor)
75 {
76     int vpn = address >> TARGET_PAGE_BITS;
77     int idx = vpn & TLB_MASK;
78     int right = 0;
79     uint32_t mr = cpu->env.tlb.dtlb[idx].mr;
80     uint32_t tr = cpu->env.tlb.dtlb[idx].tr;
81
82     if ((mr >> TARGET_PAGE_BITS) != vpn) {
83         return TLBRET_NOMATCH;
84     }
85     if (!(mr & 1)) {
86         return TLBRET_INVALID;
87     }
88     if (supervisor) {
89         if (tr & SRE) {
90             right |= PAGE_READ;
91         }
92         if (tr & SWE) {
93             right |= PAGE_WRITE;
94         }
95     } else {
96         if (tr & URE) {
97             right |= PAGE_READ;
98         }
99         if (tr & UWE) {
100             right |= PAGE_WRITE;
101         }
102     }
103
104     if (!(rw & 1) && ((right & PAGE_READ) == 0)) {
105         return TLBRET_BADADDR;
106     }
107     if ((rw & 1) && ((right & PAGE_WRITE) == 0)) {
108         return TLBRET_BADADDR;
109     }
110
111     *physical = (tr & TARGET_PAGE_MASK) | (address & ~TARGET_PAGE_MASK);
112     *prot = right;
113     return TLBRET_MATCH;
114 }
115
116 static int get_phys_addr(OpenRISCCPU *cpu, hwaddr *physical,
117                          int *prot, target_ulong address, int rw)
118 {
119     bool supervisor = (cpu->env.sr & SR_SM) != 0;
120     int ret;
121
122     /* Assume nommu results for a moment.  */
123     ret = get_phys_nommu(physical, prot, address);
124
125     /* Overwrite with TLB lookup if enabled.  */
126     if (rw == MMU_INST_FETCH) {
127         if (cpu->env.sr & SR_IME) {
128             ret = get_phys_code(cpu, physical, prot, address, rw, supervisor);
129         }
130     } else {
131         if (cpu->env.sr & SR_DME) {
132             ret = get_phys_data(cpu, physical, prot, address, rw, supervisor);
133         }
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, vaddr address, int size,
182                                   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 = get_phys_addr(cpu, &physical, &prot, address, rw);
190
191     if (ret == TLBRET_MATCH) {
192         tlb_set_page(cs, address & TARGET_PAGE_MASK,
193                      physical & TARGET_PAGE_MASK, prot,
194                      mmu_idx, TARGET_PAGE_SIZE);
195         ret = 0;
196     } else if (ret < 0) {
197         cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret);
198         ret = 1;
199     }
200
201     return ret;
202 }
203 #else
204 int openrisc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
205                                   int rw, int mmu_idx)
206 {
207     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
208     int ret = 0;
209
210     cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret);
211     ret = 1;
212
213     return ret;
214 }
215 #endif
216
217 #ifndef CONFIG_USER_ONLY
218 hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
219 {
220     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
221     hwaddr phys_addr;
222     int prot;
223     int miss;
224
225     /* Check memory for any kind of address, since during debug the
226        gdb can ask for anything, check data tlb for address */
227     miss = get_phys_addr(cpu, &phys_addr, &prot, addr, 0);
228
229     /* Check instruction tlb */
230     if (miss) {
231         miss = get_phys_addr(cpu, &phys_addr, &prot, addr, MMU_INST_FETCH);
232     }
233
234     /* Last, fall back to a plain address */
235     if (miss) {
236         miss = get_phys_nommu(&phys_addr, &prot, addr);
237     }
238
239     if (miss) {
240         return -1;
241     } else {
242         return phys_addr;
243     }
244 }
245
246 void tlb_fill(CPUState *cs, target_ulong addr, int size,
247               MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
248 {
249     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
250     int ret, prot = 0;
251     hwaddr physical = 0;
252
253     if (mmu_idx == MMU_NOMMU_IDX) {
254         ret = get_phys_nommu(&physical, &prot, addr);
255     } else {
256         bool super = mmu_idx == MMU_SUPERVISOR_IDX;
257         if (access_type == MMU_INST_FETCH) {
258             ret = get_phys_code(cpu, &physical, &prot, addr, 2, super);
259         } else {
260             ret = get_phys_data(cpu, &physical, &prot, addr,
261                                 access_type == MMU_DATA_STORE, super);
262         }
263     }
264
265     if (ret == TLBRET_MATCH) {
266         tlb_set_page(cs, addr & TARGET_PAGE_MASK,
267                      physical & TARGET_PAGE_MASK, prot,
268                      mmu_idx, TARGET_PAGE_SIZE);
269     } else if (ret < 0) {
270         int rw;
271         if (access_type == MMU_INST_FETCH) {
272             rw = 2;
273         } else if (access_type == MMU_DATA_STORE) {
274             rw = 1;
275         } else {
276             rw = 0;
277         }
278         cpu_openrisc_raise_mmu_exception(cpu, addr, rw, ret);
279         /* Raise Exception.  */
280         cpu_loop_exit_restore(cs, retaddr);
281     }
282 }
283 #endif