]>
Commit | Line | Data |
---|---|---|
e67db06e JL |
1 | /* |
2 | * OpenRISC MMU. | |
3 | * | |
4 | * Copyright (c) 2011-2012 Jia Liu <[email protected]> | |
5 | * Zhizhou Zhang <[email protected]> | |
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 | ||
ed2decc6 | 21 | #include "qemu/osdep.h" |
e67db06e JL |
22 | #include "cpu.h" |
23 | #include "qemu-common.h" | |
022c62cb | 24 | #include "exec/gdbstub.h" |
1de7afc9 | 25 | #include "qemu/host-utils.h" |
e67db06e JL |
26 | #ifndef CONFIG_USER_ONLY |
27 | #include "hw/loader.h" | |
28 | #endif | |
29 | ||
726fe045 JL |
30 | #ifndef CONFIG_USER_ONLY |
31 | int cpu_openrisc_get_phys_nommu(OpenRISCCPU *cpu, | |
a8170e5e | 32 | hwaddr *physical, |
726fe045 JL |
33 | int *prot, target_ulong address, int rw) |
34 | { | |
35 | *physical = address; | |
04359e6b | 36 | *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; |
726fe045 JL |
37 | return TLBRET_MATCH; |
38 | } | |
39 | ||
40 | int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu, | |
a8170e5e | 41 | hwaddr *physical, |
726fe045 JL |
42 | int *prot, target_ulong address, int rw) |
43 | { | |
44 | int vpn = address >> TARGET_PAGE_BITS; | |
45 | int idx = vpn & ITLB_MASK; | |
46 | int right = 0; | |
47 | ||
48 | if ((cpu->env.tlb->itlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { | |
49 | return TLBRET_NOMATCH; | |
50 | } | |
51 | if (!(cpu->env.tlb->itlb[0][idx].mr & 1)) { | |
52 | return TLBRET_INVALID; | |
53 | } | |
54 | ||
55 | if (cpu->env.sr & SR_SM) { /* supervisor mode */ | |
56 | if (cpu->env.tlb->itlb[0][idx].tr & SXE) { | |
57 | right |= PAGE_EXEC; | |
58 | } | |
59 | } else { | |
60 | if (cpu->env.tlb->itlb[0][idx].tr & UXE) { | |
61 | right |= PAGE_EXEC; | |
62 | } | |
63 | } | |
64 | ||
65 | if ((rw & 2) && ((right & PAGE_EXEC) == 0)) { | |
66 | return TLBRET_BADADDR; | |
67 | } | |
68 | ||
69 | *physical = (cpu->env.tlb->itlb[0][idx].tr & TARGET_PAGE_MASK) | | |
70 | (address & (TARGET_PAGE_SIZE-1)); | |
71 | *prot = right; | |
72 | return TLBRET_MATCH; | |
73 | } | |
74 | ||
75 | int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu, | |
a8170e5e | 76 | hwaddr *physical, |
726fe045 JL |
77 | int *prot, target_ulong address, int rw) |
78 | { | |
79 | int vpn = address >> TARGET_PAGE_BITS; | |
80 | int idx = vpn & DTLB_MASK; | |
81 | int right = 0; | |
82 | ||
83 | if ((cpu->env.tlb->dtlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { | |
84 | return TLBRET_NOMATCH; | |
85 | } | |
86 | if (!(cpu->env.tlb->dtlb[0][idx].mr & 1)) { | |
87 | return TLBRET_INVALID; | |
88 | } | |
89 | ||
90 | if (cpu->env.sr & SR_SM) { /* supervisor mode */ | |
91 | if (cpu->env.tlb->dtlb[0][idx].tr & SRE) { | |
92 | right |= PAGE_READ; | |
93 | } | |
94 | if (cpu->env.tlb->dtlb[0][idx].tr & SWE) { | |
95 | right |= PAGE_WRITE; | |
96 | } | |
97 | } else { | |
98 | if (cpu->env.tlb->dtlb[0][idx].tr & URE) { | |
99 | right |= PAGE_READ; | |
100 | } | |
101 | if (cpu->env.tlb->dtlb[0][idx].tr & UWE) { | |
102 | right |= PAGE_WRITE; | |
103 | } | |
104 | } | |
105 | ||
bf961b52 | 106 | if (!(rw & 1) && ((right & PAGE_READ) == 0)) { |
726fe045 JL |
107 | return TLBRET_BADADDR; |
108 | } | |
109 | if ((rw & 1) && ((right & PAGE_WRITE) == 0)) { | |
110 | return TLBRET_BADADDR; | |
111 | } | |
112 | ||
113 | *physical = (cpu->env.tlb->dtlb[0][idx].tr & TARGET_PAGE_MASK) | | |
114 | (address & (TARGET_PAGE_SIZE-1)); | |
115 | *prot = right; | |
116 | return TLBRET_MATCH; | |
117 | } | |
118 | ||
119 | static int cpu_openrisc_get_phys_addr(OpenRISCCPU *cpu, | |
a8170e5e | 120 | hwaddr *physical, |
726fe045 JL |
121 | int *prot, target_ulong address, |
122 | int rw) | |
123 | { | |
124 | int ret = TLBRET_MATCH; | |
125 | ||
726fe045 JL |
126 | if (rw == 2) { /* ITLB */ |
127 | *physical = 0; | |
128 | ret = cpu->env.tlb->cpu_openrisc_map_address_code(cpu, physical, | |
129 | prot, address, rw); | |
130 | } else { /* DTLB */ | |
131 | ret = cpu->env.tlb->cpu_openrisc_map_address_data(cpu, physical, | |
132 | prot, address, rw); | |
133 | } | |
134 | ||
135 | return ret; | |
136 | } | |
137 | #endif | |
138 | ||
139 | static void cpu_openrisc_raise_mmu_exception(OpenRISCCPU *cpu, | |
140 | target_ulong address, | |
141 | int rw, int tlb_error) | |
142 | { | |
27103424 | 143 | CPUState *cs = CPU(cpu); |
726fe045 JL |
144 | int exception = 0; |
145 | ||
146 | switch (tlb_error) { | |
147 | default: | |
148 | if (rw == 2) { | |
149 | exception = EXCP_IPF; | |
150 | } else { | |
151 | exception = EXCP_DPF; | |
152 | } | |
153 | break; | |
154 | #ifndef CONFIG_USER_ONLY | |
155 | case TLBRET_BADADDR: | |
156 | if (rw == 2) { | |
157 | exception = EXCP_IPF; | |
158 | } else { | |
159 | exception = EXCP_DPF; | |
160 | } | |
161 | break; | |
162 | case TLBRET_INVALID: | |
163 | case TLBRET_NOMATCH: | |
164 | /* No TLB match for a mapped address */ | |
165 | if (rw == 2) { | |
166 | exception = EXCP_ITLBMISS; | |
167 | } else { | |
168 | exception = EXCP_DTLBMISS; | |
169 | } | |
170 | break; | |
171 | #endif | |
172 | } | |
173 | ||
27103424 | 174 | cs->exception_index = exception; |
726fe045 JL |
175 | cpu->env.eear = address; |
176 | } | |
177 | ||
178 | #ifndef CONFIG_USER_ONLY | |
7510454e AF |
179 | int openrisc_cpu_handle_mmu_fault(CPUState *cs, |
180 | vaddr address, int rw, int mmu_idx) | |
726fe045 | 181 | { |
7510454e | 182 | OpenRISCCPU *cpu = OPENRISC_CPU(cs); |
726fe045 | 183 | int ret = 0; |
a8170e5e | 184 | hwaddr physical = 0; |
726fe045 | 185 | int prot = 0; |
726fe045 JL |
186 | |
187 | ret = cpu_openrisc_get_phys_addr(cpu, &physical, &prot, | |
188 | address, rw); | |
189 | ||
190 | if (ret == TLBRET_MATCH) { | |
0c591eb0 | 191 | tlb_set_page(cs, address & TARGET_PAGE_MASK, |
04359e6b | 192 | physical & TARGET_PAGE_MASK, prot, |
726fe045 JL |
193 | mmu_idx, TARGET_PAGE_SIZE); |
194 | ret = 0; | |
195 | } else if (ret < 0) { | |
196 | cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret); | |
197 | ret = 1; | |
198 | } | |
199 | ||
200 | return ret; | |
201 | } | |
202 | #else | |
7510454e AF |
203 | int openrisc_cpu_handle_mmu_fault(CPUState *cs, |
204 | vaddr address, int rw, int mmu_idx) | |
726fe045 | 205 | { |
7510454e | 206 | OpenRISCCPU *cpu = OPENRISC_CPU(cs); |
726fe045 | 207 | int ret = 0; |
726fe045 JL |
208 | |
209 | cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret); | |
210 | ret = 1; | |
211 | ||
212 | return ret; | |
213 | } | |
214 | #endif | |
215 | ||
e67db06e | 216 | #ifndef CONFIG_USER_ONLY |
00b941e5 | 217 | hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) |
e67db06e | 218 | { |
00b941e5 | 219 | OpenRISCCPU *cpu = OPENRISC_CPU(cs); |
a8170e5e | 220 | hwaddr phys_addr; |
726fe045 | 221 | int prot; |
726fe045 JL |
222 | |
223 | if (cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, 0)) { | |
224 | return -1; | |
225 | } | |
226 | ||
227 | return phys_addr; | |
e67db06e JL |
228 | } |
229 | ||
230 | void cpu_openrisc_mmu_init(OpenRISCCPU *cpu) | |
231 | { | |
726fe045 JL |
232 | cpu->env.tlb = g_malloc0(sizeof(CPUOpenRISCTLBContext)); |
233 | ||
234 | cpu->env.tlb->cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu; | |
235 | cpu->env.tlb->cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu; | |
e67db06e JL |
236 | } |
237 | #endif |