aboutsummaryrefslogtreecommitdiff
path: root/target-ppc/op_helper.c
diff options
context:
space:
mode:
authorAlexander Graf <agraf@suse.de>2011-04-30 23:34:58 +0200
committerAlexander Graf <agraf@suse.de>2011-05-12 00:24:51 +0200
commit01662f3e513399107c7278f2ee8f6ee4cf03fa70 (patch)
treeb46451002c0cbfb049afc83acfd416f18b81a709 /target-ppc/op_helper.c
parenta5858d7af064c01d9b634399b62f641386eacfcf (diff)
PPC: Implement e500 (FSL) MMU
Most of the code to support e500 style MMUs is already in place, but we're missing on some of the special TLB0-TLB1 handling code and slightly different TLB modification. This patch adds support for the FSL style MMU. Signed-off-by: Alexander Graf <agraf@suse.de>
Diffstat (limited to 'target-ppc/op_helper.c')
-rw-r--r--target-ppc/op_helper.c296
1 files changed, 296 insertions, 0 deletions
diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
index d5db484b4a..e165444102 100644
--- a/target-ppc/op_helper.c
+++ b/target-ppc/op_helper.c
@@ -4206,4 +4206,300 @@ target_ulong helper_440_tlbsx (target_ulong address)
return ppcemb_tlb_search(env, address, env->spr[SPR_440_MMUCR] & 0xFF);
}
+/* PowerPC BookE 2.06 TLB management */
+
+static ppcemb_tlb_t *booke206_cur_tlb(CPUState *env)
+{
+ uint32_t tlbncfg = 0;
+ int esel = (env->spr[SPR_BOOKE_MAS0] & MAS0_ESEL_MASK) >> MAS0_ESEL_SHIFT;
+ int ea = (env->spr[SPR_BOOKE_MAS2] & MAS2_EPN_MASK);
+ int tlb;
+
+ tlb = (env->spr[SPR_BOOKE_MAS0] & MAS0_TLBSEL_MASK) >> MAS0_TLBSEL_SHIFT;
+ tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlb];
+
+ if ((tlbncfg & TLBnCFG_HES) && (env->spr[SPR_BOOKE_MAS0] & MAS0_HES)) {
+ cpu_abort(env, "we don't support HES yet\n");
+ }
+
+ return booke206_get_tlbe(env, tlb, ea, esel);
+}
+
+static inline target_phys_addr_t booke206_tlb_to_page_size(int size)
+{
+ return (1 << (size << 1)) << 10;
+}
+
+static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size)
+{
+ return (ffs(size >> 10) - 1) >> 1;
+}
+
+void helper_booke_setpid(uint32_t pidn, target_ulong pid)
+{
+ env->spr[pidn] = pid;
+ /* changing PIDs mean we're in a different address space now */
+ tlb_flush(env, 1);
+}
+
+void helper_booke206_tlbwe(void)
+{
+ uint32_t tlbncfg, tlbn;
+ ppcemb_tlb_t *tlb;
+ target_phys_addr_t rpn;
+ int tlbe_size;
+
+ switch (env->spr[SPR_BOOKE_MAS0] & MAS0_WQ_MASK) {
+ case MAS0_WQ_ALWAYS:
+ /* good to go, write that entry */
+ break;
+ case MAS0_WQ_COND:
+ /* XXX check if reserved */
+ if (0) {
+ return;
+ }
+ break;
+ case MAS0_WQ_CLR_RSRV:
+ /* XXX clear entry */
+ return;
+ default:
+ /* no idea what to do */
+ return;
+ }
+
+ if (((env->spr[SPR_BOOKE_MAS0] & MAS0_ATSEL) == MAS0_ATSEL_LRAT) &&
+ !msr_gs) {
+ /* XXX we don't support direct LRAT setting yet */
+ fprintf(stderr, "cpu: don't support LRAT setting yet\n");
+ return;
+ }
+
+ tlbn = (env->spr[SPR_BOOKE_MAS0] & MAS0_TLBSEL_MASK) >> MAS0_TLBSEL_SHIFT;
+ tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlbn];
+
+ tlb = booke206_cur_tlb(env);
+
+ if (msr_gs) {
+ cpu_abort(env, "missing HV implementation\n");
+ } else {
+ rpn = ((uint64_t)env->spr[SPR_BOOKE_MAS7] << 32) |
+ (env->spr[SPR_BOOKE_MAS3] & 0xfffff000);
+ }
+ tlb->RPN = rpn;
+
+ tlb->PID = (env->spr[SPR_BOOKE_MAS1] & MAS1_TID_MASK) >> MAS1_TID_SHIFT;
+ if (tlbncfg & TLBnCFG_AVAIL) {
+ tlbe_size = (env->spr[SPR_BOOKE_MAS1] & MAS1_TSIZE_MASK)
+ >> MAS1_TSIZE_SHIFT;
+ } else {
+ tlbe_size = (tlbncfg & TLBnCFG_MINSIZE) >> TLBnCFG_MINSIZE_SHIFT;
+ }
+
+ tlb->size = booke206_tlb_to_page_size(tlbe_size);
+ tlb->EPN = (uint32_t)(env->spr[SPR_BOOKE_MAS2] & MAS2_EPN_MASK);
+ tlb->attr = env->spr[SPR_BOOKE_MAS2] & (MAS2_ACM | MAS2_VLE | MAS2_W |
+ MAS2_I | MAS2_M | MAS2_G | MAS2_E)
+ << 1;
+
+ if (tlbncfg & TLBnCFG_IPROT) {
+ tlb->attr |= env->spr[SPR_BOOKE_MAS1] & MAS1_IPROT;
+ }
+ tlb->attr |= (env->spr[SPR_BOOKE_MAS3] &
+ ((MAS3_U0 | MAS3_U1 | MAS3_U2 | MAS3_U3)) << 8);
+ if (env->spr[SPR_BOOKE_MAS1] & MAS1_TS) {
+ tlb->attr |= 1;
+ }
+
+ tlb->prot = 0;
+
+ if (env->spr[SPR_BOOKE_MAS1] & MAS1_VALID) {
+ tlb->prot |= PAGE_VALID;
+ }
+ if (env->spr[SPR_BOOKE_MAS3] & MAS3_UX) {
+ tlb->prot |= PAGE_EXEC;
+ }
+ if (env->spr[SPR_BOOKE_MAS3] & MAS3_SX) {
+ tlb->prot |= PAGE_EXEC << 4;
+ }
+ if (env->spr[SPR_BOOKE_MAS3] & MAS3_UW) {
+ tlb->prot |= PAGE_WRITE;
+ }
+ if (env->spr[SPR_BOOKE_MAS3] & MAS3_SW) {
+ tlb->prot |= PAGE_WRITE << 4;
+ }
+ if (env->spr[SPR_BOOKE_MAS3] & MAS3_UR) {
+ tlb->prot |= PAGE_READ;
+ }
+ if (env->spr[SPR_BOOKE_MAS3] & MAS3_SR) {
+ tlb->prot |= PAGE_READ << 4;
+ }
+
+ if (tlb->size == TARGET_PAGE_SIZE) {
+ tlb_flush_page(env, tlb->EPN);
+ } else {
+ tlb_flush(env, 1);
+ }
+}
+
+static inline void booke206_tlb_to_mas(CPUState *env, ppcemb_tlb_t *tlb)
+{
+ int tlbn = booke206_tlbe_to_tlbn(env, tlb);
+ int way = booke206_tlbe_to_way(env, tlb);
+
+ env->spr[SPR_BOOKE_MAS0] = tlbn << MAS0_TLBSEL_SHIFT;
+ env->spr[SPR_BOOKE_MAS0] |= way << MAS0_ESEL_SHIFT;
+
+ env->spr[SPR_BOOKE_MAS1] = MAS1_VALID;
+ env->spr[SPR_BOOKE_MAS2] = 0;
+
+ env->spr[SPR_BOOKE_MAS7] = (uint64_t)tlb->RPN >> 32;
+ env->spr[SPR_BOOKE_MAS3] = tlb->RPN;
+ env->spr[SPR_BOOKE_MAS1] |= tlb->PID << MAS1_TID_SHIFT;
+ env->spr[SPR_BOOKE_MAS1] |= booke206_page_size_to_tlb(tlb->size)
+ << MAS1_TSIZE_SHIFT;
+ env->spr[SPR_BOOKE_MAS1] |= tlb->attr & MAS1_IPROT;
+ if (tlb->attr & 1) {
+ env->spr[SPR_BOOKE_MAS1] |= MAS1_TS;
+ }
+
+ env->spr[SPR_BOOKE_MAS2] = tlb->EPN;
+ env->spr[SPR_BOOKE_MAS2] |= (tlb->attr >> 1) &
+ (MAS2_ACM | MAS2_VLE | MAS2_W | MAS2_I | MAS2_M | MAS2_G | MAS2_E);
+
+ if (tlb->prot & PAGE_EXEC) {
+ env->spr[SPR_BOOKE_MAS3] |= MAS3_UX;
+ }
+ if (tlb->prot & (PAGE_EXEC << 4)) {
+ env->spr[SPR_BOOKE_MAS3] |= MAS3_SX;
+ }
+ if (tlb->prot & PAGE_WRITE) {
+ env->spr[SPR_BOOKE_MAS3] |= MAS3_UW;
+ }
+ if (tlb->prot & (PAGE_WRITE << 4)) {
+ env->spr[SPR_BOOKE_MAS3] |= MAS3_SW;
+ }
+ if (tlb->prot & PAGE_READ) {
+ env->spr[SPR_BOOKE_MAS3] |= MAS3_UR;
+ }
+ if (tlb->prot & (PAGE_READ << 4)) {
+ env->spr[SPR_BOOKE_MAS3] |= MAS3_SR;
+ }
+
+ env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT;
+}
+
+void helper_booke206_tlbre(void)
+{
+ ppcemb_tlb_t *tlb = NULL;
+
+ tlb = booke206_cur_tlb(env);
+ booke206_tlb_to_mas(env, tlb);
+}
+
+void helper_booke206_tlbsx(target_ulong address)
+{
+ ppcemb_tlb_t *tlb = NULL;
+ int i, j;
+ target_phys_addr_t raddr;
+ uint32_t spid, sas;
+
+ spid = (env->spr[SPR_BOOKE_MAS6] & MAS6_SPID_MASK) >> MAS6_SPID_SHIFT;
+ sas = env->spr[SPR_BOOKE_MAS6] & MAS6_SAS;
+
+ for (i = 0; i < BOOKE206_MAX_TLBN; i++) {
+ int ways = booke206_tlb_ways(env, i);
+
+ for (j = 0; j < ways; j++) {
+ tlb = booke206_get_tlbe(env, i, address, j);
+
+ if (ppcemb_tlb_check(env, tlb, &raddr, address, spid, 0, j)) {
+ continue;
+ }
+
+ if (sas != (tlb->attr & MAS6_SAS)) {
+ continue;
+ }
+
+ booke206_tlb_to_mas(env, tlb);
+ return;
+ }
+ }
+
+ /* no entry found, fill with defaults */
+ env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK;
+ env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK;
+ env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK;
+ env->spr[SPR_BOOKE_MAS3] = 0;
+ env->spr[SPR_BOOKE_MAS7] = 0;
+
+ if (env->spr[SPR_BOOKE_MAS6] & MAS6_SAS) {
+ env->spr[SPR_BOOKE_MAS1] |= MAS1_TS;
+ }
+
+ env->spr[SPR_BOOKE_MAS1] |= (env->spr[SPR_BOOKE_MAS6] >> 16)
+ << MAS1_TID_SHIFT;
+
+ /* next victim logic */
+ env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT;
+ env->last_way++;
+ env->last_way &= booke206_tlb_ways(env, 0) - 1;
+ env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT;
+}
+
+static inline void booke206_invalidate_ea_tlb(CPUState *env, int tlbn,
+ uint32_t ea)
+{
+ int i;
+ int ways = booke206_tlb_ways(env, tlbn);
+
+ for (i = 0; i < ways; i++) {
+ ppcemb_tlb_t *tlb = booke206_get_tlbe(env, tlbn, ea, i);
+ target_phys_addr_t masked_ea = ea & ~(tlb->size - 1);
+ if ((tlb->EPN == (masked_ea >> MAS2_EPN_SHIFT)) &&
+ !(tlb->attr & MAS1_IPROT)) {
+ tlb->prot = 0;
+ }
+ }
+}
+
+void helper_booke206_tlbivax(target_ulong address)
+{
+ if (address & 0x4) {
+ /* flush all entries */
+ if (address & 0x8) {
+ /* flush all of TLB1 */
+ booke206_flush_tlb(env, BOOKE206_FLUSH_TLB1, 1);
+ } else {
+ /* flush all of TLB0 */
+ booke206_flush_tlb(env, BOOKE206_FLUSH_TLB0, 0);
+ }
+ return;
+ }
+
+ if (address & 0x8) {
+ /* flush TLB1 entries */
+ booke206_invalidate_ea_tlb(env, 1, address);
+ tlb_flush(env, 1);
+ } else {
+ /* flush TLB0 entries */
+ booke206_invalidate_ea_tlb(env, 0, address);
+ tlb_flush_page(env, address & MAS2_EPN_MASK);
+ }
+}
+
+void helper_booke206_tlbflush(uint32_t type)
+{
+ int flags = 0;
+
+ if (type & 2) {
+ flags |= BOOKE206_FLUSH_TLB1;
+ }
+
+ if (type & 4) {
+ flags |= BOOKE206_FLUSH_TLB0;
+ }
+
+ booke206_flush_tlb(env, flags, 1);
+}
+
#endif /* !CONFIG_USER_ONLY */