96 |
96 |
#define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F)
|
97 |
97 |
#endif
|
98 |
98 |
|
|
99 |
static always_inline int pp_check (int key, int pp, int nx)
|
|
100 |
{
|
|
101 |
int access;
|
|
102 |
|
|
103 |
/* Compute access rights */
|
|
104 |
/* When pp is 3/7, the result is undefined. Set it to noaccess */
|
|
105 |
access = 0;
|
|
106 |
if (key == 0) {
|
|
107 |
switch (pp) {
|
|
108 |
case 0x0:
|
|
109 |
case 0x1:
|
|
110 |
case 0x2:
|
|
111 |
access |= PAGE_WRITE;
|
|
112 |
/* No break here */
|
|
113 |
case 0x3:
|
|
114 |
case 0x6:
|
|
115 |
access |= PAGE_READ;
|
|
116 |
break;
|
|
117 |
}
|
|
118 |
} else {
|
|
119 |
switch (pp) {
|
|
120 |
case 0x0:
|
|
121 |
case 0x6:
|
|
122 |
access = 0;
|
|
123 |
break;
|
|
124 |
case 0x1:
|
|
125 |
case 0x3:
|
|
126 |
access = PAGE_READ;
|
|
127 |
break;
|
|
128 |
case 0x2:
|
|
129 |
access = PAGE_READ | PAGE_WRITE;
|
|
130 |
break;
|
|
131 |
}
|
|
132 |
}
|
|
133 |
if (nx == 0)
|
|
134 |
access |= PAGE_EXEC;
|
|
135 |
|
|
136 |
return access;
|
|
137 |
}
|
|
138 |
|
|
139 |
static always_inline int check_prot (int prot, int rw, int access_type)
|
|
140 |
{
|
|
141 |
int ret;
|
|
142 |
|
|
143 |
if (access_type == ACCESS_CODE) {
|
|
144 |
if (prot & PAGE_EXEC)
|
|
145 |
ret = 0;
|
|
146 |
else
|
|
147 |
ret = -2;
|
|
148 |
} else if (rw) {
|
|
149 |
if (prot & PAGE_WRITE)
|
|
150 |
ret = 0;
|
|
151 |
else
|
|
152 |
ret = -2;
|
|
153 |
} else {
|
|
154 |
if (prot & PAGE_READ)
|
|
155 |
ret = 0;
|
|
156 |
else
|
|
157 |
ret = -2;
|
|
158 |
}
|
|
159 |
|
|
160 |
return ret;
|
|
161 |
}
|
|
162 |
|
99 |
163 |
static always_inline int _pte_check (mmu_ctx_t *ctx, int is_64b,
|
100 |
164 |
target_ulong pte0, target_ulong pte1,
|
101 |
|
int h, int rw)
|
|
165 |
int h, int rw, int type)
|
102 |
166 |
{
|
103 |
167 |
target_ulong ptem, mmask;
|
104 |
|
int access, ret, pteh, ptev;
|
|
168 |
int access, ret, pteh, ptev, pp;
|
105 |
169 |
|
106 |
170 |
access = 0;
|
107 |
171 |
ret = -1;
|
... | ... | |
122 |
186 |
if (is_64b) {
|
123 |
187 |
ptem = pte0 & PTE64_PTEM_MASK;
|
124 |
188 |
mmask = PTE64_CHECK_MASK;
|
|
189 |
pp = (pte1 & 0x00000003) | ((pte1 >> 61) & 0x00000004);
|
|
190 |
ctx->nx |= (pte1 >> 2) & 1; /* No execute bit */
|
|
191 |
ctx->nx |= (pte1 >> 3) & 1; /* Guarded bit */
|
125 |
192 |
} else
|
126 |
193 |
#endif
|
127 |
194 |
{
|
128 |
195 |
ptem = pte0 & PTE_PTEM_MASK;
|
129 |
196 |
mmask = PTE_CHECK_MASK;
|
|
197 |
pp = pte1 & 0x00000003;
|
130 |
198 |
}
|
131 |
199 |
if (ptem == ctx->ptem) {
|
132 |
200 |
if (ctx->raddr != (target_ulong)-1) {
|
... | ... | |
138 |
206 |
}
|
139 |
207 |
}
|
140 |
208 |
/* Compute access rights */
|
141 |
|
if (ctx->key == 0) {
|
142 |
|
access = PAGE_READ;
|
143 |
|
if ((pte1 & 0x00000003) != 0x3)
|
144 |
|
access |= PAGE_WRITE;
|
145 |
|
} else {
|
146 |
|
switch (pte1 & 0x00000003) {
|
147 |
|
case 0x0:
|
148 |
|
access = 0;
|
149 |
|
break;
|
150 |
|
case 0x1:
|
151 |
|
case 0x3:
|
152 |
|
access = PAGE_READ;
|
153 |
|
break;
|
154 |
|
case 0x2:
|
155 |
|
access = PAGE_READ | PAGE_WRITE;
|
156 |
|
break;
|
157 |
|
}
|
158 |
|
}
|
|
209 |
access = pp_check(ctx->key, pp, ctx->nx);
|
159 |
210 |
/* Keep the matching PTE informations */
|
160 |
211 |
ctx->raddr = pte1;
|
161 |
212 |
ctx->prot = access;
|
162 |
|
if ((rw == 0 && (access & PAGE_READ)) ||
|
163 |
|
(rw == 1 && (access & PAGE_WRITE))) {
|
|
213 |
ret = check_prot(ctx->prot, rw, type);
|
|
214 |
if (ret == 0) {
|
164 |
215 |
/* Access granted */
|
165 |
216 |
#if defined (DEBUG_MMU)
|
166 |
217 |
if (loglevel != 0)
|
167 |
218 |
fprintf(logfile, "PTE access granted !\n");
|
168 |
219 |
#endif
|
169 |
|
ret = 0;
|
170 |
220 |
} else {
|
171 |
221 |
/* Access right violation */
|
172 |
222 |
#if defined (DEBUG_MMU)
|
173 |
223 |
if (loglevel != 0)
|
174 |
224 |
fprintf(logfile, "PTE access rejected\n");
|
175 |
225 |
#endif
|
176 |
|
ret = -2;
|
177 |
226 |
}
|
178 |
227 |
}
|
179 |
228 |
}
|
... | ... | |
181 |
230 |
return ret;
|
182 |
231 |
}
|
183 |
232 |
|
184 |
|
static int pte32_check (mmu_ctx_t *ctx,
|
185 |
|
target_ulong pte0, target_ulong pte1, int h, int rw)
|
|
233 |
static int pte32_check (mmu_ctx_t *ctx, target_ulong pte0, target_ulong pte1,
|
|
234 |
int h, int rw, int type)
|
186 |
235 |
{
|
187 |
|
return _pte_check(ctx, 0, pte0, pte1, h, rw);
|
|
236 |
return _pte_check(ctx, 0, pte0, pte1, h, rw, type);
|
188 |
237 |
}
|
189 |
238 |
|
190 |
239 |
#if defined(TARGET_PPC64)
|
191 |
|
static int pte64_check (mmu_ctx_t *ctx,
|
192 |
|
target_ulong pte0, target_ulong pte1, int h, int rw)
|
|
240 |
static int pte64_check (mmu_ctx_t *ctx, target_ulong pte0, target_ulong pte1,
|
|
241 |
int h, int rw, int type)
|
193 |
242 |
{
|
194 |
|
return _pte_check(ctx, 1, pte0, pte1, h, rw);
|
|
243 |
return _pte_check(ctx, 1, pte0, pte1, h, rw, type);
|
195 |
244 |
}
|
196 |
245 |
#endif
|
197 |
246 |
|
... | ... | |
353 |
402 |
rw ? 'S' : 'L', access_type == ACCESS_CODE ? 'I' : 'D');
|
354 |
403 |
}
|
355 |
404 |
#endif
|
356 |
|
switch (pte32_check(ctx, tlb->pte0, tlb->pte1, 0, rw)) {
|
|
405 |
switch (pte32_check(ctx, tlb->pte0, tlb->pte1, 0, rw, access_type)) {
|
357 |
406 |
case -3:
|
358 |
407 |
/* TLB inconsistency */
|
359 |
408 |
return -1;
|
... | ... | |
398 |
447 |
{
|
399 |
448 |
target_ulong *BATlt, *BATut, *BATu, *BATl;
|
400 |
449 |
target_ulong base, BEPIl, BEPIu, bl;
|
401 |
|
int i;
|
|
450 |
int i, pp;
|
402 |
451 |
int ret = -1;
|
403 |
452 |
|
404 |
453 |
#if defined (DEBUG_BATS)
|
... | ... | |
447 |
496 |
ctx->raddr = (*BATl & 0xF0000000) |
|
448 |
497 |
((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) |
|
449 |
498 |
(virtual & 0x0001F000);
|
450 |
|
if (*BATl & 0x00000001)
|
451 |
|
ctx->prot = PAGE_READ;
|
452 |
|
if (*BATl & 0x00000002)
|
453 |
|
ctx->prot = PAGE_WRITE | PAGE_READ;
|
|
499 |
/* Compute access rights */
|
|
500 |
pp = *BATl & 0x00000003;
|
|
501 |
ctx->prot = 0;
|
|
502 |
if (pp != 0) {
|
|
503 |
ctx->prot = PAGE_READ | PAGE_EXEC;
|
|
504 |
if (pp == 0x2)
|
|
505 |
ctx->prot |= PAGE_WRITE;
|
|
506 |
}
|
|
507 |
ret = check_prot(ctx->prot, rw, type);
|
454 |
508 |
#if defined (DEBUG_BATS)
|
455 |
|
if (loglevel != 0) {
|
|
509 |
if (ret == 0 && loglevel != 0) {
|
456 |
510 |
fprintf(logfile, "BAT %d match: r 0x" PADDRX
|
457 |
511 |
" prot=%c%c\n",
|
458 |
512 |
i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-',
|
459 |
513 |
ctx->prot & PAGE_WRITE ? 'W' : '-');
|
460 |
514 |
}
|
461 |
515 |
#endif
|
462 |
|
ret = 0;
|
463 |
516 |
break;
|
464 |
517 |
}
|
465 |
518 |
}
|
... | ... | |
483 |
536 |
}
|
484 |
537 |
#endif
|
485 |
538 |
}
|
|
539 |
|
486 |
540 |
/* No hit */
|
487 |
541 |
return ret;
|
488 |
542 |
}
|
489 |
543 |
|
490 |
544 |
/* PTE table lookup */
|
491 |
|
static always_inline int _find_pte (mmu_ctx_t *ctx, int is_64b, int h, int rw)
|
|
545 |
static always_inline int _find_pte (mmu_ctx_t *ctx, int is_64b, int h,
|
|
546 |
int rw, int type)
|
492 |
547 |
{
|
493 |
548 |
target_ulong base, pte0, pte1;
|
494 |
549 |
int i, good = -1;
|
... | ... | |
501 |
556 |
if (is_64b) {
|
502 |
557 |
pte0 = ldq_phys(base + (i * 16));
|
503 |
558 |
pte1 = ldq_phys(base + (i * 16) + 8);
|
504 |
|
r = pte64_check(ctx, pte0, pte1, h, rw);
|
|
559 |
r = pte64_check(ctx, pte0, pte1, h, rw, type);
|
505 |
560 |
#if defined (DEBUG_MMU)
|
506 |
561 |
if (loglevel != 0) {
|
507 |
562 |
fprintf(logfile, "Load pte from 0x" ADDRX " => 0x" ADDRX
|
... | ... | |
516 |
571 |
{
|
517 |
572 |
pte0 = ldl_phys(base + (i * 8));
|
518 |
573 |
pte1 = ldl_phys(base + (i * 8) + 4);
|
519 |
|
r = pte32_check(ctx, pte0, pte1, h, rw);
|
|
574 |
r = pte32_check(ctx, pte0, pte1, h, rw, type);
|
520 |
575 |
#if defined (DEBUG_MMU)
|
521 |
576 |
if (loglevel != 0) {
|
522 |
577 |
fprintf(logfile, "Load pte from 0x" ADDRX " => 0x" ADDRX
|
... | ... | |
577 |
632 |
return ret;
|
578 |
633 |
}
|
579 |
634 |
|
580 |
|
static int find_pte32 (mmu_ctx_t *ctx, int h, int rw)
|
|
635 |
static int find_pte32 (mmu_ctx_t *ctx, int h, int rw, int type)
|
581 |
636 |
{
|
582 |
|
return _find_pte(ctx, 0, h, rw);
|
|
637 |
return _find_pte(ctx, 0, h, rw, type);
|
583 |
638 |
}
|
584 |
639 |
|
585 |
640 |
#if defined(TARGET_PPC64)
|
586 |
|
static int find_pte64 (mmu_ctx_t *ctx, int h, int rw)
|
|
641 |
static int find_pte64 (mmu_ctx_t *ctx, int h, int rw, int type)
|
587 |
642 |
{
|
588 |
|
return _find_pte(ctx, 1, h, rw);
|
|
643 |
return _find_pte(ctx, 1, h, rw, type);
|
589 |
644 |
}
|
590 |
645 |
#endif
|
591 |
646 |
|
592 |
647 |
static always_inline int find_pte (CPUState *env, mmu_ctx_t *ctx,
|
593 |
|
int h, int rw)
|
|
648 |
int h, int rw, int type)
|
594 |
649 |
{
|
595 |
650 |
#if defined(TARGET_PPC64)
|
596 |
651 |
if (env->mmu_model == POWERPC_MMU_64B)
|
597 |
|
return find_pte64(ctx, h, rw);
|
|
652 |
return find_pte64(ctx, h, rw, type);
|
598 |
653 |
#endif
|
599 |
654 |
|
600 |
|
return find_pte32(ctx, h, rw);
|
|
655 |
return find_pte32(ctx, h, rw, type);
|
601 |
656 |
}
|
602 |
657 |
|
603 |
658 |
#if defined(TARGET_PPC64)
|
... | ... | |
796 |
851 |
#if defined(TARGET_PPC64)
|
797 |
852 |
int attr;
|
798 |
853 |
#endif
|
799 |
|
int ds, nx, vsid_sh, sdr_sh;
|
|
854 |
int ds, vsid_sh, sdr_sh;
|
800 |
855 |
int ret, ret2;
|
801 |
856 |
|
802 |
857 |
#if defined(TARGET_PPC64)
|
... | ... | |
812 |
867 |
ctx->key = ((attr & 0x40) && msr_pr == 1) ||
|
813 |
868 |
((attr & 0x80) && msr_pr == 0) ? 1 : 0;
|
814 |
869 |
ds = 0;
|
815 |
|
nx = attr & 0x20 ? 1 : 0;
|
|
870 |
ctx->nx = attr & 0x20 ? 1 : 0;
|
816 |
871 |
vsid_mask = 0x00003FFFFFFFFF80ULL;
|
817 |
872 |
vsid_sh = 7;
|
818 |
873 |
sdr_sh = 18;
|
... | ... | |
825 |
880 |
ctx->key = (((sr & 0x20000000) && msr_pr == 1) ||
|
826 |
881 |
((sr & 0x40000000) && msr_pr == 0)) ? 1 : 0;
|
827 |
882 |
ds = sr & 0x80000000 ? 1 : 0;
|
828 |
|
nx = sr & 0x10000000 ? 1 : 0;
|
|
883 |
ctx->nx = sr & 0x10000000 ? 1 : 0;
|
829 |
884 |
vsid = sr & 0x00FFFFFF;
|
830 |
885 |
vsid_mask = 0x01FFFFC0;
|
831 |
886 |
vsid_sh = 6;
|
... | ... | |
844 |
899 |
#if defined (DEBUG_MMU)
|
845 |
900 |
if (loglevel != 0) {
|
846 |
901 |
fprintf(logfile, "pte segment: key=%d ds %d nx %d vsid " ADDRX "\n",
|
847 |
|
ctx->key, ds, nx, vsid);
|
|
902 |
ctx->key, ds, ctx->nx, vsid);
|
848 |
903 |
}
|
849 |
904 |
#endif
|
850 |
905 |
ret = -1;
|
851 |
906 |
if (!ds) {
|
852 |
907 |
/* Check if instruction fetch is allowed, if needed */
|
853 |
|
if (type != ACCESS_CODE || nx == 0) {
|
|
908 |
if (type != ACCESS_CODE || ctx->nx == 0) {
|
854 |
909 |
/* Page address translation */
|
855 |
910 |
/* Primary table address */
|
856 |
911 |
sdr = env->sdr1;
|
... | ... | |
909 |
964 |
}
|
910 |
965 |
#endif
|
911 |
966 |
/* Primary table lookup */
|
912 |
|
ret = find_pte(env, ctx, 0, rw);
|
|
967 |
ret = find_pte(env, ctx, 0, rw, type);
|
913 |
968 |
if (ret < 0) {
|
914 |
969 |
/* Secondary table lookup */
|
915 |
970 |
#if defined (DEBUG_MMU)
|
... | ... | |
921 |
976 |
(uint32_t)hash, ctx->pg_addr[1]);
|
922 |
977 |
}
|
923 |
978 |
#endif
|
924 |
|
ret2 = find_pte(env, ctx, 1, rw);
|
|
979 |
ret2 = find_pte(env, ctx, 1, rw, type);
|
925 |
980 |
if (ret2 != -1)
|
926 |
981 |
ret = ret2;
|
927 |
982 |
}
|
... | ... | |
1119 |
1174 |
__func__, i, zsel, zpr, rw, tlb->attr);
|
1120 |
1175 |
}
|
1121 |
1176 |
#endif
|
1122 |
|
if (access_type == ACCESS_CODE) {
|
1123 |
|
/* Check execute enable bit */
|
1124 |
|
switch (zpr) {
|
1125 |
|
case 0x2:
|
1126 |
|
if (msr_pr)
|
1127 |
|
goto check_exec_perm;
|
1128 |
|
goto exec_granted;
|
1129 |
|
case 0x0:
|
1130 |
|
if (msr_pr) {
|
1131 |
|
ctx->prot = 0;
|
1132 |
|
ret = -3;
|
1133 |
|
break;
|
1134 |
|
}
|
1135 |
|
/* No break here */
|
1136 |
|
case 0x1:
|
1137 |
|
check_exec_perm:
|
1138 |
|
/* Check from TLB entry */
|
1139 |
|
if (!(tlb->prot & PAGE_EXEC)) {
|
1140 |
|
ret = -3;
|
1141 |
|
} else {
|
1142 |
|
if (tlb->prot & PAGE_WRITE) {
|
1143 |
|
ctx->prot = PAGE_READ | PAGE_WRITE;
|
1144 |
|
} else {
|
1145 |
|
ctx->prot = PAGE_READ;
|
1146 |
|
}
|
1147 |
|
ret = 0;
|
1148 |
|
}
|
1149 |
|
break;
|
1150 |
|
case 0x3:
|
1151 |
|
exec_granted:
|
1152 |
|
/* All accesses granted */
|
1153 |
|
ctx->prot = PAGE_READ | PAGE_WRITE;
|
1154 |
|
ret = 0;
|
1155 |
|
break;
|
1156 |
|
}
|
1157 |
|
} else {
|
1158 |
|
switch (zpr) {
|
1159 |
|
case 0x2:
|
1160 |
|
if (msr_pr)
|
1161 |
|
goto check_rw_perm;
|
1162 |
|
goto rw_granted;
|
1163 |
|
case 0x0:
|
1164 |
|
if (msr_pr) {
|
1165 |
|
ctx->prot = 0;
|
1166 |
|
ret = -2;
|
1167 |
|
break;
|
1168 |
|
}
|
1169 |
|
/* No break here */
|
1170 |
|
case 0x1:
|
1171 |
|
check_rw_perm:
|
1172 |
|
/* Check from TLB entry */
|
1173 |
|
/* Check write protection bit */
|
1174 |
|
if (tlb->prot & PAGE_WRITE) {
|
1175 |
|
ctx->prot = PAGE_READ | PAGE_WRITE;
|
1176 |
|
ret = 0;
|
1177 |
|
} else {
|
1178 |
|
ctx->prot = PAGE_READ;
|
1179 |
|
if (rw)
|
1180 |
|
ret = -2;
|
1181 |
|
else
|
1182 |
|
ret = 0;
|
1183 |
|
}
|
1184 |
|
break;
|
1185 |
|
case 0x3:
|
1186 |
|
rw_granted:
|
1187 |
|
/* All accesses granted */
|
1188 |
|
ctx->prot = PAGE_READ | PAGE_WRITE;
|
1189 |
|
ret = 0;
|
|
1177 |
/* Check execute enable bit */
|
|
1178 |
switch (zpr) {
|
|
1179 |
case 0x2:
|
|
1180 |
if (msr_pr)
|
|
1181 |
goto check_perms;
|
|
1182 |
/* No break here */
|
|
1183 |
case 0x3:
|
|
1184 |
/* All accesses granted */
|
|
1185 |
ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
|
|
1186 |
ret = 0;
|
|
1187 |
break;
|
|
1188 |
case 0x0:
|
|
1189 |
if (msr_pr) {
|
|
1190 |
ctx->prot = 0;
|
|
1191 |
ret = -2;
|
1190 |
1192 |
break;
|
1191 |
1193 |
}
|
|
1194 |
/* No break here */
|
|
1195 |
case 0x1:
|
|
1196 |
check_perms:
|
|
1197 |
/* Check from TLB entry */
|
|
1198 |
/* XXX: there is a problem here or in the TLB fill code... */
|
|
1199 |
ctx->prot = tlb->prot;
|
|
1200 |
ctx->prot |= PAGE_EXEC;
|
|
1201 |
ret = check_prot(ctx->prot, rw, access_type);
|
|
1202 |
break;
|
1192 |
1203 |
}
|
1193 |
1204 |
if (ret >= 0) {
|
1194 |
1205 |
ctx->raddr = raddr;
|
... | ... | |
1274 |
1285 |
int in_plb, ret;
|
1275 |
1286 |
|
1276 |
1287 |
ctx->raddr = eaddr;
|
1277 |
|
ctx->prot = PAGE_READ;
|
|
1288 |
ctx->prot = PAGE_READ | PAGE_EXEC;
|
1278 |
1289 |
ret = 0;
|
1279 |
1290 |
switch (env->mmu_model) {
|
1280 |
1291 |
case POWERPC_MMU_32B:
|
... | ... | |
1421 |
1432 |
}
|
1422 |
1433 |
ret = get_physical_address(env, &ctx, address, rw, access_type, 1);
|
1423 |
1434 |
if (ret == 0) {
|
1424 |
|
ret = tlb_set_page(env, address & TARGET_PAGE_MASK,
|
1425 |
|
ctx.raddr & TARGET_PAGE_MASK, ctx.prot,
|
1426 |
|
mmu_idx, is_softmmu);
|
|
1435 |
ret = tlb_set_page_exec(env, address & TARGET_PAGE_MASK,
|
|
1436 |
ctx.raddr & TARGET_PAGE_MASK, ctx.prot,
|
|
1437 |
mmu_idx, is_softmmu);
|
1427 |
1438 |
} else if (ret < 0) {
|
1428 |
1439 |
#if defined (DEBUG_MMU)
|
1429 |
1440 |
if (loglevel != 0)
|