Commit b227a8e9aa5f27d29f77ba90d5eb9d0662a1175e
1 parent
dbdd2506
Properly implement non-execute bit on PowerPC segments and PTEs.
Fix page protection bits for PowerPC 64 MMU. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3395 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
2 changed files
with
139 additions
and
127 deletions
target-ppc/cpu.h
... | ... | @@ -596,6 +596,7 @@ struct mmu_ctx_t { |
596 | 596 | target_phys_addr_t pg_addr[2]; /* PTE tables base addresses */ |
597 | 597 | target_ulong ptem; /* Virtual segment ID | API */ |
598 | 598 | int key; /* Access key */ |
599 | + int nx; /* Non-execute area */ | |
599 | 600 | }; |
600 | 601 | |
601 | 602 | /*****************************************************************************/ | ... | ... |
target-ppc/helper.c
... | ... | @@ -96,12 +96,76 @@ static always_inline void pte64_invalidate (target_ulong *pte0) |
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,11 +186,15 @@ static always_inline int _pte_check (mmu_ctx_t *ctx, int is_64b, |
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,42 +206,23 @@ static always_inline int _pte_check (mmu_ctx_t *ctx, int is_64b, |
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,17 +230,17 @@ static always_inline int _pte_check (mmu_ctx_t *ctx, int is_64b, |
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,7 +402,7 @@ static int ppc6xx_tlb_check (CPUState *env, mmu_ctx_t *ctx, |
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,7 +447,7 @@ static int get_bat (CPUState *env, mmu_ctx_t *ctx, |
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,19 +496,23 @@ static int get_bat (CPUState *env, mmu_ctx_t *ctx, |
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,12 +536,14 @@ static int get_bat (CPUState *env, mmu_ctx_t *ctx, |
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,7 +556,7 @@ static always_inline int _find_pte (mmu_ctx_t *ctx, int is_64b, int h, int rw) |
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,7 +571,7 @@ static always_inline int _find_pte (mmu_ctx_t *ctx, int is_64b, int h, int rw) |
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,27 +632,27 @@ static always_inline int _find_pte (mmu_ctx_t *ctx, int is_64b, int h, int rw) |
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,7 +851,7 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, |
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,7 +867,7 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, |
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,7 +880,7 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, |
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,13 +899,13 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, |
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,7 +964,7 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, |
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,7 +976,7 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, |
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,76 +1174,32 @@ int mmu40x_get_physical_address (CPUState *env, mmu_ctx_t *ctx, |
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,7 +1285,7 @@ static int check_physical (CPUState *env, mmu_ctx_t *ctx, |
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,9 +1432,9 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, |
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) | ... | ... |