/* * PowerPC MMU, TLB, SLB and BAT emulation helpers for QEMU. * * Copyright (c) 2003-2007 Jocelyn Mayer * Copyright (c) 2013 David Gibson, IBM Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ #include "cpu.h" #include "helper.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "mmu-hash64.h" //#define DEBUG_SLB #ifdef DEBUG_SLB # define LOG_SLB(...) qemu_log(__VA_ARGS__) #else # define LOG_SLB(...) do { } while (0) #endif /* * SLB handling */ ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr) { uint64_t esid_256M, esid_1T; int n; LOG_SLB("%s: eaddr " TARGET_FMT_lx "\n", __func__, eaddr); esid_256M = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V; esid_1T = (eaddr & SEGMENT_MASK_1T) | SLB_ESID_V; for (n = 0; n < env->slb_nr; n++) { ppc_slb_t *slb = &env->slb[n]; LOG_SLB("%s: slot %d %016" PRIx64 " %016" PRIx64 "\n", __func__, n, slb->esid, slb->vsid); /* We check for 1T matches on all MMUs here - if the MMU * doesn't have 1T segment support, we will have prevented 1T * entries from being inserted in the slbmte code. */ if (((slb->esid == esid_256M) && ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_256M)) || ((slb->esid == esid_1T) && ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_1T))) { return slb; } } return NULL; } void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env) { int i; uint64_t slbe, slbv; cpu_synchronize_state(env); cpu_fprintf(f, "SLB\tESID\t\t\tVSID\n"); for (i = 0; i < env->slb_nr; i++) { slbe = env->slb[i].esid; slbv = env->slb[i].vsid; if (slbe == 0 && slbv == 0) { continue; } cpu_fprintf(f, "%d\t0x%016" PRIx64 "\t0x%016" PRIx64 "\n", i, slbe, slbv); } } void helper_slbia(CPUPPCState *env) { int n, do_invalidate; do_invalidate = 0; /* XXX: Warning: slbia never invalidates the first segment */ for (n = 1; n < env->slb_nr; n++) { ppc_slb_t *slb = &env->slb[n]; if (slb->esid & SLB_ESID_V) { slb->esid &= ~SLB_ESID_V; /* XXX: given the fact that segment size is 256 MB or 1TB, * and we still don't have a tlb_flush_mask(env, n, mask) * in QEMU, we just invalidate all TLBs */ do_invalidate = 1; } } if (do_invalidate) { tlb_flush(env, 1); } } void helper_slbie(CPUPPCState *env, target_ulong addr) { ppc_slb_t *slb; slb = slb_lookup(env, addr); if (!slb) { return; } if (slb->esid & SLB_ESID_V) { slb->esid &= ~SLB_ESID_V; /* XXX: given the fact that segment size is 256 MB or 1TB, * and we still don't have a tlb_flush_mask(env, n, mask) * in QEMU, we just invalidate all TLBs */ tlb_flush(env, 1); } } int ppc_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) { int slot = rb & 0xfff; ppc_slb_t *slb = &env->slb[slot]; if (rb & (0x1000 - env->slb_nr)) { return -1; /* Reserved bits set or slot too high */ } if (rs & (SLB_VSID_B & ~SLB_VSID_B_1T)) { return -1; /* Bad segment size */ } if ((rs & SLB_VSID_B) && !(env->mmu_model & POWERPC_MMU_1TSEG)) { return -1; /* 1T segment on MMU that doesn't support it */ } /* Mask out the slot number as we store the entry */ slb->esid = rb & (SLB_ESID_ESID | SLB_ESID_V); slb->vsid = rs; LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64 " %016" PRIx64 "\n", __func__, slot, rb, rs, slb->esid, slb->vsid); return 0; } static int ppc_load_slb_esid(CPUPPCState *env, target_ulong rb, target_ulong *rt) { int slot = rb & 0xfff; ppc_slb_t *slb = &env->slb[slot]; if (slot >= env->slb_nr) { return -1; } *rt = slb->esid; return 0; } static int ppc_load_slb_vsid(CPUPPCState *env, target_ulong rb, target_ulong *rt) { int slot = rb & 0xfff; ppc_slb_t *slb = &env->slb[slot]; if (slot >= env->slb_nr) { return -1; } *rt = slb->vsid; return 0; } void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) { if (ppc_store_slb(env, rb, rs) < 0) { helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL); } } target_ulong helper_load_slb_esid(CPUPPCState *env, target_ulong rb) { target_ulong rt = 0; if (ppc_load_slb_esid(env, rb, &rt) < 0) { helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL); } return rt; } target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) { target_ulong rt = 0; if (ppc_load_slb_vsid(env, rb, &rt) < 0) { helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL); } return rt; }