qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

mmu-hash32.c (16112B)


      1 /*
      2  *  PowerPC MMU, TLB and BAT emulation helpers for QEMU.
      3  *
      4  *  Copyright (c) 2003-2007 Jocelyn Mayer
      5  *  Copyright (c) 2013 David Gibson, IBM Corporation
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Lesser General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2.1 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Lesser General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Lesser General Public
     18  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
     19  */
     20 
     21 #include "qemu/osdep.h"
     22 #include "cpu.h"
     23 #include "exec/exec-all.h"
     24 #include "sysemu/kvm.h"
     25 #include "kvm_ppc.h"
     26 #include "internal.h"
     27 #include "mmu-hash32.h"
     28 #include "mmu-books.h"
     29 #include "exec/log.h"
     30 
     31 /* #define DEBUG_BATS */
     32 
     33 #ifdef DEBUG_BATS
     34 #  define LOG_BATS(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__)
     35 #else
     36 #  define LOG_BATS(...) do { } while (0)
     37 #endif
     38 
     39 struct mmu_ctx_hash32 {
     40     hwaddr raddr;      /* Real address              */
     41     int prot;                      /* Protection bits           */
     42     int key;                       /* Access key                */
     43 };
     44 
     45 static int ppc_hash32_pp_prot(int key, int pp, int nx)
     46 {
     47     int prot;
     48 
     49     if (key == 0) {
     50         switch (pp) {
     51         case 0x0:
     52         case 0x1:
     53         case 0x2:
     54             prot = PAGE_READ | PAGE_WRITE;
     55             break;
     56 
     57         case 0x3:
     58             prot = PAGE_READ;
     59             break;
     60 
     61         default:
     62             abort();
     63         }
     64     } else {
     65         switch (pp) {
     66         case 0x0:
     67             prot = 0;
     68             break;
     69 
     70         case 0x1:
     71         case 0x3:
     72             prot = PAGE_READ;
     73             break;
     74 
     75         case 0x2:
     76             prot = PAGE_READ | PAGE_WRITE;
     77             break;
     78 
     79         default:
     80             abort();
     81         }
     82     }
     83     if (nx == 0) {
     84         prot |= PAGE_EXEC;
     85     }
     86 
     87     return prot;
     88 }
     89 
     90 static int ppc_hash32_pte_prot(int mmu_idx,
     91                                target_ulong sr, ppc_hash_pte32_t pte)
     92 {
     93     unsigned pp, key;
     94 
     95     key = !!(mmuidx_pr(mmu_idx) ? (sr & SR32_KP) : (sr & SR32_KS));
     96     pp = pte.pte1 & HPTE32_R_PP;
     97 
     98     return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX));
     99 }
    100 
    101 static target_ulong hash32_bat_size(int mmu_idx,
    102                                     target_ulong batu, target_ulong batl)
    103 {
    104     if ((mmuidx_pr(mmu_idx) && !(batu & BATU32_VP))
    105         || (!mmuidx_pr(mmu_idx) && !(batu & BATU32_VS))) {
    106         return 0;
    107     }
    108 
    109     return BATU32_BEPI & ~((batu & BATU32_BL) << 15);
    110 }
    111 
    112 static int hash32_bat_prot(PowerPCCPU *cpu,
    113                            target_ulong batu, target_ulong batl)
    114 {
    115     int pp, prot;
    116 
    117     prot = 0;
    118     pp = batl & BATL32_PP;
    119     if (pp != 0) {
    120         prot = PAGE_READ | PAGE_EXEC;
    121         if (pp == 0x2) {
    122             prot |= PAGE_WRITE;
    123         }
    124     }
    125     return prot;
    126 }
    127 
    128 static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea,
    129                                     MMUAccessType access_type, int *prot,
    130                                     int mmu_idx)
    131 {
    132     CPUPPCState *env = &cpu->env;
    133     target_ulong *BATlt, *BATut;
    134     bool ifetch = access_type == MMU_INST_FETCH;
    135     int i;
    136 
    137     LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__,
    138              ifetch ? 'I' : 'D', ea);
    139     if (ifetch) {
    140         BATlt = env->IBAT[1];
    141         BATut = env->IBAT[0];
    142     } else {
    143         BATlt = env->DBAT[1];
    144         BATut = env->DBAT[0];
    145     }
    146     for (i = 0; i < env->nb_BATs; i++) {
    147         target_ulong batu = BATut[i];
    148         target_ulong batl = BATlt[i];
    149         target_ulong mask;
    150 
    151         mask = hash32_bat_size(mmu_idx, batu, batl);
    152         LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
    153                  " BATl " TARGET_FMT_lx "\n", __func__,
    154                  ifetch ? 'I' : 'D', i, ea, batu, batl);
    155 
    156         if (mask && ((ea & mask) == (batu & BATU32_BEPI))) {
    157             hwaddr raddr = (batl & mask) | (ea & ~mask);
    158 
    159             *prot = hash32_bat_prot(cpu, batu, batl);
    160 
    161             return raddr & TARGET_PAGE_MASK;
    162         }
    163     }
    164 
    165     /* No hit */
    166 #if defined(DEBUG_BATS)
    167     if (qemu_log_enabled()) {
    168         target_ulong *BATu, *BATl;
    169         target_ulong BEPIl, BEPIu, bl;
    170 
    171         LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", ea);
    172         for (i = 0; i < 4; i++) {
    173             BATu = &BATut[i];
    174             BATl = &BATlt[i];
    175             BEPIu = *BATu & BATU32_BEPIU;
    176             BEPIl = *BATu & BATU32_BEPIL;
    177             bl = (*BATu & 0x00001FFC) << 15;
    178             LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
    179                      " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " "
    180                      TARGET_FMT_lx " " TARGET_FMT_lx "\n",
    181                      __func__, ifetch ? 'I' : 'D', i, ea,
    182                      *BATu, *BATl, BEPIu, BEPIl, bl);
    183         }
    184     }
    185 #endif
    186 
    187     return -1;
    188 }
    189 
    190 static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr,
    191                                     target_ulong eaddr,
    192                                     MMUAccessType access_type,
    193                                     hwaddr *raddr, int *prot, int mmu_idx,
    194                                     bool guest_visible)
    195 {
    196     CPUState *cs = CPU(cpu);
    197     CPUPPCState *env = &cpu->env;
    198     int key = !!(mmuidx_pr(mmu_idx) ? (sr & SR32_KP) : (sr & SR32_KS));
    199 
    200     qemu_log_mask(CPU_LOG_MMU, "direct store...\n");
    201 
    202     if (access_type == MMU_INST_FETCH) {
    203         /* No code fetch is allowed in direct-store areas */
    204         if (guest_visible) {
    205             cs->exception_index = POWERPC_EXCP_ISI;
    206             env->error_code = 0x10000000;
    207         }
    208         return false;
    209     }
    210 
    211     /*
    212      * From ppc_cpu_get_phys_page_debug, env->access_type is not set.
    213      * Assume ACCESS_INT for that case.
    214      */
    215     switch (guest_visible ? env->access_type : ACCESS_INT) {
    216     case ACCESS_INT:
    217         /* Integer load/store : only access allowed */
    218         break;
    219     case ACCESS_FLOAT:
    220         /* Floating point load/store */
    221         cs->exception_index = POWERPC_EXCP_ALIGN;
    222         env->error_code = POWERPC_EXCP_ALIGN_FP;
    223         env->spr[SPR_DAR] = eaddr;
    224         return false;
    225     case ACCESS_RES:
    226         /* lwarx, ldarx or srwcx. */
    227         env->error_code = 0;
    228         env->spr[SPR_DAR] = eaddr;
    229         if (access_type == MMU_DATA_STORE) {
    230             env->spr[SPR_DSISR] = 0x06000000;
    231         } else {
    232             env->spr[SPR_DSISR] = 0x04000000;
    233         }
    234         return false;
    235     case ACCESS_CACHE:
    236         /*
    237          * dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi
    238          *
    239          * Should make the instruction do no-op.  As it already do
    240          * no-op, it's quite easy :-)
    241          */
    242         *raddr = eaddr;
    243         return true;
    244     case ACCESS_EXT:
    245         /* eciwx or ecowx */
    246         cs->exception_index = POWERPC_EXCP_DSI;
    247         env->error_code = 0;
    248         env->spr[SPR_DAR] = eaddr;
    249         if (access_type == MMU_DATA_STORE) {
    250             env->spr[SPR_DSISR] = 0x06100000;
    251         } else {
    252             env->spr[SPR_DSISR] = 0x04100000;
    253         }
    254         return false;
    255     default:
    256         cpu_abort(cs, "ERROR: insn should not need address translation\n");
    257     }
    258 
    259     *prot = key ? PAGE_READ | PAGE_WRITE : PAGE_READ;
    260     if (*prot & prot_for_access_type(access_type)) {
    261         *raddr = eaddr;
    262         return true;
    263     }
    264 
    265     if (guest_visible) {
    266         cs->exception_index = POWERPC_EXCP_DSI;
    267         env->error_code = 0;
    268         env->spr[SPR_DAR] = eaddr;
    269         if (access_type == MMU_DATA_STORE) {
    270             env->spr[SPR_DSISR] = 0x0a000000;
    271         } else {
    272             env->spr[SPR_DSISR] = 0x08000000;
    273         }
    274     }
    275     return false;
    276 }
    277 
    278 hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash)
    279 {
    280     target_ulong mask = ppc_hash32_hpt_mask(cpu);
    281 
    282     return (hash * HASH_PTEG_SIZE_32) & mask;
    283 }
    284 
    285 static hwaddr ppc_hash32_pteg_search(PowerPCCPU *cpu, hwaddr pteg_off,
    286                                      bool secondary, target_ulong ptem,
    287                                      ppc_hash_pte32_t *pte)
    288 {
    289     hwaddr pte_offset = pteg_off;
    290     target_ulong pte0, pte1;
    291     int i;
    292 
    293     for (i = 0; i < HPTES_PER_GROUP; i++) {
    294         pte0 = ppc_hash32_load_hpte0(cpu, pte_offset);
    295         /*
    296          * pte0 contains the valid bit and must be read before pte1,
    297          * otherwise we might see an old pte1 with a new valid bit and
    298          * thus an inconsistent hpte value
    299          */
    300         smp_rmb();
    301         pte1 = ppc_hash32_load_hpte1(cpu, pte_offset);
    302 
    303         if ((pte0 & HPTE32_V_VALID)
    304             && (secondary == !!(pte0 & HPTE32_V_SECONDARY))
    305             && HPTE32_V_COMPARE(pte0, ptem)) {
    306             pte->pte0 = pte0;
    307             pte->pte1 = pte1;
    308             return pte_offset;
    309         }
    310 
    311         pte_offset += HASH_PTE_SIZE_32;
    312     }
    313 
    314     return -1;
    315 }
    316 
    317 static void ppc_hash32_set_r(PowerPCCPU *cpu, hwaddr pte_offset, uint32_t pte1)
    318 {
    319     target_ulong base = ppc_hash32_hpt_base(cpu);
    320     hwaddr offset = pte_offset + 6;
    321 
    322     /* The HW performs a non-atomic byte update */
    323     stb_phys(CPU(cpu)->as, base + offset, ((pte1 >> 8) & 0xff) | 0x01);
    324 }
    325 
    326 static void ppc_hash32_set_c(PowerPCCPU *cpu, hwaddr pte_offset, uint64_t pte1)
    327 {
    328     target_ulong base = ppc_hash32_hpt_base(cpu);
    329     hwaddr offset = pte_offset + 7;
    330 
    331     /* The HW performs a non-atomic byte update */
    332     stb_phys(CPU(cpu)->as, base + offset, (pte1 & 0xff) | 0x80);
    333 }
    334 
    335 static hwaddr ppc_hash32_htab_lookup(PowerPCCPU *cpu,
    336                                      target_ulong sr, target_ulong eaddr,
    337                                      ppc_hash_pte32_t *pte)
    338 {
    339     hwaddr pteg_off, pte_offset;
    340     hwaddr hash;
    341     uint32_t vsid, pgidx, ptem;
    342 
    343     vsid = sr & SR32_VSID;
    344     pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS;
    345     hash = vsid ^ pgidx;
    346     ptem = (vsid << 7) | (pgidx >> 10);
    347 
    348     /* Page address translation */
    349     qemu_log_mask(CPU_LOG_MMU, "htab_base " TARGET_FMT_plx
    350             " htab_mask " TARGET_FMT_plx
    351             " hash " TARGET_FMT_plx "\n",
    352             ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash);
    353 
    354     /* Primary PTEG lookup */
    355     qemu_log_mask(CPU_LOG_MMU, "0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
    356             " vsid=%" PRIx32 " ptem=%" PRIx32
    357             " hash=" TARGET_FMT_plx "\n",
    358             ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu),
    359             vsid, ptem, hash);
    360     pteg_off = get_pteg_offset32(cpu, hash);
    361     pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 0, ptem, pte);
    362     if (pte_offset == -1) {
    363         /* Secondary PTEG lookup */
    364         qemu_log_mask(CPU_LOG_MMU, "1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
    365                 " vsid=%" PRIx32 " api=%" PRIx32
    366                 " hash=" TARGET_FMT_plx "\n", ppc_hash32_hpt_base(cpu),
    367                 ppc_hash32_hpt_mask(cpu), vsid, ptem, ~hash);
    368         pteg_off = get_pteg_offset32(cpu, ~hash);
    369         pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 1, ptem, pte);
    370     }
    371 
    372     return pte_offset;
    373 }
    374 
    375 static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte,
    376                                    target_ulong eaddr)
    377 {
    378     hwaddr rpn = pte.pte1 & HPTE32_R_RPN;
    379     hwaddr mask = ~TARGET_PAGE_MASK;
    380 
    381     return (rpn & ~mask) | (eaddr & mask);
    382 }
    383 
    384 bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type,
    385                       hwaddr *raddrp, int *psizep, int *protp, int mmu_idx,
    386                       bool guest_visible)
    387 {
    388     CPUState *cs = CPU(cpu);
    389     CPUPPCState *env = &cpu->env;
    390     target_ulong sr;
    391     hwaddr pte_offset;
    392     ppc_hash_pte32_t pte;
    393     int prot;
    394     int need_prot;
    395     hwaddr raddr;
    396 
    397     /* There are no hash32 large pages. */
    398     *psizep = TARGET_PAGE_BITS;
    399 
    400     /* 1. Handle real mode accesses */
    401     if (mmuidx_real(mmu_idx)) {
    402         /* Translation is off */
    403         *raddrp = eaddr;
    404         *protp = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
    405         return true;
    406     }
    407 
    408     need_prot = prot_for_access_type(access_type);
    409 
    410     /* 2. Check Block Address Translation entries (BATs) */
    411     if (env->nb_BATs != 0) {
    412         raddr = ppc_hash32_bat_lookup(cpu, eaddr, access_type, protp, mmu_idx);
    413         if (raddr != -1) {
    414             if (need_prot & ~*protp) {
    415                 if (guest_visible) {
    416                     if (access_type == MMU_INST_FETCH) {
    417                         cs->exception_index = POWERPC_EXCP_ISI;
    418                         env->error_code = 0x08000000;
    419                     } else {
    420                         cs->exception_index = POWERPC_EXCP_DSI;
    421                         env->error_code = 0;
    422                         env->spr[SPR_DAR] = eaddr;
    423                         if (access_type == MMU_DATA_STORE) {
    424                             env->spr[SPR_DSISR] = 0x0a000000;
    425                         } else {
    426                             env->spr[SPR_DSISR] = 0x08000000;
    427                         }
    428                     }
    429                 }
    430                 return false;
    431             }
    432             *raddrp = raddr;
    433             return true;
    434         }
    435     }
    436 
    437     /* 3. Look up the Segment Register */
    438     sr = env->sr[eaddr >> 28];
    439 
    440     /* 4. Handle direct store segments */
    441     if (sr & SR32_T) {
    442         return ppc_hash32_direct_store(cpu, sr, eaddr, access_type,
    443                                        raddrp, protp, mmu_idx, guest_visible);
    444     }
    445 
    446     /* 5. Check for segment level no-execute violation */
    447     if (access_type == MMU_INST_FETCH && (sr & SR32_NX)) {
    448         if (guest_visible) {
    449             cs->exception_index = POWERPC_EXCP_ISI;
    450             env->error_code = 0x10000000;
    451         }
    452         return false;
    453     }
    454 
    455     /* 6. Locate the PTE in the hash table */
    456     pte_offset = ppc_hash32_htab_lookup(cpu, sr, eaddr, &pte);
    457     if (pte_offset == -1) {
    458         if (guest_visible) {
    459             if (access_type == MMU_INST_FETCH) {
    460                 cs->exception_index = POWERPC_EXCP_ISI;
    461                 env->error_code = 0x40000000;
    462             } else {
    463                 cs->exception_index = POWERPC_EXCP_DSI;
    464                 env->error_code = 0;
    465                 env->spr[SPR_DAR] = eaddr;
    466                 if (access_type == MMU_DATA_STORE) {
    467                     env->spr[SPR_DSISR] = 0x42000000;
    468                 } else {
    469                     env->spr[SPR_DSISR] = 0x40000000;
    470                 }
    471             }
    472         }
    473         return false;
    474     }
    475     qemu_log_mask(CPU_LOG_MMU,
    476                 "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset);
    477 
    478     /* 7. Check access permissions */
    479 
    480     prot = ppc_hash32_pte_prot(mmu_idx, sr, pte);
    481 
    482     if (need_prot & ~prot) {
    483         /* Access right violation */
    484         qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n");
    485         if (guest_visible) {
    486             if (access_type == MMU_INST_FETCH) {
    487                 cs->exception_index = POWERPC_EXCP_ISI;
    488                 env->error_code = 0x08000000;
    489             } else {
    490                 cs->exception_index = POWERPC_EXCP_DSI;
    491                 env->error_code = 0;
    492                 env->spr[SPR_DAR] = eaddr;
    493                 if (access_type == MMU_DATA_STORE) {
    494                     env->spr[SPR_DSISR] = 0x0a000000;
    495                 } else {
    496                     env->spr[SPR_DSISR] = 0x08000000;
    497                 }
    498             }
    499         }
    500         return false;
    501     }
    502 
    503     qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n");
    504 
    505     /* 8. Update PTE referenced and changed bits if necessary */
    506 
    507     if (!(pte.pte1 & HPTE32_R_R)) {
    508         ppc_hash32_set_r(cpu, pte_offset, pte.pte1);
    509     }
    510     if (!(pte.pte1 & HPTE32_R_C)) {
    511         if (access_type == MMU_DATA_STORE) {
    512             ppc_hash32_set_c(cpu, pte_offset, pte.pte1);
    513         } else {
    514             /*
    515              * Treat the page as read-only for now, so that a later write
    516              * will pass through this function again to set the C bit
    517              */
    518             prot &= ~PAGE_WRITE;
    519         }
    520      }
    521 
    522     /* 9. Determine the real address from the PTE */
    523 
    524     *raddrp = ppc_hash32_pte_raddr(sr, pte, eaddr);
    525     *protp = prot;
    526     return true;
    527 }