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 30*ba1b5df0SFabiano Rosas /* #define DEBUG_BATS */ 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()) { 202*ba1b5df0SFabiano Rosas target_ulong *BATu, *BATl; 203*ba1b5df0SFabiano Rosas target_ulong BEPIl, BEPIu, bl; 204*ba1b5df0SFabiano Rosas 205145e52f3SDavid Gibson LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", ea); 20698132796SDavid Gibson for (i = 0; i < 4; i++) { 20798132796SDavid Gibson BATu = &BATut[i]; 20898132796SDavid Gibson BATl = &BATlt[i]; 209d5aea6f3SDavid Gibson BEPIu = *BATu & BATU32_BEPIU; 210d5aea6f3SDavid Gibson BEPIl = *BATu & BATU32_BEPIL; 21198132796SDavid Gibson bl = (*BATu & 0x00001FFC) << 15; 21298132796SDavid Gibson LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx 21398132796SDavid Gibson " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " 21498132796SDavid Gibson TARGET_FMT_lx " " TARGET_FMT_lx "\n", 21531fa64ecSRichard Henderson __func__, ifetch ? 'I' : 'D', i, ea, 21698132796SDavid Gibson *BATu, *BATl, BEPIu, BEPIl, bl); 21798132796SDavid Gibson } 21898132796SDavid Gibson } 21998132796SDavid Gibson #endif 220145e52f3SDavid Gibson 221145e52f3SDavid Gibson return -1; 22298132796SDavid Gibson } 22398132796SDavid Gibson 2246c3c873cSRichard Henderson static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, 22531fa64ecSRichard Henderson target_ulong eaddr, 22631fa64ecSRichard Henderson MMUAccessType access_type, 2276c3c873cSRichard Henderson hwaddr *raddr, int *prot, 2286c3c873cSRichard Henderson bool guest_visible) 229723ed73aSDavid Gibson { 2307ef23068SDavid Gibson CPUState *cs = CPU(cpu); 2317ef23068SDavid Gibson CPUPPCState *env = &cpu->env; 232723ed73aSDavid Gibson int key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS)); 233723ed73aSDavid Gibson 234339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); 235723ed73aSDavid Gibson 236723ed73aSDavid Gibson if ((sr & 0x1FF00000) >> 20 == 0x07f) { 237596e3ca8SDavid Gibson /* 238596e3ca8SDavid Gibson * Memory-forced I/O controller interface access 239596e3ca8SDavid Gibson * 240596e3ca8SDavid Gibson * If T=1 and BUID=x'07F', the 601 performs a memory access 241723ed73aSDavid Gibson * to SR[28-31] LA[4-31], bypassing all protection mechanisms. 242723ed73aSDavid Gibson */ 243723ed73aSDavid Gibson *raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); 244723ed73aSDavid Gibson *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; 2456c3c873cSRichard Henderson return true; 246723ed73aSDavid Gibson } 247723ed73aSDavid Gibson 24831fa64ecSRichard Henderson if (access_type == MMU_INST_FETCH) { 249723ed73aSDavid Gibson /* No code fetch is allowed in direct-store areas */ 2506c3c873cSRichard Henderson if (guest_visible) { 25127103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ISI; 252caa597bdSDavid Gibson env->error_code = 0x10000000; 2536c3c873cSRichard Henderson } 2546c3c873cSRichard Henderson return false; 255723ed73aSDavid Gibson } 256723ed73aSDavid Gibson 2576c3c873cSRichard Henderson /* 2586c3c873cSRichard Henderson * From ppc_cpu_get_phys_page_debug, env->access_type is not set. 2596c3c873cSRichard Henderson * Assume ACCESS_INT for that case. 2606c3c873cSRichard Henderson */ 2616c3c873cSRichard Henderson switch (guest_visible ? env->access_type : ACCESS_INT) { 262723ed73aSDavid Gibson case ACCESS_INT: 263723ed73aSDavid Gibson /* Integer load/store : only access allowed */ 264723ed73aSDavid Gibson break; 265723ed73aSDavid Gibson case ACCESS_FLOAT: 266723ed73aSDavid Gibson /* Floating point load/store */ 26727103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ALIGN; 268caa597bdSDavid Gibson env->error_code = POWERPC_EXCP_ALIGN_FP; 269caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 2706c3c873cSRichard Henderson return false; 271723ed73aSDavid Gibson case ACCESS_RES: 272723ed73aSDavid Gibson /* lwarx, ldarx or srwcx. */ 273caa597bdSDavid Gibson env->error_code = 0; 274caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 27531fa64ecSRichard Henderson if (access_type == MMU_DATA_STORE) { 276caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x06000000; 277caa597bdSDavid Gibson } else { 278caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x04000000; 279caa597bdSDavid Gibson } 2806c3c873cSRichard Henderson return false; 281723ed73aSDavid Gibson case ACCESS_CACHE: 282596e3ca8SDavid Gibson /* 283596e3ca8SDavid Gibson * dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi 284596e3ca8SDavid Gibson * 285596e3ca8SDavid Gibson * Should make the instruction do no-op. As it already do 286596e3ca8SDavid Gibson * no-op, it's quite easy :-) 287723ed73aSDavid Gibson */ 288723ed73aSDavid Gibson *raddr = eaddr; 2896c3c873cSRichard Henderson return true; 290723ed73aSDavid Gibson case ACCESS_EXT: 291723ed73aSDavid Gibson /* eciwx or ecowx */ 29227103424SAndreas Färber cs->exception_index = POWERPC_EXCP_DSI; 293caa597bdSDavid Gibson env->error_code = 0; 294caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 29531fa64ecSRichard Henderson if (access_type == MMU_DATA_STORE) { 296caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x06100000; 297caa597bdSDavid Gibson } else { 298caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x04100000; 299caa597bdSDavid Gibson } 3006c3c873cSRichard Henderson return false; 301723ed73aSDavid Gibson default: 3026c3c873cSRichard Henderson cpu_abort(cs, "ERROR: insn should not need address translation\n"); 303723ed73aSDavid Gibson } 3046c3c873cSRichard Henderson 3056c3c873cSRichard Henderson *prot = key ? PAGE_READ | PAGE_WRITE : PAGE_READ; 3066c3c873cSRichard Henderson if (*prot & prot_for_access_type(access_type)) { 307723ed73aSDavid Gibson *raddr = eaddr; 3086c3c873cSRichard Henderson return true; 3096c3c873cSRichard Henderson } 3106c3c873cSRichard Henderson 3116c3c873cSRichard Henderson if (guest_visible) { 31227103424SAndreas Färber cs->exception_index = POWERPC_EXCP_DSI; 313caa597bdSDavid Gibson env->error_code = 0; 314caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 31531fa64ecSRichard Henderson if (access_type == MMU_DATA_STORE) { 316caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x0a000000; 317caa597bdSDavid Gibson } else { 318caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x08000000; 319caa597bdSDavid Gibson } 320723ed73aSDavid Gibson } 3216c3c873cSRichard Henderson return false; 322723ed73aSDavid Gibson } 323723ed73aSDavid Gibson 3247ef23068SDavid Gibson hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash) 32559191721SDavid Gibson { 32636778660SDavid Gibson target_ulong mask = ppc_hash32_hpt_mask(cpu); 3277ef23068SDavid Gibson 32836778660SDavid Gibson return (hash * HASH_PTEG_SIZE_32) & mask; 32959191721SDavid Gibson } 33059191721SDavid Gibson 3317ef23068SDavid Gibson static hwaddr ppc_hash32_pteg_search(PowerPCCPU *cpu, hwaddr pteg_off, 332aea390e4SDavid Gibson bool secondary, target_ulong ptem, 333aea390e4SDavid Gibson ppc_hash_pte32_t *pte) 334aea390e4SDavid Gibson { 335aea390e4SDavid Gibson hwaddr pte_offset = pteg_off; 336aea390e4SDavid Gibson target_ulong pte0, pte1; 337aea390e4SDavid Gibson int i; 338aea390e4SDavid Gibson 339aea390e4SDavid Gibson for (i = 0; i < HPTES_PER_GROUP; i++) { 3407ef23068SDavid Gibson pte0 = ppc_hash32_load_hpte0(cpu, pte_offset); 3413054b0caSBenjamin Herrenschmidt /* 3423054b0caSBenjamin Herrenschmidt * pte0 contains the valid bit and must be read before pte1, 3433054b0caSBenjamin Herrenschmidt * otherwise we might see an old pte1 with a new valid bit and 3443054b0caSBenjamin Herrenschmidt * thus an inconsistent hpte value 3453054b0caSBenjamin Herrenschmidt */ 3463054b0caSBenjamin Herrenschmidt smp_rmb(); 3477ef23068SDavid Gibson pte1 = ppc_hash32_load_hpte1(cpu, pte_offset); 348aea390e4SDavid Gibson 349aea390e4SDavid Gibson if ((pte0 & HPTE32_V_VALID) 350aea390e4SDavid Gibson && (secondary == !!(pte0 & HPTE32_V_SECONDARY)) 351aea390e4SDavid Gibson && HPTE32_V_COMPARE(pte0, ptem)) { 352aea390e4SDavid Gibson pte->pte0 = pte0; 353aea390e4SDavid Gibson pte->pte1 = pte1; 354aea390e4SDavid Gibson return pte_offset; 355aea390e4SDavid Gibson } 356aea390e4SDavid Gibson 357aea390e4SDavid Gibson pte_offset += HASH_PTE_SIZE_32; 358aea390e4SDavid Gibson } 359aea390e4SDavid Gibson 360aea390e4SDavid Gibson return -1; 361aea390e4SDavid Gibson } 362aea390e4SDavid Gibson 3636e8a65abSBenjamin Herrenschmidt static void ppc_hash32_set_r(PowerPCCPU *cpu, hwaddr pte_offset, uint32_t pte1) 3646e8a65abSBenjamin Herrenschmidt { 3656e8a65abSBenjamin Herrenschmidt target_ulong base = ppc_hash32_hpt_base(cpu); 3666e8a65abSBenjamin Herrenschmidt hwaddr offset = pte_offset + 6; 3676e8a65abSBenjamin Herrenschmidt 3686e8a65abSBenjamin Herrenschmidt /* The HW performs a non-atomic byte update */ 3696e8a65abSBenjamin Herrenschmidt stb_phys(CPU(cpu)->as, base + offset, ((pte1 >> 8) & 0xff) | 0x01); 3706e8a65abSBenjamin Herrenschmidt } 3716e8a65abSBenjamin Herrenschmidt 3726e8a65abSBenjamin Herrenschmidt static void ppc_hash32_set_c(PowerPCCPU *cpu, hwaddr pte_offset, uint64_t pte1) 3736e8a65abSBenjamin Herrenschmidt { 3746e8a65abSBenjamin Herrenschmidt target_ulong base = ppc_hash32_hpt_base(cpu); 3756e8a65abSBenjamin Herrenschmidt hwaddr offset = pte_offset + 7; 3766e8a65abSBenjamin Herrenschmidt 3776e8a65abSBenjamin Herrenschmidt /* The HW performs a non-atomic byte update */ 3786e8a65abSBenjamin Herrenschmidt stb_phys(CPU(cpu)->as, base + offset, (pte1 & 0xff) | 0x80); 3796e8a65abSBenjamin Herrenschmidt } 3806e8a65abSBenjamin Herrenschmidt 3817ef23068SDavid Gibson static hwaddr ppc_hash32_htab_lookup(PowerPCCPU *cpu, 3827f3bdc2dSDavid Gibson target_ulong sr, target_ulong eaddr, 3837f3bdc2dSDavid Gibson ppc_hash_pte32_t *pte) 384c69b6151SDavid Gibson { 385aea390e4SDavid Gibson hwaddr pteg_off, pte_offset; 386a1ff751aSDavid Gibson hwaddr hash; 387a1ff751aSDavid Gibson uint32_t vsid, pgidx, ptem; 388c69b6151SDavid Gibson 389a1ff751aSDavid Gibson vsid = sr & SR32_VSID; 390a1ff751aSDavid Gibson pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS; 391a1ff751aSDavid Gibson hash = vsid ^ pgidx; 392a1ff751aSDavid Gibson ptem = (vsid << 7) | (pgidx >> 10); 393a1ff751aSDavid Gibson 394a1ff751aSDavid Gibson /* Page address translation */ 395339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "htab_base " TARGET_FMT_plx 396339aaf5bSAntony Pavlov " htab_mask " TARGET_FMT_plx 397a1ff751aSDavid Gibson " hash " TARGET_FMT_plx "\n", 39836778660SDavid Gibson ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash); 399a1ff751aSDavid Gibson 400a1ff751aSDavid Gibson /* Primary PTEG lookup */ 401339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx 402a1ff751aSDavid Gibson " vsid=%" PRIx32 " ptem=%" PRIx32 403a1ff751aSDavid Gibson " hash=" TARGET_FMT_plx "\n", 40436778660SDavid Gibson ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), 40536778660SDavid Gibson vsid, ptem, hash); 4067ef23068SDavid Gibson pteg_off = get_pteg_offset32(cpu, hash); 4077ef23068SDavid Gibson pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 0, ptem, pte); 408a1ff751aSDavid Gibson if (pte_offset == -1) { 409a1ff751aSDavid Gibson /* Secondary PTEG lookup */ 410339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx 411a1ff751aSDavid Gibson " vsid=%" PRIx32 " api=%" PRIx32 41236778660SDavid Gibson " hash=" TARGET_FMT_plx "\n", ppc_hash32_hpt_base(cpu), 41336778660SDavid Gibson ppc_hash32_hpt_mask(cpu), vsid, ptem, ~hash); 4147ef23068SDavid Gibson pteg_off = get_pteg_offset32(cpu, ~hash); 4157ef23068SDavid Gibson pte_offset = ppc_hash32_pteg_search(cpu, pteg_off, 1, ptem, pte); 416a1ff751aSDavid Gibson } 417a1ff751aSDavid Gibson 4187f3bdc2dSDavid Gibson return pte_offset; 419c69b6151SDavid Gibson } 4200480884fSDavid Gibson 4216d11d998SDavid Gibson static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte, 4226d11d998SDavid Gibson target_ulong eaddr) 4236d11d998SDavid Gibson { 42475d5ec89SDavid Gibson hwaddr rpn = pte.pte1 & HPTE32_R_RPN; 4256d11d998SDavid Gibson hwaddr mask = ~TARGET_PAGE_MASK; 4266d11d998SDavid Gibson 4276d11d998SDavid Gibson return (rpn & ~mask) | (eaddr & mask); 4286d11d998SDavid Gibson } 4296d11d998SDavid Gibson 43051806b54SRichard Henderson bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, 4316c3c873cSRichard Henderson hwaddr *raddrp, int *psizep, int *protp, 4326c3c873cSRichard Henderson bool guest_visible) 4330480884fSDavid Gibson { 434d0e39c5dSAndreas Färber CPUState *cs = CPU(cpu); 435d0e39c5dSAndreas Färber CPUPPCState *env = &cpu->env; 436a1ff751aSDavid Gibson target_ulong sr; 4377f3bdc2dSDavid Gibson hwaddr pte_offset; 4387f3bdc2dSDavid Gibson ppc_hash_pte32_t pte; 439caa597bdSDavid Gibson int prot; 440182357dbSRichard Henderson int need_prot; 441caa597bdSDavid Gibson hwaddr raddr; 4420480884fSDavid Gibson 4436c3c873cSRichard Henderson /* There are no hash32 large pages. */ 4446c3c873cSRichard Henderson *psizep = TARGET_PAGE_BITS; 4456a980110SDavid Gibson 44665d61643SDavid Gibson /* 1. Handle real mode accesses */ 44731fa64ecSRichard Henderson if (access_type == MMU_INST_FETCH ? !msr_ir : !msr_dr) { 44865d61643SDavid Gibson /* Translation is off */ 4496c3c873cSRichard Henderson *raddrp = eaddr; 4506c3c873cSRichard Henderson *protp = PAGE_READ | PAGE_WRITE | PAGE_EXEC; 4516c3c873cSRichard Henderson return true; 45265d61643SDavid Gibson } 45365d61643SDavid Gibson 4546c3c873cSRichard Henderson need_prot = prot_for_access_type(access_type); 4556c3c873cSRichard Henderson 45665d61643SDavid Gibson /* 2. Check Block Address Translation entries (BATs) */ 45765d61643SDavid Gibson if (env->nb_BATs != 0) { 4586c3c873cSRichard Henderson raddr = ppc_hash32_bat_lookup(cpu, eaddr, access_type, protp); 459caa597bdSDavid Gibson if (raddr != -1) { 4606c3c873cSRichard Henderson if (need_prot & ~*protp) { 4616c3c873cSRichard Henderson if (guest_visible) { 46231fa64ecSRichard Henderson if (access_type == MMU_INST_FETCH) { 46327103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ISI; 464caa597bdSDavid Gibson env->error_code = 0x08000000; 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; 46931fa64ecSRichard Henderson if (access_type == MMU_DATA_STORE) { 470caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x0a000000; 471caa597bdSDavid Gibson } else { 472caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x08000000; 473e01b4445SDavid Gibson } 474caa597bdSDavid Gibson } 475caa597bdSDavid Gibson } 4766c3c873cSRichard Henderson return false; 4776c3c873cSRichard Henderson } 4786c3c873cSRichard Henderson *raddrp = raddr; 4796c3c873cSRichard Henderson return true; 48065d61643SDavid Gibson } 481145e52f3SDavid Gibson } 48265d61643SDavid Gibson 4834b9605a5SDavid Gibson /* 3. Look up the Segment Register */ 4840480884fSDavid Gibson sr = env->sr[eaddr >> 28]; 4854b9605a5SDavid Gibson 486723ed73aSDavid Gibson /* 4. Handle direct store segments */ 487723ed73aSDavid Gibson if (sr & SR32_T) { 4886c3c873cSRichard Henderson return ppc_hash32_direct_store(cpu, sr, eaddr, access_type, 4896c3c873cSRichard Henderson raddrp, protp, guest_visible); 490723ed73aSDavid Gibson } 491723ed73aSDavid Gibson 492bb218042SDavid Gibson /* 5. Check for segment level no-execute violation */ 49331fa64ecSRichard Henderson if (access_type == MMU_INST_FETCH && (sr & SR32_NX)) { 4946c3c873cSRichard Henderson if (guest_visible) { 49527103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ISI; 496caa597bdSDavid Gibson env->error_code = 0x10000000; 4976c3c873cSRichard Henderson } 4986c3c873cSRichard Henderson return false; 499bb218042SDavid Gibson } 5007f3bdc2dSDavid Gibson 5017f3bdc2dSDavid Gibson /* 6. Locate the PTE in the hash table */ 5027ef23068SDavid Gibson pte_offset = ppc_hash32_htab_lookup(cpu, sr, eaddr, &pte); 5037f3bdc2dSDavid Gibson if (pte_offset == -1) { 5046c3c873cSRichard Henderson if (guest_visible) { 50531fa64ecSRichard Henderson if (access_type == MMU_INST_FETCH) { 50627103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ISI; 507caa597bdSDavid Gibson env->error_code = 0x40000000; 508caa597bdSDavid Gibson } else { 50927103424SAndreas Färber cs->exception_index = POWERPC_EXCP_DSI; 510caa597bdSDavid Gibson env->error_code = 0; 511caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 51231fa64ecSRichard Henderson if (access_type == MMU_DATA_STORE) { 513caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x42000000; 514caa597bdSDavid Gibson } else { 515caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x40000000; 516caa597bdSDavid Gibson } 517caa597bdSDavid Gibson } 5186c3c873cSRichard Henderson } 5196c3c873cSRichard Henderson return false; 5207f3bdc2dSDavid Gibson } 521339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, 522339aaf5bSAntony Pavlov "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); 5237f3bdc2dSDavid Gibson 5247f3bdc2dSDavid Gibson /* 7. Check access permissions */ 5256a980110SDavid Gibson 5267ef23068SDavid Gibson prot = ppc_hash32_pte_prot(cpu, sr, pte); 5276a980110SDavid Gibson 528182357dbSRichard Henderson if (need_prot & ~prot) { 5296a980110SDavid Gibson /* Access right violation */ 530339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); 5316c3c873cSRichard Henderson if (guest_visible) { 53231fa64ecSRichard Henderson if (access_type == MMU_INST_FETCH) { 53327103424SAndreas Färber cs->exception_index = POWERPC_EXCP_ISI; 534caa597bdSDavid Gibson env->error_code = 0x08000000; 535caa597bdSDavid Gibson } else { 53627103424SAndreas Färber cs->exception_index = POWERPC_EXCP_DSI; 537caa597bdSDavid Gibson env->error_code = 0; 538caa597bdSDavid Gibson env->spr[SPR_DAR] = eaddr; 53931fa64ecSRichard Henderson if (access_type == MMU_DATA_STORE) { 540caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x0a000000; 541caa597bdSDavid Gibson } else { 542caa597bdSDavid Gibson env->spr[SPR_DSISR] = 0x08000000; 543caa597bdSDavid Gibson } 544caa597bdSDavid Gibson } 5456c3c873cSRichard Henderson } 5466c3c873cSRichard Henderson return false; 5476a980110SDavid Gibson } 5486a980110SDavid Gibson 549339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); 55087dc3fd1SDavid Gibson 55187dc3fd1SDavid Gibson /* 8. Update PTE referenced and changed bits if necessary */ 55287dc3fd1SDavid Gibson 5536e8a65abSBenjamin Herrenschmidt if (!(pte.pte1 & HPTE32_R_R)) { 5546e8a65abSBenjamin Herrenschmidt ppc_hash32_set_r(cpu, pte_offset, pte.pte1); 5556e8a65abSBenjamin Herrenschmidt } 5566e8a65abSBenjamin Herrenschmidt if (!(pte.pte1 & HPTE32_R_C)) { 55731fa64ecSRichard Henderson if (access_type == MMU_DATA_STORE) { 5586e8a65abSBenjamin Herrenschmidt ppc_hash32_set_c(cpu, pte_offset, pte.pte1); 559b3440746SDavid Gibson } else { 560596e3ca8SDavid Gibson /* 561596e3ca8SDavid Gibson * Treat the page as read-only for now, so that a later write 562596e3ca8SDavid Gibson * will pass through this function again to set the C bit 563596e3ca8SDavid Gibson */ 564caa597bdSDavid Gibson prot &= ~PAGE_WRITE; 565b3440746SDavid Gibson } 5667f3bdc2dSDavid Gibson } 5670480884fSDavid Gibson 5686d11d998SDavid Gibson /* 9. Determine the real address from the PTE */ 5696d11d998SDavid Gibson 5706c3c873cSRichard Henderson *raddrp = ppc_hash32_pte_raddr(sr, pte, eaddr); 5716c3c873cSRichard Henderson *protp = prot; 5726c3c873cSRichard Henderson return true; 5736c3c873cSRichard Henderson } 574