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 106bd039cdSChetan Pant * version 2.1 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 210d75590dSPeter Maydell #include "qemu/osdep.h" 229d7c3f4aSDavid Gibson #include "cpu.h" 2363c91552SPaolo Bonzini #include "exec/exec-all.h" 249d7c3f4aSDavid Gibson #include "sysemu/kvm.h" 259d7c3f4aSDavid Gibson #include "kvm_ppc.h" 26182357dbSRichard Henderson #include "internal.h" 279d7c3f4aSDavid Gibson #include "mmu-hash32.h" 28508127e2SPaolo Bonzini #include "exec/log.h" 299d7c3f4aSDavid Gibson 30596e3ca8SDavid Gibson /* #define DEBUG_BAT */ 319d7c3f4aSDavid Gibson 3298132796SDavid Gibson #ifdef DEBUG_BATS 3348880da6SPaolo Bonzini # define LOG_BATS(...) qemu_log_mask(CPU_LOG_MMU, __VA_ARGS__) 3498132796SDavid Gibson #else 3598132796SDavid Gibson # define LOG_BATS(...) do { } while (0) 3698132796SDavid Gibson #endif 3798132796SDavid Gibson 385dc68eb0SDavid Gibson struct mmu_ctx_hash32 { 395dc68eb0SDavid Gibson hwaddr raddr; /* Real address */ 405dc68eb0SDavid Gibson int prot; /* Protection bits */ 415dc68eb0SDavid Gibson int key; /* Access key */ 425dc68eb0SDavid Gibson }; 435dc68eb0SDavid Gibson 44e01b4445SDavid Gibson static int ppc_hash32_pp_prot(int key, int pp, int nx) 45496272a7SDavid Gibson { 46e01b4445SDavid Gibson int prot; 47496272a7SDavid Gibson 48496272a7SDavid Gibson if (key == 0) { 49496272a7SDavid Gibson switch (pp) { 50496272a7SDavid Gibson case 0x0: 51496272a7SDavid Gibson case 0x1: 52496272a7SDavid Gibson case 0x2: 53e01b4445SDavid Gibson prot = PAGE_READ | PAGE_WRITE; 54496272a7SDavid Gibson break; 55e01b4445SDavid Gibson 56e01b4445SDavid Gibson case 0x3: 57e01b4445SDavid Gibson prot = PAGE_READ; 58e01b4445SDavid Gibson break; 59e01b4445SDavid Gibson 60e01b4445SDavid Gibson default: 61e01b4445SDavid Gibson abort(); 62496272a7SDavid Gibson } 63496272a7SDavid Gibson } else { 64496272a7SDavid Gibson switch (pp) { 65496272a7SDavid Gibson case 0x0: 66e01b4445SDavid Gibson prot = 0; 67496272a7SDavid Gibson break; 68e01b4445SDavid Gibson 69496272a7SDavid Gibson case 0x1: 70496272a7SDavid Gibson case 0x3: 71e01b4445SDavid Gibson prot = PAGE_READ; 72496272a7SDavid Gibson break; 73e01b4445SDavid Gibson 74496272a7SDavid Gibson case 0x2: 75e01b4445SDavid Gibson prot = PAGE_READ | PAGE_WRITE; 76496272a7SDavid Gibson break; 77e01b4445SDavid Gibson 78e01b4445SDavid Gibson default: 79e01b4445SDavid Gibson abort(); 80496272a7SDavid Gibson } 81496272a7SDavid Gibson } 82496272a7SDavid Gibson if (nx == 0) { 83e01b4445SDavid Gibson prot |= PAGE_EXEC; 84496272a7SDavid Gibson } 85496272a7SDavid Gibson 86e01b4445SDavid Gibson return prot; 87496272a7SDavid Gibson } 88496272a7SDavid Gibson 897ef23068SDavid Gibson static int ppc_hash32_pte_prot(PowerPCCPU *cpu, 90e01b4445SDavid Gibson target_ulong sr, ppc_hash_pte32_t pte) 91496272a7SDavid Gibson { 927ef23068SDavid Gibson CPUPPCState *env = &cpu->env; 93e01b4445SDavid Gibson unsigned pp, key; 94496272a7SDavid Gibson 95e01b4445SDavid Gibson key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS)); 96e01b4445SDavid Gibson pp = pte.pte1 & HPTE32_R_PP; 97496272a7SDavid Gibson 98e01b4445SDavid Gibson return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX)); 99496272a7SDavid Gibson } 100496272a7SDavid Gibson 1017ef23068SDavid Gibson static target_ulong hash32_bat_size(PowerPCCPU *cpu, 1029986ed1eSDavid Gibson target_ulong batu, target_ulong batl) 10398132796SDavid Gibson { 1047ef23068SDavid Gibson CPUPPCState *env = &cpu->env; 1057ef23068SDavid Gibson 1066fc76aa9SDavid Gibson if ((msr_pr && !(batu & BATU32_VP)) 1076fc76aa9SDavid Gibson || (!msr_pr && !(batu & BATU32_VS))) { 1086fc76aa9SDavid Gibson return 0; 109e1d49515SDavid Gibson } 1106fc76aa9SDavid Gibson 1116fc76aa9SDavid Gibson return BATU32_BEPI & ~((batu & BATU32_BL) << 15); 112e1d49515SDavid Gibson } 113e1d49515SDavid Gibson 1147ef23068SDavid Gibson static int hash32_bat_prot(PowerPCCPU *cpu, 115e1d49515SDavid Gibson target_ulong batu, target_ulong batl) 116e1d49515SDavid Gibson { 117e1d49515SDavid Gibson int pp, prot; 118e1d49515SDavid Gibson 119e1d49515SDavid Gibson prot = 0; 1209986ed1eSDavid Gibson pp = batl & BATL32_PP; 12198132796SDavid Gibson if (pp != 0) { 12298132796SDavid Gibson prot = PAGE_READ | PAGE_EXEC; 12398132796SDavid Gibson if (pp == 0x2) { 12498132796SDavid Gibson prot |= PAGE_WRITE; 12598132796SDavid Gibson } 12698132796SDavid Gibson } 127e1d49515SDavid Gibson return prot; 12898132796SDavid Gibson } 12998132796SDavid Gibson 1307ef23068SDavid Gibson static target_ulong hash32_bat_601_size(PowerPCCPU *cpu, 1319986ed1eSDavid Gibson target_ulong batu, target_ulong batl) 13298132796SDavid Gibson { 1336fc76aa9SDavid Gibson if (!(batl & BATL32_601_V)) { 1346fc76aa9SDavid Gibson return 0; 1356fc76aa9SDavid Gibson } 13698132796SDavid Gibson 1376fc76aa9SDavid Gibson return BATU32_BEPI & ~((batl & BATL32_601_BL) << 17); 138e1d49515SDavid Gibson } 139e1d49515SDavid Gibson 1407ef23068SDavid Gibson static int hash32_bat_601_prot(PowerPCCPU *cpu, 141e1d49515SDavid Gibson target_ulong batu, target_ulong batl) 142e1d49515SDavid Gibson { 1437ef23068SDavid Gibson CPUPPCState *env = &cpu->env; 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 15531fa64ecSRichard Henderson static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, 15631fa64ecSRichard Henderson MMUAccessType access_type, int *prot) 15798132796SDavid Gibson { 1587ef23068SDavid Gibson CPUPPCState *env = &cpu->env; 1599986ed1eSDavid Gibson target_ulong *BATlt, *BATut; 16031fa64ecSRichard Henderson bool ifetch = access_type == MMU_INST_FETCH; 161145e52f3SDavid Gibson int i; 16298132796SDavid Gibson 16398132796SDavid Gibson LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__, 16431fa64ecSRichard Henderson ifetch ? 'I' : 'D', ea); 16531fa64ecSRichard Henderson if (ifetch) { 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)) { 1787ef23068SDavid Gibson mask = hash32_bat_601_size(cpu, batu, batl); 17998132796SDavid Gibson } else { 1807ef23068SDavid Gibson mask = hash32_bat_size(cpu, 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__, 18431fa64ecSRichard Henderson ifetch ? '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)) { 1907ef23068SDavid Gibson *prot = hash32_bat_601_prot(cpu, batu, batl); 191145e52f3SDavid Gibson } else { 1927ef23068SDavid Gibson *prot = hash32_bat_prot(cpu, 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", 21231fa64ecSRichard Henderson __func__, ifetch ? '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 2216c3c873cSRichard Henderson static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, 22231fa64ecSRichard Henderson target_ulong eaddr, 22331fa64ecSRichard Henderson MMUAccessType access_type, 2246c3c873cSRichard Henderson hwaddr *raddr, int *prot, 2256c3c873cSRichard Henderson bool guest_visible) 226723ed73aSDavid Gibson { 2277ef23068SDavid Gibson CPUState *cs = CPU(cpu); 2287ef23068SDavid Gibson CPUPPCState *env = &cpu->env; 229723ed73aSDavid Gibson int key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS)); 230723ed73aSDavid Gibson 231339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); 232723ed73aSDavid Gibson 233723ed73aSDavid Gibson if ((sr & 0x1FF00000) >> 20 == 0x07f) { 234596e3ca8SDavid Gibson /* 235596e3ca8SDavid Gibson * Memory-forced I/O controller interface access 236596e3ca8SDavid Gibson * 237596e3ca8SDavid Gibson * If T=1 and BUID=x'07F', the 601 performs a memory access 238723ed73aSDavid Gibson * to SR[28-31] LA[4-31], bypassing all protection mechanisms. 239723ed73aSDavid Gibson */ 240723ed73aSDavid Gibson *raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); 241723ed73aSDavid Gibson *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; 2426c3c873cSRichard Henderson return true; 243723ed73aSDavid Gibson } 244723ed73aSDavid Gibson 24531fa64ecSRichard Henderson if (access_type == MMU_INST_FETCH) { 246723ed73aSDavid Gibson /* No code fetch is allowed in direct-store areas */ 2476c3c873cSRichard Henderson if (guest_visible) { 24827103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ISI; 249caa597bdSDavid Gibson env->error_code = 0x10000000; 2506c3c873cSRichard Henderson } 2516c3c873cSRichard Henderson return false; 252723ed73aSDavid Gibson } 253723ed73aSDavid Gibson 2546c3c873cSRichard Henderson /* 2556c3c873cSRichard Henderson * From ppc_cpu_get_phys_page_debug, env->access_type is not set. 2566c3c873cSRichard Henderson * Assume ACCESS_INT for that case. 2576c3c873cSRichard Henderson */ 2586c3c873cSRichard Henderson switch (guest_visible ? env->access_type : ACCESS_INT) { 259723ed73aSDavid Gibson case ACCESS_INT: 260723ed73aSDavid Gibson /* Integer load/store : only access allowed */ 261723ed73aSDavid Gibson break; 262723ed73aSDavid Gibson case ACCESS_FLOAT: 263723ed73aSDavid Gibson /* Floating point load/store */ 26427103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ALIGN; 265caa597bdSDavid Gibson env->error_code = POWERPC_EXCP_ALIGN_FP; 266caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 2676c3c873cSRichard Henderson return false; 268723ed73aSDavid Gibson case ACCESS_RES: 269723ed73aSDavid Gibson /* lwarx, ldarx or srwcx. */ 270caa597bdSDavid Gibson env->error_code = 0; 271caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 27231fa64ecSRichard Henderson if (access_type == MMU_DATA_STORE) { 273caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x06000000; 274caa597bdSDavid Gibson } else { 275caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x04000000; 276caa597bdSDavid Gibson } 2776c3c873cSRichard Henderson return false; 278723ed73aSDavid Gibson case ACCESS_CACHE: 279596e3ca8SDavid Gibson /* 280596e3ca8SDavid Gibson * dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi 281596e3ca8SDavid Gibson * 282596e3ca8SDavid Gibson * Should make the instruction do no-op. As it already do 283596e3ca8SDavid Gibson * no-op, it's quite easy :-) 284723ed73aSDavid Gibson */ 285723ed73aSDavid Gibson *raddr = eaddr; 2866c3c873cSRichard Henderson return true; 287723ed73aSDavid Gibson case ACCESS_EXT: 288723ed73aSDavid Gibson /* eciwx or ecowx */ 28927103424SAndreas Färber cs->exception_index = POWERPC_EXCP_DSI; 290caa597bdSDavid Gibson env->error_code = 0; 291caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 29231fa64ecSRichard Henderson if (access_type == MMU_DATA_STORE) { 293caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x06100000; 294caa597bdSDavid Gibson } else { 295caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x04100000; 296caa597bdSDavid Gibson } 2976c3c873cSRichard Henderson return false; 298723ed73aSDavid Gibson default: 2996c3c873cSRichard Henderson cpu_abort(cs, "ERROR: insn should not need address translation\n"); 300723ed73aSDavid Gibson } 3016c3c873cSRichard Henderson 3026c3c873cSRichard Henderson *prot = key ? PAGE_READ | PAGE_WRITE : PAGE_READ; 3036c3c873cSRichard Henderson if (*prot & prot_for_access_type(access_type)) { 304723ed73aSDavid Gibson *raddr = eaddr; 3056c3c873cSRichard Henderson return true; 3066c3c873cSRichard Henderson } 3076c3c873cSRichard Henderson 3086c3c873cSRichard Henderson if (guest_visible) { 30927103424SAndreas Färber cs->exception_index = POWERPC_EXCP_DSI; 310caa597bdSDavid Gibson env->error_code = 0; 311caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 31231fa64ecSRichard Henderson if (access_type == MMU_DATA_STORE) { 313caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x0a000000; 314caa597bdSDavid Gibson } else { 315caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x08000000; 316caa597bdSDavid Gibson } 317723ed73aSDavid Gibson } 3186c3c873cSRichard Henderson return false; 319723ed73aSDavid Gibson } 320723ed73aSDavid Gibson 3217ef23068SDavid Gibson hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash) 32259191721SDavid Gibson { 32336778660SDavid Gibson target_ulong mask = ppc_hash32_hpt_mask(cpu); 3247ef23068SDavid Gibson 32536778660SDavid Gibson return (hash * HASH_PTEG_SIZE_32) & mask; 32659191721SDavid Gibson } 32759191721SDavid Gibson 3287ef23068SDavid Gibson static hwaddr ppc_hash32_pteg_search(PowerPCCPU *cpu, hwaddr pteg_off, 329aea390e4SDavid Gibson bool secondary, target_ulong ptem, 330aea390e4SDavid Gibson ppc_hash_pte32_t *pte) 331aea390e4SDavid Gibson { 332aea390e4SDavid Gibson hwaddr pte_offset = pteg_off; 333aea390e4SDavid Gibson target_ulong pte0, pte1; 334aea390e4SDavid Gibson int i; 335aea390e4SDavid Gibson 336aea390e4SDavid Gibson for (i = 0; i < HPTES_PER_GROUP; i++) { 3377ef23068SDavid Gibson pte0 = ppc_hash32_load_hpte0(cpu, pte_offset); 3383054b0caSBenjamin Herrenschmidt /* 3393054b0caSBenjamin Herrenschmidt * pte0 contains the valid bit and must be read before pte1, 3403054b0caSBenjamin Herrenschmidt * otherwise we might see an old pte1 with a new valid bit and 3413054b0caSBenjamin Herrenschmidt * thus an inconsistent hpte value 3423054b0caSBenjamin Herrenschmidt */ 3433054b0caSBenjamin Herrenschmidt smp_rmb(); 3447ef23068SDavid Gibson pte1 = ppc_hash32_load_hpte1(cpu, pte_offset); 345aea390e4SDavid Gibson 346aea390e4SDavid Gibson if ((pte0 & HPTE32_V_VALID) 347aea390e4SDavid Gibson && (secondary == !!(pte0 & HPTE32_V_SECONDARY)) 348aea390e4SDavid Gibson && HPTE32_V_COMPARE(pte0, ptem)) { 349aea390e4SDavid Gibson pte->pte0 = pte0; 350aea390e4SDavid Gibson pte->pte1 = pte1; 351aea390e4SDavid Gibson return pte_offset; 352aea390e4SDavid Gibson } 353aea390e4SDavid Gibson 354aea390e4SDavid Gibson pte_offset += HASH_PTE_SIZE_32; 355aea390e4SDavid Gibson } 356aea390e4SDavid Gibson 357aea390e4SDavid Gibson return -1; 358aea390e4SDavid Gibson } 359aea390e4SDavid Gibson 3606e8a65abSBenjamin Herrenschmidt static void ppc_hash32_set_r(PowerPCCPU *cpu, hwaddr pte_offset, uint32_t pte1) 3616e8a65abSBenjamin Herrenschmidt { 3626e8a65abSBenjamin Herrenschmidt target_ulong base = ppc_hash32_hpt_base(cpu); 3636e8a65abSBenjamin Herrenschmidt hwaddr offset = pte_offset + 6; 3646e8a65abSBenjamin Herrenschmidt 3656e8a65abSBenjamin Herrenschmidt /* The HW performs a non-atomic byte update */ 3666e8a65abSBenjamin Herrenschmidt stb_phys(CPU(cpu)->as, base + offset, ((pte1 >> 8) & 0xff) | 0x01); 3676e8a65abSBenjamin Herrenschmidt } 3686e8a65abSBenjamin Herrenschmidt 3696e8a65abSBenjamin Herrenschmidt static void ppc_hash32_set_c(PowerPCCPU *cpu, hwaddr pte_offset, uint64_t pte1) 3706e8a65abSBenjamin Herrenschmidt { 3716e8a65abSBenjamin Herrenschmidt target_ulong base = ppc_hash32_hpt_base(cpu); 3726e8a65abSBenjamin Herrenschmidt hwaddr offset = pte_offset + 7; 3736e8a65abSBenjamin Herrenschmidt 3746e8a65abSBenjamin Herrenschmidt /* The HW performs a non-atomic byte update */ 3756e8a65abSBenjamin Herrenschmidt stb_phys(CPU(cpu)->as, base + offset, (pte1 & 0xff) | 0x80); 3766e8a65abSBenjamin Herrenschmidt } 3776e8a65abSBenjamin Herrenschmidt 3787ef23068SDavid Gibson static hwaddr ppc_hash32_htab_lookup(PowerPCCPU *cpu, 3797f3bdc2dSDavid Gibson target_ulong sr, target_ulong eaddr, 3807f3bdc2dSDavid Gibson ppc_hash_pte32_t *pte) 381c69b6151SDavid Gibson { 382aea390e4SDavid Gibson hwaddr pteg_off, pte_offset; 383a1ff751aSDavid Gibson hwaddr hash; 384a1ff751aSDavid Gibson uint32_t vsid, pgidx, ptem; 385c69b6151SDavid Gibson 386a1ff751aSDavid Gibson vsid = sr & SR32_VSID; 387a1ff751aSDavid Gibson pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS; 388a1ff751aSDavid Gibson hash = vsid ^ pgidx; 389a1ff751aSDavid Gibson ptem = (vsid << 7) | (pgidx >> 10); 390a1ff751aSDavid Gibson 391a1ff751aSDavid Gibson /* Page address translation */ 392339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "htab_base " TARGET_FMT_plx 393339aaf5bSAntony Pavlov " htab_mask " TARGET_FMT_plx 394a1ff751aSDavid Gibson " hash " TARGET_FMT_plx "\n", 39536778660SDavid Gibson ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash); 396a1ff751aSDavid Gibson 397a1ff751aSDavid Gibson /* Primary PTEG lookup */ 398339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx 399a1ff751aSDavid Gibson " vsid=%" PRIx32 " ptem=%" PRIx32 400a1ff751aSDavid Gibson " hash=" TARGET_FMT_plx "\n", 40136778660SDavid Gibson ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), 40236778660SDavid Gibson vsid, ptem, hash); 4037ef23068SDavid Gibson pteg_off = get_pteg_offset32(cpu, hash); 4047ef23068SDavid Gibson pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 0, ptem, pte); 405a1ff751aSDavid Gibson if (pte_offset == -1) { 406a1ff751aSDavid Gibson /* Secondary PTEG lookup */ 407339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx 408a1ff751aSDavid Gibson " vsid=%" PRIx32 " api=%" PRIx32 40936778660SDavid Gibson " hash=" TARGET_FMT_plx "\n", ppc_hash32_hpt_base(cpu), 41036778660SDavid Gibson ppc_hash32_hpt_mask(cpu), vsid, ptem, ~hash); 4117ef23068SDavid Gibson pteg_off = get_pteg_offset32(cpu, ~hash); 4127ef23068SDavid Gibson pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 1, ptem, pte); 413a1ff751aSDavid Gibson } 414a1ff751aSDavid Gibson 4157f3bdc2dSDavid Gibson return pte_offset; 416c69b6151SDavid Gibson } 4170480884fSDavid Gibson 4186d11d998SDavid Gibson static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte, 4196d11d998SDavid Gibson target_ulong eaddr) 4206d11d998SDavid Gibson { 42175d5ec89SDavid Gibson hwaddr rpn = pte.pte1 & HPTE32_R_RPN; 4226d11d998SDavid Gibson hwaddr mask = ~TARGET_PAGE_MASK; 4236d11d998SDavid Gibson 4246d11d998SDavid Gibson return (rpn & ~mask) | (eaddr & mask); 4256d11d998SDavid Gibson } 4266d11d998SDavid Gibson 427*51806b54SRichard Henderson bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, 4286c3c873cSRichard Henderson hwaddr *raddrp, int *psizep, int *protp, 4296c3c873cSRichard Henderson bool guest_visible) 4300480884fSDavid Gibson { 431d0e39c5dSAndreas Färber CPUState *cs = CPU(cpu); 432d0e39c5dSAndreas Färber CPUPPCState *env = &cpu->env; 433a1ff751aSDavid Gibson target_ulong sr; 4347f3bdc2dSDavid Gibson hwaddr pte_offset; 4357f3bdc2dSDavid Gibson ppc_hash_pte32_t pte; 436caa597bdSDavid Gibson int prot; 437182357dbSRichard Henderson int need_prot; 438caa597bdSDavid Gibson hwaddr raddr; 4390480884fSDavid Gibson 4406c3c873cSRichard Henderson /* There are no hash32 large pages. */ 4416c3c873cSRichard Henderson *psizep = TARGET_PAGE_BITS; 4426a980110SDavid Gibson 44365d61643SDavid Gibson /* 1. Handle real mode accesses */ 44431fa64ecSRichard Henderson if (access_type == MMU_INST_FETCH ? !msr_ir : !msr_dr) { 44565d61643SDavid Gibson /* Translation is off */ 4466c3c873cSRichard Henderson *raddrp = eaddr; 4476c3c873cSRichard Henderson *protp = PAGE_READ | PAGE_WRITE | PAGE_EXEC; 4486c3c873cSRichard Henderson return true; 44965d61643SDavid Gibson } 45065d61643SDavid Gibson 4516c3c873cSRichard Henderson need_prot = prot_for_access_type(access_type); 4526c3c873cSRichard Henderson 45365d61643SDavid Gibson /* 2. Check Block Address Translation entries (BATs) */ 45465d61643SDavid Gibson if (env->nb_BATs != 0) { 4556c3c873cSRichard Henderson raddr = ppc_hash32_bat_lookup(cpu, eaddr, access_type, protp); 456caa597bdSDavid Gibson if (raddr != -1) { 4576c3c873cSRichard Henderson if (need_prot & ~*protp) { 4586c3c873cSRichard Henderson if (guest_visible) { 45931fa64ecSRichard Henderson if (access_type == MMU_INST_FETCH) { 46027103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ISI; 461caa597bdSDavid Gibson env->error_code = 0x08000000; 462caa597bdSDavid Gibson } else { 46327103424SAndreas Färber cs->exception_index = POWERPC_EXCP_DSI; 464caa597bdSDavid Gibson env->error_code = 0; 465caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 46631fa64ecSRichard Henderson if (access_type == MMU_DATA_STORE) { 467caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x0a000000; 468caa597bdSDavid Gibson } else { 469caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x08000000; 470e01b4445SDavid Gibson } 471caa597bdSDavid Gibson } 472caa597bdSDavid Gibson } 4736c3c873cSRichard Henderson return false; 4746c3c873cSRichard Henderson } 4756c3c873cSRichard Henderson *raddrp = raddr; 4766c3c873cSRichard Henderson return true; 47765d61643SDavid Gibson } 478145e52f3SDavid Gibson } 47965d61643SDavid Gibson 4804b9605a5SDavid Gibson /* 3. Look up the Segment Register */ 4810480884fSDavid Gibson sr = env->sr[eaddr >> 28]; 4824b9605a5SDavid Gibson 483723ed73aSDavid Gibson /* 4. Handle direct store segments */ 484723ed73aSDavid Gibson if (sr & SR32_T) { 4856c3c873cSRichard Henderson return ppc_hash32_direct_store(cpu, sr, eaddr, access_type, 4866c3c873cSRichard Henderson raddrp, protp, guest_visible); 487723ed73aSDavid Gibson } 488723ed73aSDavid Gibson 489bb218042SDavid Gibson /* 5. Check for segment level no-execute violation */ 49031fa64ecSRichard Henderson if (access_type == MMU_INST_FETCH && (sr & SR32_NX)) { 4916c3c873cSRichard Henderson if (guest_visible) { 49227103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ISI; 493caa597bdSDavid Gibson env->error_code = 0x10000000; 4946c3c873cSRichard Henderson } 4956c3c873cSRichard Henderson return false; 496bb218042SDavid Gibson } 4977f3bdc2dSDavid Gibson 4987f3bdc2dSDavid Gibson /* 6. Locate the PTE in the hash table */ 4997ef23068SDavid Gibson pte_offset = ppc_hash32_htab_lookup(cpu, sr, eaddr, &pte); 5007f3bdc2dSDavid Gibson if (pte_offset == -1) { 5016c3c873cSRichard Henderson if (guest_visible) { 50231fa64ecSRichard Henderson if (access_type == MMU_INST_FETCH) { 50327103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ISI; 504caa597bdSDavid Gibson env->error_code = 0x40000000; 505caa597bdSDavid Gibson } else { 50627103424SAndreas Färber cs->exception_index = POWERPC_EXCP_DSI; 507caa597bdSDavid Gibson env->error_code = 0; 508caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 50931fa64ecSRichard Henderson if (access_type == MMU_DATA_STORE) { 510caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x42000000; 511caa597bdSDavid Gibson } else { 512caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x40000000; 513caa597bdSDavid Gibson } 514caa597bdSDavid Gibson } 5156c3c873cSRichard Henderson } 5166c3c873cSRichard Henderson return false; 5177f3bdc2dSDavid Gibson } 518339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, 519339aaf5bSAntony Pavlov "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); 5207f3bdc2dSDavid Gibson 5217f3bdc2dSDavid Gibson /* 7. Check access permissions */ 5226a980110SDavid Gibson 5237ef23068SDavid Gibson prot = ppc_hash32_pte_prot(cpu, sr, pte); 5246a980110SDavid Gibson 525182357dbSRichard Henderson if (need_prot & ~prot) { 5266a980110SDavid Gibson /* Access right violation */ 527339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); 5286c3c873cSRichard Henderson if (guest_visible) { 52931fa64ecSRichard Henderson if (access_type == MMU_INST_FETCH) { 53027103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ISI; 531caa597bdSDavid Gibson env->error_code = 0x08000000; 532caa597bdSDavid Gibson } else { 53327103424SAndreas Färber cs->exception_index = POWERPC_EXCP_DSI; 534caa597bdSDavid Gibson env->error_code = 0; 535caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 53631fa64ecSRichard Henderson if (access_type == MMU_DATA_STORE) { 537caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x0a000000; 538caa597bdSDavid Gibson } else { 539caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x08000000; 540caa597bdSDavid Gibson } 541caa597bdSDavid Gibson } 5426c3c873cSRichard Henderson } 5436c3c873cSRichard Henderson return false; 5446a980110SDavid Gibson } 5456a980110SDavid Gibson 546339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); 54787dc3fd1SDavid Gibson 54887dc3fd1SDavid Gibson /* 8. Update PTE referenced and changed bits if necessary */ 54987dc3fd1SDavid Gibson 5506e8a65abSBenjamin Herrenschmidt if (!(pte.pte1 & HPTE32_R_R)) { 5516e8a65abSBenjamin Herrenschmidt ppc_hash32_set_r(cpu, pte_offset, pte.pte1); 5526e8a65abSBenjamin Herrenschmidt } 5536e8a65abSBenjamin Herrenschmidt if (!(pte.pte1 & HPTE32_R_C)) { 55431fa64ecSRichard Henderson if (access_type == MMU_DATA_STORE) { 5556e8a65abSBenjamin Herrenschmidt ppc_hash32_set_c(cpu, pte_offset, pte.pte1); 556b3440746SDavid Gibson } else { 557596e3ca8SDavid Gibson /* 558596e3ca8SDavid Gibson * Treat the page as read-only for now, so that a later write 559596e3ca8SDavid Gibson * will pass through this function again to set the C bit 560596e3ca8SDavid Gibson */ 561caa597bdSDavid Gibson prot &= ~PAGE_WRITE; 562b3440746SDavid Gibson } 5637f3bdc2dSDavid Gibson } 5640480884fSDavid Gibson 5656d11d998SDavid Gibson /* 9. Determine the real address from the PTE */ 5666d11d998SDavid Gibson 5676c3c873cSRichard Henderson *raddrp = ppc_hash32_pte_raddr(sr, pte, eaddr); 5686c3c873cSRichard Henderson *protp = prot; 5696c3c873cSRichard Henderson return true; 5706c3c873cSRichard Henderson } 571