19d7c3f4aSDavid Gibson /* 29d7c3f4aSDavid Gibson * PowerPC MMU, TLB and BAT emulation helpers for QEMU. 39d7c3f4aSDavid Gibson * 49d7c3f4aSDavid Gibson * Copyright (c) 2003-2007 Jocelyn Mayer 59d7c3f4aSDavid Gibson * Copyright (c) 2013 David Gibson, IBM Corporation 69d7c3f4aSDavid Gibson * 79d7c3f4aSDavid Gibson * This library is free software; you can redistribute it and/or 89d7c3f4aSDavid Gibson * modify it under the terms of the GNU Lesser General Public 99d7c3f4aSDavid Gibson * License as published by the Free Software Foundation; either 109d7c3f4aSDavid Gibson * version 2 of the License, or (at your option) any later version. 119d7c3f4aSDavid Gibson * 129d7c3f4aSDavid Gibson * This library is distributed in the hope that it will be useful, 139d7c3f4aSDavid Gibson * but WITHOUT ANY WARRANTY; without even the implied warranty of 149d7c3f4aSDavid Gibson * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 159d7c3f4aSDavid Gibson * Lesser General Public License for more details. 169d7c3f4aSDavid Gibson * 179d7c3f4aSDavid Gibson * You should have received a copy of the GNU Lesser General Public 189d7c3f4aSDavid Gibson * License along with this library; if not, see <http://www.gnu.org/licenses/>. 199d7c3f4aSDavid Gibson */ 209d7c3f4aSDavid Gibson 219d7c3f4aSDavid Gibson #include "cpu.h" 229d7c3f4aSDavid Gibson #include "helper.h" 239d7c3f4aSDavid Gibson #include "sysemu/kvm.h" 249d7c3f4aSDavid Gibson #include "kvm_ppc.h" 259d7c3f4aSDavid Gibson #include "mmu-hash32.h" 269d7c3f4aSDavid Gibson 279d7c3f4aSDavid Gibson //#define DEBUG_MMU 2898132796SDavid Gibson //#define DEBUG_BAT 299d7c3f4aSDavid Gibson 309d7c3f4aSDavid Gibson #ifdef DEBUG_MMU 319d7c3f4aSDavid Gibson # define LOG_MMU(...) qemu_log(__VA_ARGS__) 329d7c3f4aSDavid Gibson # define LOG_MMU_STATE(env) log_cpu_state((env), 0) 339d7c3f4aSDavid Gibson #else 349d7c3f4aSDavid Gibson # define LOG_MMU(...) do { } while (0) 359d7c3f4aSDavid Gibson # define LOG_MMU_STATE(...) do { } while (0) 369d7c3f4aSDavid Gibson #endif 379d7c3f4aSDavid Gibson 3898132796SDavid Gibson #ifdef DEBUG_BATS 3998132796SDavid Gibson # define LOG_BATS(...) qemu_log(__VA_ARGS__) 4098132796SDavid Gibson #else 4198132796SDavid Gibson # define LOG_BATS(...) do { } while (0) 4298132796SDavid Gibson #endif 4398132796SDavid Gibson 445dc68eb0SDavid Gibson struct mmu_ctx_hash32 { 455dc68eb0SDavid Gibson hwaddr raddr; /* Real address */ 465dc68eb0SDavid Gibson int prot; /* Protection bits */ 475dc68eb0SDavid Gibson int key; /* Access key */ 485dc68eb0SDavid Gibson }; 495dc68eb0SDavid Gibson 50e01b4445SDavid Gibson static int ppc_hash32_pp_prot(int key, int pp, int nx) 51496272a7SDavid Gibson { 52e01b4445SDavid Gibson int prot; 53496272a7SDavid Gibson 54496272a7SDavid Gibson if (key == 0) { 55496272a7SDavid Gibson switch (pp) { 56496272a7SDavid Gibson case 0x0: 57496272a7SDavid Gibson case 0x1: 58496272a7SDavid Gibson case 0x2: 59e01b4445SDavid Gibson prot = PAGE_READ | PAGE_WRITE; 60496272a7SDavid Gibson break; 61e01b4445SDavid Gibson 62e01b4445SDavid Gibson case 0x3: 63e01b4445SDavid Gibson prot = PAGE_READ; 64e01b4445SDavid Gibson break; 65e01b4445SDavid Gibson 66e01b4445SDavid Gibson default: 67e01b4445SDavid Gibson abort(); 68496272a7SDavid Gibson } 69496272a7SDavid Gibson } else { 70496272a7SDavid Gibson switch (pp) { 71496272a7SDavid Gibson case 0x0: 72e01b4445SDavid Gibson prot = 0; 73496272a7SDavid Gibson break; 74e01b4445SDavid Gibson 75496272a7SDavid Gibson case 0x1: 76496272a7SDavid Gibson case 0x3: 77e01b4445SDavid Gibson prot = PAGE_READ; 78496272a7SDavid Gibson break; 79e01b4445SDavid Gibson 80496272a7SDavid Gibson case 0x2: 81e01b4445SDavid Gibson prot = PAGE_READ | PAGE_WRITE; 82496272a7SDavid Gibson break; 83e01b4445SDavid Gibson 84e01b4445SDavid Gibson default: 85e01b4445SDavid Gibson abort(); 86496272a7SDavid Gibson } 87496272a7SDavid Gibson } 88496272a7SDavid Gibson if (nx == 0) { 89e01b4445SDavid Gibson prot |= PAGE_EXEC; 90496272a7SDavid Gibson } 91496272a7SDavid Gibson 92e01b4445SDavid Gibson return prot; 93496272a7SDavid Gibson } 94496272a7SDavid Gibson 95e01b4445SDavid Gibson static int ppc_hash32_pte_prot(CPUPPCState *env, 96e01b4445SDavid Gibson target_ulong sr, ppc_hash_pte32_t pte) 97496272a7SDavid Gibson { 98e01b4445SDavid Gibson unsigned pp, key; 99496272a7SDavid Gibson 100e01b4445SDavid Gibson key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS)); 101e01b4445SDavid Gibson pp = pte.pte1 & HPTE32_R_PP; 102496272a7SDavid Gibson 103e01b4445SDavid Gibson return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX)); 104496272a7SDavid Gibson } 105496272a7SDavid Gibson 1066fc76aa9SDavid Gibson static target_ulong hash32_bat_size(CPUPPCState *env, 1079986ed1eSDavid Gibson target_ulong batu, target_ulong batl) 10898132796SDavid Gibson { 1096fc76aa9SDavid Gibson if ((msr_pr && !(batu & BATU32_VP)) 1106fc76aa9SDavid Gibson || (!msr_pr && !(batu & BATU32_VS))) { 1116fc76aa9SDavid Gibson return 0; 112e1d49515SDavid Gibson } 1136fc76aa9SDavid Gibson 1146fc76aa9SDavid Gibson return BATU32_BEPI & ~((batu & BATU32_BL) << 15); 115e1d49515SDavid Gibson } 116e1d49515SDavid Gibson 117e1d49515SDavid Gibson static int hash32_bat_prot(CPUPPCState *env, 118e1d49515SDavid Gibson target_ulong batu, target_ulong batl) 119e1d49515SDavid Gibson { 120e1d49515SDavid Gibson int pp, prot; 121e1d49515SDavid Gibson 122e1d49515SDavid Gibson prot = 0; 1239986ed1eSDavid Gibson pp = batl & BATL32_PP; 12498132796SDavid Gibson if (pp != 0) { 12598132796SDavid Gibson prot = PAGE_READ | PAGE_EXEC; 12698132796SDavid Gibson if (pp == 0x2) { 12798132796SDavid Gibson prot |= PAGE_WRITE; 12898132796SDavid Gibson } 12998132796SDavid Gibson } 130e1d49515SDavid Gibson return prot; 13198132796SDavid Gibson } 13298132796SDavid Gibson 1336fc76aa9SDavid Gibson static target_ulong hash32_bat_601_size(CPUPPCState *env, 1349986ed1eSDavid Gibson target_ulong batu, target_ulong batl) 13598132796SDavid Gibson { 1366fc76aa9SDavid Gibson if (!(batl & BATL32_601_V)) { 1376fc76aa9SDavid Gibson return 0; 1386fc76aa9SDavid Gibson } 13998132796SDavid Gibson 1406fc76aa9SDavid Gibson return BATU32_BEPI & ~((batl & BATL32_601_BL) << 17); 141e1d49515SDavid Gibson } 142e1d49515SDavid Gibson 143e1d49515SDavid Gibson static int hash32_bat_601_prot(CPUPPCState *env, 144e1d49515SDavid Gibson target_ulong batu, target_ulong batl) 145e1d49515SDavid Gibson { 146e1d49515SDavid Gibson int key, pp; 147e1d49515SDavid Gibson 1489986ed1eSDavid Gibson pp = batu & BATU32_601_PP; 14998132796SDavid Gibson if (msr_pr == 0) { 1509986ed1eSDavid Gibson key = !!(batu & BATU32_601_KS); 15198132796SDavid Gibson } else { 1529986ed1eSDavid Gibson key = !!(batu & BATU32_601_KP); 15398132796SDavid Gibson } 154e01b4445SDavid Gibson return ppc_hash32_pp_prot(key, pp, 0); 15598132796SDavid Gibson } 15698132796SDavid Gibson 157145e52f3SDavid Gibson static hwaddr ppc_hash32_bat_lookup(CPUPPCState *env, target_ulong ea, int rwx, 158145e52f3SDavid Gibson int *prot) 15998132796SDavid Gibson { 1609986ed1eSDavid Gibson target_ulong *BATlt, *BATut; 161145e52f3SDavid Gibson int i; 16298132796SDavid Gibson 16398132796SDavid Gibson LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__, 164145e52f3SDavid Gibson rwx == 2 ? 'I' : 'D', ea); 16591cda45bSDavid Gibson if (rwx == 2) { 16698132796SDavid Gibson BATlt = env->IBAT[1]; 16798132796SDavid Gibson BATut = env->IBAT[0]; 16891cda45bSDavid Gibson } else { 16998132796SDavid Gibson BATlt = env->DBAT[1]; 17098132796SDavid Gibson BATut = env->DBAT[0]; 17198132796SDavid Gibson } 17298132796SDavid Gibson for (i = 0; i < env->nb_BATs; i++) { 1739986ed1eSDavid Gibson target_ulong batu = BATut[i]; 1749986ed1eSDavid Gibson target_ulong batl = BATlt[i]; 1756fc76aa9SDavid Gibson target_ulong mask; 1769986ed1eSDavid Gibson 17798132796SDavid Gibson if (unlikely(env->mmu_model == POWERPC_MMU_601)) { 1786fc76aa9SDavid Gibson mask = hash32_bat_601_size(env, batu, batl); 17998132796SDavid Gibson } else { 1806fc76aa9SDavid Gibson mask = hash32_bat_size(env, batu, batl); 18198132796SDavid Gibson } 18298132796SDavid Gibson LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx 18398132796SDavid Gibson " BATl " TARGET_FMT_lx "\n", __func__, 184145e52f3SDavid Gibson type == ACCESS_CODE ? 'I' : 'D', i, ea, batu, batl); 1856fc76aa9SDavid Gibson 186145e52f3SDavid Gibson if (mask && ((ea & mask) == (batu & BATU32_BEPI))) { 187145e52f3SDavid Gibson hwaddr raddr = (batl & mask) | (ea & ~mask); 188145e52f3SDavid Gibson 189145e52f3SDavid Gibson if (unlikely(env->mmu_model == POWERPC_MMU_601)) { 190145e52f3SDavid Gibson *prot = hash32_bat_601_prot(env, batu, batl); 191145e52f3SDavid Gibson } else { 192145e52f3SDavid Gibson *prot = hash32_bat_prot(env, batu, batl); 19398132796SDavid Gibson } 194145e52f3SDavid Gibson 195145e52f3SDavid Gibson return raddr & TARGET_PAGE_MASK; 19698132796SDavid Gibson } 19798132796SDavid Gibson } 198145e52f3SDavid Gibson 199145e52f3SDavid Gibson /* No hit */ 20098132796SDavid Gibson #if defined(DEBUG_BATS) 20198132796SDavid Gibson if (qemu_log_enabled()) { 202145e52f3SDavid Gibson LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", ea); 20398132796SDavid Gibson for (i = 0; i < 4; i++) { 20498132796SDavid Gibson BATu = &BATut[i]; 20598132796SDavid Gibson BATl = &BATlt[i]; 206d5aea6f3SDavid Gibson BEPIu = *BATu & BATU32_BEPIU; 207d5aea6f3SDavid Gibson BEPIl = *BATu & BATU32_BEPIL; 20898132796SDavid Gibson bl = (*BATu & 0x00001FFC) << 15; 20998132796SDavid Gibson LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx 21098132796SDavid Gibson " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " 21198132796SDavid Gibson TARGET_FMT_lx " " TARGET_FMT_lx "\n", 212145e52f3SDavid Gibson __func__, type == ACCESS_CODE ? 'I' : 'D', i, ea, 21398132796SDavid Gibson *BATu, *BATl, BEPIu, BEPIl, bl); 21498132796SDavid Gibson } 21598132796SDavid Gibson } 21698132796SDavid Gibson #endif 217145e52f3SDavid Gibson 218145e52f3SDavid Gibson return -1; 21998132796SDavid Gibson } 22098132796SDavid Gibson 221723ed73aSDavid Gibson static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr, 222723ed73aSDavid Gibson target_ulong eaddr, int rwx, 223723ed73aSDavid Gibson hwaddr *raddr, int *prot) 224723ed73aSDavid Gibson { 225723ed73aSDavid Gibson int key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS)); 226723ed73aSDavid Gibson 227723ed73aSDavid Gibson LOG_MMU("direct store...\n"); 228723ed73aSDavid Gibson 229723ed73aSDavid Gibson if ((sr & 0x1FF00000) >> 20 == 0x07f) { 230723ed73aSDavid Gibson /* Memory-forced I/O controller interface access */ 231723ed73aSDavid Gibson /* If T=1 and BUID=x'07F', the 601 performs a memory access 232723ed73aSDavid Gibson * to SR[28-31] LA[4-31], bypassing all protection mechanisms. 233723ed73aSDavid Gibson */ 234723ed73aSDavid Gibson *raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); 235723ed73aSDavid Gibson *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; 236723ed73aSDavid Gibson return 0; 237723ed73aSDavid Gibson } 238723ed73aSDavid Gibson 239723ed73aSDavid Gibson if (rwx == 2) { 240723ed73aSDavid Gibson /* No code fetch is allowed in direct-store areas */ 241723ed73aSDavid Gibson return -4; 242723ed73aSDavid Gibson } 243723ed73aSDavid Gibson 244723ed73aSDavid Gibson switch (env->access_type) { 245723ed73aSDavid Gibson case ACCESS_INT: 246723ed73aSDavid Gibson /* Integer load/store : only access allowed */ 247723ed73aSDavid Gibson break; 248723ed73aSDavid Gibson case ACCESS_FLOAT: 249723ed73aSDavid Gibson /* Floating point load/store */ 250723ed73aSDavid Gibson return -4; 251723ed73aSDavid Gibson case ACCESS_RES: 252723ed73aSDavid Gibson /* lwarx, ldarx or srwcx. */ 253723ed73aSDavid Gibson return -4; 254723ed73aSDavid Gibson case ACCESS_CACHE: 255723ed73aSDavid Gibson /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */ 256723ed73aSDavid Gibson /* Should make the instruction do no-op. 257723ed73aSDavid Gibson * As it already do no-op, it's quite easy :-) 258723ed73aSDavid Gibson */ 259723ed73aSDavid Gibson *raddr = eaddr; 260723ed73aSDavid Gibson return 0; 261723ed73aSDavid Gibson case ACCESS_EXT: 262723ed73aSDavid Gibson /* eciwx or ecowx */ 263723ed73aSDavid Gibson return -4; 264723ed73aSDavid Gibson default: 265723ed73aSDavid Gibson qemu_log("ERROR: instruction should not need " 266723ed73aSDavid Gibson "address translation\n"); 267723ed73aSDavid Gibson return -4; 268723ed73aSDavid Gibson } 269723ed73aSDavid Gibson if ((rwx == 1 || key != 1) && (rwx == 0 || key != 0)) { 270723ed73aSDavid Gibson *raddr = eaddr; 271723ed73aSDavid Gibson return 2; 272723ed73aSDavid Gibson } else { 273723ed73aSDavid Gibson return -2; 274723ed73aSDavid Gibson } 275723ed73aSDavid Gibson } 276723ed73aSDavid Gibson 27759191721SDavid Gibson hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash) 27859191721SDavid Gibson { 279d5aea6f3SDavid Gibson return (hash * HASH_PTEG_SIZE_32) & env->htab_mask; 28059191721SDavid Gibson } 28159191721SDavid Gibson 282aea390e4SDavid Gibson static hwaddr ppc_hash32_pteg_search(CPUPPCState *env, hwaddr pteg_off, 283aea390e4SDavid Gibson bool secondary, target_ulong ptem, 284aea390e4SDavid Gibson ppc_hash_pte32_t *pte) 285aea390e4SDavid Gibson { 286aea390e4SDavid Gibson hwaddr pte_offset = pteg_off; 287aea390e4SDavid Gibson target_ulong pte0, pte1; 288aea390e4SDavid Gibson int i; 289aea390e4SDavid Gibson 290aea390e4SDavid Gibson for (i = 0; i < HPTES_PER_GROUP; i++) { 291aea390e4SDavid Gibson pte0 = ppc_hash32_load_hpte0(env, pte_offset); 292aea390e4SDavid Gibson pte1 = ppc_hash32_load_hpte1(env, pte_offset); 293aea390e4SDavid Gibson 294aea390e4SDavid Gibson if ((pte0 & HPTE32_V_VALID) 295aea390e4SDavid Gibson && (secondary == !!(pte0 & HPTE32_V_SECONDARY)) 296aea390e4SDavid Gibson && HPTE32_V_COMPARE(pte0, ptem)) { 297aea390e4SDavid Gibson pte->pte0 = pte0; 298aea390e4SDavid Gibson pte->pte1 = pte1; 299aea390e4SDavid Gibson return pte_offset; 300aea390e4SDavid Gibson } 301aea390e4SDavid Gibson 302aea390e4SDavid Gibson pte_offset += HASH_PTE_SIZE_32; 303aea390e4SDavid Gibson } 304aea390e4SDavid Gibson 305aea390e4SDavid Gibson return -1; 306aea390e4SDavid Gibson } 307aea390e4SDavid Gibson 3087f3bdc2dSDavid Gibson static hwaddr ppc_hash32_htab_lookup(CPUPPCState *env, 3097f3bdc2dSDavid Gibson target_ulong sr, target_ulong eaddr, 3107f3bdc2dSDavid Gibson ppc_hash_pte32_t *pte) 311c69b6151SDavid Gibson { 312aea390e4SDavid Gibson hwaddr pteg_off, pte_offset; 313a1ff751aSDavid Gibson hwaddr hash; 314a1ff751aSDavid Gibson uint32_t vsid, pgidx, ptem; 315c69b6151SDavid Gibson 316a1ff751aSDavid Gibson vsid = sr & SR32_VSID; 317a1ff751aSDavid Gibson pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS; 318a1ff751aSDavid Gibson hash = vsid ^ pgidx; 319a1ff751aSDavid Gibson ptem = (vsid << 7) | (pgidx >> 10); 320a1ff751aSDavid Gibson 321a1ff751aSDavid Gibson /* Page address translation */ 322a1ff751aSDavid Gibson LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx 323a1ff751aSDavid Gibson " hash " TARGET_FMT_plx "\n", 324a1ff751aSDavid Gibson env->htab_base, env->htab_mask, hash); 325a1ff751aSDavid Gibson 326a1ff751aSDavid Gibson /* Primary PTEG lookup */ 327a1ff751aSDavid Gibson LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx 328a1ff751aSDavid Gibson " vsid=%" PRIx32 " ptem=%" PRIx32 329a1ff751aSDavid Gibson " hash=" TARGET_FMT_plx "\n", 330a1ff751aSDavid Gibson env->htab_base, env->htab_mask, vsid, ptem, hash); 331a1ff751aSDavid Gibson pteg_off = get_pteg_offset32(env, hash); 3327f3bdc2dSDavid Gibson pte_offset = ppc_hash32_pteg_search(env, pteg_off, 0, ptem, pte); 333a1ff751aSDavid Gibson if (pte_offset == -1) { 334a1ff751aSDavid Gibson /* Secondary PTEG lookup */ 335a1ff751aSDavid Gibson LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx 336a1ff751aSDavid Gibson " vsid=%" PRIx32 " api=%" PRIx32 337a1ff751aSDavid Gibson " hash=" TARGET_FMT_plx "\n", env->htab_base, 338a1ff751aSDavid Gibson env->htab_mask, vsid, ptem, ~hash); 339a1ff751aSDavid Gibson pteg_off = get_pteg_offset32(env, ~hash); 3407f3bdc2dSDavid Gibson pte_offset = ppc_hash32_pteg_search(env, pteg_off, 1, ptem, pte); 341a1ff751aSDavid Gibson } 342a1ff751aSDavid Gibson 3437f3bdc2dSDavid Gibson return pte_offset; 344c69b6151SDavid Gibson } 3450480884fSDavid Gibson 3466d11d998SDavid Gibson static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte, 3476d11d998SDavid Gibson target_ulong eaddr) 3486d11d998SDavid Gibson { 34975d5ec89SDavid Gibson hwaddr rpn = pte.pte1 & HPTE32_R_RPN; 3506d11d998SDavid Gibson hwaddr mask = ~TARGET_PAGE_MASK; 3516d11d998SDavid Gibson 3526d11d998SDavid Gibson return (rpn & ~mask) | (eaddr & mask); 3536d11d998SDavid Gibson } 3546d11d998SDavid Gibson 35565d61643SDavid Gibson static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, 35691cda45bSDavid Gibson target_ulong eaddr, int rwx) 3570480884fSDavid Gibson { 358a1ff751aSDavid Gibson target_ulong sr; 3597f3bdc2dSDavid Gibson hwaddr pte_offset; 3607f3bdc2dSDavid Gibson ppc_hash_pte32_t pte; 361b3440746SDavid Gibson uint32_t new_pte1; 362e01b4445SDavid Gibson const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; 3630480884fSDavid Gibson 3646a980110SDavid Gibson assert((rwx == 0) || (rwx == 1) || (rwx == 2)); 3656a980110SDavid Gibson 36665d61643SDavid Gibson /* 1. Handle real mode accesses */ 36765d61643SDavid Gibson if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { 36865d61643SDavid Gibson /* Translation is off */ 36965d61643SDavid Gibson ctx->raddr = eaddr; 37065d61643SDavid Gibson ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE; 37165d61643SDavid Gibson return 0; 37265d61643SDavid Gibson } 37365d61643SDavid Gibson 37465d61643SDavid Gibson /* 2. Check Block Address Translation entries (BATs) */ 37565d61643SDavid Gibson if (env->nb_BATs != 0) { 376145e52f3SDavid Gibson ctx->raddr = ppc_hash32_bat_lookup(env, eaddr, rwx, &ctx->prot); 377145e52f3SDavid Gibson if (ctx->raddr != -1) { 378e01b4445SDavid Gibson if (need_prot[rwx] & ~ctx->prot) { 379e01b4445SDavid Gibson return -2; 380e01b4445SDavid Gibson } 381e01b4445SDavid Gibson return 0; 38265d61643SDavid Gibson } 383145e52f3SDavid Gibson } 38465d61643SDavid Gibson 3854b9605a5SDavid Gibson /* 3. Look up the Segment Register */ 3860480884fSDavid Gibson sr = env->sr[eaddr >> 28]; 3874b9605a5SDavid Gibson 388723ed73aSDavid Gibson /* 4. Handle direct store segments */ 389723ed73aSDavid Gibson if (sr & SR32_T) { 390723ed73aSDavid Gibson return ppc_hash32_direct_store(env, sr, eaddr, rwx, 391723ed73aSDavid Gibson &ctx->raddr, &ctx->prot); 392723ed73aSDavid Gibson } 393723ed73aSDavid Gibson 394bb218042SDavid Gibson /* 5. Check for segment level no-execute violation */ 395e01b4445SDavid Gibson if ((rwx == 2) && (sr & SR32_NX)) { 396bb218042SDavid Gibson return -3; 397bb218042SDavid Gibson } 3987f3bdc2dSDavid Gibson 3997f3bdc2dSDavid Gibson /* 6. Locate the PTE in the hash table */ 4007f3bdc2dSDavid Gibson pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte); 4017f3bdc2dSDavid Gibson if (pte_offset == -1) { 4027f3bdc2dSDavid Gibson return -1; 4037f3bdc2dSDavid Gibson } 4047f3bdc2dSDavid Gibson LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); 4057f3bdc2dSDavid Gibson 4067f3bdc2dSDavid Gibson /* 7. Check access permissions */ 4076a980110SDavid Gibson 408e01b4445SDavid Gibson ctx->prot = ppc_hash32_pte_prot(env, sr, pte); 4096a980110SDavid Gibson 410e01b4445SDavid Gibson if (need_prot[rwx] & ~ctx->prot) { 4116a980110SDavid Gibson /* Access right violation */ 4126a980110SDavid Gibson LOG_MMU("PTE access rejected\n"); 413e01b4445SDavid Gibson return -2; 4146a980110SDavid Gibson } 4156a980110SDavid Gibson 41687dc3fd1SDavid Gibson LOG_MMU("PTE access granted !\n"); 41787dc3fd1SDavid Gibson 41887dc3fd1SDavid Gibson /* 8. Update PTE referenced and changed bits if necessary */ 41987dc3fd1SDavid Gibson 420b3440746SDavid Gibson new_pte1 = pte.pte1 | HPTE32_R_R; /* set referenced bit */ 421b3440746SDavid Gibson if (rwx == 1) { 422b3440746SDavid Gibson new_pte1 |= HPTE32_R_C; /* set changed (dirty) bit */ 423b3440746SDavid Gibson } else { 424b3440746SDavid Gibson /* Treat the page as read-only for now, so that a later write 425b3440746SDavid Gibson * will pass through this function again to set the C bit */ 426b3440746SDavid Gibson ctx->prot &= ~PAGE_WRITE; 427b3440746SDavid Gibson } 428b3440746SDavid Gibson 429b3440746SDavid Gibson if (new_pte1 != pte.pte1) { 430b3440746SDavid Gibson ppc_hash32_store_hpte1(env, pte_offset, new_pte1); 4317f3bdc2dSDavid Gibson } 4320480884fSDavid Gibson 4336d11d998SDavid Gibson /* 9. Determine the real address from the PTE */ 4346d11d998SDavid Gibson 4356d11d998SDavid Gibson ctx->raddr = ppc_hash32_pte_raddr(sr, pte, eaddr); 436e01b4445SDavid Gibson 437e01b4445SDavid Gibson return 0; 4380480884fSDavid Gibson } 439629bd516SDavid Gibson 440f2ad6be8SDavid Gibson hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr) 441f2ad6be8SDavid Gibson { 4425dc68eb0SDavid Gibson struct mmu_ctx_hash32 ctx; 443f2ad6be8SDavid Gibson 44491cda45bSDavid Gibson /* FIXME: Will not behave sanely for direct store segments, but 44591cda45bSDavid Gibson * they're almost never used */ 44665d61643SDavid Gibson if (unlikely(ppc_hash32_translate(env, &ctx, addr, 0) 447f2ad6be8SDavid Gibson != 0)) { 448f2ad6be8SDavid Gibson return -1; 449f2ad6be8SDavid Gibson } 450f2ad6be8SDavid Gibson 451f2ad6be8SDavid Gibson return ctx.raddr & TARGET_PAGE_MASK; 452f2ad6be8SDavid Gibson } 453f2ad6be8SDavid Gibson 45491cda45bSDavid Gibson int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rwx, 45525de24abSDavid Gibson int mmu_idx) 45625de24abSDavid Gibson { 4575dc68eb0SDavid Gibson struct mmu_ctx_hash32 ctx; 45825de24abSDavid Gibson int ret = 0; 45925de24abSDavid Gibson 46065d61643SDavid Gibson ret = ppc_hash32_translate(env, &ctx, address, rwx); 46125de24abSDavid Gibson if (ret == 0) { 46225de24abSDavid Gibson tlb_set_page(env, address & TARGET_PAGE_MASK, 46325de24abSDavid Gibson ctx.raddr & TARGET_PAGE_MASK, ctx.prot, 46425de24abSDavid Gibson mmu_idx, TARGET_PAGE_SIZE); 46525de24abSDavid Gibson ret = 0; 46625de24abSDavid Gibson } else if (ret < 0) { 46725de24abSDavid Gibson LOG_MMU_STATE(env); 46891cda45bSDavid Gibson if (rwx == 2) { 46925de24abSDavid Gibson switch (ret) { 47025de24abSDavid Gibson case -1: 47125de24abSDavid Gibson /* No matches in page tables or TLB */ 47225de24abSDavid Gibson env->exception_index = POWERPC_EXCP_ISI; 47325de24abSDavid Gibson env->error_code = 0x40000000; 47425de24abSDavid Gibson break; 47525de24abSDavid Gibson case -2: 47625de24abSDavid Gibson /* Access rights violation */ 47725de24abSDavid Gibson env->exception_index = POWERPC_EXCP_ISI; 47825de24abSDavid Gibson env->error_code = 0x08000000; 47925de24abSDavid Gibson break; 48025de24abSDavid Gibson case -3: 48125de24abSDavid Gibson /* No execute protection violation */ 48225de24abSDavid Gibson env->exception_index = POWERPC_EXCP_ISI; 48325de24abSDavid Gibson env->error_code = 0x10000000; 48425de24abSDavid Gibson break; 48525de24abSDavid Gibson case -4: 48625de24abSDavid Gibson /* Direct store exception */ 48725de24abSDavid Gibson /* No code fetch is allowed in direct-store areas */ 48825de24abSDavid Gibson env->exception_index = POWERPC_EXCP_ISI; 48925de24abSDavid Gibson env->error_code = 0x10000000; 49025de24abSDavid Gibson break; 49125de24abSDavid Gibson } 49225de24abSDavid Gibson } else { 49325de24abSDavid Gibson switch (ret) { 49425de24abSDavid Gibson case -1: 49525de24abSDavid Gibson /* No matches in page tables or TLB */ 49625de24abSDavid Gibson env->exception_index = POWERPC_EXCP_DSI; 49725de24abSDavid Gibson env->error_code = 0; 49825de24abSDavid Gibson env->spr[SPR_DAR] = address; 49991cda45bSDavid Gibson if (rwx == 1) { 50025de24abSDavid Gibson env->spr[SPR_DSISR] = 0x42000000; 50125de24abSDavid Gibson } else { 50225de24abSDavid Gibson env->spr[SPR_DSISR] = 0x40000000; 50325de24abSDavid Gibson } 50425de24abSDavid Gibson break; 50525de24abSDavid Gibson case -2: 50625de24abSDavid Gibson /* Access rights violation */ 50725de24abSDavid Gibson env->exception_index = POWERPC_EXCP_DSI; 50825de24abSDavid Gibson env->error_code = 0; 50925de24abSDavid Gibson env->spr[SPR_DAR] = address; 51091cda45bSDavid Gibson if (rwx == 1) { 51125de24abSDavid Gibson env->spr[SPR_DSISR] = 0x0A000000; 51225de24abSDavid Gibson } else { 51325de24abSDavid Gibson env->spr[SPR_DSISR] = 0x08000000; 51425de24abSDavid Gibson } 51525de24abSDavid Gibson break; 51625de24abSDavid Gibson case -4: 51725de24abSDavid Gibson /* Direct store exception */ 51891cda45bSDavid Gibson switch (env->access_type) { 51925de24abSDavid Gibson case ACCESS_FLOAT: 52025de24abSDavid Gibson /* Floating point load/store */ 52125de24abSDavid Gibson env->exception_index = POWERPC_EXCP_ALIGN; 52225de24abSDavid Gibson env->error_code = POWERPC_EXCP_ALIGN_FP; 52325de24abSDavid Gibson env->spr[SPR_DAR] = address; 52425de24abSDavid Gibson break; 52525de24abSDavid Gibson case ACCESS_RES: 52625de24abSDavid Gibson /* lwarx, ldarx or stwcx. */ 52725de24abSDavid Gibson env->exception_index = POWERPC_EXCP_DSI; 52825de24abSDavid Gibson env->error_code = 0; 52925de24abSDavid Gibson env->spr[SPR_DAR] = address; 53091cda45bSDavid Gibson if (rwx == 1) { 53125de24abSDavid Gibson env->spr[SPR_DSISR] = 0x06000000; 53225de24abSDavid Gibson } else { 53325de24abSDavid Gibson env->spr[SPR_DSISR] = 0x04000000; 53425de24abSDavid Gibson } 53525de24abSDavid Gibson break; 53625de24abSDavid Gibson case ACCESS_EXT: 53725de24abSDavid Gibson /* eciwx or ecowx */ 53825de24abSDavid Gibson env->exception_index = POWERPC_EXCP_DSI; 53925de24abSDavid Gibson env->error_code = 0; 54025de24abSDavid Gibson env->spr[SPR_DAR] = address; 54191cda45bSDavid Gibson if (rwx == 1) { 54225de24abSDavid Gibson env->spr[SPR_DSISR] = 0x06100000; 54325de24abSDavid Gibson } else { 54425de24abSDavid Gibson env->spr[SPR_DSISR] = 0x04100000; 54525de24abSDavid Gibson } 54625de24abSDavid Gibson break; 54725de24abSDavid Gibson default: 54825de24abSDavid Gibson printf("DSI: invalid exception (%d)\n", ret); 54925de24abSDavid Gibson env->exception_index = POWERPC_EXCP_PROGRAM; 55025de24abSDavid Gibson env->error_code = 55125de24abSDavid Gibson POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; 55225de24abSDavid Gibson env->spr[SPR_DAR] = address; 55325de24abSDavid Gibson break; 55425de24abSDavid Gibson } 55525de24abSDavid Gibson break; 55625de24abSDavid Gibson } 55725de24abSDavid Gibson } 55825de24abSDavid Gibson #if 0 55925de24abSDavid Gibson printf("%s: set exception to %d %02x\n", __func__, 56025de24abSDavid Gibson env->exception, env->error_code); 56125de24abSDavid Gibson #endif 56225de24abSDavid Gibson ret = 1; 56325de24abSDavid Gibson } 56425de24abSDavid Gibson 56525de24abSDavid Gibson return ret; 56625de24abSDavid Gibson } 567