int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, int rwx, int mmu_idx) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; ppc_slb_t *slb; hwaddr pte_offset; ppc_hash_pte64_t pte; int pp_prot, amr_prot, prot; uint64_t new_pte1; const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; hwaddr raddr; assert((rwx == 0) || (rwx == 1) || (rwx == 2)); /* 1. Handle real mode accesses */ if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { /* Translation is off */ /* In real mode the top 4 effective address bits are ignored */ raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, TARGET_PAGE_SIZE); return 0; } /* 2. Translation is on, so look up the SLB */ slb = slb_lookup(cpu, eaddr); if (!slb) { if (rwx == 2) { cs->exception_index = POWERPC_EXCP_ISEG; env->error_code = 0; } else { cs->exception_index = POWERPC_EXCP_DSEG; env->error_code = 0; env->spr[SPR_DAR] = eaddr; } return 1; } /* 3. Check for segment level no-execute violation */ if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) { cs->exception_index = POWERPC_EXCP_ISI; env->error_code = 0x10000000; return 1; } /* 4. Locate the PTE in the hash table */ pte_offset = ppc_hash64_htab_lookup(cpu, slb, eaddr, &pte); if (pte_offset == -1) { if (rwx == 2) { cs->exception_index = POWERPC_EXCP_ISI; env->error_code = 0x40000000; } else { cs->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; env->spr[SPR_DAR] = eaddr; if (rwx == 1) { env->spr[SPR_DSISR] = 0x42000000; } else { env->spr[SPR_DSISR] = 0x40000000; } } return 1; } qemu_log_mask(CPU_LOG_MMU, "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); /* 5. Check access permissions */ pp_prot = ppc_hash64_pte_prot(cpu, slb, pte); amr_prot = ppc_hash64_amr_prot(cpu, pte); prot = pp_prot & amr_prot; if ((need_prot[rwx] & ~prot) != 0) { /* Access right violation */ qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); if (rwx == 2) { cs->exception_index = POWERPC_EXCP_ISI; env->error_code = 0x08000000; } else { target_ulong dsisr = 0; cs->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; env->spr[SPR_DAR] = eaddr; if (need_prot[rwx] & ~pp_prot) { dsisr |= 0x08000000; } if (rwx == 1) { dsisr |= 0x02000000; } if (need_prot[rwx] & ~amr_prot) { dsisr |= 0x00200000; } env->spr[SPR_DSISR] = dsisr; } return 1; } qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); /* 6. Update PTE referenced and changed bits if necessary */ new_pte1 = pte.pte1 | HPTE64_R_R; /* set referenced bit */ if (rwx == 1) { new_pte1 |= HPTE64_R_C; /* set changed (dirty) bit */ } else { /* Treat the page as read-only for now, so that a later write * will pass through this function again to set the C bit */ prot &= ~PAGE_WRITE; } if (new_pte1 != pte.pte1) { ppc_hash64_store_hpte(cpu, pte_offset / HASH_PTE_SIZE_64, pte.pte0, new_pte1); } /* 7. Determine the real address from the PTE */ raddr = deposit64(pte.pte1 & HPTE64_R_RPN, 0, slb->sps->page_shift, eaddr); tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, prot, mmu_idx, TARGET_PAGE_SIZE); return 0; }