?? helper.c
字號:
/* * PowerPC emulation helpers for qemu. * * Copyright (c) 2003-2007 Jocelyn Mayer * * 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <stdarg.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <inttypes.h>#include <signal.h>#include <assert.h>#include "cpu.h"#include "exec-all.h"#include "helper_regs.h"//#define DEBUG_MMU//#define DEBUG_BATS//#define DEBUG_SLB//#define DEBUG_SOFTWARE_TLB//#define DUMP_PAGE_TABLES//#define DEBUG_EXCEPTIONS//#define FLUSH_ALL_TLBS/*****************************************************************************//* PowerPC MMU emulation */#if defined(CONFIG_USER_ONLY)int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, int mmu_idx, int is_softmmu){ int exception, error_code; if (rw == 2) { exception = POWERPC_EXCP_ISI; error_code = 0x40000000; } else { exception = POWERPC_EXCP_DSI; error_code = 0x40000000; if (rw) error_code |= 0x02000000; env->spr[SPR_DAR] = address; env->spr[SPR_DSISR] = error_code; } env->exception_index = exception; env->error_code = error_code; return 1;}target_phys_addr_t cpu_get_phys_page_debug (CPUState *env, target_ulong addr){ return addr;}#else/* Common routines used by software and hardware TLBs emulation */static always_inline int pte_is_valid (target_ulong pte0){ return pte0 & 0x80000000 ? 1 : 0;}static always_inline void pte_invalidate (target_ulong *pte0){ *pte0 &= ~0x80000000;}#if defined(TARGET_PPC64)static always_inline int pte64_is_valid (target_ulong pte0){ return pte0 & 0x0000000000000001ULL ? 1 : 0;}static always_inline void pte64_invalidate (target_ulong *pte0){ *pte0 &= ~0x0000000000000001ULL;}#endif#define PTE_PTEM_MASK 0x7FFFFFBF#define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B)#if defined(TARGET_PPC64)#define PTE64_PTEM_MASK 0xFFFFFFFFFFFFFF80ULL#define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F)#endifstatic always_inline int pp_check (int key, int pp, int nx){ int access; /* Compute access rights */ /* When pp is 3/7, the result is undefined. Set it to noaccess */ access = 0; if (key == 0) { switch (pp) { case 0x0: case 0x1: case 0x2: access |= PAGE_WRITE; /* No break here */ case 0x3: case 0x6: access |= PAGE_READ; break; } } else { switch (pp) { case 0x0: case 0x6: access = 0; break; case 0x1: case 0x3: access = PAGE_READ; break; case 0x2: access = PAGE_READ | PAGE_WRITE; break; } } if (nx == 0) access |= PAGE_EXEC; return access;}static always_inline int check_prot (int prot, int rw, int access_type){ int ret; if (access_type == ACCESS_CODE) { if (prot & PAGE_EXEC) ret = 0; else ret = -2; } else if (rw) { if (prot & PAGE_WRITE) ret = 0; else ret = -2; } else { if (prot & PAGE_READ) ret = 0; else ret = -2; } return ret;}static always_inline int _pte_check (mmu_ctx_t *ctx, int is_64b, target_ulong pte0, target_ulong pte1, int h, int rw, int type){ target_ulong ptem, mmask; int access, ret, pteh, ptev, pp; access = 0; ret = -1; /* Check validity and table match */#if defined(TARGET_PPC64) if (is_64b) { ptev = pte64_is_valid(pte0); pteh = (pte0 >> 1) & 1; } else#endif { ptev = pte_is_valid(pte0); pteh = (pte0 >> 6) & 1; } if (ptev && h == pteh) { /* Check vsid & api */#if defined(TARGET_PPC64) if (is_64b) { ptem = pte0 & PTE64_PTEM_MASK; mmask = PTE64_CHECK_MASK; pp = (pte1 & 0x00000003) | ((pte1 >> 61) & 0x00000004); ctx->nx |= (pte1 >> 2) & 1; /* No execute bit */ ctx->nx |= (pte1 >> 3) & 1; /* Guarded bit */ } else#endif { ptem = pte0 & PTE_PTEM_MASK; mmask = PTE_CHECK_MASK; pp = pte1 & 0x00000003; } if (ptem == ctx->ptem) { if (ctx->raddr != (target_phys_addr_t)-1ULL) { /* all matches should have equal RPN, WIMG & PP */ if ((ctx->raddr & mmask) != (pte1 & mmask)) { if (loglevel != 0) fprintf(logfile, "Bad RPN/WIMG/PP\n"); return -3; } } /* Compute access rights */ access = pp_check(ctx->key, pp, ctx->nx); /* Keep the matching PTE informations */ ctx->raddr = pte1; ctx->prot = access; ret = check_prot(ctx->prot, rw, type); if (ret == 0) { /* Access granted */#if defined (DEBUG_MMU) if (loglevel != 0) fprintf(logfile, "PTE access granted !\n");#endif } else { /* Access right violation */#if defined (DEBUG_MMU) if (loglevel != 0) fprintf(logfile, "PTE access rejected\n");#endif } } } return ret;}static always_inline int pte32_check (mmu_ctx_t *ctx, target_ulong pte0, target_ulong pte1, int h, int rw, int type){ return _pte_check(ctx, 0, pte0, pte1, h, rw, type);}#if defined(TARGET_PPC64)static always_inline int pte64_check (mmu_ctx_t *ctx, target_ulong pte0, target_ulong pte1, int h, int rw, int type){ return _pte_check(ctx, 1, pte0, pte1, h, rw, type);}#endifstatic always_inline int pte_update_flags (mmu_ctx_t *ctx, target_ulong *pte1p, int ret, int rw){ int store = 0; /* Update page flags */ if (!(*pte1p & 0x00000100)) { /* Update accessed flag */ *pte1p |= 0x00000100; store = 1; } if (!(*pte1p & 0x00000080)) { if (rw == 1 && ret == 0) { /* Update changed flag */ *pte1p |= 0x00000080; store = 1; } else { /* Force page fault for first write access */ ctx->prot &= ~PAGE_WRITE; } } return store;}/* Software driven TLB helpers */static always_inline int ppc6xx_tlb_getnum (CPUState *env, target_ulong eaddr, int way, int is_code){ int nr; /* Select TLB num in a way from address */ nr = (eaddr >> TARGET_PAGE_BITS) & (env->tlb_per_way - 1); /* Select TLB way */ nr += env->tlb_per_way * way; /* 6xx have separate TLBs for instructions and data */ if (is_code && env->id_tlbs == 1) nr += env->nb_tlb; return nr;}static always_inline void ppc6xx_tlb_invalidate_all (CPUState *env){ ppc6xx_tlb_t *tlb; int nr, max;#if defined (DEBUG_SOFTWARE_TLB) && 0 if (loglevel != 0) { fprintf(logfile, "Invalidate all TLBs\n"); }#endif /* Invalidate all defined software TLB */ max = env->nb_tlb; if (env->id_tlbs == 1) max *= 2; for (nr = 0; nr < max; nr++) { tlb = &env->tlb[nr].tlb6; pte_invalidate(&tlb->pte0); } tlb_flush(env, 1);}static always_inline void __ppc6xx_tlb_invalidate_virt (CPUState *env, target_ulong eaddr, int is_code, int match_epn){#if !defined(FLUSH_ALL_TLBS) ppc6xx_tlb_t *tlb; int way, nr; /* Invalidate ITLB + DTLB, all ways */ for (way = 0; way < env->nb_ways; way++) { nr = ppc6xx_tlb_getnum(env, eaddr, way, is_code); tlb = &env->tlb[nr].tlb6; if (pte_is_valid(tlb->pte0) && (match_epn == 0 || eaddr == tlb->EPN)) {#if defined (DEBUG_SOFTWARE_TLB) if (loglevel != 0) { fprintf(logfile, "TLB invalidate %d/%d " ADDRX "\n", nr, env->nb_tlb, eaddr); }#endif pte_invalidate(&tlb->pte0); tlb_flush_page(env, tlb->EPN); } }#else /* XXX: PowerPC specification say this is valid as well */ ppc6xx_tlb_invalidate_all(env);#endif}static always_inline void ppc6xx_tlb_invalidate_virt (CPUState *env, target_ulong eaddr, int is_code){ __ppc6xx_tlb_invalidate_virt(env, eaddr, is_code, 0);}void ppc6xx_tlb_store (CPUState *env, target_ulong EPN, int way, int is_code, target_ulong pte0, target_ulong pte1){ ppc6xx_tlb_t *tlb; int nr; nr = ppc6xx_tlb_getnum(env, EPN, way, is_code); tlb = &env->tlb[nr].tlb6;#if defined (DEBUG_SOFTWARE_TLB) if (loglevel != 0) { fprintf(logfile, "Set TLB %d/%d EPN " ADDRX " PTE0 " ADDRX " PTE1 " ADDRX "\n", nr, env->nb_tlb, EPN, pte0, pte1); }#endif /* Invalidate any pending reference in Qemu for this virtual address */ __ppc6xx_tlb_invalidate_virt(env, EPN, is_code, 1); tlb->pte0 = pte0; tlb->pte1 = pte1; tlb->EPN = EPN; /* Store last way for LRU mechanism */ env->last_way = way;}static always_inline int ppc6xx_tlb_check (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr, int rw, int access_type){ ppc6xx_tlb_t *tlb; int nr, best, way; int ret; best = -1; ret = -1; /* No TLB found */ for (way = 0; way < env->nb_ways; way++) { nr = ppc6xx_tlb_getnum(env, eaddr, way, access_type == ACCESS_CODE ? 1 : 0); tlb = &env->tlb[nr].tlb6; /* This test "emulates" the PTE index match for hardware TLBs */ if ((eaddr & TARGET_PAGE_MASK) != tlb->EPN) {#if defined (DEBUG_SOFTWARE_TLB) if (loglevel != 0) { fprintf(logfile, "TLB %d/%d %s [" ADDRX " " ADDRX "] <> " ADDRX "\n", nr, env->nb_tlb, pte_is_valid(tlb->pte0) ? "valid" : "inval", tlb->EPN, tlb->EPN + TARGET_PAGE_SIZE, eaddr); }#endif continue; }#if defined (DEBUG_SOFTWARE_TLB) if (loglevel != 0) { fprintf(logfile, "TLB %d/%d %s " ADDRX " <> " ADDRX " " ADDRX " %c %c\n", nr, env->nb_tlb, pte_is_valid(tlb->pte0) ? "valid" : "inval", tlb->EPN, eaddr, tlb->pte1, rw ? 'S' : 'L', access_type == ACCESS_CODE ? 'I' : 'D'); }#endif switch (pte32_check(ctx, tlb->pte0, tlb->pte1, 0, rw, access_type)) { case -3:
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -