fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/cpu/arm/mmu.c *
7 * Created: 2004-11-03 by Hampa Hug <hampa@hampa.ch> *
8 * Copyright: (C) 2004-2011 Hampa Hug <hampa@hampa.ch> *
9 * Copyright: (C) 2004-2006 Lukas Ruf <ruf@lpr.ch> *
10 *****************************************************************************/
11
12/*****************************************************************************
13 * This program is free software. You can redistribute it and / or modify it *
14 * under the terms of the GNU General Public License version 2 as published *
15 * by the Free Software Foundation. *
16 * *
17 * This program is distributed in the hope that it will be useful, but *
18 * WITHOUT ANY WARRANTY, without even the implied warranty of *
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
20 * Public License for more details. *
21 *****************************************************************************/
22
23/*****************************************************************************
24 * This software was developed at the Computer Engineering and Networks *
25 * Laboratory (TIK), Swiss Federal Institute of Technology (ETH) Zurich. *
26 *****************************************************************************/
27
28
29#include <stdio.h>
30#include <stdlib.h>
31
32#include "arm.h"
33#include "internal.h"
34
35
36#if defined(PCE_HOST_PPC) || defined (PCE_HOST_SPARC)
37#define ARM_HOST_BE
38#endif
39
40#if defined(PCE_HOST_IA32)
41#define ARM_HOST_LE
42#endif
43
44
45static
46void arm_mmu_fault (arm_t *c, uint32_t addr, unsigned status, unsigned domn)
47{
48 arm_copr15_t *mmu;
49
50 mmu = arm_get_mmu (c);
51
52 mmu->reg[6] = addr;
53 mmu->reg[5] = ((domn & 0x0f) << 4) | (status & 0x0f);
54
55 arm_exception_data_abort (c);
56}
57
58static
59void arm_mmu_translation_fault (arm_t *c, uint32_t addr, unsigned domn, int sect)
60{
61 arm_mmu_fault (c, addr, sect ? 0x05 : 0x07, domn);
62}
63
64static
65void arm_mmu_domain_fault (arm_t *c, uint32_t addr, unsigned domn, int sect)
66{
67 arm_mmu_fault (c, addr, sect ? 0x09 : 0x0b, domn);
68}
69
70static
71void arm_mmu_permission_fault (arm_t *c, uint32_t addr, unsigned domn, int sect)
72{
73 arm_mmu_fault (c, addr, sect ? 0x0d : 0x0f, domn);
74}
75
76
77static inline
78void arm_tbuf_set (arm_tbuf_t *tb, uint32_t vaddr, uint32_t raddr, uint32_t mask)
79{
80 tb->vaddr = vaddr & mask;
81 tb->vmask = mask;
82 tb->raddr = raddr & mask;
83 tb->rmask = ~mask;
84 tb->valid = 1;
85}
86
87
88/*!***************************************************************************
89 * @short Check access permissions for reading
90 * @param cr The control register (coprocessor 15 register 1)
91 * @param perm The page or section permission bits
92 * @param priv Check for privileged access if true
93 * @return Zero if access violation, non-zero if ok
94 *****************************************************************************/
95static
96int arm_mmu_check_perm_read (uint32_t cr, unsigned perm, int priv)
97{
98 if (perm == 0) {
99 switch (cr & (ARM_C15_CR_S | ARM_C15_CR_R)) {
100 case 0x00: /* no access */
101 return (0);
102
103 case ARM_C15_CR_S: /* priv: read only, user: no access */
104 return (priv);
105
106 case ARM_C15_CR_R: /* read only */
107 return (1);
108
109 case ARM_C15_CR_S | ARM_C15_CR_R: /* undefined */
110 return (0);
111 }
112
113 return (0);
114 }
115
116 if (priv) {
117 return (1);
118 }
119
120 switch (perm) {
121 case 0x01: /* no access */
122 return (0);
123
124 case 0x02: /* read only */
125 return (1);
126
127 case 0x03: /* read/write */
128 return (1);
129 }
130
131 return (0);
132}
133
134/*!***************************************************************************
135 * @short Check access permissions for writing
136 * @param cr The control register (coprocessor 15 register 1)
137 * @param perm The page or section permission bits
138 * @param priv Check for privileged access if true
139 * @return Zero if access violation, non-zero if ok
140 *****************************************************************************/
141static
142int arm_mmu_check_perm_write (uint32_t cr, unsigned perm, int priv)
143{
144 if (perm == 0) {
145 switch (cr & (ARM_C15_CR_S | ARM_C15_CR_R)) {
146 case 0x00: /* no access */
147 return (0);
148
149 case ARM_C15_CR_S: /* priv: read only, user: no access */
150 return (0);
151
152 case ARM_C15_CR_R: /* read only */
153 return (0);
154
155 case ARM_C15_CR_S | ARM_C15_CR_R: /* undefined */
156 return (0);
157 }
158
159 return (0);
160 }
161
162 if (priv) {
163 return (1);
164 }
165
166 switch (perm) {
167 case 0x01: /* no access */
168 return (0);
169
170 case 0x02: /* read only */
171 return (0);
172
173 case 0x03: /* read/write */
174 return (1);
175 }
176
177 return (0);
178}
179
180/*!***************************************************************************
181 * @short Translate a virtual address
182 * @param c The ARM context
183 * @param addr Virtual address input, real address output
184 * @retval domn Returns the domain number
185 * @retval perm The page or section permission bits
186 * @retval sect The virtual address is in a section if true, in a page if false
187 * @return Non-zero on translation fault
188 *****************************************************************************/
189static
190int arm_translate (arm_t *c, uint32_t *addr, uint32_t *mask,
191 unsigned *domn, unsigned *perm, int *sect)
192{
193 arm_copr15_t *mmu;
194 unsigned ap;
195 uint32_t addr1, addr2;
196 uint32_t desc1, desc2;
197
198 mmu = arm_get_mmu (c);
199
200 addr1 = (mmu->reg[2] & 0xffffc000) | ((*addr >> 18) & 0x00003ffc);
201 desc1 = c->get_uint32 (c->mem_ext, addr1);
202
203 addr2 = 0;
204
205 switch (desc1 & 0x03) {
206 case 0x00:
207 /* section translation fault */
208 *mask = 0;
209 *domn = 0;
210 *sect = 1;
211 return (1);
212
213 case 0x01:
214 /* coarse */
215 addr2 = (desc1 & 0xfffffc00) | ((*addr >> 10) & 0x000003fc);
216 *domn = arm_get_bits (desc1, 5, 4);
217 break;
218
219 case 0x02:
220 /* section */
221 *addr = (desc1 & 0xfff00000) | (*addr & 0x000fffff);
222 *mask = 0xfff00000;
223 *domn = arm_get_bits (desc1, 5, 4);
224 *perm = arm_get_bits (desc1, 10, 2);
225 *sect = 1;
226 return (0);
227
228 case 0x03:
229 /* fine */
230 addr2 = (desc1 & 0xfffff000) | ((*addr >> 8) & 0x00000ffc);
231 *domn = arm_get_bits (desc1, 5, 4);
232 break;
233 }
234
235 desc2 = c->get_uint32 (c->mem_ext, addr2);
236
237 *sect = 0;
238
239 switch (desc2 & 0x03) {
240 case 0x00:
241 /* page translation fault */
242 *mask = 0;
243 *perm = 0;
244 return (1);
245
246 case 0x01:
247 /* large page */
248 ap = 4 + 2 * arm_get_bits (*addr, 14, 2);
249 *addr = (desc2 & 0xffff0000) | (*addr & 0x0000ffff);
250 *mask = 0xffff0000;
251 *perm = arm_get_bits (desc2, ap, 2);
252 return (0);
253
254 case 0x02:
255 /* small page */
256 ap = 4 + 2 * arm_get_bits (*addr, 10, 2);
257 *addr = (desc2 & 0xfffff000) | (*addr & 0x00000fff);
258 *mask = 0xfffff000;
259 *perm = arm_get_bits (desc2, ap, 2);
260 return (0);
261
262 case 0x03:
263 if ((desc1 & 0x03) == 0x01) {
264 /* xscale extended small page */
265 *addr = (desc2 & 0xfffff000) | (*addr & 0x00000fff);
266 *mask = 0xfffff000;
267 *perm = arm_get_bits (desc2, 4, 2);
268 return (0);
269 }
270
271 /* tiny page */
272 *perm = arm_get_bits (desc2, 4, 2);
273 *addr = (desc2 & 0xfffffc00) | (*addr & 0x000003ff);
274 *mask = 0xfffffc00;
275 return (0);
276 }
277
278 /* non-reachable */
279
280 *perm = 0;
281
282 return (1);
283}
284
285static
286int arm_translate_exec (arm_t *c, uint32_t *addr, int priv)
287{
288 arm_copr15_t *mmu;
289 unsigned domn, perm;
290 int sect;
291 uint32_t vaddr, mask;
292
293 mmu = arm_get_mmu (c);
294
295 if ((mmu->reg[1] & ARM_C15_CR_M) == 0) {
296 return (0);
297 }
298
299 vaddr = *addr;
300
301 if (mmu->tbuf_exec.valid) {
302 arm_tbuf_t *tb = &mmu->tbuf_exec;
303
304 if ((vaddr & tb->vmask) == tb->vaddr) {
305 *addr = tb->raddr | (vaddr & tb->rmask);
306 return (0);
307 }
308 }
309
310 if (arm_translate (c, addr, &mask, &domn, &perm, §)) {
311 arm_exception_prefetch_abort (c);
312 return (1);
313 }
314
315 /* check domain */
316 switch ((mmu->reg[3] >> (2 * domn)) & 0x03) {
317 case 0x00: /* no access */
318 arm_mmu_domain_fault (c, vaddr, domn, sect);
319 return (1);
320
321 case 0x01: /* client */
322 if (arm_mmu_check_perm_read (mmu->reg[1], perm, priv) == 0) {
323 arm_exception_prefetch_abort (c);
324 return (1);
325 }
326 arm_tbuf_set (&mmu->tbuf_exec, vaddr, *addr, mask);
327 return (0);
328
329 case 0x02: /* undefined */
330 return (0);
331
332 case 0x03: /* manager */
333 arm_tbuf_set (&mmu->tbuf_exec, vaddr, *addr, mask);
334 return (0);
335 }
336
337 return (0);
338}
339
340static
341int arm_translate_read (arm_t *c, uint32_t *addr, int priv)
342{
343 arm_copr15_t *mmu;
344 unsigned domn, perm;
345 int sect;
346 uint32_t vaddr, mask;
347
348 mmu = arm_get_mmu (c);
349
350 if ((mmu->reg[1] & ARM_C15_CR_M) == 0) {
351 return (0);
352 }
353
354 vaddr = *addr;
355
356 if (mmu->tbuf_read.valid) {
357 arm_tbuf_t *tb = &mmu->tbuf_read;
358
359 if ((vaddr & tb->vmask) == tb->vaddr) {
360 *addr = tb->raddr | (vaddr & tb->rmask);
361 return (0);
362 }
363 }
364
365 if (arm_translate (c, addr, &mask, &domn, &perm, §)) {
366 arm_mmu_translation_fault (c, vaddr, domn, sect);
367 return (1);
368 }
369
370 /* check domain */
371 switch ((mmu->reg[3] >> (2 * domn)) & 0x03) {
372 case 0x00: /* no access */
373 arm_mmu_domain_fault (c, vaddr, domn, sect);
374 return (1);
375
376 case 0x01: /* client */
377 if (arm_mmu_check_perm_read (mmu->reg[1], perm, priv) == 0) {
378 arm_mmu_permission_fault (c, vaddr, domn, sect);
379 return (1);
380 }
381 arm_tbuf_set (&mmu->tbuf_read, vaddr, *addr, mask);
382 return (0);
383
384 case 0x02: /* undefined */
385 return (0);
386
387 case 0x03: /* manager */
388 arm_tbuf_set (&mmu->tbuf_read, vaddr, *addr, mask);
389 return (0);
390 }
391
392 return (0);
393}
394
395static
396int arm_translate_write (arm_t *c, uint32_t *addr, int priv)
397{
398 arm_copr15_t *mmu;
399 unsigned domn, perm;
400 int sect;
401 uint32_t vaddr, mask;
402
403 mmu = arm_get_mmu (c);
404
405 if ((mmu->reg[1] & ARM_C15_CR_M) == 0) {
406 return (0);
407 }
408
409 vaddr = *addr;
410
411 if (mmu->tbuf_write.valid) {
412 arm_tbuf_t *tb = &mmu->tbuf_write;
413
414 if ((vaddr & tb->vmask) == tb->vaddr) {
415 *addr = tb->raddr | (vaddr & tb->rmask);
416 return (0);
417 }
418 }
419
420 if (arm_translate (c, addr, &mask, &domn, &perm, §)) {
421 arm_mmu_translation_fault (c, vaddr, domn, sect);
422 return (1);
423 }
424
425 /* check domain */
426 switch ((mmu->reg[3] >> (2 * domn)) & 0x03) {
427 case 0x00: /* no access */
428 arm_mmu_domain_fault (c, vaddr, domn, sect);
429 return (1);
430
431 case 0x01: /* client */
432 if (arm_mmu_check_perm_write (mmu->reg[1], perm, priv) == 0) {
433 arm_mmu_permission_fault (c, vaddr, domn, sect);
434 return (1);
435 }
436
437 arm_tbuf_set (&mmu->tbuf_write, vaddr, *addr, mask);
438
439 return (0);
440
441 case 0x02: /* undefined */
442 return (0);
443
444 case 0x03: /* manager */
445 arm_tbuf_set (&mmu->tbuf_write, vaddr, *addr, mask);
446 return (0);
447 }
448
449 return (0);
450}
451
452/* translate without causing exceptions */
453int arm_translate_extern (arm_t *c, uint32_t *addr, unsigned xlat,
454 unsigned *domn, unsigned *perm)
455{
456 arm_copr15_t *mmu;
457 unsigned domn1, perm1;
458 int sect;
459 uint32_t mask;
460
461 if (domn == NULL) {
462 domn = &domn1;
463 }
464
465 if (perm == NULL) {
466 perm = &perm1;
467 }
468
469 *domn = 0;
470 *perm = 0;
471
472 if (xlat == ARM_XLAT_REAL) {
473 return (0);
474 }
475
476 mmu = arm_get_mmu (c);
477
478 if ((xlat == ARM_XLAT_CPU) && ((mmu->reg[1] & ARM_C15_CR_M) == 0)) {
479 return (0);
480 }
481
482 if (arm_translate (c, addr, &mask, domn, perm, §)) {
483 return (1);
484 }
485
486 return (0);
487}
488
489
490int arm_ifetch (arm_t *c, uint32_t addr, uint32_t *val)
491{
492 uint32_t tmp;
493
494 addr &= ~0x03UL;
495
496 if (arm_translate_exec (c, &addr, arm_is_privileged (c))) {
497 return (1);
498 }
499
500 if (addr < c->ram_cnt) {
501 unsigned char *p = &c->ram[addr];
502
503 if (c->bigendian) {
504#ifdef ARM_HOST_BE
505 tmp = *(uint32_t *)p;
506#else
507 tmp = (uint32_t) p[0] << 24;
508 tmp |= (uint32_t) p[1] << 16;
509 tmp |= (uint32_t) p[2] << 8;
510 tmp |= p[3];
511#endif
512 }
513 else {
514#ifdef ARM_HOST_LE
515 tmp = *(uint32_t *)p;
516#else
517 tmp = p[0];
518 tmp |= (uint32_t) p[1] << 8;
519 tmp |= (uint32_t) p[2] << 16;
520 tmp |= (uint32_t) p[3] << 24;
521#endif
522 }
523 }
524 else {
525 tmp = c->get_uint32 (c->mem_ext, addr);
526 }
527
528 *val = tmp;
529
530 return (0);
531}
532
533int arm_dload8 (arm_t *c, uint32_t addr, uint8_t *val)
534{
535 if (arm_translate_read (c, &addr, arm_is_privileged (c))) {
536 return (1);
537 }
538
539 if (addr < c->ram_cnt) {
540 *val = c->ram[addr];
541 }
542 else {
543 *val = c->get_uint8 (c->mem_ext, addr);
544 }
545
546 return (0);
547}
548
549int arm_dload16 (arm_t *c, uint32_t addr, uint16_t *val)
550{
551 if (arm_translate_read (c, &addr, arm_is_privileged (c))) {
552 return (1);
553 }
554
555 if ((addr + 1) < c->ram_cnt) {
556 unsigned char *p = &c->ram[addr];
557
558 if (c->bigendian) {
559#ifdef ARM_HOST_BE
560 *val = *(uint16_t *) p;
561#else
562 *val = ((uint16_t) p[0] << 8) | p[1];
563#endif
564 }
565 else {
566#ifdef ARM_HOST_LE
567 *val = *(uint16_t *) p;
568#else
569 *val = ((uint16_t) p[1] << 8) | p[0];
570#endif
571 }
572 }
573 else {
574 *val = c->get_uint16 (c->mem_ext, addr);
575 }
576
577 return (0);
578}
579
580int arm_dload32 (arm_t *c, uint32_t addr, uint32_t *val)
581{
582 if (arm_translate_read (c, &addr, arm_is_privileged (c))) {
583 return (1);
584 }
585
586 if ((addr + 3) < c->ram_cnt) {
587 unsigned char *p = &c->ram[addr];
588
589 if (c->bigendian) {
590#ifdef ARM_HOST_BE
591 *val = *(uint32_t *) p;
592#else
593 *val = (uint32_t) p[0] << 24;
594 *val |= (uint32_t) p[1] << 16;
595 *val |= (uint32_t) p[2] << 8;
596 *val |= p[3];
597#endif
598 }
599 else {
600#ifdef ARM_HOST_LE
601 *val = *(uint32_t *) p;
602#else
603 *val = p[0];
604 *val |= (uint32_t) p[1] << 8;
605 *val |= (uint32_t) p[2] << 16;
606 *val |= (uint32_t) p[3] << 24;
607#endif
608 }
609 }
610 else {
611 *val = c->get_uint32 (c->mem_ext, addr);
612 }
613
614 return (0);
615}
616
617int arm_dstore8 (arm_t *c, uint32_t addr, uint8_t val)
618{
619 if (arm_translate_write (c, &addr, arm_is_privileged (c))) {
620 return (1);
621 }
622
623 if (addr < c->ram_cnt) {
624 c->ram[addr] = val;
625 }
626 else {
627 c->set_uint8 (c->mem_ext, addr, val);
628 }
629
630 return (0);
631}
632
633int arm_dstore16 (arm_t *c, uint32_t addr, uint16_t val)
634{
635 if (arm_translate_write (c, &addr, arm_is_privileged (c))) {
636 return (1);
637 }
638
639 if ((addr + 1) < c->ram_cnt) {
640 unsigned char *p = &c->ram[addr];
641
642 if (c->bigendian) {
643#ifdef ARM_HOST_BE
644 *(uint16_t *) p = val;
645#else
646 p[0] = (val >> 8) & 0xff;
647 p[1] = val & 0xff;
648#endif
649 }
650 else {
651#ifdef ARM_HOST_LE
652 *(uint16_t *) p = val;
653#else
654 p[0] = val & 0xff;
655 p[1] = (val >> 8) & 0xff;
656#endif
657 }
658 }
659 else {
660 c->set_uint16 (c->mem_ext, addr, val);
661 }
662
663 return (0);
664}
665
666int arm_dstore32 (arm_t *c, uint32_t addr, uint32_t val)
667{
668 if (arm_translate_write (c, &addr, arm_is_privileged (c))) {
669 return (1);
670 }
671
672 if ((addr + 3) < c->ram_cnt) {
673 unsigned char *p = &c->ram[addr];
674
675 if (c->bigendian) {
676#ifdef ARM_HOST_BE
677 *(uint32_t *) p = val;
678#else
679 p[0] = (val >> 24) & 0xff;
680 p[1] = (val >> 16) & 0xff;
681 p[2] = (val >> 8) & 0xff;
682 p[3] = val & 0xff;
683#endif
684 }
685 else {
686#ifdef ARM_HOST_LE
687 *(uint32_t *) p = val;
688#else
689 p[0] = val & 0xff;
690 p[1] = (val >> 8) & 0xff;
691 p[2] = (val >> 16) & 0xff;
692 p[3] = (val >> 24) & 0xff;
693#endif
694 }
695 }
696 else {
697 c->set_uint32 (c->mem_ext, addr, val);
698 }
699
700 return (0);
701}
702
703int arm_dload8_t (arm_t *c, uint32_t addr, uint8_t *val)
704{
705 if (arm_translate_read (c, &addr, 0)) {
706 return (1);
707 }
708
709 *val = c->get_uint8 (c->mem_ext, addr);
710
711 return (0);
712}
713
714int arm_dload16_t (arm_t *c, uint32_t addr, uint16_t *val)
715{
716 if (arm_translate_read (c, &addr, 0)) {
717 return (1);
718 }
719
720 *val = c->get_uint16 (c->mem_ext, addr);
721
722 return (0);
723}
724
725int arm_dload32_t (arm_t *c, uint32_t addr, uint32_t *val)
726{
727 if (arm_translate_read (c, &addr, 0)) {
728 return (1);
729 }
730
731 *val = c->get_uint32 (c->mem_ext, addr);
732
733 return (0);
734}
735
736int arm_dstore8_t (arm_t *c, uint32_t addr, uint8_t val)
737{
738 if (arm_translate_write (c, &addr, 0)) {
739 return (1);
740 }
741
742 c->set_uint8 (c->mem_ext, addr, val);
743
744 return (0);
745}
746
747int arm_dstore16_t (arm_t *c, uint32_t addr, uint16_t val)
748{
749 if (arm_translate_write (c, &addr, 0)) {
750 return (1);
751 }
752
753 c->set_uint16 (c->mem_ext, addr, val);
754
755 return (0);
756}
757
758int arm_dstore32_t (arm_t *c, uint32_t addr, uint32_t val)
759{
760 if (arm_translate_write (c, &addr, 0)) {
761 return (1);
762 }
763
764 c->set_uint32 (c->mem_ext, addr, val);
765
766 return (0);
767}
768
769
770int arm_get_mem8 (arm_t *c, uint32_t addr, unsigned xlat, uint8_t *val)
771{
772 if (arm_translate_extern (c, &addr, xlat, NULL, NULL)) {
773 return (1);
774 }
775
776 if (c->get_uint8 != NULL) {
777 *val = c->get_uint8 (c->mem_ext, addr);
778 }
779 else {
780 *val = 0xff;
781 }
782
783 return (0);
784}
785
786int arm_get_mem16 (arm_t *c, uint32_t addr, unsigned xlat, uint16_t *val)
787{
788 if (arm_translate_extern (c, &addr, xlat, NULL, NULL)) {
789 return (1);
790 }
791
792 if (c->get_uint16 != NULL) {
793 *val = c->get_uint16 (c->mem_ext, addr);
794 }
795 else {
796 *val = 0xffff;
797 }
798
799 return (0);
800}
801
802int arm_get_mem32 (arm_t *c, uint32_t addr, unsigned xlat, uint32_t *val)
803{
804 if (arm_translate_extern (c, &addr, xlat, NULL, NULL)) {
805 return (1);
806 }
807
808 if (c->get_uint32 != NULL) {
809 *val = c->get_uint32 (c->mem_ext, addr);
810 }
811 else {
812 *val = 0xffffffff;
813 }
814
815 return (0);
816}
817
818int arm_set_mem8 (arm_t *c, uint32_t addr, unsigned xlat, uint8_t val)
819{
820 if (arm_translate_extern (c, &addr, xlat, NULL, NULL)) {
821 return (1);
822 }
823
824 if (c->set_uint8 != NULL) {
825 c->set_uint8 (c->mem_ext, addr, val);
826 }
827
828 return (0);
829}
830
831int arm_set_mem16 (arm_t *c, uint32_t addr, unsigned xlat, uint16_t val)
832{
833 if (arm_translate_extern (c, &addr, xlat, NULL, NULL)) {
834 return (1);
835 }
836
837 if (c->set_uint16 != NULL) {
838 c->set_uint16 (c->mem_ext, addr, val);
839 }
840
841 return (0);
842}
843
844int arm_set_mem32 (arm_t *c, uint32_t addr, unsigned xlat, uint32_t val)
845{
846 if (arm_translate_extern (c, &addr, xlat, NULL, NULL)) {
847 return (1);
848 }
849
850 if (c->set_uint32 != NULL) {
851 c->set_uint32 (c->mem_ext, addr, val);
852 }
853
854 return (0);
855}