* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdarg.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <inttypes.h>
-#include <signal.h>
#include "cpu.h"
-#include "exec-all.h"
#include "helper_regs.h"
-#include "qemu-common.h"
#include "kvm.h"
+#include "kvm_ppc.h"
+#include "cpus.h"
//#define DEBUG_MMU
//#define DEBUG_BATS
#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 mmu_idx)
{
int exception, error_code;
if (env->id_tlbs == 1)
max *= 2;
for (nr = 0; nr < max; nr++) {
- tlb = &env->tlb[nr].tlb6;
+ tlb = &env->tlb.tlb6[nr];
pte_invalidate(&tlb->pte0);
}
tlb_flush(env, 1);
/* 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;
+ tlb = &env->tlb.tlb6[nr];
if (pte_is_valid(tlb->pte0) && (match_epn == 0 || eaddr == tlb->EPN)) {
LOG_SWTLB("TLB invalidate %d/%d " TARGET_FMT_lx "\n", nr,
env->nb_tlb, eaddr);
int nr;
nr = ppc6xx_tlb_getnum(env, EPN, way, is_code);
- tlb = &env->tlb[nr].tlb6;
+ tlb = &env->tlb.tlb6[nr];
LOG_SWTLB("Set TLB %d/%d EPN " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx
" PTE1 " TARGET_FMT_lx "\n", nr, env->nb_tlb, EPN, pte0, pte1);
/* Invalidate any pending reference in Qemu for this virtual address */
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;
+ tlb = &env->tlb.tlb6[nr];
/* This test "emulates" the PTE index match for hardware TLBs */
if ((eaddr & TARGET_PAGE_MASK) != tlb->EPN) {
LOG_SWTLB("TLB %d/%d %s [" TARGET_FMT_lx " " TARGET_FMT_lx
LOG_SWTLB("found TLB at addr " TARGET_FMT_plx " prot=%01x ret=%d\n",
ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret);
/* Update page flags */
- pte_update_flags(ctx, &env->tlb[best].tlb6.pte1, ret, rw);
+ pte_update_flags(ctx, &env->tlb.tlb6[best].pte1, ret, rw);
}
return ret;
BEPIl = *BATu & 0x0FFE0000;
bl = (*BATu & 0x00001FFC) << 15;
LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
- " BATl " TARGET_FMT_lx " \n\t" TARGET_FMT_lx " "
+ " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " "
TARGET_FMT_lx " " TARGET_FMT_lx "\n",
__func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual,
*BATu, *BATl, BEPIu, BEPIl, bl);
ret = -3;
}
} else {
+ target_ulong sr;
LOG_MMU("direct store...\n");
/* Direct-store segment : absolutely *BUGGY* for now */
+
+ /* Direct-store implies a 32-bit MMU.
+ * Check the Segment Register's bus unit ID (BUID).
+ */
+ sr = env->sr[eaddr >> 28];
+ if ((sr & 0x1FF00000) >> 20 == 0x07f) {
+ /* Memory-forced I/O controller interface access */
+ /* If T=1 and BUID=x'07F', the 601 performs a memory access
+ * to SR[28-31] LA[4-31], bypassing all protection mechanisms.
+ */
+ ctx->raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF);
+ ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ return 0;
+ }
+
switch (type) {
case ACCESS_INT:
/* Integer load/store : only access allowed */
/* Default return value is no match */
ret = -1;
for (i = 0; i < env->nb_tlb; i++) {
- tlb = &env->tlb[i].tlbe;
+ tlb = &env->tlb.tlbe[i];
if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, 0, i) == 0) {
ret = i;
break;
int i;
for (i = 0; i < env->nb_tlb; i++) {
- tlb = &env->tlb[i].tlbe;
+ tlb = &env->tlb.tlbe[i];
tlb->prot &= ~PAGE_VALID;
}
tlb_flush(env, 1);
int i;
for (i = 0; i < env->nb_tlb; i++) {
- tlb = &env->tlb[i].tlbe;
+ tlb = &env->tlb.tlbe[i];
if (ppcemb_tlb_check(env, tlb, &raddr, eaddr, pid, 0, i) == 0) {
end = tlb->EPN + tlb->size;
for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE)
raddr = (target_phys_addr_t)-1ULL;
pr = msr_pr;
for (i = 0; i < env->nb_tlb; i++) {
- tlb = &env->tlb[i].tlbe;
+ tlb = &env->tlb.tlbe[i];
if (ppcemb_tlb_check(env, tlb, &raddr, address,
env->spr[SPR_40x_PID], 0, i) < 0)
continue;
ret = -1;
raddr = (target_phys_addr_t)-1ULL;
for (i = 0; i < env->nb_tlb; i++) {
- tlb = &env->tlb[i].tlbe;
+ tlb = &env->tlb.tlbe[i];
ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw,
access_type, i);
if (!ret) {
{
int tlb_size;
int i, j;
- ppc_tlb_t *tlb = env->tlb;
+ ppcmas_tlb_t *tlb = env->tlb.tlbm;
for (i = 0; i < BOOKE206_MAX_TLBN; i++) {
if (flags & (1 << i)) {
tlb_size = booke206_tlb_size(env, i);
for (j = 0; j < tlb_size; j++) {
- if (!check_iprot || !(tlb[j].tlbe.attr & MAS1_IPROT)) {
- tlb[j].tlbe.prot = 0;
+ if (!check_iprot || !(tlb[j].mas1 & MAS1_IPROT)) {
+ tlb[j].mas1 &= ~MAS1_VALID;
}
}
}
tlb_flush(env, 1);
}
+target_phys_addr_t booke206_tlb_to_page_size(CPUState *env, ppcmas_tlb_t *tlb)
+{
+ int tlbm_size;
+
+ tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT;
+
+ return 1024ULL << tlbm_size;
+}
+
+/* TLB check function for MAS based SoftTLBs */
+int ppcmas_tlb_check(CPUState *env, ppcmas_tlb_t *tlb,
+ target_phys_addr_t *raddrp,
+ target_ulong address, uint32_t pid)
+{
+ target_ulong mask;
+ uint32_t tlb_pid;
+
+ /* Check valid flag */
+ if (!(tlb->mas1 & MAS1_VALID)) {
+ return -1;
+ }
+
+ mask = ~(booke206_tlb_to_page_size(env, tlb) - 1);
+ LOG_SWTLB("%s: TLB ADDR=0x" TARGET_FMT_lx " PID=0x%x MAS1=0x%x MAS2=0x%"
+ PRIx64 " mask=0x" TARGET_FMT_lx " MAS7_3=0x%" PRIx64 " MAS8=%x\n",
+ __func__, address, pid, tlb->mas1, tlb->mas2, mask, tlb->mas7_3,
+ tlb->mas8);
+
+ /* Check PID */
+ tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT;
+ if (tlb_pid != 0 && tlb_pid != pid) {
+ return -1;
+ }
+
+ /* Check effective address */
+ if ((address & mask) != (tlb->mas2 & MAS2_EPN_MASK)) {
+ return -1;
+ }
+
+ if (raddrp) {
+ *raddrp = (tlb->mas7_3 & mask) | (address & ~mask);
+ }
+
+ return 0;
+}
+
+static int mmubooke206_check_tlb(CPUState *env, ppcmas_tlb_t *tlb,
+ target_phys_addr_t *raddr, int *prot,
+ target_ulong address, int rw,
+ int access_type)
+{
+ int ret;
+ int _prot = 0;
+
+ if (ppcmas_tlb_check(env, tlb, raddr, address,
+ env->spr[SPR_BOOKE_PID]) >= 0) {
+ goto found_tlb;
+ }
+
+ if (env->spr[SPR_BOOKE_PID1] &&
+ ppcmas_tlb_check(env, tlb, raddr, address,
+ env->spr[SPR_BOOKE_PID1]) >= 0) {
+ goto found_tlb;
+ }
+
+ if (env->spr[SPR_BOOKE_PID2] &&
+ ppcmas_tlb_check(env, tlb, raddr, address,
+ env->spr[SPR_BOOKE_PID2]) >= 0) {
+ goto found_tlb;
+ }
+
+ LOG_SWTLB("%s: TLB entry not found\n", __func__);
+ return -1;
+
+found_tlb:
+
+ if (msr_pr != 0) {
+ if (tlb->mas7_3 & MAS3_UR) {
+ _prot |= PAGE_READ;
+ }
+ if (tlb->mas7_3 & MAS3_UW) {
+ _prot |= PAGE_WRITE;
+ }
+ if (tlb->mas7_3 & MAS3_UX) {
+ _prot |= PAGE_EXEC;
+ }
+ } else {
+ if (tlb->mas7_3 & MAS3_SR) {
+ _prot |= PAGE_READ;
+ }
+ if (tlb->mas7_3 & MAS3_SW) {
+ _prot |= PAGE_WRITE;
+ }
+ if (tlb->mas7_3 & MAS3_SX) {
+ _prot |= PAGE_EXEC;
+ }
+ }
+
+ /* Check the address space and permissions */
+ if (access_type == ACCESS_CODE) {
+ if (msr_ir != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) {
+ LOG_SWTLB("%s: AS doesn't match\n", __func__);
+ return -1;
+ }
+
+ *prot = _prot;
+ if (_prot & PAGE_EXEC) {
+ LOG_SWTLB("%s: good TLB!\n", __func__);
+ return 0;
+ }
+
+ LOG_SWTLB("%s: no PAGE_EXEC: %x\n", __func__, _prot);
+ ret = -3;
+ } else {
+ if (msr_dr != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) {
+ LOG_SWTLB("%s: AS doesn't match\n", __func__);
+ return -1;
+ }
+
+ *prot = _prot;
+ if ((!rw && _prot & PAGE_READ) || (rw && (_prot & PAGE_WRITE))) {
+ LOG_SWTLB("%s: found TLB!\n", __func__);
+ return 0;
+ }
+
+ LOG_SWTLB("%s: PAGE_READ/WRITE doesn't match: %x\n", __func__, _prot);
+ ret = -2;
+ }
+
+ return ret;
+}
+
static int mmubooke206_get_physical_address(CPUState *env, mmu_ctx_t *ctx,
- target_ulong address, int rw,
- int access_type)
+ target_ulong address, int rw,
+ int access_type)
{
- ppcemb_tlb_t *tlb;
+ ppcmas_tlb_t *tlb;
target_phys_addr_t raddr;
int i, j, ret;
int ways = booke206_tlb_ways(env, i);
for (j = 0; j < ways; j++) {
- tlb = booke206_get_tlbe(env, i, address, j);
- ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw,
- access_type, j);
+ tlb = booke206_get_tlbm(env, i, address, j);
+ if (!tlb) {
+ continue;
+ }
+ ret = mmubooke206_check_tlb(env, tlb, &raddr, &ctx->prot, address,
+ rw, access_type);
if (ret != -1) {
goto found_tlb;
}
return ret;
}
+static const char *book3e_tsize_to_str[32] = {
+ "1K", "2K", "4K", "8K", "16K", "32K", "64K", "128K", "256K", "512K",
+ "1M", "2M", "4M", "8M", "16M", "32M", "64M", "128M", "256M", "512M",
+ "1G", "2G", "4G", "8G", "16G", "32G", "64G", "128G", "256G", "512G",
+ "1T", "2T"
+};
+
+static void mmubooke206_dump_one_tlb(FILE *f, fprintf_function cpu_fprintf,
+ CPUState *env, int tlbn, int offset,
+ int tlbsize)
+{
+ ppcmas_tlb_t *entry;
+ int i;
+
+ cpu_fprintf(f, "\nTLB%d:\n", tlbn);
+ cpu_fprintf(f, "Effective Physical Size TID TS SRWX URWX WIMGE U0123\n");
+
+ entry = &env->tlb.tlbm[offset];
+ for (i = 0; i < tlbsize; i++, entry++) {
+ target_phys_addr_t ea, pa, size;
+ int tsize;
+
+ if (!(entry->mas1 & MAS1_VALID)) {
+ continue;
+ }
+
+ tsize = (entry->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT;
+ size = 1024ULL << tsize;
+ ea = entry->mas2 & ~(size - 1);
+ pa = entry->mas7_3 & ~(size - 1);
+
+ cpu_fprintf(f, "0x%016" PRIx64 " 0x%016" PRIx64 " %4s %-5u %1u S%c%c%c U%c%c%c %c%c%c%c%c U%c%c%c%c\n",
+ (uint64_t)ea, (uint64_t)pa,
+ book3e_tsize_to_str[tsize],
+ (entry->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT,
+ (entry->mas1 & MAS1_TS) >> MAS1_TS_SHIFT,
+ entry->mas7_3 & MAS3_SR ? 'R' : '-',
+ entry->mas7_3 & MAS3_SW ? 'W' : '-',
+ entry->mas7_3 & MAS3_SX ? 'X' : '-',
+ entry->mas7_3 & MAS3_UR ? 'R' : '-',
+ entry->mas7_3 & MAS3_UW ? 'W' : '-',
+ entry->mas7_3 & MAS3_UX ? 'X' : '-',
+ entry->mas2 & MAS2_W ? 'W' : '-',
+ entry->mas2 & MAS2_I ? 'I' : '-',
+ entry->mas2 & MAS2_M ? 'M' : '-',
+ entry->mas2 & MAS2_G ? 'G' : '-',
+ entry->mas2 & MAS2_E ? 'E' : '-',
+ entry->mas7_3 & MAS3_U0 ? '0' : '-',
+ entry->mas7_3 & MAS3_U1 ? '1' : '-',
+ entry->mas7_3 & MAS3_U2 ? '2' : '-',
+ entry->mas7_3 & MAS3_U3 ? '3' : '-');
+ }
+}
+
+static void mmubooke206_dump_mmu(FILE *f, fprintf_function cpu_fprintf,
+ CPUState *env)
+{
+ int offset = 0;
+ int i;
+
+ if (kvm_enabled() && !env->kvm_sw_tlb) {
+ cpu_fprintf(f, "Cannot access KVM TLB\n");
+ return;
+ }
+
+ for (i = 0; i < BOOKE206_MAX_TLBN; i++) {
+ int size = booke206_tlb_size(env, i);
+
+ if (size == 0) {
+ continue;
+ }
+
+ mmubooke206_dump_one_tlb(f, cpu_fprintf, env, i, offset, size);
+ offset += size;
+ }
+}
+
+#if defined(TARGET_PPC64)
+static void mmubooks_dump_mmu(FILE *f, fprintf_function cpu_fprintf,
+ CPUState *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);
+ }
+}
+#endif
+
+void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env)
+{
+ switch (env->mmu_model) {
+ case POWERPC_MMU_BOOKE206:
+ mmubooke206_dump_mmu(f, cpu_fprintf, env);
+ break;
+#if defined(TARGET_PPC64)
+ case POWERPC_MMU_64B:
+ case POWERPC_MMU_2_06:
+ mmubooks_dump_mmu(f, cpu_fprintf, env);
+ break;
+#endif
+ default:
+ cpu_fprintf(f, "%s: unimplemented\n", __func__);
+ }
+}
+
static inline int check_physical(CPUState *env, mmu_ctx_t *ctx,
target_ulong eaddr, int rw)
{
rw, access_type);
} else if (env->mmu_model == POWERPC_MMU_BOOKE206) {
ret = mmubooke206_get_physical_address(env, ctx, eaddr, rw,
- access_type);
+ access_type);
} else {
/* No address translation. */
ret = check_physical(env, ctx, eaddr, rw);
/* Perform address translation */
int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
- int mmu_idx, int is_softmmu)
+ int mmu_idx)
{
mmu_ctx_t ctx;
int access_type;
env->exception_index = POWERPC_EXCP_DTLB;
env->error_code = 0;
env->spr[SPR_BOOKE_DEAR] = address;
- env->spr[SPR_BOOKE_ESR] = rw ? 1 << ESR_ST : 0;
+ env->spr[SPR_BOOKE_ESR] = rw ? ESR_ST : 0;
return -1;
case POWERPC_MMU_REAL:
cpu_abort(env, "PowerPC in real mode should never raise "
} else if ((env->mmu_model == POWERPC_MMU_BOOKE) ||
(env->mmu_model == POWERPC_MMU_BOOKE206)) {
env->spr[SPR_BOOKE_DEAR] = address;
- env->spr[SPR_BOOKE_ESR] = rw ? 1 << ESR_ST : 0;
+ env->spr[SPR_BOOKE_ESR] = rw ? ESR_ST : 0;
} else {
env->spr[SPR_DAR] = address;
if (rw == 1) {
if (lpes1 == 0)
new_msr |= (target_ulong)MSR_HVB;
msr |= 0x00080000;
+ env->spr[SPR_BOOKE_ESR] = ESR_PIL;
break;
case POWERPC_EXCP_PRIV:
if (lpes1 == 0)
new_msr |= (target_ulong)MSR_HVB;
msr |= 0x00040000;
+ env->spr[SPR_BOOKE_ESR] = ESR_PPR;
break;
case POWERPC_EXCP_TRAP:
if (lpes1 == 0)
new_msr |= (target_ulong)MSR_HVB;
msr |= 0x00020000;
+ env->spr[SPR_BOOKE_ESR] = ESR_PTR;
break;
default:
/* Should never occur */
cpu_abort(env, "Debug exception is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */
+ env->spr[SPR_BOOKE_ESR] = ESR_SPV;
goto store_current;
case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */
/* XXX: TODO */
cpu_abort(env, "Embedded floating point data exception "
"is not implemented yet !\n");
+ env->spr[SPR_BOOKE_ESR] = ESR_SPV;
goto store_next;
case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */
/* XXX: TODO */
cpu_abort(env, "Embedded floating point round exception "
"is not implemented yet !\n");
+ env->spr[SPR_BOOKE_ESR] = ESR_SPV;
goto store_next;
case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */
/* XXX: TODO */
"Performance counter exception is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */
- /* XXX: TODO */
- cpu_abort(env,
- "Embedded doorbell interrupt is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */
- switch (excp_model) {
- case POWERPC_EXCP_BOOKE:
- srr0 = SPR_BOOKE_CSRR0;
- srr1 = SPR_BOOKE_CSRR1;
- break;
- default:
- break;
- }
- /* XXX: TODO */
- cpu_abort(env, "Embedded doorbell critical interrupt "
- "is not implemented yet !\n");
+ srr0 = SPR_BOOKE_CSRR0;
+ srr1 = SPR_BOOKE_CSRR1;
goto store_next;
case POWERPC_EXCP_RESET: /* System reset exception */
if (msr_pow) {
TARGET_FMT_lx "\n", RA, msr);
}
-void cpu_reset(CPUPPCState *env)
+void cpu_state_reset(CPUPPCState *env)
{
target_ulong msr;
if (!def)
return NULL;
- env = qemu_mallocz(sizeof(CPUPPCState));
+ env = g_malloc0(sizeof(CPUPPCState));
cpu_exec_init(env);
- ppc_translate_init();
+ if (tcg_enabled()) {
+ ppc_translate_init();
+ }
+ /* Adjust cpu index for SMT */
+#if !defined(CONFIG_USER_ONLY)
+ if (kvm_enabled()) {
+ int smt = kvmppc_smt_threads();
+
+ env->cpu_index = (env->cpu_index / smp_threads)*smt
+ + (env->cpu_index % smp_threads);
+ }
+#endif /* !CONFIG_USER_ONLY */
env->cpu_model_str = cpu_model;
cpu_ppc_register_internal(env, def);
void cpu_ppc_close (CPUPPCState *env)
{
/* Should also remove all opcode tables... */
- qemu_free(env);
+ g_free(env);
}