bf1b52d199
There is no "version 2" of the "Lesser" General Public License. It is either "GPL version 2.0" or "Lesser GPL version 2.1". This patch replaces all occurrences of "Lesser GPL version 2" with "Lesser GPL version 2.1" in comment section. Signed-off-by: Chetan Pant <chetan4windows@gmail.com> Message-Id: <20201023121649.19123-1-chetan4windows@gmail.com> Reviewed-by: Thomas Huth <thuth@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com>
321 lines
8.9 KiB
C
321 lines
8.9 KiB
C
/*
|
|
* CRIS helper routines.
|
|
*
|
|
* Copyright (c) 2007 AXIS Communications AB
|
|
* Written by Edgar E. Iglesias.
|
|
*
|
|
* 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.1 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 "qemu/osdep.h"
|
|
#include "cpu.h"
|
|
#include "mmu.h"
|
|
#include "qemu/host-utils.h"
|
|
#include "exec/exec-all.h"
|
|
#include "exec/cpu_ldst.h"
|
|
#include "exec/helper-proto.h"
|
|
|
|
|
|
//#define CRIS_HELPER_DEBUG
|
|
|
|
|
|
#ifdef CRIS_HELPER_DEBUG
|
|
#define D(x) x
|
|
#define D_LOG(...) qemu_log(__VA_ARGS__)
|
|
#else
|
|
#define D(x)
|
|
#define D_LOG(...) do { } while (0)
|
|
#endif
|
|
|
|
#if defined(CONFIG_USER_ONLY)
|
|
|
|
void cris_cpu_do_interrupt(CPUState *cs)
|
|
{
|
|
CRISCPU *cpu = CRIS_CPU(cs);
|
|
CPUCRISState *env = &cpu->env;
|
|
|
|
cs->exception_index = -1;
|
|
env->pregs[PR_ERP] = env->pc;
|
|
}
|
|
|
|
void crisv10_cpu_do_interrupt(CPUState *cs)
|
|
{
|
|
cris_cpu_do_interrupt(cs);
|
|
}
|
|
|
|
bool cris_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
|
MMUAccessType access_type, int mmu_idx,
|
|
bool probe, uintptr_t retaddr)
|
|
{
|
|
CRISCPU *cpu = CRIS_CPU(cs);
|
|
|
|
cs->exception_index = 0xaa;
|
|
cpu->env.pregs[PR_EDA] = address;
|
|
cpu_loop_exit_restore(cs, retaddr);
|
|
}
|
|
|
|
#else /* !CONFIG_USER_ONLY */
|
|
|
|
|
|
static void cris_shift_ccs(CPUCRISState *env)
|
|
{
|
|
uint32_t ccs;
|
|
/* Apply the ccs shift. */
|
|
ccs = env->pregs[PR_CCS];
|
|
ccs = ((ccs & 0xc0000000) | ((ccs << 12) >> 2)) & ~0x3ff;
|
|
env->pregs[PR_CCS] = ccs;
|
|
}
|
|
|
|
bool cris_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
|
MMUAccessType access_type, int mmu_idx,
|
|
bool probe, uintptr_t retaddr)
|
|
{
|
|
CRISCPU *cpu = CRIS_CPU(cs);
|
|
CPUCRISState *env = &cpu->env;
|
|
struct cris_mmu_result res;
|
|
int prot, miss;
|
|
target_ulong phy;
|
|
|
|
miss = cris_mmu_translate(&res, env, address & TARGET_PAGE_MASK,
|
|
access_type, mmu_idx, 0);
|
|
if (likely(!miss)) {
|
|
/*
|
|
* Mask off the cache selection bit. The ETRAX busses do not
|
|
* see the top bit.
|
|
*/
|
|
phy = res.phy & ~0x80000000;
|
|
prot = res.prot;
|
|
tlb_set_page(cs, address & TARGET_PAGE_MASK, phy,
|
|
prot, mmu_idx, TARGET_PAGE_SIZE);
|
|
return true;
|
|
}
|
|
|
|
if (probe) {
|
|
return false;
|
|
}
|
|
|
|
if (cs->exception_index == EXCP_BUSFAULT) {
|
|
cpu_abort(cs, "CRIS: Illegal recursive bus fault."
|
|
"addr=%" VADDR_PRIx " access_type=%d\n",
|
|
address, access_type);
|
|
}
|
|
|
|
env->pregs[PR_EDA] = address;
|
|
cs->exception_index = EXCP_BUSFAULT;
|
|
env->fault_vector = res.bf_vec;
|
|
if (retaddr) {
|
|
if (cpu_restore_state(cs, retaddr, true)) {
|
|
/* Evaluate flags after retranslation. */
|
|
helper_top_evaluate_flags(env);
|
|
}
|
|
}
|
|
cpu_loop_exit(cs);
|
|
}
|
|
|
|
void crisv10_cpu_do_interrupt(CPUState *cs)
|
|
{
|
|
CRISCPU *cpu = CRIS_CPU(cs);
|
|
CPUCRISState *env = &cpu->env;
|
|
int ex_vec = -1;
|
|
|
|
D_LOG("exception index=%d interrupt_req=%d\n",
|
|
cs->exception_index,
|
|
cs->interrupt_request);
|
|
|
|
if (env->dslot) {
|
|
/* CRISv10 never takes interrupts while in a delay-slot. */
|
|
cpu_abort(cs, "CRIS: Interrupt on delay-slot\n");
|
|
}
|
|
|
|
assert(!(env->pregs[PR_CCS] & PFIX_FLAG));
|
|
switch (cs->exception_index) {
|
|
case EXCP_BREAK:
|
|
/* These exceptions are genereated by the core itself.
|
|
ERP should point to the insn following the brk. */
|
|
ex_vec = env->trap_vector;
|
|
env->pregs[PRV10_BRP] = env->pc;
|
|
break;
|
|
|
|
case EXCP_NMI:
|
|
/* NMI is hardwired to vector zero. */
|
|
ex_vec = 0;
|
|
env->pregs[PR_CCS] &= ~M_FLAG_V10;
|
|
env->pregs[PRV10_BRP] = env->pc;
|
|
break;
|
|
|
|
case EXCP_BUSFAULT:
|
|
cpu_abort(cs, "Unhandled busfault");
|
|
break;
|
|
|
|
default:
|
|
/* The interrupt controller gives us the vector. */
|
|
ex_vec = env->interrupt_vector;
|
|
/* Normal interrupts are taken between
|
|
TB's. env->pc is valid here. */
|
|
env->pregs[PR_ERP] = env->pc;
|
|
break;
|
|
}
|
|
|
|
if (env->pregs[PR_CCS] & U_FLAG) {
|
|
/* Swap stack pointers. */
|
|
env->pregs[PR_USP] = env->regs[R_SP];
|
|
env->regs[R_SP] = env->ksp;
|
|
}
|
|
|
|
/* Now that we are in kernel mode, load the handlers address. */
|
|
env->pc = cpu_ldl_code(env, env->pregs[PR_EBP] + ex_vec * 4);
|
|
env->locked_irq = 1;
|
|
env->pregs[PR_CCS] |= F_FLAG_V10; /* set F. */
|
|
|
|
qemu_log_mask(CPU_LOG_INT, "%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n",
|
|
__func__, env->pc, ex_vec,
|
|
env->pregs[PR_CCS],
|
|
env->pregs[PR_PID],
|
|
env->pregs[PR_ERP]);
|
|
}
|
|
|
|
void cris_cpu_do_interrupt(CPUState *cs)
|
|
{
|
|
CRISCPU *cpu = CRIS_CPU(cs);
|
|
CPUCRISState *env = &cpu->env;
|
|
int ex_vec = -1;
|
|
|
|
D_LOG("exception index=%d interrupt_req=%d\n",
|
|
cs->exception_index,
|
|
cs->interrupt_request);
|
|
|
|
switch (cs->exception_index) {
|
|
case EXCP_BREAK:
|
|
/* These exceptions are genereated by the core itself.
|
|
ERP should point to the insn following the brk. */
|
|
ex_vec = env->trap_vector;
|
|
env->pregs[PR_ERP] = env->pc;
|
|
break;
|
|
|
|
case EXCP_NMI:
|
|
/* NMI is hardwired to vector zero. */
|
|
ex_vec = 0;
|
|
env->pregs[PR_CCS] &= ~M_FLAG_V32;
|
|
env->pregs[PR_NRP] = env->pc;
|
|
break;
|
|
|
|
case EXCP_BUSFAULT:
|
|
ex_vec = env->fault_vector;
|
|
env->pregs[PR_ERP] = env->pc;
|
|
break;
|
|
|
|
default:
|
|
/* The interrupt controller gives us the vector. */
|
|
ex_vec = env->interrupt_vector;
|
|
/* Normal interrupts are taken between
|
|
TB's. env->pc is valid here. */
|
|
env->pregs[PR_ERP] = env->pc;
|
|
break;
|
|
}
|
|
|
|
/* Fill in the IDX field. */
|
|
env->pregs[PR_EXS] = (ex_vec & 0xff) << 8;
|
|
|
|
if (env->dslot) {
|
|
D_LOG("excp isr=%x PC=%x ds=%d SP=%x"
|
|
" ERP=%x pid=%x ccs=%x cc=%d %x\n",
|
|
ex_vec, env->pc, env->dslot,
|
|
env->regs[R_SP],
|
|
env->pregs[PR_ERP], env->pregs[PR_PID],
|
|
env->pregs[PR_CCS],
|
|
env->cc_op, env->cc_mask);
|
|
/* We loose the btarget, btaken state here so rexec the
|
|
branch. */
|
|
env->pregs[PR_ERP] -= env->dslot;
|
|
/* Exception starts with dslot cleared. */
|
|
env->dslot = 0;
|
|
}
|
|
|
|
if (env->pregs[PR_CCS] & U_FLAG) {
|
|
/* Swap stack pointers. */
|
|
env->pregs[PR_USP] = env->regs[R_SP];
|
|
env->regs[R_SP] = env->ksp;
|
|
}
|
|
|
|
/* Apply the CRIS CCS shift. Clears U if set. */
|
|
cris_shift_ccs(env);
|
|
|
|
/* Now that we are in kernel mode, load the handlers address.
|
|
This load may not fault, real hw leaves that behaviour as
|
|
undefined. */
|
|
env->pc = cpu_ldl_code(env, env->pregs[PR_EBP] + ex_vec * 4);
|
|
|
|
/* Clear the excption_index to avoid spurios hw_aborts for recursive
|
|
bus faults. */
|
|
cs->exception_index = -1;
|
|
|
|
D_LOG("%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n",
|
|
__func__, env->pc, ex_vec,
|
|
env->pregs[PR_CCS],
|
|
env->pregs[PR_PID],
|
|
env->pregs[PR_ERP]);
|
|
}
|
|
|
|
hwaddr cris_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
|
|
{
|
|
CRISCPU *cpu = CRIS_CPU(cs);
|
|
uint32_t phy = addr;
|
|
struct cris_mmu_result res;
|
|
int miss;
|
|
|
|
miss = cris_mmu_translate(&res, &cpu->env, addr, 0, 0, 1);
|
|
/* If D TLB misses, try I TLB. */
|
|
if (miss) {
|
|
miss = cris_mmu_translate(&res, &cpu->env, addr, 2, 0, 1);
|
|
}
|
|
|
|
if (!miss) {
|
|
phy = res.phy;
|
|
}
|
|
D(fprintf(stderr, "%s %x -> %x\n", __func__, addr, phy));
|
|
return phy;
|
|
}
|
|
#endif
|
|
|
|
bool cris_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
|
{
|
|
CPUClass *cc = CPU_GET_CLASS(cs);
|
|
CRISCPU *cpu = CRIS_CPU(cs);
|
|
CPUCRISState *env = &cpu->env;
|
|
bool ret = false;
|
|
|
|
if (interrupt_request & CPU_INTERRUPT_HARD
|
|
&& (env->pregs[PR_CCS] & I_FLAG)
|
|
&& !env->locked_irq) {
|
|
cs->exception_index = EXCP_IRQ;
|
|
cc->do_interrupt(cs);
|
|
ret = true;
|
|
}
|
|
if (interrupt_request & CPU_INTERRUPT_NMI) {
|
|
unsigned int m_flag_archval;
|
|
if (env->pregs[PR_VR] < 32) {
|
|
m_flag_archval = M_FLAG_V10;
|
|
} else {
|
|
m_flag_archval = M_FLAG_V32;
|
|
}
|
|
if ((env->pregs[PR_CCS] & m_flag_archval)) {
|
|
cs->exception_index = EXCP_NMI;
|
|
cc->do_interrupt(cs);
|
|
ret = true;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|