289 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			289 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 *  MicroBlaze helper routines.
 | 
						|
 *
 | 
						|
 *  Copyright (c) 2009 Edgar E. Iglesias <edgar.iglesias@gmail.com>
 | 
						|
 *  Copyright (c) 2009-2012 PetaLogix Qld Pty Ltd.
 | 
						|
 *
 | 
						|
 * 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, see <http://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
 | 
						|
#include "cpu.h"
 | 
						|
#include "qemu/host-utils.h"
 | 
						|
 | 
						|
#define D(x)
 | 
						|
#define DMMU(x)
 | 
						|
 | 
						|
#if defined(CONFIG_USER_ONLY)
 | 
						|
 | 
						|
void mb_cpu_do_interrupt(CPUState *cs)
 | 
						|
{
 | 
						|
    MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs);
 | 
						|
    CPUMBState *env = &cpu->env;
 | 
						|
 | 
						|
    cs->exception_index = -1;
 | 
						|
    env->res_addr = RES_ADDR_NONE;
 | 
						|
    env->regs[14] = env->sregs[SR_PC];
 | 
						|
}
 | 
						|
 | 
						|
int mb_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
 | 
						|
                            int mmu_idx)
 | 
						|
{
 | 
						|
    cs->exception_index = 0xaa;
 | 
						|
    cpu_dump_state(cs, stderr, fprintf, 0);
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
#else /* !CONFIG_USER_ONLY */
 | 
						|
 | 
						|
int mb_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
 | 
						|
                            int mmu_idx)
 | 
						|
{
 | 
						|
    MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs);
 | 
						|
    CPUMBState *env = &cpu->env;
 | 
						|
    unsigned int hit;
 | 
						|
    unsigned int mmu_available;
 | 
						|
    int r = 1;
 | 
						|
    int prot;
 | 
						|
 | 
						|
    mmu_available = 0;
 | 
						|
    if (env->pvr.regs[0] & PVR0_USE_MMU) {
 | 
						|
        mmu_available = 1;
 | 
						|
        if ((env->pvr.regs[0] & PVR0_PVR_FULL_MASK)
 | 
						|
            && (env->pvr.regs[11] & PVR11_USE_MMU) != PVR11_USE_MMU) {
 | 
						|
            mmu_available = 0;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /* Translate if the MMU is available and enabled.  */
 | 
						|
    if (mmu_available && (env->sregs[SR_MSR] & MSR_VM)) {
 | 
						|
        target_ulong vaddr, paddr;
 | 
						|
        struct microblaze_mmu_lookup lu;
 | 
						|
 | 
						|
        hit = mmu_translate(&env->mmu, &lu, address, rw, mmu_idx);
 | 
						|
        if (hit) {
 | 
						|
            vaddr = address & TARGET_PAGE_MASK;
 | 
						|
            paddr = lu.paddr + vaddr - lu.vaddr;
 | 
						|
 | 
						|
            DMMU(qemu_log("MMU map mmu=%d v=%x p=%x prot=%x\n",
 | 
						|
                     mmu_idx, vaddr, paddr, lu.prot));
 | 
						|
            tlb_set_page(cs, vaddr, paddr, lu.prot, mmu_idx, TARGET_PAGE_SIZE);
 | 
						|
            r = 0;
 | 
						|
        } else {
 | 
						|
            env->sregs[SR_EAR] = address;
 | 
						|
            DMMU(qemu_log("mmu=%d miss v=%x\n", mmu_idx, address));
 | 
						|
 | 
						|
            switch (lu.err) {
 | 
						|
                case ERR_PROT:
 | 
						|
                    env->sregs[SR_ESR] = rw == 2 ? 17 : 16;
 | 
						|
                    env->sregs[SR_ESR] |= (rw == 1) << 10;
 | 
						|
                    break;
 | 
						|
                case ERR_MISS:
 | 
						|
                    env->sregs[SR_ESR] = rw == 2 ? 19 : 18;
 | 
						|
                    env->sregs[SR_ESR] |= (rw == 1) << 10;
 | 
						|
                    break;
 | 
						|
                default:
 | 
						|
                    abort();
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
 | 
						|
            if (cs->exception_index == EXCP_MMU) {
 | 
						|
                cpu_abort(cs, "recursive faults\n");
 | 
						|
            }
 | 
						|
 | 
						|
            /* TLB miss.  */
 | 
						|
            cs->exception_index = EXCP_MMU;
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        /* MMU disabled or not available.  */
 | 
						|
        address &= TARGET_PAGE_MASK;
 | 
						|
        prot = PAGE_BITS;
 | 
						|
        tlb_set_page(cs, address, address, prot, mmu_idx, TARGET_PAGE_SIZE);
 | 
						|
        r = 0;
 | 
						|
    }
 | 
						|
    return r;
 | 
						|
}
 | 
						|
 | 
						|
void mb_cpu_do_interrupt(CPUState *cs)
 | 
						|
{
 | 
						|
    MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs);
 | 
						|
    CPUMBState *env = &cpu->env;
 | 
						|
    uint32_t t;
 | 
						|
 | 
						|
    /* IMM flag cannot propagate across a branch and into the dslot.  */
 | 
						|
    assert(!((env->iflags & D_FLAG) && (env->iflags & IMM_FLAG)));
 | 
						|
    assert(!(env->iflags & (DRTI_FLAG | DRTE_FLAG | DRTB_FLAG)));
 | 
						|
/*    assert(env->sregs[SR_MSR] & (MSR_EE)); Only for HW exceptions.  */
 | 
						|
    env->res_addr = RES_ADDR_NONE;
 | 
						|
    switch (cs->exception_index) {
 | 
						|
        case EXCP_HW_EXCP:
 | 
						|
            if (!(env->pvr.regs[0] & PVR0_USE_EXC_MASK)) {
 | 
						|
                qemu_log("Exception raised on system without exceptions!\n");
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            env->regs[17] = env->sregs[SR_PC] + 4;
 | 
						|
            env->sregs[SR_ESR] &= ~(1 << 12);
 | 
						|
 | 
						|
            /* Exception breaks branch + dslot sequence?  */
 | 
						|
            if (env->iflags & D_FLAG) {
 | 
						|
                env->sregs[SR_ESR] |= 1 << 12 ;
 | 
						|
                env->sregs[SR_BTR] = env->btarget;
 | 
						|
            }
 | 
						|
 | 
						|
            /* Disable the MMU.  */
 | 
						|
            t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1;
 | 
						|
            env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM);
 | 
						|
            env->sregs[SR_MSR] |= t;
 | 
						|
            /* Exception in progress.  */
 | 
						|
            env->sregs[SR_MSR] |= MSR_EIP;
 | 
						|
 | 
						|
            qemu_log_mask(CPU_LOG_INT,
 | 
						|
                          "hw exception at pc=%x ear=%x esr=%x iflags=%x\n",
 | 
						|
                          env->sregs[SR_PC], env->sregs[SR_EAR],
 | 
						|
                          env->sregs[SR_ESR], env->iflags);
 | 
						|
            log_cpu_state_mask(CPU_LOG_INT, cs, 0);
 | 
						|
            env->iflags &= ~(IMM_FLAG | D_FLAG);
 | 
						|
            env->sregs[SR_PC] = cpu->base_vectors + 0x20;
 | 
						|
            break;
 | 
						|
 | 
						|
        case EXCP_MMU:
 | 
						|
            env->regs[17] = env->sregs[SR_PC];
 | 
						|
 | 
						|
            env->sregs[SR_ESR] &= ~(1 << 12);
 | 
						|
            /* Exception breaks branch + dslot sequence?  */
 | 
						|
            if (env->iflags & D_FLAG) {
 | 
						|
                D(qemu_log("D_FLAG set at exception bimm=%d\n", env->bimm));
 | 
						|
                env->sregs[SR_ESR] |= 1 << 12 ;
 | 
						|
                env->sregs[SR_BTR] = env->btarget;
 | 
						|
 | 
						|
                /* Reexecute the branch.  */
 | 
						|
                env->regs[17] -= 4;
 | 
						|
                /* was the branch immprefixed?.  */
 | 
						|
                if (env->bimm) {
 | 
						|
                    qemu_log_mask(CPU_LOG_INT,
 | 
						|
                                  "bimm exception at pc=%x iflags=%x\n",
 | 
						|
                                  env->sregs[SR_PC], env->iflags);
 | 
						|
                    env->regs[17] -= 4;
 | 
						|
                    log_cpu_state_mask(CPU_LOG_INT, cs, 0);
 | 
						|
                }
 | 
						|
            } else if (env->iflags & IMM_FLAG) {
 | 
						|
                D(qemu_log("IMM_FLAG set at exception\n"));
 | 
						|
                env->regs[17] -= 4;
 | 
						|
            }
 | 
						|
 | 
						|
            /* Disable the MMU.  */
 | 
						|
            t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1;
 | 
						|
            env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM);
 | 
						|
            env->sregs[SR_MSR] |= t;
 | 
						|
            /* Exception in progress.  */
 | 
						|
            env->sregs[SR_MSR] |= MSR_EIP;
 | 
						|
 | 
						|
            qemu_log_mask(CPU_LOG_INT,
 | 
						|
                          "exception at pc=%x ear=%x iflags=%x\n",
 | 
						|
                          env->sregs[SR_PC], env->sregs[SR_EAR], env->iflags);
 | 
						|
            log_cpu_state_mask(CPU_LOG_INT, cs, 0);
 | 
						|
            env->iflags &= ~(IMM_FLAG | D_FLAG);
 | 
						|
            env->sregs[SR_PC] = cpu->base_vectors + 0x20;
 | 
						|
            break;
 | 
						|
 | 
						|
        case EXCP_IRQ:
 | 
						|
            assert(!(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP)));
 | 
						|
            assert(env->sregs[SR_MSR] & MSR_IE);
 | 
						|
            assert(!(env->iflags & D_FLAG));
 | 
						|
 | 
						|
            t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1;
 | 
						|
 | 
						|
#if 0
 | 
						|
#include "disas/disas.h"
 | 
						|
 | 
						|
/* Useful instrumentation when debugging interrupt issues in either
 | 
						|
   the models or in sw.  */
 | 
						|
            {
 | 
						|
                const char *sym;
 | 
						|
 | 
						|
                sym = lookup_symbol(env->sregs[SR_PC]);
 | 
						|
                if (sym
 | 
						|
                    && (!strcmp("netif_rx", sym)
 | 
						|
                        || !strcmp("process_backlog", sym))) {
 | 
						|
 | 
						|
                    qemu_log(
 | 
						|
                         "interrupt at pc=%x msr=%x %x iflags=%x sym=%s\n",
 | 
						|
                         env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags,
 | 
						|
                         sym);
 | 
						|
 | 
						|
                    log_cpu_state(cs, 0);
 | 
						|
                }
 | 
						|
            }
 | 
						|
#endif
 | 
						|
            qemu_log_mask(CPU_LOG_INT,
 | 
						|
                         "interrupt at pc=%x msr=%x %x iflags=%x\n",
 | 
						|
                         env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags);
 | 
						|
 | 
						|
            env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM \
 | 
						|
                                    | MSR_UM | MSR_IE);
 | 
						|
            env->sregs[SR_MSR] |= t;
 | 
						|
 | 
						|
            env->regs[14] = env->sregs[SR_PC];
 | 
						|
            env->sregs[SR_PC] = cpu->base_vectors + 0x10;
 | 
						|
            //log_cpu_state_mask(CPU_LOG_INT, cs, 0);
 | 
						|
            break;
 | 
						|
 | 
						|
        case EXCP_BREAK:
 | 
						|
        case EXCP_HW_BREAK:
 | 
						|
            assert(!(env->iflags & IMM_FLAG));
 | 
						|
            assert(!(env->iflags & D_FLAG));
 | 
						|
            t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1;
 | 
						|
            qemu_log_mask(CPU_LOG_INT,
 | 
						|
                        "break at pc=%x msr=%x %x iflags=%x\n",
 | 
						|
                        env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags);
 | 
						|
            log_cpu_state_mask(CPU_LOG_INT, cs, 0);
 | 
						|
            env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM);
 | 
						|
            env->sregs[SR_MSR] |= t;
 | 
						|
            env->sregs[SR_MSR] |= MSR_BIP;
 | 
						|
            if (cs->exception_index == EXCP_HW_BREAK) {
 | 
						|
                env->regs[16] = env->sregs[SR_PC];
 | 
						|
                env->sregs[SR_MSR] |= MSR_BIP;
 | 
						|
                env->sregs[SR_PC] = cpu->base_vectors + 0x18;
 | 
						|
            } else
 | 
						|
                env->sregs[SR_PC] = env->btarget;
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            cpu_abort(cs, "unhandled exception type=%d\n",
 | 
						|
                      cs->exception_index);
 | 
						|
            break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
hwaddr mb_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
 | 
						|
{
 | 
						|
    MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs);
 | 
						|
    CPUMBState *env = &cpu->env;
 | 
						|
    target_ulong vaddr, paddr = 0;
 | 
						|
    struct microblaze_mmu_lookup lu;
 | 
						|
    unsigned int hit;
 | 
						|
 | 
						|
    if (env->sregs[SR_MSR] & MSR_VM) {
 | 
						|
        hit = mmu_translate(&env->mmu, &lu, addr, 0, 0);
 | 
						|
        if (hit) {
 | 
						|
            vaddr = addr & TARGET_PAGE_MASK;
 | 
						|
            paddr = lu.paddr + vaddr - lu.vaddr;
 | 
						|
        } else
 | 
						|
            paddr = 0; /* ???.  */
 | 
						|
    } else
 | 
						|
        paddr = addr & TARGET_PAGE_MASK;
 | 
						|
 | 
						|
    return paddr;
 | 
						|
}
 | 
						|
#endif
 |