mmu-hash*: Add hash pte load/store helpers
On real hardware the ppc hash page table is stored in memory; accordingly our mmu emulation code can read a hash page table in guest memory. But, when paravirtualized under PAPR, the real hash page table is in host memory, accessible to the guest only via hypercalls. We model this by also allowing the MMU emulation code to access a specially allocated hash page table outside the guest's memory image. At present these two options are implemented with some ugly conditionals at each access point in the mmu emulation code. In the implementation of the PAPR hypercalls, we assume the external hash table. This patch cleans things up by adding helpers to load and store from the hash table for both 32-bit and 64-bit hash mmus. The 64-bit versions handle both the in-guest-memory and outside guest memory cases. The 32-bit versions only handle the in-guest-memory case since no 32-bit systems can have an external hash table at present. Signed-off-by: David Gibson <david@gibson.dropbear.id.au> Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
parent
d5aea6f367
commit
dffdaf6162
@ -52,7 +52,7 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
target_ulong page_shift = 12;
|
||||
target_ulong raddr;
|
||||
target_ulong i;
|
||||
uint8_t *hpte;
|
||||
hwaddr hpte;
|
||||
|
||||
/* only handle 4k and 16M pages for now */
|
||||
if (pteh & HPTE64_V_LARGE) {
|
||||
@ -97,26 +97,26 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
}
|
||||
if (likely((flags & H_EXACT) == 0)) {
|
||||
pte_index &= ~7ULL;
|
||||
hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
|
||||
hpte = pte_index * HASH_PTE_SIZE_64;
|
||||
for (i = 0; ; ++i) {
|
||||
if (i == 8) {
|
||||
return H_PTEG_FULL;
|
||||
}
|
||||
if ((ldq_p(hpte) & HPTE64_V_VALID) == 0) {
|
||||
if ((ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) == 0) {
|
||||
break;
|
||||
}
|
||||
hpte += HASH_PTE_SIZE_64;
|
||||
}
|
||||
} else {
|
||||
i = 0;
|
||||
hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
|
||||
if (ldq_p(hpte) & HPTE64_V_VALID) {
|
||||
hpte = pte_index * HASH_PTE_SIZE_64;
|
||||
if (ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) {
|
||||
return H_PTEG_FULL;
|
||||
}
|
||||
}
|
||||
stq_p(hpte + (HASH_PTE_SIZE_64/2), ptel);
|
||||
ppc_hash64_store_hpte1(env, hpte, ptel);
|
||||
/* eieio(); FIXME: need some sort of barrier for smp? */
|
||||
stq_p(hpte, pteh);
|
||||
ppc_hash64_store_hpte0(env, hpte, pteh);
|
||||
|
||||
args[0] = pte_index + i;
|
||||
return H_SUCCESS;
|
||||
@ -134,17 +134,17 @@ static target_ulong remove_hpte(CPUPPCState *env, target_ulong ptex,
|
||||
target_ulong flags,
|
||||
target_ulong *vp, target_ulong *rp)
|
||||
{
|
||||
uint8_t *hpte;
|
||||
hwaddr hpte;
|
||||
target_ulong v, r, rb;
|
||||
|
||||
if ((ptex * HASH_PTE_SIZE_64) & ~env->htab_mask) {
|
||||
return REMOVE_PARM;
|
||||
}
|
||||
|
||||
hpte = env->external_htab + (ptex * HASH_PTE_SIZE_64);
|
||||
hpte = ptex * HASH_PTE_SIZE_64;
|
||||
|
||||
v = ldq_p(hpte);
|
||||
r = ldq_p(hpte + (HASH_PTE_SIZE_64/2));
|
||||
v = ppc_hash64_load_hpte0(env, hpte);
|
||||
r = ppc_hash64_load_hpte1(env, hpte);
|
||||
|
||||
if ((v & HPTE64_V_VALID) == 0 ||
|
||||
((flags & H_AVPN) && (v & ~0x7fULL) != avpn) ||
|
||||
@ -153,7 +153,7 @@ static target_ulong remove_hpte(CPUPPCState *env, target_ulong ptex,
|
||||
}
|
||||
*vp = v;
|
||||
*rp = r;
|
||||
stq_p(hpte, 0);
|
||||
ppc_hash64_store_hpte0(env, hpte, 0);
|
||||
rb = compute_tlbie_rb(v, r, ptex);
|
||||
ppc_tlb_invalidate_one(env, rb);
|
||||
return REMOVE_SUCCESS;
|
||||
@ -260,17 +260,17 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
target_ulong flags = args[0];
|
||||
target_ulong pte_index = args[1];
|
||||
target_ulong avpn = args[2];
|
||||
uint8_t *hpte;
|
||||
hwaddr hpte;
|
||||
target_ulong v, r, rb;
|
||||
|
||||
if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
|
||||
hpte = pte_index * HASH_PTE_SIZE_64;
|
||||
|
||||
v = ldq_p(hpte);
|
||||
r = ldq_p(hpte + (HASH_PTE_SIZE_64/2));
|
||||
v = ppc_hash64_load_hpte0(env, hpte);
|
||||
r = ppc_hash64_load_hpte1(env, hpte);
|
||||
|
||||
if ((v & HPTE64_V_VALID) == 0 ||
|
||||
((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) {
|
||||
@ -283,11 +283,11 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
r |= (flags << 48) & HPTE64_R_KEY_HI;
|
||||
r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO);
|
||||
rb = compute_tlbie_rb(v, r, pte_index);
|
||||
stq_p(hpte, v & ~HPTE64_V_VALID);
|
||||
ppc_hash64_store_hpte0(env, hpte, v & ~HPTE64_V_VALID);
|
||||
ppc_tlb_invalidate_one(env, rb);
|
||||
stq_p(hpte + (HASH_PTE_SIZE_64/2), r);
|
||||
ppc_hash64_store_hpte1(env, hpte, r);
|
||||
/* Don't need a memory barrier, due to qemu's global lock */
|
||||
stq_p(hpte, v);
|
||||
ppc_hash64_store_hpte0(env, hpte, v);
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -328,13 +328,8 @@ static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, int h,
|
||||
ret = -1; /* No entry found */
|
||||
pteg_off = get_pteg_offset32(env, ctx->hash[h]);
|
||||
for (i = 0; i < HPTES_PER_GROUP; i++) {
|
||||
if (env->external_htab) {
|
||||
pte0 = ldl_p(env->external_htab + pteg_off + (i * 8));
|
||||
pte1 = ldl_p(env->external_htab + pteg_off + (i * 8) + 4);
|
||||
} else {
|
||||
pte0 = ldl_phys(env->htab_base + pteg_off + (i * 8));
|
||||
pte1 = ldl_phys(env->htab_base + pteg_off + (i * 8) + 4);
|
||||
}
|
||||
pte0 = ppc_hash32_load_hpte0(env, pteg_off + i*HASH_PTE_SIZE_32);
|
||||
pte1 = ppc_hash32_load_hpte1(env, pteg_off + i*HASH_PTE_SIZE_32);
|
||||
r = pte_check_hash32(ctx, pte0, pte1, h, rw, type);
|
||||
LOG_MMU("Load pte from %08" HWADDR_PRIx " => " TARGET_FMT_lx " "
|
||||
TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n",
|
||||
@ -371,13 +366,8 @@ static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, int h,
|
||||
/* Update page flags */
|
||||
pte1 = ctx->raddr;
|
||||
if (ppc_hash32_pte_update_flags(ctx, &pte1, ret, rw) == 1) {
|
||||
if (env->external_htab) {
|
||||
stl_p(env->external_htab + pteg_off + (good * 8) + 4,
|
||||
pte1);
|
||||
} else {
|
||||
stl_phys_notdirty(env->htab_base + pteg_off +
|
||||
(good * 8) + 4, pte1);
|
||||
}
|
||||
ppc_hash32_store_hpte1(env, pteg_off + good * HASH_PTE_SIZE_32,
|
||||
pte1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,6 +69,34 @@ int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
|
||||
#define HPTE32_R_WIMG 0x00000078
|
||||
#define HPTE32_R_PP 0x00000003
|
||||
|
||||
static inline target_ulong ppc_hash32_load_hpte0(CPUPPCState *env,
|
||||
hwaddr pte_offset)
|
||||
{
|
||||
assert(!env->external_htab); /* Not supported on 32-bit for now */
|
||||
return ldl_phys(env->htab_base + pte_offset);
|
||||
}
|
||||
|
||||
static inline target_ulong ppc_hash32_load_hpte1(CPUPPCState *env,
|
||||
hwaddr pte_offset)
|
||||
{
|
||||
assert(!env->external_htab); /* Not supported on 32-bit for now */
|
||||
return ldl_phys(env->htab_base + pte_offset + HASH_PTE_SIZE_32/2);
|
||||
}
|
||||
|
||||
static inline void ppc_hash32_store_hpte0(CPUPPCState *env,
|
||||
hwaddr pte_offset, target_ulong pte0)
|
||||
{
|
||||
assert(!env->external_htab); /* Not supported on 32-bit for now */
|
||||
stl_phys(env->htab_base + pte_offset, pte0);
|
||||
}
|
||||
|
||||
static inline void ppc_hash32_store_hpte1(CPUPPCState *env,
|
||||
hwaddr pte_offset, target_ulong pte1)
|
||||
{
|
||||
assert(!env->external_htab); /* Not supported on 32-bit for now */
|
||||
stl_phys(env->htab_base + pte_offset + HASH_PTE_SIZE_32/2, pte1);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USER_ONLY */
|
||||
|
||||
#endif /* __MMU_HASH32_H__ */
|
||||
|
@ -382,13 +382,8 @@ static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, int h,
|
||||
ret = -1; /* No entry found */
|
||||
pteg_off = (ctx->hash[h] * HASH_PTEG_SIZE_64) & env->htab_mask;
|
||||
for (i = 0; i < HPTES_PER_GROUP; i++) {
|
||||
if (env->external_htab) {
|
||||
pte0 = ldq_p(env->external_htab + pteg_off + (i * 16));
|
||||
pte1 = ldq_p(env->external_htab + pteg_off + (i * 16) + 8);
|
||||
} else {
|
||||
pte0 = ldq_phys(env->htab_base + pteg_off + (i * 16));
|
||||
pte1 = ldq_phys(env->htab_base + pteg_off + (i * 16) + 8);
|
||||
}
|
||||
pte0 = ppc_hash64_load_hpte0(env, pteg_off + i*HASH_PTE_SIZE_64);
|
||||
pte1 = ppc_hash64_load_hpte1(env, pteg_off + i*HASH_PTE_SIZE_64);
|
||||
|
||||
r = pte64_check(ctx, pte0, pte1, h, rw, type);
|
||||
LOG_MMU("Load pte from %016" HWADDR_PRIx " => " TARGET_FMT_lx " "
|
||||
@ -426,13 +421,7 @@ static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, int h,
|
||||
/* Update page flags */
|
||||
pte1 = ctx->raddr;
|
||||
if (ppc_hash64_pte_update_flags(ctx, &pte1, ret, rw) == 1) {
|
||||
if (env->external_htab) {
|
||||
stq_p(env->external_htab + pteg_off + (good * 16) + 8,
|
||||
pte1);
|
||||
} else {
|
||||
stq_phys_notdirty(env->htab_base + pteg_off +
|
||||
(good * 16) + 8, pte1);
|
||||
}
|
||||
ppc_hash64_store_hpte1(env, pteg_off + good * HASH_PTE_SIZE_64, pte1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,46 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
|
||||
#define HPTE64_V_1TB_SEG 0x4000000000000000ULL
|
||||
#define HPTE64_V_VRMA_MASK 0x4001ffffff000000ULL
|
||||
|
||||
static inline target_ulong ppc_hash64_load_hpte0(CPUPPCState *env,
|
||||
hwaddr pte_offset)
|
||||
{
|
||||
if (env->external_htab) {
|
||||
return ldq_p(env->external_htab + pte_offset);
|
||||
} else {
|
||||
return ldq_phys(env->htab_base + pte_offset);
|
||||
}
|
||||
}
|
||||
|
||||
static inline target_ulong ppc_hash64_load_hpte1(CPUPPCState *env,
|
||||
hwaddr pte_offset)
|
||||
{
|
||||
if (env->external_htab) {
|
||||
return ldq_p(env->external_htab + pte_offset + HASH_PTE_SIZE_64/2);
|
||||
} else {
|
||||
return ldq_phys(env->htab_base + pte_offset + HASH_PTE_SIZE_64/2);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ppc_hash64_store_hpte0(CPUPPCState *env,
|
||||
hwaddr pte_offset, target_ulong pte0)
|
||||
{
|
||||
if (env->external_htab) {
|
||||
stq_p(env->external_htab + pte_offset, pte0);
|
||||
} else {
|
||||
stq_phys(env->htab_base + pte_offset, pte0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ppc_hash64_store_hpte1(CPUPPCState *env,
|
||||
hwaddr pte_offset, target_ulong pte1)
|
||||
{
|
||||
if (env->external_htab) {
|
||||
stq_p(env->external_htab + pte_offset + HASH_PTE_SIZE_64/2, pte1);
|
||||
} else {
|
||||
stq_phys(env->htab_base + pte_offset + HASH_PTE_SIZE_64/2, pte1);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USER_ONLY */
|
||||
|
||||
#endif /* !defined (__MMU_HASH64_H__) */
|
||||
|
Loading…
Reference in New Issue
Block a user