Commit caa4039ced651551503b709b640bc6066b8eb2c2

Authored by j_mayer
1 parent 8b67546f

Code provision for PowerPC 64 MMU model support.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3186 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 1 changed file with 276 additions and 65 deletions
target-ppc/helper.c
... ... @@ -77,24 +77,62 @@ static inline void pte_invalidate (target_ulong *pte0)
77 77 *pte0 &= ~0x80000000;
78 78 }
79 79  
  80 +#if defined(TARGET_PPC64)
  81 +static inline int pte64_is_valid (target_ulong pte0)
  82 +{
  83 + return pte0 & 0x0000000000000001ULL ? 1 : 0;
  84 +}
  85 +
  86 +static inline void pte64_invalidate (target_ulong *pte0)
  87 +{
  88 + *pte0 &= ~0x0000000000000001ULL;
  89 +}
  90 +#endif
  91 +
80 92 #define PTE_PTEM_MASK 0x7FFFFFBF
81 93 #define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B)
  94 +#if defined(TARGET_PPC64)
  95 +#define PTE64_PTEM_MASK 0xFFFFFFFFFFFFFF80ULL
  96 +#define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F)
  97 +#endif
82 98  
83   -static int pte_check (mmu_ctx_t *ctx,
84   - target_ulong pte0, target_ulong pte1, int h, int rw)
  99 +static inline int _pte_check (mmu_ctx_t *ctx, int is_64b,
  100 + target_ulong pte0, target_ulong pte1,
  101 + int h, int rw)
85 102 {
86   - int access, ret;
  103 + target_ulong ptem, mmask;
  104 + int access, ret, pteh, ptev;
87 105  
88 106 access = 0;
89 107 ret = -1;
90 108 /* Check validity and table match */
91   - if (pte_is_valid(pte0) && (h == ((pte0 >> 6) & 1))) {
  109 +#if defined(TARGET_PPC64)
  110 + if (is_64b) {
  111 + ptev = pte64_is_valid(pte0);
  112 + pteh = (pte0 >> 1) & 1;
  113 + } else
  114 +#endif
  115 + {
  116 + ptev = pte_is_valid(pte0);
  117 + pteh = (pte0 >> 6) & 1;
  118 + }
  119 + if (ptev && h == pteh) {
92 120 /* Check vsid & api */
93   - if ((pte0 & PTE_PTEM_MASK) == ctx->ptem) {
  121 +#if defined(TARGET_PPC64)
  122 + if (is_64b) {
  123 + ptem = pte0 & PTE64_PTEM_MASK;
  124 + mmask = PTE64_CHECK_MASK;
  125 + } else
  126 +#endif
  127 + {
  128 + ptem = pte0 & PTE_PTEM_MASK;
  129 + mmask = PTE_CHECK_MASK;
  130 + }
  131 + if (ptem == ctx->ptem) {
94 132 if (ctx->raddr != (target_ulong)-1) {
95 133 /* all matches should have equal RPN, WIMG & PP */
96   - if ((ctx->raddr & PTE_CHECK_MASK) != (pte1 & PTE_CHECK_MASK)) {
97   - if (loglevel > 0)
  134 + if ((ctx->raddr & mmask) != (pte1 & mmask)) {
  135 + if (loglevel != 0)
98 136 fprintf(logfile, "Bad RPN/WIMG/PP\n");
99 137 return -3;
100 138 }
... ... @@ -143,6 +181,20 @@ static int pte_check (mmu_ctx_t *ctx,
143 181 return ret;
144 182 }
145 183  
  184 +static int pte32_check (mmu_ctx_t *ctx,
  185 + target_ulong pte0, target_ulong pte1, int h, int rw)
  186 +{
  187 + return _pte_check(ctx, 0, pte0, pte1, h, rw);
  188 +}
  189 +
  190 +#if defined(TARGET_PPC64)
  191 +static int pte64_check (mmu_ctx_t *ctx,
  192 + target_ulong pte0, target_ulong pte1, int h, int rw)
  193 +{
  194 + return _pte_check(ctx, 1, pte0, pte1, h, rw);
  195 +}
  196 +#endif
  197 +
146 198 static int pte_update_flags (mmu_ctx_t *ctx, target_ulong *pte1p,
147 199 int ret, int rw)
148 200 {
... ... @@ -305,7 +357,7 @@ static int ppc6xx_tlb_check (CPUState *env, mmu_ctx_t *ctx,
305 357 rw ? 'S' : 'L', access_type == ACCESS_CODE ? 'I' : 'D');
306 358 }
307 359 #endif
308   - switch (pte_check(ctx, tlb->pte0, tlb->pte1, 0, rw)) {
  360 + switch (pte32_check(ctx, tlb->pte0, tlb->pte1, 0, rw)) {
309 361 case -3:
310 362 /* TLB inconsistency */
311 363 return -1;
... ... @@ -440,26 +492,36 @@ static int get_bat (CPUState *env, mmu_ctx_t *ctx,
440 492 }
441 493  
442 494 /* PTE table lookup */
443   -static int find_pte (mmu_ctx_t *ctx, int h, int rw)
  495 +static inline int _find_pte (mmu_ctx_t *ctx, int is_64b, int h, int rw)
444 496 {
445 497 target_ulong base, pte0, pte1;
446 498 int i, good = -1;
447   - int ret;
  499 + int ret, r;
448 500  
449 501 ret = -1; /* No entry found */
450 502 base = ctx->pg_addr[h];
451 503 for (i = 0; i < 8; i++) {
452   - pte0 = ldl_phys(base + (i * 8));
453   - pte1 = ldl_phys(base + (i * 8) + 4);
  504 +#if defined(TARGET_PPC64)
  505 + if (is_64b) {
  506 + pte0 = ldq_phys(base + (i * 16));
  507 + pte1 = ldq_phys(base + (i * 16) + 8);
  508 + r = pte64_check(ctx, pte0, pte1, h, rw);
  509 + } else
  510 +#endif
  511 + {
  512 + pte0 = ldl_phys(base + (i * 8));
  513 + pte1 = ldl_phys(base + (i * 8) + 4);
  514 + r = pte32_check(ctx, pte0, pte1, h, rw);
  515 + }
454 516 #if defined (DEBUG_MMU)
455   - if (loglevel > 0) {
  517 + if (loglevel != 0) {
456 518 fprintf(logfile, "Load pte from 0x" ADDRX " => 0x" ADDRX
457 519 " 0x" ADDRX " %d %d %d 0x" ADDRX "\n",
458 520 base + (i * 8), pte0, pte1,
459   - pte0 >> 31, h, (pte0 >> 6) & 1, ctx->ptem);
  521 + (int)(pte0 >> 31), h, (int)((pte0 >> 6) & 1), ctx->ptem);
460 522 }
461 523 #endif
462   - switch (pte_check(ctx, pte0, pte1, h, rw)) {
  524 + switch (r) {
463 525 case -3:
464 526 /* PTE inconsistency */
465 527 return -1;
... ... @@ -494,59 +556,183 @@ static int find_pte (mmu_ctx_t *ctx, int h, int rw)
494 556 #endif
495 557 /* Update page flags */
496 558 pte1 = ctx->raddr;
497   - if (pte_update_flags(ctx, &pte1, ret, rw) == 1)
498   - stl_phys_notdirty(base + (good * 8) + 4, pte1);
  559 + if (pte_update_flags(ctx, &pte1, ret, rw) == 1) {
  560 +#if defined(TARGET_PPC64)
  561 + if (is_64b) {
  562 + stq_phys_notdirty(base + (good * 16) + 8, pte1);
  563 + } else
  564 +#endif
  565 + {
  566 + stl_phys_notdirty(base + (good * 8) + 4, pte1);
  567 + }
  568 + }
499 569 }
500 570  
501 571 return ret;
502 572 }
503 573  
  574 +static int find_pte32 (mmu_ctx_t *ctx, int h, int rw)
  575 +{
  576 + return _find_pte(ctx, 0, h, rw);
  577 +}
  578 +
  579 +#if defined(TARGET_PPC64)
  580 +static int find_pte64 (mmu_ctx_t *ctx, int h, int rw)
  581 +{
  582 + return _find_pte(ctx, 1, h, rw);
  583 +}
  584 +#endif
  585 +
  586 +static inline int find_pte (CPUState *env, mmu_ctx_t *ctx, int h, int rw)
  587 +{
  588 +#if defined(TARGET_PPC64)
  589 + if (PPC_MMU(env) == PPC_FLAGS_MMU_64B ||
  590 + PPC_MMU(env) == PPC_FLAGS_MMU_64BRIDGE)
  591 + return find_pte64(ctx, h, rw);
  592 +#endif
  593 +
  594 + return find_pte32(ctx, h, rw);
  595 +}
  596 +
504 597 static inline target_phys_addr_t get_pgaddr (target_phys_addr_t sdr1,
  598 + int sdr_sh,
505 599 target_phys_addr_t hash,
506 600 target_phys_addr_t mask)
507 601 {
508   - return (sdr1 & 0xFFFF0000) | (hash & mask);
  602 + return (sdr1 & ((target_ulong)(-1ULL) << sdr_sh)) | (hash & mask);
509 603 }
510 604  
  605 +#if defined(TARGET_PPC64)
  606 +static int slb_lookup (CPUState *env, target_ulong eaddr,
  607 + target_ulong *vsid, target_ulong *page_mask, int *attr)
  608 +{
  609 + target_phys_addr_t sr_base;
  610 + target_ulong mask;
  611 + uint64_t tmp64;
  612 + uint32_t tmp;
  613 + int n, ret;
  614 + int slb_nr;
  615 +
  616 + ret = -5;
  617 + sr_base = env->spr[SPR_ASR];
  618 + mask = 0x0000000000000000ULL; /* Avoid gcc warning */
  619 +#if 0 /* XXX: Fix this */
  620 + slb_nr = env->slb_nr;
  621 +#else
  622 + slb_nr = 32;
  623 +#endif
  624 + for (n = 0; n < slb_nr; n++) {
  625 + tmp64 = ldq_phys(sr_base);
  626 + if (tmp64 & 0x0000000008000000ULL) {
  627 + /* SLB entry is valid */
  628 + switch (tmp64 & 0x0000000006000000ULL) {
  629 + case 0x0000000000000000ULL:
  630 + /* 256 MB segment */
  631 + mask = 0xFFFFFFFFF0000000ULL;
  632 + break;
  633 + case 0x0000000002000000ULL:
  634 + /* 1 TB segment */
  635 + mask = 0xFFFF000000000000ULL;
  636 + break;
  637 + case 0x0000000004000000ULL:
  638 + case 0x0000000006000000ULL:
  639 + /* Reserved => segment is invalid */
  640 + continue;
  641 + }
  642 + if ((eaddr & mask) == (tmp64 & mask)) {
  643 + /* SLB match */
  644 + tmp = ldl_phys(sr_base + 8);
  645 + *vsid = ((tmp64 << 24) | (tmp >> 8)) & 0x0003FFFFFFFFFFFFULL;
  646 + *page_mask = ~mask;
  647 + *attr = tmp & 0xFF;
  648 + ret = 0;
  649 + break;
  650 + }
  651 + }
  652 + sr_base += 12;
  653 + }
  654 +
  655 + return ret;
  656 +}
  657 +#endif /* defined(TARGET_PPC64) */
  658 +
511 659 /* Perform segment based translation */
512 660 static int get_segment (CPUState *env, mmu_ctx_t *ctx,
513 661 target_ulong eaddr, int rw, int type)
514 662 {
515   - target_phys_addr_t sdr, hash, mask;
516   - target_ulong sr, vsid, pgidx;
517   - int ret = -1, ret2;
518   -
519   - sr = env->sr[eaddr >> 28];
520   -#if defined (DEBUG_MMU)
521   - if (loglevel > 0) {
522   - fprintf(logfile, "Check segment v=0x" ADDRX " %d 0x" ADDRX " nip=0x"
523   - ADDRX " lr=0x" ADDRX " ir=%d dr=%d pr=%d %d t=%d\n",
524   - eaddr, eaddr >> 28, sr, env->nip,
525   - env->lr, msr_ir, msr_dr, msr_pr, rw, type);
526   - }
  663 + target_phys_addr_t sdr, hash, mask, sdr_mask;
  664 + target_ulong sr, vsid, vsid_mask, pgidx, page_mask;
  665 +#if defined(TARGET_PPC64)
  666 + int attr;
527 667 #endif
528   - ctx->key = (((sr & 0x20000000) && msr_pr == 1) ||
529   - ((sr & 0x40000000) && msr_pr == 0)) ? 1 : 0;
530   - if ((sr & 0x80000000) == 0) {
  668 + int ds, nx, vsid_sh, sdr_sh;
  669 + int ret, ret2;
  670 +
  671 +#if defined(TARGET_PPC64)
  672 + if (PPC_MMU(env) == PPC_FLAGS_MMU_64B) {
  673 + ret = slb_lookup(env, eaddr, &vsid, &page_mask, &attr);
  674 + if (ret < 0)
  675 + return ret;
  676 + ctx->key = ((attr & 0x40) && msr_pr == 1) ||
  677 + ((attr & 0x80) && msr_pr == 0) ? 1 : 0;
  678 + ds = 0;
  679 + nx = attr & 0x20 ? 1 : 0;
  680 + vsid_mask = 0x00003FFFFFFFFF80ULL;
  681 + vsid_sh = 7;
  682 + sdr_sh = 18;
  683 + sdr_mask = 0x3FF80;
  684 + } else
  685 +#endif /* defined(TARGET_PPC64) */
  686 + {
  687 + sr = env->sr[eaddr >> 28];
  688 + page_mask = 0x0FFFFFFF;
  689 + ctx->key = (((sr & 0x20000000) && msr_pr == 1) ||
  690 + ((sr & 0x40000000) && msr_pr == 0)) ? 1 : 0;
  691 + ds = sr & 0x80000000 ? 1 : 0;
  692 + nx = sr & 0x10000000 ? 1 : 0;
  693 + vsid = sr & 0x00FFFFFF;
  694 + vsid_mask = 0x01FFFFC0;
  695 + vsid_sh = 6;
  696 + sdr_sh = 16;
  697 + sdr_mask = 0xFFC0;
531 698 #if defined (DEBUG_MMU)
532   - if (loglevel > 0)
  699 + if (loglevel != 0) {
  700 + fprintf(logfile, "Check segment v=0x" ADDRX " %d 0x" ADDRX
  701 + " nip=0x" ADDRX " lr=0x" ADDRX
  702 + " ir=%d dr=%d pr=%d %d t=%d\n",
  703 + eaddr, (int)(eaddr >> 28), sr, env->nip,
  704 + env->lr, msr_ir, msr_dr, msr_pr, rw, type);
  705 + }
  706 + if (!ds && loglevel != 0) {
533 707 fprintf(logfile, "pte segment: key=%d n=0x" ADDRX "\n",
534 708 ctx->key, sr & 0x10000000);
  709 + }
535 710 #endif
  711 + }
  712 + ret = -1;
  713 + if (!ds) {
536 714 /* Check if instruction fetch is allowed, if needed */
537   - if (type != ACCESS_CODE || (sr & 0x10000000) == 0) {
  715 + if (type != ACCESS_CODE || nx == 0) {
538 716 /* Page address translation */
539   - pgidx = (eaddr >> TARGET_PAGE_BITS) & 0xFFFF;
540   - vsid = sr & 0x00FFFFFF;
541   - hash = ((vsid ^ pgidx) & 0x0007FFFF) << 6;
  717 + pgidx = (eaddr & page_mask) >> TARGET_PAGE_BITS;
  718 + hash = ((vsid ^ pgidx) << vsid_sh) & vsid_mask;
542 719 /* Primary table address */
543 720 sdr = env->sdr1;
544   - mask = ((sdr & 0x000001FF) << 16) | 0xFFC0;
545   - ctx->pg_addr[0] = get_pgaddr(sdr, hash, mask);
  721 + mask = ((sdr & 0x000001FF) << sdr_sh) | sdr_mask;
  722 + ctx->pg_addr[0] = get_pgaddr(sdr, sdr_sh, hash, mask);
546 723 /* Secondary table address */
547   - hash = (~hash) & 0x01FFFFC0;
548   - ctx->pg_addr[1] = get_pgaddr(sdr, hash, mask);
549   - ctx->ptem = (vsid << 7) | (pgidx >> 10);
  724 + hash = (~hash) & vsid_mask;
  725 + ctx->pg_addr[1] = get_pgaddr(sdr, sdr_sh, hash, mask);
  726 +#if defined(TARGET_PPC64)
  727 + if (PPC_MMU(env) == PPC_FLAGS_MMU_64B ||
  728 + PPC_MMU(env) == PPC_FLAGS_MMU_64BRIDGE) {
  729 + /* Only 5 bits of the page index are used in the AVPN */
  730 + ctx->ptem = (vsid << 12) | ((pgidx >> 4) & 0x0F80);
  731 + } else
  732 +#endif
  733 + {
  734 + ctx->ptem = (vsid << 7) | (pgidx >> 10);
  735 + }
550 736 /* Initialize real address with an invalid value */
551 737 ctx->raddr = (target_ulong)-1;
552 738 if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_6xx)) {
... ... @@ -562,7 +748,7 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx,
562 748 }
563 749 #endif
564 750 /* Primary table lookup */
565   - ret = find_pte(ctx, 0, rw);
  751 + ret = find_pte(env, ctx, 0, rw);
566 752 if (ret < 0) {
567 753 /* Secondary table lookup */
568 754 #if defined (DEBUG_MMU)
... ... @@ -574,7 +760,7 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx,
574 760 (uint32_t)hash, ctx->pg_addr[1]);
575 761 }
576 762 #endif
577   - ret2 = find_pte(ctx, 1, rw);
  763 + ret2 = find_pte(env, ctx, 1, rw);
578 764 if (ret2 != -1)
579 765 ret = ret2;
580 766 }
... ... @@ -835,29 +1021,54 @@ static int check_physical (CPUState *env, mmu_ctx_t *ctx,
835 1021 ctx->raddr = eaddr;
836 1022 ctx->prot = PAGE_READ;
837 1023 ret = 0;
838   - if (unlikely(msr_pe != 0 && PPC_MMU(env) == PPC_FLAGS_MMU_403)) {
839   - /* 403 family add some particular protections,
840   - * using PBL/PBU registers for accesses with no translation.
841   - */
842   - in_plb =
843   - /* Check PLB validity */
844   - (env->pb[0] < env->pb[1] &&
845   - /* and address in plb area */
846   - eaddr >= env->pb[0] && eaddr < env->pb[1]) ||
847   - (env->pb[2] < env->pb[3] &&
848   - eaddr >= env->pb[2] && eaddr < env->pb[3]) ? 1 : 0;
849   - if (in_plb ^ msr_px) {
850   - /* Access in protected area */
851   - if (rw == 1) {
852   - /* Access is not allowed */
853   - ret = -2;
  1024 + switch (PPC_MMU(env)) {
  1025 + case PPC_FLAGS_MMU_32B:
  1026 + case PPC_FLAGS_MMU_SOFT_6xx:
  1027 + case PPC_FLAGS_MMU_601:
  1028 + case PPC_FLAGS_MMU_SOFT_4xx:
  1029 + ctx->prot |= PAGE_WRITE;
  1030 + break;
  1031 +#if defined(TARGET_PPC64)
  1032 + case PPC_FLAGS_MMU_64B:
  1033 + case PPC_FLAGS_MMU_64BRIDGE:
  1034 +#endif
  1035 + /* Real address are 60 bits long */
  1036 + ctx->raddr &= 0x0FFFFFFFFFFFFFFFUL;
  1037 + ctx->prot |= PAGE_WRITE;
  1038 + break;
  1039 + case PPC_FLAGS_MMU_403:
  1040 + if (unlikely(msr_pe != 0)) {
  1041 + /* 403 family add some particular protections,
  1042 + * using PBL/PBU registers for accesses with no translation.
  1043 + */
  1044 + in_plb =
  1045 + /* Check PLB validity */
  1046 + (env->pb[0] < env->pb[1] &&
  1047 + /* and address in plb area */
  1048 + eaddr >= env->pb[0] && eaddr < env->pb[1]) ||
  1049 + (env->pb[2] < env->pb[3] &&
  1050 + eaddr >= env->pb[2] && eaddr < env->pb[3]) ? 1 : 0;
  1051 + if (in_plb ^ msr_px) {
  1052 + /* Access in protected area */
  1053 + if (rw == 1) {
  1054 + /* Access is not allowed */
  1055 + ret = -2;
  1056 + }
  1057 + } else {
  1058 + /* Read-write access is allowed */
  1059 + ctx->prot |= PAGE_WRITE;
854 1060 }
855   - } else {
856   - /* Read-write access is allowed */
857   - ctx->prot |= PAGE_WRITE;
858 1061 }
859   - } else {
  1062 + case PPC_FLAGS_MMU_BOOKE:
860 1063 ctx->prot |= PAGE_WRITE;
  1064 + break;
  1065 + case PPC_FLAGS_MMU_BOOKE_FSL:
  1066 + /* XXX: TODO */
  1067 + cpu_abort(env, "BookE FSL MMU model not implemented\n");
  1068 + break;
  1069 + default:
  1070 + cpu_abort(env, "Unknown or invalid MMU model\n");
  1071 + return -1;
861 1072 }
862 1073  
863 1074 return ret;
... ...