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