1 |
dpavlin |
14 |
/* |
2 |
|
|
* Copyright (C) 2005 Anders Gavare. All rights reserved. |
3 |
|
|
* |
4 |
|
|
* Redistribution and use in source and binary forms, with or without |
5 |
|
|
* modification, are permitted provided that the following conditions are met: |
6 |
|
|
* |
7 |
|
|
* 1. Redistributions of source code must retain the above copyright |
8 |
|
|
* notice, this list of conditions and the following disclaimer. |
9 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
10 |
|
|
* notice, this list of conditions and the following disclaimer in the |
11 |
|
|
* documentation and/or other materials provided with the distribution. |
12 |
|
|
* 3. The name of the author may not be used to endorse or promote products |
13 |
|
|
* derived from this software without specific prior written permission. |
14 |
|
|
* |
15 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
|
|
* SUCH DAMAGE. |
26 |
|
|
* |
27 |
|
|
* |
28 |
dpavlin |
26 |
* $Id: memory_ppc.c,v 1.24 2006/06/24 21:47:23 debug Exp $ |
29 |
dpavlin |
14 |
* |
30 |
|
|
* Included from cpu_ppc.c. |
31 |
|
|
*/ |
32 |
|
|
|
33 |
|
|
|
34 |
dpavlin |
20 |
#include "ppc_bat.h" |
35 |
|
|
#include "ppc_pte.h" |
36 |
|
|
|
37 |
|
|
|
38 |
dpavlin |
14 |
/* |
39 |
dpavlin |
20 |
* ppc_bat(): |
40 |
|
|
* |
41 |
|
|
* BAT translation. Returns -1 if there was no BAT hit, >= 0 for a hit. |
42 |
|
|
* (0 for access denied, 1 for read-only, and 2 for read-write access allowed.) |
43 |
|
|
*/ |
44 |
dpavlin |
26 |
int ppc_bat(struct cpu *cpu, uint64_t vaddr, uint64_t *return_paddr, int flags, |
45 |
dpavlin |
20 |
int user) |
46 |
|
|
{ |
47 |
dpavlin |
22 |
int i, istart = 0, iend = 8, pp; |
48 |
dpavlin |
20 |
|
49 |
dpavlin |
22 |
if (flags & FLAG_INSTR) |
50 |
|
|
iend = 4; |
51 |
|
|
else |
52 |
|
|
istart = 4; |
53 |
|
|
|
54 |
dpavlin |
20 |
if (cpu->cd.ppc.bits != 32) { |
55 |
|
|
fatal("TODO: ppc_bat() for non-32-bit\n"); |
56 |
|
|
exit(1); |
57 |
|
|
} |
58 |
|
|
if (cpu->cd.ppc.cpu_type.flags & PPC_601) { |
59 |
|
|
fatal("TODO: ppc_bat() for PPC 601\n"); |
60 |
|
|
exit(1); |
61 |
|
|
} |
62 |
|
|
|
63 |
dpavlin |
22 |
/* Scan either the 4 instruction BATs or the 4 data BATs: */ |
64 |
|
|
for (i=istart; i<iend; i++) { |
65 |
|
|
int regnr = SPR_IBAT0U + i * 2; |
66 |
dpavlin |
20 |
uint32_t upper = cpu->cd.ppc.spr[regnr]; |
67 |
|
|
uint32_t lower = cpu->cd.ppc.spr[regnr + 1]; |
68 |
|
|
uint32_t phys = lower & BAT_RPN, ebs = upper & BAT_EPI; |
69 |
|
|
uint32_t mask = ((upper & BAT_BL) << 15) | 0x1ffff; |
70 |
|
|
|
71 |
|
|
/* Not valid in either supervisor or user mode? */ |
72 |
|
|
if (user && !(upper & BAT_Vu)) |
73 |
|
|
continue; |
74 |
|
|
if (!user && !(upper & BAT_Vs)) |
75 |
|
|
continue; |
76 |
|
|
|
77 |
|
|
/* Virtual address mismatch? Then skip. */ |
78 |
|
|
if ((vaddr & ~mask) != (ebs & ~mask)) |
79 |
|
|
continue; |
80 |
|
|
|
81 |
dpavlin |
26 |
*return_paddr = (vaddr & mask) | (phys & ~mask); |
82 |
dpavlin |
20 |
|
83 |
|
|
pp = lower & BAT_PP; |
84 |
|
|
switch (pp) { |
85 |
|
|
case BAT_PP_NONE: |
86 |
|
|
return 0; |
87 |
|
|
case BAT_PP_RO_S: |
88 |
|
|
case BAT_PP_RO: |
89 |
|
|
return (flags & FLAG_WRITEFLAG)? 0 : 1; |
90 |
|
|
default:/* BAT_PP_RW: */ |
91 |
|
|
return 2; |
92 |
|
|
} |
93 |
|
|
} |
94 |
|
|
|
95 |
|
|
return -1; |
96 |
|
|
} |
97 |
|
|
|
98 |
|
|
|
99 |
|
|
/* |
100 |
|
|
* get_pte_low(): |
101 |
|
|
* |
102 |
|
|
* Scan a PTE group for a cmp (compare) value. |
103 |
|
|
* |
104 |
|
|
* Returns 1 if the value was found, and *lowp is set to the low PTE word. |
105 |
|
|
* Returns 0 if no match was found. |
106 |
|
|
*/ |
107 |
|
|
static int get_pte_low(struct cpu *cpu, uint64_t pteg_select, |
108 |
|
|
uint32_t *lowp, uint32_t cmp) |
109 |
|
|
{ |
110 |
dpavlin |
22 |
unsigned char *d = memory_paddr_to_hostaddr(cpu->mem, pteg_select, 1) |
111 |
|
|
+ (pteg_select & ((1 << BITS_PER_MEMBLOCK) - 1)); |
112 |
dpavlin |
20 |
int i; |
113 |
|
|
|
114 |
|
|
for (i=0; i<8; i++) { |
115 |
dpavlin |
22 |
uint32_t *ep = (uint32_t *) (d + (i << 3)), upper; |
116 |
|
|
upper = *ep; |
117 |
|
|
upper = BE32_TO_HOST(upper); |
118 |
dpavlin |
20 |
|
119 |
|
|
/* Valid PTE, and correct api and vsid? */ |
120 |
|
|
if (upper == cmp) { |
121 |
dpavlin |
22 |
uint32_t lo = ep[1]; |
122 |
|
|
lo = BE32_TO_HOST(lo); |
123 |
|
|
*lowp = lo; |
124 |
dpavlin |
20 |
return 1; |
125 |
|
|
} |
126 |
|
|
} |
127 |
|
|
|
128 |
|
|
return 0; |
129 |
|
|
} |
130 |
|
|
|
131 |
|
|
|
132 |
|
|
/* |
133 |
|
|
* ppc_vtp32(): |
134 |
|
|
* |
135 |
|
|
* Virtual to physical address translation (32-bit mode). |
136 |
|
|
* |
137 |
|
|
* Returns 1 if a translation was found, 0 if none was found. However, finding |
138 |
|
|
* a translation does not mean that it should be returned; there can be |
139 |
|
|
* a permission violation. *resp is set to 0 for no access, 1 for read-only |
140 |
|
|
* access, or 2 for read/write access. |
141 |
|
|
*/ |
142 |
dpavlin |
26 |
static int ppc_vtp32(struct cpu *cpu, uint32_t vaddr, uint64_t *return_paddr, |
143 |
dpavlin |
20 |
int *resp, uint64_t msr, int writeflag, int instr) |
144 |
|
|
{ |
145 |
|
|
int srn = (vaddr >> 28) & 15, api = (vaddr >> 22) & PTE_API; |
146 |
|
|
int access, key, match; |
147 |
|
|
uint32_t vsid = cpu->cd.ppc.sr[srn] & 0x00ffffff; |
148 |
|
|
uint64_t sdr1 = cpu->cd.ppc.spr[SPR_SDR1], htaborg; |
149 |
|
|
uint32_t hash1, hash2, pteg_select, tmp; |
150 |
|
|
uint32_t lower_pte = 0, cmp; |
151 |
|
|
|
152 |
|
|
htaborg = sdr1 & 0xffff0000UL; |
153 |
|
|
|
154 |
|
|
/* Primary hash: */ |
155 |
|
|
hash1 = (vsid & 0x7ffff) ^ ((vaddr >> 12) & 0xffff); |
156 |
|
|
tmp = (hash1 >> 10) & (sdr1 & 0x1ff); |
157 |
|
|
pteg_select = htaborg & 0xfe000000; |
158 |
|
|
pteg_select |= ((hash1 & 0x3ff) << 6); |
159 |
|
|
pteg_select |= (htaborg & 0x01ff0000) | (tmp << 16); |
160 |
|
|
cpu->cd.ppc.spr[SPR_HASH1] = pteg_select; |
161 |
|
|
cmp = cpu->cd.ppc.spr[instr? SPR_ICMP : SPR_DCMP] = |
162 |
|
|
PTE_VALID | api | (vsid << PTE_VSID_SHFT); |
163 |
|
|
match = get_pte_low(cpu, pteg_select, &lower_pte, cmp); |
164 |
|
|
|
165 |
|
|
/* Secondary hash: */ |
166 |
|
|
hash2 = hash1 ^ 0x7ffff; |
167 |
|
|
tmp = (hash2 >> 10) & (sdr1 & 0x1ff); |
168 |
|
|
pteg_select = htaborg & 0xfe000000; |
169 |
|
|
pteg_select |= ((hash2 & 0x3ff) << 6); |
170 |
|
|
pteg_select |= (htaborg & 0x01ff0000) | (tmp << 16); |
171 |
|
|
cpu->cd.ppc.spr[SPR_HASH2] = pteg_select; |
172 |
|
|
if (!match) { |
173 |
|
|
cmp |= PTE_HID; |
174 |
|
|
match = get_pte_low(cpu, pteg_select, &lower_pte, cmp); |
175 |
|
|
} |
176 |
|
|
|
177 |
|
|
*resp = 0; |
178 |
|
|
|
179 |
|
|
if (!match) |
180 |
|
|
return 0; |
181 |
|
|
|
182 |
|
|
/* Non-executable, or Guarded page? */ |
183 |
|
|
if (instr && cpu->cd.ppc.sr[srn] & SR_NOEXEC) |
184 |
|
|
return 1; |
185 |
|
|
if (instr && lower_pte & PTE_G) |
186 |
|
|
return 1; |
187 |
|
|
|
188 |
|
|
access = lower_pte & PTE_PP; |
189 |
dpavlin |
26 |
*return_paddr = (lower_pte & PTE_RPGN) | (vaddr & ~PTE_RPGN); |
190 |
dpavlin |
20 |
|
191 |
|
|
key = (cpu->cd.ppc.sr[srn] & SR_PRKEY && msr & PPC_MSR_PR) || |
192 |
|
|
(cpu->cd.ppc.sr[srn] & SR_SUKEY && !(msr & PPC_MSR_PR)); |
193 |
|
|
|
194 |
|
|
if (key) { |
195 |
|
|
switch (access) { |
196 |
|
|
case 1: |
197 |
|
|
case 3: *resp = writeflag? 0 : 1; |
198 |
|
|
break; |
199 |
|
|
case 2: *resp = 2; |
200 |
|
|
break; |
201 |
|
|
} |
202 |
|
|
} else { |
203 |
|
|
switch (access) { |
204 |
|
|
case 3: *resp = writeflag? 0 : 1; |
205 |
|
|
break; |
206 |
|
|
default:*resp = 2; |
207 |
|
|
} |
208 |
|
|
} |
209 |
|
|
|
210 |
|
|
return 1; |
211 |
|
|
} |
212 |
|
|
|
213 |
|
|
|
214 |
|
|
/* |
215 |
dpavlin |
26 |
* ppc_translate_v2p(): |
216 |
dpavlin |
14 |
* |
217 |
|
|
* Don't call this function is userland_emul is non-NULL, or cpu is NULL. |
218 |
|
|
* |
219 |
|
|
* Return values: |
220 |
|
|
* 0 Failure |
221 |
|
|
* 1 Success, the page is readable only |
222 |
|
|
* 2 Success, the page is read/write |
223 |
|
|
*/ |
224 |
dpavlin |
26 |
int ppc_translate_v2p(struct cpu *cpu, uint64_t vaddr, |
225 |
|
|
uint64_t *return_paddr, int flags) |
226 |
dpavlin |
14 |
{ |
227 |
dpavlin |
20 |
int instr = flags & FLAG_INSTR, res = 0, match, user; |
228 |
|
|
int writeflag = flags & FLAG_WRITEFLAG? 1 : 0; |
229 |
|
|
uint64_t msr; |
230 |
dpavlin |
14 |
|
231 |
dpavlin |
20 |
reg_access_msr(cpu, &msr, 0, 0); |
232 |
|
|
user = msr & PPC_MSR_PR? 1 : 0; |
233 |
|
|
|
234 |
dpavlin |
14 |
if (cpu->cd.ppc.bits == 32) |
235 |
|
|
vaddr &= 0xffffffff; |
236 |
|
|
|
237 |
dpavlin |
20 |
if ((instr && !(msr & PPC_MSR_IR)) || (!instr && !(msr & PPC_MSR_DR))) { |
238 |
dpavlin |
26 |
*return_paddr = vaddr; |
239 |
dpavlin |
14 |
return 2; |
240 |
|
|
} |
241 |
|
|
|
242 |
dpavlin |
20 |
if (cpu->cd.ppc.cpu_type.flags & PPC_601) { |
243 |
dpavlin |
26 |
fatal("ppc_translate_v2p(): TODO: 601\n"); |
244 |
dpavlin |
20 |
exit(1); |
245 |
|
|
} |
246 |
dpavlin |
14 |
|
247 |
dpavlin |
20 |
/* Try the BATs first: */ |
248 |
|
|
if (cpu->cd.ppc.bits == 32) { |
249 |
dpavlin |
26 |
res = ppc_bat(cpu, vaddr, return_paddr, flags, user); |
250 |
dpavlin |
20 |
if (res > 0) |
251 |
|
|
return res; |
252 |
|
|
if (res == 0) { |
253 |
|
|
fatal("[ TODO: BAT exception ]\n"); |
254 |
|
|
exit(1); |
255 |
dpavlin |
14 |
} |
256 |
dpavlin |
20 |
} |
257 |
dpavlin |
14 |
|
258 |
dpavlin |
20 |
/* Virtual to physical translation: */ |
259 |
|
|
if (cpu->cd.ppc.bits == 32) { |
260 |
dpavlin |
26 |
match = ppc_vtp32(cpu, vaddr, return_paddr, &res, msr, |
261 |
dpavlin |
20 |
writeflag, instr); |
262 |
|
|
if (match && res > 0) |
263 |
|
|
return res; |
264 |
|
|
} else { |
265 |
|
|
/* htaborg = sdr1 & 0xfffffffffffc0000ULL; */ |
266 |
|
|
fatal("TODO: ppc 64-bit translation\n"); |
267 |
|
|
exit(1); |
268 |
dpavlin |
14 |
} |
269 |
|
|
|
270 |
dpavlin |
20 |
|
271 |
|
|
/* |
272 |
|
|
* No match? Then cause an exception. |
273 |
|
|
* |
274 |
|
|
* PPC603: cause a software TLB reload exception. |
275 |
|
|
* All others: cause a DSI or ISI. |
276 |
|
|
*/ |
277 |
|
|
|
278 |
dpavlin |
14 |
if (flags & FLAG_NOEXCEPTIONS) |
279 |
|
|
return 0; |
280 |
|
|
|
281 |
dpavlin |
20 |
if (!quiet_mode) |
282 |
dpavlin |
24 |
fatal("[ memory_ppc: exception! vaddr=0x%"PRIx64" pc=0x%"PRIx64 |
283 |
|
|
" instr=%i user=%i wf=%i ]\n", (uint64_t) vaddr, |
284 |
|
|
(uint64_t) cpu->pc, instr, user, writeflag); |
285 |
dpavlin |
20 |
|
286 |
|
|
if (cpu->cd.ppc.cpu_type.flags & PPC_603) { |
287 |
|
|
cpu->cd.ppc.spr[instr? SPR_IMISS : SPR_DMISS] = vaddr; |
288 |
|
|
|
289 |
|
|
msr |= PPC_MSR_TGPR; |
290 |
|
|
reg_access_msr(cpu, &msr, 1, 0); |
291 |
|
|
|
292 |
|
|
ppc_exception(cpu, instr? 0x10 : (writeflag? 0x12 : 0x11)); |
293 |
|
|
} else { |
294 |
|
|
if (!instr) { |
295 |
|
|
cpu->cd.ppc.spr[SPR_DAR] = vaddr; |
296 |
|
|
cpu->cd.ppc.spr[SPR_DSISR] = match? |
297 |
|
|
DSISR_PROTECT : DSISR_NOTFOUND; |
298 |
|
|
if (writeflag) |
299 |
|
|
cpu->cd.ppc.spr[SPR_DSISR] |= DSISR_STORE; |
300 |
|
|
} |
301 |
|
|
ppc_exception(cpu, instr? |
302 |
|
|
PPC_EXCEPTION_ISI : PPC_EXCEPTION_DSI); |
303 |
|
|
} |
304 |
|
|
|
305 |
dpavlin |
14 |
return 0; |
306 |
|
|
} |
307 |
|
|
|