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