1 |
/* |
2 |
* Copyright (C) 2007 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 |
* $Id: memory_m88k.c,v 1.8 2007/05/25 11:51:36 debug Exp $ |
29 |
* |
30 |
* Virtual to physical memory translation for M88K emulation. |
31 |
* |
32 |
* (This is where the actual work of the M8820x chips is emulated.) |
33 |
* |
34 |
* |
35 |
* TODO: |
36 |
* M88204 stuff, where it differs from the M88200. |
37 |
*/ |
38 |
|
39 |
#include <stdio.h> |
40 |
#include <stdlib.h> |
41 |
#include <string.h> |
42 |
|
43 |
#include "cpu.h" |
44 |
#include "machine.h" |
45 |
#include "memory.h" |
46 |
#include "misc.h" |
47 |
|
48 |
|
49 |
#include "m8820x.h" |
50 |
#include "m8820x_pte.h" |
51 |
|
52 |
|
53 |
/* #define M8820X_TABLE_SEARCH_DEBUG */ |
54 |
|
55 |
|
56 |
/* |
57 |
* m8820x_mark_page_as_modified(): |
58 |
* |
59 |
* Helper function which traverses the page table structure in emulated |
60 |
* memory, and marks a page as Modified and Used. |
61 |
*/ |
62 |
void m8820x_mark_page_as_modified(struct cpu *cpu, |
63 |
struct m8820x_cmmu *cmmu, uint32_t apr, uint32_t vaddr) |
64 |
{ |
65 |
int seg_nr = vaddr >> 22, page_nr = (vaddr >> 12) & 0x3ff; |
66 |
uint32_t *seg_base, *page_base; |
67 |
sdt_entry_t seg_descriptor; |
68 |
pt_entry_t page_descriptor; |
69 |
|
70 |
/* Read the segment descriptor from memory: */ |
71 |
seg_base = (uint32_t *) memory_paddr_to_hostaddr( |
72 |
cpu->mem, apr & 0xfffff000, 1); |
73 |
seg_descriptor = seg_base[seg_nr]; |
74 |
if (cpu->byte_order == EMUL_LITTLE_ENDIAN) |
75 |
seg_descriptor = LE32_TO_HOST(seg_descriptor); |
76 |
else |
77 |
seg_descriptor = BE32_TO_HOST(seg_descriptor); |
78 |
|
79 |
/* ... and the page descriptor: */ |
80 |
page_base = (uint32_t *) memory_paddr_to_hostaddr( |
81 |
cpu->mem, seg_descriptor & 0xfffff000, 1); |
82 |
page_descriptor = page_base[page_nr]; |
83 |
if (cpu->byte_order == EMUL_LITTLE_ENDIAN) |
84 |
page_descriptor = LE32_TO_HOST(page_descriptor); |
85 |
else |
86 |
page_descriptor = BE32_TO_HOST(page_descriptor); |
87 |
|
88 |
/* ... set the Modified and Used bits: */ |
89 |
page_descriptor |= PG_M | PG_U; |
90 |
|
91 |
/* ... and write it back: */ |
92 |
if (cpu->byte_order == EMUL_LITTLE_ENDIAN) |
93 |
page_descriptor = LE32_TO_HOST(page_descriptor); |
94 |
else |
95 |
page_descriptor = BE32_TO_HOST(page_descriptor); |
96 |
page_base[page_nr] = page_descriptor; |
97 |
} |
98 |
|
99 |
|
100 |
/* |
101 |
* m88k_translate_v2p(): |
102 |
* |
103 |
* Returns 0 for translation failure (access denied), 1 for read access |
104 |
* (success), or 2 for read/write access (success). |
105 |
*/ |
106 |
int m88k_translate_v2p(struct cpu *cpu, uint64_t vaddr64, |
107 |
uint64_t *return_paddr, int flags) |
108 |
{ |
109 |
int instr = flags & FLAG_INSTR; |
110 |
int writeflag = (flags & FLAG_WRITEFLAG)? 1 : 0; |
111 |
int no_exceptions = flags & FLAG_NOEXCEPTIONS; |
112 |
int exception_type = instr? M88K_EXCEPTION_INSTRUCTION_ACCESS |
113 |
: M88K_EXCEPTION_DATA_ACCESS; |
114 |
int supervisor = cpu->cd.m88k.cr[M88K_CR_PSR] & M88K_PSR_MODE; |
115 |
struct m8820x_cmmu *cmmu = cpu->cd.m88k.cmmu[instr? 0 : 1]; |
116 |
uint32_t vaddr = vaddr64; |
117 |
uint32_t apr; |
118 |
uint32_t *seg_base, *page_base; |
119 |
sdt_entry_t seg_descriptor; |
120 |
pt_entry_t page_descriptor; |
121 |
int pfsr_status = CMMU_PFSR_SUCCESS; |
122 |
uint32_t pfar = 0; |
123 |
int accumulated_flags; |
124 |
int i, seg_nr = vaddr >> 22, page_nr = (vaddr >> 12) & 0x3ff; |
125 |
|
126 |
if (flags & MEMORY_USER_ACCESS) |
127 |
supervisor = 0; |
128 |
|
129 |
|
130 |
/* |
131 |
* Is the CMMU not yet initialized? Then return physical = virtual. |
132 |
*/ |
133 |
*return_paddr = vaddr; |
134 |
if (cmmu == NULL) |
135 |
return 2; |
136 |
|
137 |
if (supervisor) |
138 |
apr = cmmu->reg[CMMU_SAPR]; |
139 |
else |
140 |
apr = cmmu->reg[CMMU_UAPR]; |
141 |
|
142 |
|
143 |
/* |
144 |
* Address translation not enabled? Then return physical = virtual. |
145 |
*/ |
146 |
if (!(apr & APR_V)) |
147 |
return 2; |
148 |
|
149 |
/* |
150 |
* BATC lookup: |
151 |
* |
152 |
* The BATC is a 10-entry array of virtual to physical mappings, |
153 |
* where the top 13 bits of the virtual address must match. |
154 |
*/ |
155 |
for (i=0; i<N_M88200_BATC_REGS; i++) { |
156 |
uint32_t batc = cmmu->batc[i]; |
157 |
|
158 |
/* The batc entry must be valid: */ |
159 |
if (!(batc & BATC_V)) |
160 |
continue; |
161 |
|
162 |
/* ... and have a matching supervisor/user bit: */ |
163 |
if ((supervisor && !(batc & BATC_SO)) || |
164 |
(!supervisor && (batc & BATC_SO))) |
165 |
continue; |
166 |
|
167 |
/* ... and matching virtual address: */ |
168 |
if ((vaddr & 0xfff80000) != (batc & 0xfff80000)) |
169 |
continue; |
170 |
|
171 |
/* A matching BATC entry was found! */ |
172 |
|
173 |
/* Is it write protected? */ |
174 |
if ((batc & BATC_PROT) && writeflag) { |
175 |
pfsr_status = CMMU_PFSR_WRITE; |
176 |
goto exception; |
177 |
} |
178 |
|
179 |
*return_paddr = ((batc & 0x0007ffc0) << 13) |
180 |
| (vaddr & 0x0007ffff); |
181 |
|
182 |
return batc & BATC_PROT? 1 : 2; |
183 |
} |
184 |
|
185 |
/* |
186 |
* PATC lookup: |
187 |
* |
188 |
* The PATC is a 56-entry array of virtual to physical mappings for |
189 |
* 4 KB pages. If writeflag is set, and a PATC entry is found without |
190 |
* the Modified bit set, a page table search must be performed to |
191 |
* set the Modified bit in emulated memory. |
192 |
*/ |
193 |
for (i=0; i<N_M88200_PATC_ENTRIES; i++) { |
194 |
uint32_t vaddr_and_control = cmmu->patc_v_and_control[i]; |
195 |
uint32_t paddr_and_sbit = cmmu->patc_p_and_supervisorbit[i]; |
196 |
|
197 |
/* Skip this entry if the valid bit isn't set: */ |
198 |
if (!(vaddr_and_control & PG_V)) |
199 |
continue; |
200 |
|
201 |
/* ... or the virtual addresses don't match: */ |
202 |
if ((vaddr & 0xfffff000) != (vaddr_and_control & 0xfffff000)) |
203 |
continue; |
204 |
|
205 |
/* ... or if the supervisor bit doesn't match: */ |
206 |
if (((paddr_and_sbit & M8820X_PATC_SUPERVISOR_BIT) |
207 |
&& !supervisor) || (supervisor && |
208 |
!(paddr_and_sbit & M8820X_PATC_SUPERVISOR_BIT))) |
209 |
continue; |
210 |
|
211 |
/* A matching PATC entry was found! */ |
212 |
|
213 |
/* Is it write protected? */ |
214 |
if ((vaddr_and_control & PG_PROT) && writeflag) { |
215 |
pfsr_status = CMMU_PFSR_WRITE; |
216 |
goto exception; |
217 |
} |
218 |
|
219 |
/* On writes: Is the page not yet marked as modified? */ |
220 |
if (!(vaddr_and_control & PG_M) && writeflag && |
221 |
!no_exceptions) { |
222 |
/* Then perform a page table search and mark |
223 |
it as Modified (and used): */ |
224 |
m8820x_mark_page_as_modified(cpu, cmmu, apr, vaddr); |
225 |
|
226 |
/* ... and mark the PATC entry too: */ |
227 |
cmmu->patc_v_and_control[i] |= PG_M; |
228 |
} |
229 |
|
230 |
/* Set the Used bit of the PATC entry: */ |
231 |
if (!no_exceptions) |
232 |
cmmu->patc_v_and_control[i] |= PG_U; |
233 |
|
234 |
/* ... and finally return the physical address: */ |
235 |
*return_paddr = (paddr_and_sbit & 0xfffff000) | (vaddr & 0xfff); |
236 |
return vaddr_and_control & PG_PROT? 1 : 2; |
237 |
} |
238 |
|
239 |
/* |
240 |
* The address was neither found in the BATC, nor the PATC. |
241 |
*/ |
242 |
|
243 |
/* |
244 |
* Attempt a search through page tables, to refill the PATC: |
245 |
*/ |
246 |
seg_base = (uint32_t *) memory_paddr_to_hostaddr( |
247 |
cpu->mem, apr & 0xfffff000, 1); |
248 |
|
249 |
seg_descriptor = seg_base[seg_nr]; |
250 |
if (cpu->byte_order == EMUL_LITTLE_ENDIAN) |
251 |
seg_descriptor = LE32_TO_HOST(seg_descriptor); |
252 |
else |
253 |
seg_descriptor = BE32_TO_HOST(seg_descriptor); |
254 |
|
255 |
#ifdef M8820X_TABLE_SEARCH_DEBUG |
256 |
printf("+--- M8820x page table search debug:\n"); |
257 |
printf("| vaddr 0x%08"PRIx32"\n", vaddr); |
258 |
printf("| apr 0x%08"PRIx32"\n", apr); |
259 |
printf("| seg_base %p (on the host)\n", seg_base); |
260 |
printf("| seg_nr 0x%03x\n", seg_nr); |
261 |
printf("| page_nr 0x%03x\n", page_nr); |
262 |
printf("| sd 0x%08"PRIx32"\n", seg_descriptor); |
263 |
#endif |
264 |
|
265 |
/* Segment descriptor invalid? Then cause a segfault exception. */ |
266 |
if (!(seg_descriptor & SG_V)) { |
267 |
/* PFAR = physical address of faulting segment descriptor: */ |
268 |
pfar = (apr & 0xfffff000) + seg_nr * sizeof(uint32_t); |
269 |
pfsr_status = CMMU_PFSR_SFAULT; |
270 |
goto exception; |
271 |
} |
272 |
|
273 |
/* Usermode attempted to access a supervisor segment? */ |
274 |
if ((seg_descriptor & SG_SO) && !supervisor) { |
275 |
/* PFAR = physical address of faulting segment descriptor: */ |
276 |
pfar = (apr & 0xfffff000) + seg_nr * sizeof(uint32_t); |
277 |
pfsr_status = CMMU_PFSR_SUPER; |
278 |
goto exception; |
279 |
} |
280 |
|
281 |
accumulated_flags = seg_descriptor & SG_RO; |
282 |
|
283 |
page_base = (uint32_t *) memory_paddr_to_hostaddr( |
284 |
cpu->mem, seg_descriptor & 0xfffff000, 1); |
285 |
|
286 |
page_descriptor = page_base[page_nr]; |
287 |
if (cpu->byte_order == EMUL_LITTLE_ENDIAN) |
288 |
page_descriptor = LE32_TO_HOST(page_descriptor); |
289 |
else |
290 |
page_descriptor = BE32_TO_HOST(page_descriptor); |
291 |
|
292 |
#ifdef M8820X_TABLE_SEARCH_DEBUG |
293 |
printf("| page_base %p (on the host)\n", page_base); |
294 |
printf("| pd 0x%08"PRIx32"\n", page_descriptor); |
295 |
#endif |
296 |
|
297 |
/* Page descriptor invalid? Then cause a page fault exception. */ |
298 |
if (!(page_descriptor & PG_V)) { |
299 |
/* PFAR = physical address of faulting page descriptor: */ |
300 |
pfar = (seg_descriptor & 0xfffff000) |
301 |
+ page_nr * sizeof(uint32_t); |
302 |
pfsr_status = CMMU_PFSR_PFAULT; |
303 |
goto exception; |
304 |
} |
305 |
|
306 |
/* Usermode attempted to access a supervisor page? */ |
307 |
if ((page_descriptor & PG_SO) && !supervisor) { |
308 |
/* PFAR = physical address of faulting page descriptor: */ |
309 |
pfar = (seg_descriptor & 0xfffff000) |
310 |
+ page_nr * sizeof(uint32_t); |
311 |
pfsr_status = CMMU_PFSR_SUPER; |
312 |
goto exception; |
313 |
} |
314 |
|
315 |
accumulated_flags |= (page_descriptor & PG_RO); |
316 |
|
317 |
|
318 |
/* |
319 |
* Overwrite the next entry in the PATC with a new entry: |
320 |
*/ |
321 |
|
322 |
if (!no_exceptions) { |
323 |
i = cmmu->patc_update_index; |
324 |
|
325 |
/* Invalidate the current entry, if it is valid: */ |
326 |
if (cmmu->patc_v_and_control[i] & PG_V) |
327 |
cpu->invalidate_translation_caches(cpu, |
328 |
cmmu->patc_v_and_control[i] & 0xfffff000, |
329 |
INVALIDATE_VADDR); |
330 |
|
331 |
/* ... and write the new one: */ |
332 |
cmmu->patc_update_index ++; |
333 |
cmmu->patc_update_index %= N_M88200_PATC_ENTRIES; |
334 |
cmmu->patc_v_and_control[i] = |
335 |
(vaddr & 0xfffff000) | accumulated_flags | PG_V; |
336 |
cmmu->patc_p_and_supervisorbit[i] = |
337 |
(page_descriptor & 0xfffff000) | |
338 |
(supervisor? M8820X_PATC_SUPERVISOR_BIT : 0); |
339 |
} |
340 |
|
341 |
/* Check for writes to read-only pages: */ |
342 |
if (writeflag && (accumulated_flags & PG_RO)) { |
343 |
pfsr_status = CMMU_PFSR_WRITE; |
344 |
goto exception; |
345 |
} |
346 |
|
347 |
if (!no_exceptions) { |
348 |
uint32_t tmp; |
349 |
|
350 |
/* We now know that the page is in use. */ |
351 |
cmmu->patc_v_and_control[i] |= PG_U; |
352 |
if (writeflag) |
353 |
cmmu->patc_v_and_control[i] |= PG_M; |
354 |
|
355 |
/* |
356 |
* Write back the U bit (and possibly the M bit) to the page |
357 |
* descriptor in emulated memory: |
358 |
*/ |
359 |
tmp = page_descriptor | PG_U; |
360 |
if (writeflag) |
361 |
tmp |= PG_M; |
362 |
|
363 |
if (cpu->byte_order == EMUL_LITTLE_ENDIAN) |
364 |
tmp = LE32_TO_HOST(tmp); |
365 |
else |
366 |
tmp = BE32_TO_HOST(tmp); |
367 |
|
368 |
page_base[page_nr] = tmp; |
369 |
} |
370 |
|
371 |
/* Now finally return with the translated page address: */ |
372 |
*return_paddr = (page_descriptor & 0xfffff000) | (vaddr & 0xfff); |
373 |
return (accumulated_flags & PG_RO)? 1 : 2; |
374 |
|
375 |
|
376 |
exception: |
377 |
if (no_exceptions) |
378 |
return 0; |
379 |
|
380 |
/* |
381 |
* Update the Page Fault Status Register of the CMMU which this fault |
382 |
* was associated with, but also clear the PFSR of the _other_ CMMU: |
383 |
*/ |
384 |
|
385 |
cmmu->reg[CMMU_PFSR] = pfsr_status << 16; |
386 |
cpu->cd.m88k.cmmu[instr? 1 : 0]->reg[CMMU_PFSR] = |
387 |
CMMU_PFSR_SUCCESS << 16; |
388 |
|
389 |
/* ... and (if necessary) update the Page Fault Address Register: */ |
390 |
|
391 |
switch (pfsr_status) { |
392 |
|
393 |
case CMMU_PFSR_SUCCESS: |
394 |
break; |
395 |
|
396 |
case CMMU_PFSR_WRITE: |
397 |
/* Note: The PFAR is "destroyed"/undefined on write faults. */ |
398 |
cmmu->reg[CMMU_PFAR] = 0; |
399 |
break; |
400 |
|
401 |
case CMMU_PFSR_SUPER: |
402 |
case CMMU_PFSR_SFAULT: |
403 |
case CMMU_PFSR_PFAULT: |
404 |
cmmu->reg[CMMU_PFAR] = pfar; |
405 |
break; |
406 |
|
407 |
default: |
408 |
fatal("Internal error in memory_m88k? pfsr_status = %i\n", |
409 |
pfsr_status); |
410 |
exit(1); |
411 |
} |
412 |
|
413 |
/* ... and finally cause the exception: */ |
414 |
m88k_exception(cpu, exception_type, 0); |
415 |
|
416 |
return 0; |
417 |
} |
418 |
|