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" 222ef6175aSRichard Henderson #include "exec/helper-proto.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 3177710e7aSAndreas Färber # define LOG_MMU_STATE(cpu) log_cpu_state((cpu), 0) 329d7c3f4aSDavid Gibson #else 3377710e7aSAndreas Färber # define LOG_MMU_STATE(cpu) do { } while (0) 349d7c3f4aSDavid Gibson #endif 359d7c3f4aSDavid Gibson 3698132796SDavid Gibson #ifdef DEBUG_BATS 3798132796SDavid Gibson # define LOG_BATS(...) qemu_log(__VA_ARGS__) 3898132796SDavid Gibson #else 3998132796SDavid Gibson # define LOG_BATS(...) do { } while (0) 4098132796SDavid Gibson #endif 4198132796SDavid Gibson 425dc68eb0SDavid Gibson struct mmu_ctx_hash32 { 435dc68eb0SDavid Gibson hwaddr raddr; /* Real address */ 445dc68eb0SDavid Gibson int prot; /* Protection bits */ 455dc68eb0SDavid Gibson int key; /* Access key */ 465dc68eb0SDavid Gibson }; 475dc68eb0SDavid Gibson 48e01b4445SDavid Gibson static int ppc_hash32_pp_prot(int key, int pp, int nx) 49496272a7SDavid Gibson { 50e01b4445SDavid Gibson int prot; 51496272a7SDavid Gibson 52496272a7SDavid Gibson if (key == 0) { 53496272a7SDavid Gibson switch (pp) { 54496272a7SDavid Gibson case 0x0: 55496272a7SDavid Gibson case 0x1: 56496272a7SDavid Gibson case 0x2: 57e01b4445SDavid Gibson prot = PAGE_READ | PAGE_WRITE; 58496272a7SDavid Gibson break; 59e01b4445SDavid Gibson 60e01b4445SDavid Gibson case 0x3: 61e01b4445SDavid Gibson prot = PAGE_READ; 62e01b4445SDavid Gibson break; 63e01b4445SDavid Gibson 64e01b4445SDavid Gibson default: 65e01b4445SDavid Gibson abort(); 66496272a7SDavid Gibson } 67496272a7SDavid Gibson } else { 68496272a7SDavid Gibson switch (pp) { 69496272a7SDavid Gibson case 0x0: 70e01b4445SDavid Gibson prot = 0; 71496272a7SDavid Gibson break; 72e01b4445SDavid Gibson 73496272a7SDavid Gibson case 0x1: 74496272a7SDavid Gibson case 0x3: 75e01b4445SDavid Gibson prot = PAGE_READ; 76496272a7SDavid Gibson break; 77e01b4445SDavid Gibson 78496272a7SDavid Gibson case 0x2: 79e01b4445SDavid Gibson prot = PAGE_READ | PAGE_WRITE; 80496272a7SDavid Gibson break; 81e01b4445SDavid Gibson 82e01b4445SDavid Gibson default: 83e01b4445SDavid Gibson abort(); 84496272a7SDavid Gibson } 85496272a7SDavid Gibson } 86496272a7SDavid Gibson if (nx == 0) { 87e01b4445SDavid Gibson prot |= PAGE_EXEC; 88496272a7SDavid Gibson } 89496272a7SDavid Gibson 90e01b4445SDavid Gibson return prot; 91496272a7SDavid Gibson } 92496272a7SDavid Gibson 93e01b4445SDavid Gibson static int ppc_hash32_pte_prot(CPUPPCState *env, 94e01b4445SDavid Gibson target_ulong sr, ppc_hash_pte32_t pte) 95496272a7SDavid Gibson { 96e01b4445SDavid Gibson unsigned pp, key; 97496272a7SDavid Gibson 98e01b4445SDavid Gibson key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS)); 99e01b4445SDavid Gibson pp = pte.pte1 & HPTE32_R_PP; 100496272a7SDavid Gibson 101e01b4445SDavid Gibson return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX)); 102496272a7SDavid Gibson } 103496272a7SDavid Gibson 1046fc76aa9SDavid Gibson static target_ulong hash32_bat_size(CPUPPCState *env, 1059986ed1eSDavid Gibson target_ulong batu, target_ulong batl) 10698132796SDavid Gibson { 1076fc76aa9SDavid Gibson if ((msr_pr && !(batu & BATU32_VP)) 1086fc76aa9SDavid Gibson || (!msr_pr && !(batu & BATU32_VS))) { 1096fc76aa9SDavid Gibson return 0; 110e1d49515SDavid Gibson } 1116fc76aa9SDavid Gibson 1126fc76aa9SDavid Gibson return BATU32_BEPI & ~((batu & BATU32_BL) << 15); 113e1d49515SDavid Gibson } 114e1d49515SDavid Gibson 115e1d49515SDavid Gibson static int hash32_bat_prot(CPUPPCState *env, 116e1d49515SDavid Gibson target_ulong batu, target_ulong batl) 117e1d49515SDavid Gibson { 118e1d49515SDavid Gibson int pp, prot; 119e1d49515SDavid Gibson 120e1d49515SDavid Gibson prot = 0; 1219986ed1eSDavid Gibson pp = batl & BATL32_PP; 12298132796SDavid Gibson if (pp != 0) { 12398132796SDavid Gibson prot = PAGE_READ | PAGE_EXEC; 12498132796SDavid Gibson if (pp == 0x2) { 12598132796SDavid Gibson prot |= PAGE_WRITE; 12698132796SDavid Gibson } 12798132796SDavid Gibson } 128e1d49515SDavid Gibson return prot; 12998132796SDavid Gibson } 13098132796SDavid Gibson 1316fc76aa9SDavid Gibson static target_ulong hash32_bat_601_size(CPUPPCState *env, 1329986ed1eSDavid Gibson target_ulong batu, target_ulong batl) 13398132796SDavid Gibson { 1346fc76aa9SDavid Gibson if (!(batl & BATL32_601_V)) { 1356fc76aa9SDavid Gibson return 0; 1366fc76aa9SDavid Gibson } 13798132796SDavid Gibson 1386fc76aa9SDavid Gibson return BATU32_BEPI & ~((batl & BATL32_601_BL) << 17); 139e1d49515SDavid Gibson } 140e1d49515SDavid Gibson 141e1d49515SDavid Gibson static int hash32_bat_601_prot(CPUPPCState *env, 142e1d49515SDavid Gibson target_ulong batu, target_ulong batl) 143e1d49515SDavid Gibson { 144e1d49515SDavid Gibson int key, pp; 145e1d49515SDavid Gibson 1469986ed1eSDavid Gibson pp = batu & BATU32_601_PP; 14798132796SDavid Gibson if (msr_pr == 0) { 1489986ed1eSDavid Gibson key = !!(batu & BATU32_601_KS); 14998132796SDavid Gibson } else { 1509986ed1eSDavid Gibson key = !!(batu & BATU32_601_KP); 15198132796SDavid Gibson } 152e01b4445SDavid Gibson return ppc_hash32_pp_prot(key, pp, 0); 15398132796SDavid Gibson } 15498132796SDavid Gibson 155145e52f3SDavid Gibson static hwaddr ppc_hash32_bat_lookup(CPUPPCState *env, target_ulong ea, int rwx, 156145e52f3SDavid Gibson int *prot) 15798132796SDavid Gibson { 1589986ed1eSDavid Gibson target_ulong *BATlt, *BATut; 159145e52f3SDavid Gibson int i; 16098132796SDavid Gibson 16198132796SDavid Gibson LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__, 162145e52f3SDavid Gibson rwx == 2 ? 'I' : 'D', ea); 16391cda45bSDavid Gibson if (rwx == 2) { 16498132796SDavid Gibson BATlt = env->IBAT[1]; 16598132796SDavid Gibson BATut = env->IBAT[0]; 16691cda45bSDavid Gibson } else { 16798132796SDavid Gibson BATlt = env->DBAT[1]; 16898132796SDavid Gibson BATut = env->DBAT[0]; 16998132796SDavid Gibson } 17098132796SDavid Gibson for (i = 0; i < env->nb_BATs; i++) { 1719986ed1eSDavid Gibson target_ulong batu = BATut[i]; 1729986ed1eSDavid Gibson target_ulong batl = BATlt[i]; 1736fc76aa9SDavid Gibson target_ulong mask; 1749986ed1eSDavid Gibson 17598132796SDavid Gibson if (unlikely(env->mmu_model == POWERPC_MMU_601)) { 1766fc76aa9SDavid Gibson mask = hash32_bat_601_size(env, batu, batl); 17798132796SDavid Gibson } else { 1786fc76aa9SDavid Gibson mask = hash32_bat_size(env, batu, batl); 17998132796SDavid Gibson } 18098132796SDavid Gibson LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx 18198132796SDavid Gibson " BATl " TARGET_FMT_lx "\n", __func__, 182145e52f3SDavid Gibson type == ACCESS_CODE ? 'I' : 'D', i, ea, batu, batl); 1836fc76aa9SDavid Gibson 184145e52f3SDavid Gibson if (mask && ((ea & mask) == (batu & BATU32_BEPI))) { 185145e52f3SDavid Gibson hwaddr raddr = (batl & mask) | (ea & ~mask); 186145e52f3SDavid Gibson 187145e52f3SDavid Gibson if (unlikely(env->mmu_model == POWERPC_MMU_601)) { 188145e52f3SDavid Gibson *prot = hash32_bat_601_prot(env, batu, batl); 189145e52f3SDavid Gibson } else { 190145e52f3SDavid Gibson *prot = hash32_bat_prot(env, batu, batl); 19198132796SDavid Gibson } 192145e52f3SDavid Gibson 193145e52f3SDavid Gibson return raddr & TARGET_PAGE_MASK; 19498132796SDavid Gibson } 19598132796SDavid Gibson } 196145e52f3SDavid Gibson 197145e52f3SDavid Gibson /* No hit */ 19898132796SDavid Gibson #if defined(DEBUG_BATS) 19998132796SDavid Gibson if (qemu_log_enabled()) { 200145e52f3SDavid Gibson LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", ea); 20198132796SDavid Gibson for (i = 0; i < 4; i++) { 20298132796SDavid Gibson BATu = &BATut[i]; 20398132796SDavid Gibson BATl = &BATlt[i]; 204d5aea6f3SDavid Gibson BEPIu = *BATu & BATU32_BEPIU; 205d5aea6f3SDavid Gibson BEPIl = *BATu & BATU32_BEPIL; 20698132796SDavid Gibson bl = (*BATu & 0x00001FFC) << 15; 20798132796SDavid Gibson LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx 20898132796SDavid Gibson " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " 20998132796SDavid Gibson TARGET_FMT_lx " " TARGET_FMT_lx "\n", 210145e52f3SDavid Gibson __func__, type == ACCESS_CODE ? 'I' : 'D', i, ea, 21198132796SDavid Gibson *BATu, *BATl, BEPIu, BEPIl, bl); 21298132796SDavid Gibson } 21398132796SDavid Gibson } 21498132796SDavid Gibson #endif 215145e52f3SDavid Gibson 216145e52f3SDavid Gibson return -1; 21798132796SDavid Gibson } 21898132796SDavid Gibson 219723ed73aSDavid Gibson static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr, 220723ed73aSDavid Gibson target_ulong eaddr, int rwx, 221723ed73aSDavid Gibson hwaddr *raddr, int *prot) 222723ed73aSDavid Gibson { 22327103424SAndreas Färber CPUState *cs = CPU(ppc_env_get_cpu(env)); 224723ed73aSDavid Gibson int key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS)); 225723ed73aSDavid Gibson 226339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); 227723ed73aSDavid Gibson 228723ed73aSDavid Gibson if ((sr & 0x1FF00000) >> 20 == 0x07f) { 229723ed73aSDavid Gibson /* Memory-forced I/O controller interface access */ 230723ed73aSDavid Gibson /* If T=1 and BUID=x'07F', the 601 performs a memory access 231723ed73aSDavid Gibson * to SR[28-31] LA[4-31], bypassing all protection mechanisms. 232723ed73aSDavid Gibson */ 233723ed73aSDavid Gibson *raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); 234723ed73aSDavid Gibson *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; 235723ed73aSDavid Gibson return 0; 236723ed73aSDavid Gibson } 237723ed73aSDavid Gibson 238723ed73aSDavid Gibson if (rwx == 2) { 239723ed73aSDavid Gibson /* No code fetch is allowed in direct-store areas */ 24027103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ISI; 241caa597bdSDavid Gibson env->error_code = 0x10000000; 242caa597bdSDavid Gibson return 1; 243723ed73aSDavid Gibson } 244723ed73aSDavid Gibson 245723ed73aSDavid Gibson switch (env->access_type) { 246723ed73aSDavid Gibson case ACCESS_INT: 247723ed73aSDavid Gibson /* Integer load/store : only access allowed */ 248723ed73aSDavid Gibson break; 249723ed73aSDavid Gibson case ACCESS_FLOAT: 250723ed73aSDavid Gibson /* Floating point load/store */ 25127103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ALIGN; 252caa597bdSDavid Gibson env->error_code = POWERPC_EXCP_ALIGN_FP; 253caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 254caa597bdSDavid Gibson return 1; 255723ed73aSDavid Gibson case ACCESS_RES: 256723ed73aSDavid Gibson /* lwarx, ldarx or srwcx. */ 257caa597bdSDavid Gibson env->error_code = 0; 258caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 259caa597bdSDavid Gibson if (rwx == 1) { 260caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x06000000; 261caa597bdSDavid Gibson } else { 262caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x04000000; 263caa597bdSDavid Gibson } 264caa597bdSDavid Gibson return 1; 265723ed73aSDavid Gibson case ACCESS_CACHE: 266723ed73aSDavid Gibson /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */ 267723ed73aSDavid Gibson /* Should make the instruction do no-op. 268723ed73aSDavid Gibson * As it already do no-op, it's quite easy :-) 269723ed73aSDavid Gibson */ 270723ed73aSDavid Gibson *raddr = eaddr; 271723ed73aSDavid Gibson return 0; 272723ed73aSDavid Gibson case ACCESS_EXT: 273723ed73aSDavid Gibson /* eciwx or ecowx */ 27427103424SAndreas Färber cs->exception_index = POWERPC_EXCP_DSI; 275caa597bdSDavid Gibson env->error_code = 0; 276caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 277caa597bdSDavid Gibson if (rwx == 1) { 278caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x06100000; 279caa597bdSDavid Gibson } else { 280caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x04100000; 281caa597bdSDavid Gibson } 282caa597bdSDavid Gibson return 1; 283723ed73aSDavid Gibson default: 284723ed73aSDavid Gibson qemu_log("ERROR: instruction should not need " 285723ed73aSDavid Gibson "address translation\n"); 286caa597bdSDavid Gibson abort(); 287723ed73aSDavid Gibson } 288723ed73aSDavid Gibson if ((rwx == 1 || key != 1) && (rwx == 0 || key != 0)) { 289723ed73aSDavid Gibson *raddr = eaddr; 290caa597bdSDavid Gibson return 0; 291723ed73aSDavid Gibson } else { 29227103424SAndreas Färber cs->exception_index = POWERPC_EXCP_DSI; 293caa597bdSDavid Gibson env->error_code = 0; 294caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 295caa597bdSDavid Gibson if (rwx == 1) { 296caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x0a000000; 297caa597bdSDavid Gibson } else { 298caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x08000000; 299caa597bdSDavid Gibson } 300caa597bdSDavid Gibson return 1; 301723ed73aSDavid Gibson } 302723ed73aSDavid Gibson } 303723ed73aSDavid Gibson 30459191721SDavid Gibson hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash) 30559191721SDavid Gibson { 306d5aea6f3SDavid Gibson return (hash * HASH_PTEG_SIZE_32) & env->htab_mask; 30759191721SDavid Gibson } 30859191721SDavid Gibson 309aea390e4SDavid Gibson static hwaddr ppc_hash32_pteg_search(CPUPPCState *env, hwaddr pteg_off, 310aea390e4SDavid Gibson bool secondary, target_ulong ptem, 311aea390e4SDavid Gibson ppc_hash_pte32_t *pte) 312aea390e4SDavid Gibson { 313aea390e4SDavid Gibson hwaddr pte_offset = pteg_off; 314aea390e4SDavid Gibson target_ulong pte0, pte1; 315aea390e4SDavid Gibson int i; 316aea390e4SDavid Gibson 317aea390e4SDavid Gibson for (i = 0; i < HPTES_PER_GROUP; i++) { 318aea390e4SDavid Gibson pte0 = ppc_hash32_load_hpte0(env, pte_offset); 319aea390e4SDavid Gibson pte1 = ppc_hash32_load_hpte1(env, pte_offset); 320aea390e4SDavid Gibson 321aea390e4SDavid Gibson if ((pte0 & HPTE32_V_VALID) 322aea390e4SDavid Gibson && (secondary == !!(pte0 & HPTE32_V_SECONDARY)) 323aea390e4SDavid Gibson && HPTE32_V_COMPARE(pte0, ptem)) { 324aea390e4SDavid Gibson pte->pte0 = pte0; 325aea390e4SDavid Gibson pte->pte1 = pte1; 326aea390e4SDavid Gibson return pte_offset; 327aea390e4SDavid Gibson } 328aea390e4SDavid Gibson 329aea390e4SDavid Gibson pte_offset += HASH_PTE_SIZE_32; 330aea390e4SDavid Gibson } 331aea390e4SDavid Gibson 332aea390e4SDavid Gibson return -1; 333aea390e4SDavid Gibson } 334aea390e4SDavid Gibson 3357f3bdc2dSDavid Gibson static hwaddr ppc_hash32_htab_lookup(CPUPPCState *env, 3367f3bdc2dSDavid Gibson target_ulong sr, target_ulong eaddr, 3377f3bdc2dSDavid Gibson ppc_hash_pte32_t *pte) 338c69b6151SDavid Gibson { 339aea390e4SDavid Gibson hwaddr pteg_off, pte_offset; 340a1ff751aSDavid Gibson hwaddr hash; 341a1ff751aSDavid Gibson uint32_t vsid, pgidx, ptem; 342c69b6151SDavid Gibson 343a1ff751aSDavid Gibson vsid = sr & SR32_VSID; 344a1ff751aSDavid Gibson pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS; 345a1ff751aSDavid Gibson hash = vsid ^ pgidx; 346a1ff751aSDavid Gibson ptem = (vsid << 7) | (pgidx >> 10); 347a1ff751aSDavid Gibson 348a1ff751aSDavid Gibson /* Page address translation */ 349339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "htab_base " TARGET_FMT_plx 350339aaf5bSAntony Pavlov " htab_mask " TARGET_FMT_plx 351a1ff751aSDavid Gibson " hash " TARGET_FMT_plx "\n", 352a1ff751aSDavid Gibson env->htab_base, env->htab_mask, hash); 353a1ff751aSDavid Gibson 354a1ff751aSDavid Gibson /* Primary PTEG lookup */ 355339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx 356a1ff751aSDavid Gibson " vsid=%" PRIx32 " ptem=%" PRIx32 357a1ff751aSDavid Gibson " hash=" TARGET_FMT_plx "\n", 358a1ff751aSDavid Gibson env->htab_base, env->htab_mask, vsid, ptem, hash); 359a1ff751aSDavid Gibson pteg_off = get_pteg_offset32(env, hash); 3607f3bdc2dSDavid Gibson pte_offset = ppc_hash32_pteg_search(env, pteg_off, 0, ptem, pte); 361a1ff751aSDavid Gibson if (pte_offset == -1) { 362a1ff751aSDavid Gibson /* Secondary PTEG lookup */ 363339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx 364a1ff751aSDavid Gibson " vsid=%" PRIx32 " api=%" PRIx32 365a1ff751aSDavid Gibson " hash=" TARGET_FMT_plx "\n", env->htab_base, 366a1ff751aSDavid Gibson env->htab_mask, vsid, ptem, ~hash); 367a1ff751aSDavid Gibson pteg_off = get_pteg_offset32(env, ~hash); 3687f3bdc2dSDavid Gibson pte_offset = ppc_hash32_pteg_search(env, pteg_off, 1, ptem, pte); 369a1ff751aSDavid Gibson } 370a1ff751aSDavid Gibson 3717f3bdc2dSDavid Gibson return pte_offset; 372c69b6151SDavid Gibson } 3730480884fSDavid Gibson 3746d11d998SDavid Gibson static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte, 3756d11d998SDavid Gibson target_ulong eaddr) 3766d11d998SDavid Gibson { 37775d5ec89SDavid Gibson hwaddr rpn = pte.pte1 & HPTE32_R_RPN; 3786d11d998SDavid Gibson hwaddr mask = ~TARGET_PAGE_MASK; 3796d11d998SDavid Gibson 3806d11d998SDavid Gibson return (rpn & ~mask) | (eaddr & mask); 3816d11d998SDavid Gibson } 3826d11d998SDavid Gibson 383d0e39c5dSAndreas Färber int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, int rwx, 384caa597bdSDavid Gibson int mmu_idx) 3850480884fSDavid Gibson { 386d0e39c5dSAndreas Färber CPUState *cs = CPU(cpu); 387d0e39c5dSAndreas Färber CPUPPCState *env = &cpu->env; 388a1ff751aSDavid Gibson target_ulong sr; 3897f3bdc2dSDavid Gibson hwaddr pte_offset; 3907f3bdc2dSDavid Gibson ppc_hash_pte32_t pte; 391caa597bdSDavid Gibson int prot; 392b3440746SDavid Gibson uint32_t new_pte1; 393e01b4445SDavid Gibson const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; 394caa597bdSDavid Gibson hwaddr raddr; 3950480884fSDavid Gibson 3966a980110SDavid Gibson assert((rwx == 0) || (rwx == 1) || (rwx == 2)); 3976a980110SDavid Gibson 39865d61643SDavid Gibson /* 1. Handle real mode accesses */ 39965d61643SDavid Gibson if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { 40065d61643SDavid Gibson /* Translation is off */ 401caa597bdSDavid Gibson raddr = eaddr; 4020c591eb0SAndreas Färber tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, 403caa597bdSDavid Gibson PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, 404caa597bdSDavid Gibson TARGET_PAGE_SIZE); 40565d61643SDavid Gibson return 0; 40665d61643SDavid Gibson } 40765d61643SDavid Gibson 40865d61643SDavid Gibson /* 2. Check Block Address Translation entries (BATs) */ 40965d61643SDavid Gibson if (env->nb_BATs != 0) { 410caa597bdSDavid Gibson raddr = ppc_hash32_bat_lookup(env, eaddr, rwx, &prot); 411caa597bdSDavid Gibson if (raddr != -1) { 412caa597bdSDavid Gibson if (need_prot[rwx] & ~prot) { 413caa597bdSDavid Gibson if (rwx == 2) { 41427103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ISI; 415caa597bdSDavid Gibson env->error_code = 0x08000000; 416caa597bdSDavid Gibson } else { 41727103424SAndreas Färber cs->exception_index = POWERPC_EXCP_DSI; 418caa597bdSDavid Gibson env->error_code = 0; 419caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 420caa597bdSDavid Gibson if (rwx == 1) { 421caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x0a000000; 422caa597bdSDavid Gibson } else { 423caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x08000000; 424e01b4445SDavid Gibson } 425caa597bdSDavid Gibson } 426caa597bdSDavid Gibson return 1; 427caa597bdSDavid Gibson } 428caa597bdSDavid Gibson 4290c591eb0SAndreas Färber tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, 430caa597bdSDavid Gibson raddr & TARGET_PAGE_MASK, prot, mmu_idx, 431caa597bdSDavid Gibson TARGET_PAGE_SIZE); 432e01b4445SDavid Gibson return 0; 43365d61643SDavid Gibson } 434145e52f3SDavid Gibson } 43565d61643SDavid Gibson 4364b9605a5SDavid Gibson /* 3. Look up the Segment Register */ 4370480884fSDavid Gibson sr = env->sr[eaddr >> 28]; 4384b9605a5SDavid Gibson 439723ed73aSDavid Gibson /* 4. Handle direct store segments */ 440723ed73aSDavid Gibson if (sr & SR32_T) { 441caa597bdSDavid Gibson if (ppc_hash32_direct_store(env, sr, eaddr, rwx, 442caa597bdSDavid Gibson &raddr, &prot) == 0) { 4430c591eb0SAndreas Färber tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, 444caa597bdSDavid Gibson raddr & TARGET_PAGE_MASK, prot, mmu_idx, 445caa597bdSDavid Gibson TARGET_PAGE_SIZE); 446caa597bdSDavid Gibson return 0; 447caa597bdSDavid Gibson } else { 448caa597bdSDavid Gibson return 1; 449caa597bdSDavid Gibson } 450723ed73aSDavid Gibson } 451723ed73aSDavid Gibson 452bb218042SDavid Gibson /* 5. Check for segment level no-execute violation */ 453e01b4445SDavid Gibson if ((rwx == 2) && (sr & SR32_NX)) { 45427103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ISI; 455caa597bdSDavid Gibson env->error_code = 0x10000000; 456caa597bdSDavid Gibson return 1; 457bb218042SDavid Gibson } 4587f3bdc2dSDavid Gibson 4597f3bdc2dSDavid Gibson /* 6. Locate the PTE in the hash table */ 4607f3bdc2dSDavid Gibson pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte); 4617f3bdc2dSDavid Gibson if (pte_offset == -1) { 462caa597bdSDavid Gibson if (rwx == 2) { 46327103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ISI; 464caa597bdSDavid Gibson env->error_code = 0x40000000; 465caa597bdSDavid Gibson } else { 46627103424SAndreas Färber cs->exception_index = POWERPC_EXCP_DSI; 467caa597bdSDavid Gibson env->error_code = 0; 468caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 469caa597bdSDavid Gibson if (rwx == 1) { 470caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x42000000; 471caa597bdSDavid Gibson } else { 472caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x40000000; 473caa597bdSDavid Gibson } 474caa597bdSDavid Gibson } 475caa597bdSDavid Gibson 476caa597bdSDavid Gibson return 1; 4777f3bdc2dSDavid Gibson } 478339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, 479339aaf5bSAntony Pavlov "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); 4807f3bdc2dSDavid Gibson 4817f3bdc2dSDavid Gibson /* 7. Check access permissions */ 4826a980110SDavid Gibson 483caa597bdSDavid Gibson prot = ppc_hash32_pte_prot(env, sr, pte); 4846a980110SDavid Gibson 485caa597bdSDavid Gibson if (need_prot[rwx] & ~prot) { 4866a980110SDavid Gibson /* Access right violation */ 487339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); 488caa597bdSDavid Gibson if (rwx == 2) { 48927103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ISI; 490caa597bdSDavid Gibson env->error_code = 0x08000000; 491caa597bdSDavid Gibson } else { 49227103424SAndreas Färber cs->exception_index = POWERPC_EXCP_DSI; 493caa597bdSDavid Gibson env->error_code = 0; 494caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 495caa597bdSDavid Gibson if (rwx == 1) { 496caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x0a000000; 497caa597bdSDavid Gibson } else { 498caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x08000000; 499caa597bdSDavid Gibson } 500caa597bdSDavid Gibson } 501caa597bdSDavid Gibson return 1; 5026a980110SDavid Gibson } 5036a980110SDavid Gibson 504339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); 50587dc3fd1SDavid Gibson 50687dc3fd1SDavid Gibson /* 8. Update PTE referenced and changed bits if necessary */ 50787dc3fd1SDavid Gibson 508b3440746SDavid Gibson new_pte1 = pte.pte1 | HPTE32_R_R; /* set referenced bit */ 509b3440746SDavid Gibson if (rwx == 1) { 510b3440746SDavid Gibson new_pte1 |= HPTE32_R_C; /* set changed (dirty) bit */ 511b3440746SDavid Gibson } else { 512b3440746SDavid Gibson /* Treat the page as read-only for now, so that a later write 513b3440746SDavid Gibson * will pass through this function again to set the C bit */ 514caa597bdSDavid Gibson prot &= ~PAGE_WRITE; 515b3440746SDavid Gibson } 516b3440746SDavid Gibson 517b3440746SDavid Gibson if (new_pte1 != pte.pte1) { 518b3440746SDavid Gibson ppc_hash32_store_hpte1(env, pte_offset, new_pte1); 5197f3bdc2dSDavid Gibson } 5200480884fSDavid Gibson 5216d11d998SDavid Gibson /* 9. Determine the real address from the PTE */ 5226d11d998SDavid Gibson 523caa597bdSDavid Gibson raddr = ppc_hash32_pte_raddr(sr, pte, eaddr); 524caa597bdSDavid Gibson 5250c591eb0SAndreas Färber tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, 526caa597bdSDavid Gibson prot, mmu_idx, TARGET_PAGE_SIZE); 527e01b4445SDavid Gibson 528e01b4445SDavid Gibson return 0; 5290480884fSDavid Gibson } 530629bd516SDavid Gibson 5315883d8b2SDavid Gibson hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong eaddr) 532f2ad6be8SDavid Gibson { 5335883d8b2SDavid Gibson target_ulong sr; 5345883d8b2SDavid Gibson hwaddr pte_offset; 5355883d8b2SDavid Gibson ppc_hash_pte32_t pte; 5365883d8b2SDavid Gibson int prot; 537f2ad6be8SDavid Gibson 5385883d8b2SDavid Gibson if (msr_dr == 0) { 5395883d8b2SDavid Gibson /* Translation is off */ 5405883d8b2SDavid Gibson return eaddr; 5415883d8b2SDavid Gibson } 5425883d8b2SDavid Gibson 5435883d8b2SDavid Gibson if (env->nb_BATs != 0) { 5445883d8b2SDavid Gibson hwaddr raddr = ppc_hash32_bat_lookup(env, eaddr, 0, &prot); 5455883d8b2SDavid Gibson if (raddr != -1) { 5465883d8b2SDavid Gibson return raddr; 5475883d8b2SDavid Gibson } 5485883d8b2SDavid Gibson } 5495883d8b2SDavid Gibson 5505883d8b2SDavid Gibson sr = env->sr[eaddr >> 28]; 5515883d8b2SDavid Gibson 5525883d8b2SDavid Gibson if (sr & SR32_T) { 5535883d8b2SDavid Gibson /* FIXME: Add suitable debug support for Direct Store segments */ 554f2ad6be8SDavid Gibson return -1; 555f2ad6be8SDavid Gibson } 556f2ad6be8SDavid Gibson 5575883d8b2SDavid Gibson pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte); 5585883d8b2SDavid Gibson if (pte_offset == -1) { 5595883d8b2SDavid Gibson return -1; 5605883d8b2SDavid Gibson } 5615883d8b2SDavid Gibson 5625883d8b2SDavid Gibson return ppc_hash32_pte_raddr(sr, pte, eaddr) & TARGET_PAGE_MASK; 563f2ad6be8SDavid Gibson } 564