Revision b227a8e9 target-ppc/helper.c
b/target-ppc/helper.c | ||
---|---|---|
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) |
Also available in: Unified diff