ARMv7 support.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3572 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
ee4e83ed8d
commit
9ee6e8bb85
@ -17,6 +17,7 @@
|
||||
- MIPS mipssim pequdo machine (Thiemo Seufer)
|
||||
- Strace for Linux userland emulation (Stuart Anderson, Thayne Harbaugh)
|
||||
- OMAP310 MPU emulation plus Palm T|E machine (Andrzej Zaborowski)
|
||||
- ARM v6, v7, NEON SIMD and SMP emulation (Paul Brook/CodeSourcery)
|
||||
|
||||
version 0.9.0:
|
||||
|
||||
|
@ -493,7 +493,9 @@ ifeq ($(TARGET_BASE_ARCH), arm)
|
||||
VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o
|
||||
VL_OBJS+= arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
|
||||
VL_OBJS+= versatile_pci.o sd.o ptimer.o
|
||||
VL_OBJS+= arm_gic.o realview.o arm_sysctl.o
|
||||
VL_OBJS+= realview_gic.o realview.o arm_sysctl.o mpcore.o
|
||||
VL_OBJS+= armv7m.o armv7m_nvic.o stellaris.o i2c.o ssd0303.o pl022.o
|
||||
VL_OBJS+= ssd0323.o pl061.o
|
||||
VL_OBJS+= arm-semi.o
|
||||
VL_OBJS+= pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
|
||||
VL_OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o max111x.o max7310.o
|
||||
|
13
cpu-exec.c
13
cpu-exec.c
@ -173,6 +173,7 @@ static inline TranslationBlock *tb_find_fast(void)
|
||||
flags |= (1 << 6);
|
||||
if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30))
|
||||
flags |= (1 << 7);
|
||||
flags |= (env->condexec_bits << 8);
|
||||
cs_base = 0;
|
||||
pc = env->regs[15];
|
||||
#elif defined(TARGET_SPARC)
|
||||
@ -511,8 +512,18 @@ int cpu_exec(CPUState *env1)
|
||||
env->exception_index = EXCP_FIQ;
|
||||
do_interrupt(env);
|
||||
}
|
||||
/* ARMv7-M interrupt return works by loading a magic value
|
||||
into the PC. On real hardware the load causes the
|
||||
return to occur. The qemu implementation performs the
|
||||
jump normally, then does the exception return when the
|
||||
CPU tries to execute code at the magic address.
|
||||
This will cause the magic PC value to be pushed to
|
||||
the stack if an interrupt occured at the wrong time.
|
||||
We avoid this by disabling interrupts when
|
||||
pc contains a magic address. */
|
||||
if (interrupt_request & CPU_INTERRUPT_HARD
|
||||
&& !(env->uncached_cpsr & CPSR_I)) {
|
||||
&& ((IS_M(env) && env->regs[15] < 0xfffffff0)
|
||||
|| !(env->uncached_cpsr & CPSR_I))) {
|
||||
env->exception_index = EXCP_IRQ;
|
||||
do_interrupt(env);
|
||||
}
|
||||
|
@ -224,6 +224,11 @@ INLINE float32 float32_chs(float32 a)
|
||||
return -a;
|
||||
}
|
||||
|
||||
INLINE float32 float32_scalbn(float32 a, int n)
|
||||
{
|
||||
return scalbnf(a, n);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE double-precision conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
@ -311,6 +316,11 @@ INLINE float64 float64_chs(float64 a)
|
||||
return -a;
|
||||
}
|
||||
|
||||
INLINE float64 float64_scalbn(float64 a, int n)
|
||||
{
|
||||
return scalbn(a, n);
|
||||
}
|
||||
|
||||
#ifdef FLOATX80
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
@ -391,4 +401,10 @@ INLINE floatx80 floatx80_chs(floatx80 a)
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
INLINE floatx80 floatx80_scalbn(floatx80 a, int n)
|
||||
{
|
||||
return scalbnl(a, n);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -5377,3 +5377,78 @@ int float ## s ## _compare_quiet( float ## s a, float ## s b STATUS_PARAM ) \
|
||||
|
||||
COMPARE(32, 0xff)
|
||||
COMPARE(64, 0x7ff)
|
||||
|
||||
/* Multiply A by 2 raised to the power N. */
|
||||
float32 float32_scalbn( float32 a, int n STATUS_PARAM )
|
||||
{
|
||||
flag aSign;
|
||||
int16 aExp;
|
||||
bits32 aSig;
|
||||
|
||||
aSig = extractFloat32Frac( a );
|
||||
aExp = extractFloat32Exp( a );
|
||||
aSign = extractFloat32Sign( a );
|
||||
|
||||
if ( aExp == 0xFF ) {
|
||||
return a;
|
||||
}
|
||||
aExp += n;
|
||||
return roundAndPackFloat32( aSign, aExp, aSig STATUS_VAR );
|
||||
}
|
||||
|
||||
float64 float64_scalbn( float64 a, int n STATUS_PARAM )
|
||||
{
|
||||
flag aSign;
|
||||
int16 aExp;
|
||||
bits64 aSig;
|
||||
|
||||
aSig = extractFloat64Frac( a );
|
||||
aExp = extractFloat64Exp( a );
|
||||
aSign = extractFloat64Sign( a );
|
||||
|
||||
if ( aExp == 0x7FF ) {
|
||||
return a;
|
||||
}
|
||||
aExp += n;
|
||||
return roundAndPackFloat64( aSign, aExp, aSig STATUS_VAR );
|
||||
}
|
||||
|
||||
#ifdef FLOATX80
|
||||
floatx80 floatx80_scalbn( floatx80 a, int n STATUS_PARAM )
|
||||
{
|
||||
flag aSign;
|
||||
int16 aExp;
|
||||
bits64 aSig;
|
||||
|
||||
aSig = extractFloatx80Frac( a );
|
||||
aExp = extractFloatx80Exp( a );
|
||||
aSign = extractFloatx80Sign( a );
|
||||
|
||||
if ( aExp == 0x7FF ) {
|
||||
return a;
|
||||
}
|
||||
aExp += n;
|
||||
return roundAndPackFloatx80( STATUS(floatx80_rounding_precision),
|
||||
aSign, aExp, aSig, 0 STATUS_VAR );
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FLOAT128
|
||||
float128 float128_scalbn( float128 a, int n STATUS_PARAM )
|
||||
{
|
||||
flag aSign;
|
||||
int32 aExp;
|
||||
bits64 aSig0, aSig1;
|
||||
|
||||
aSig1 = extractFloat128Frac1( a );
|
||||
aSig0 = extractFloat128Frac0( a );
|
||||
aExp = extractFloat128Exp( a );
|
||||
aSign = extractFloat128Sign( a );
|
||||
if ( aExp == 0x7FFF ) {
|
||||
return a;
|
||||
}
|
||||
aExp += n;
|
||||
return roundAndPackFloat128( aSign, aExp, aSig0, aSig1, 0 STATUS_VAR );
|
||||
|
||||
}
|
||||
#endif
|
||||
|
@ -244,6 +244,7 @@ int float32_compare( float32, float32 STATUS_PARAM );
|
||||
int float32_compare_quiet( float32, float32 STATUS_PARAM );
|
||||
int float32_is_nan( float32 );
|
||||
int float32_is_signaling_nan( float32 );
|
||||
float32 float32_scalbn( float32, int STATUS_PARAM );
|
||||
|
||||
INLINE float32 float32_abs(float32 a)
|
||||
{
|
||||
@ -295,6 +296,7 @@ int float64_compare( float64, float64 STATUS_PARAM );
|
||||
int float64_compare_quiet( float64, float64 STATUS_PARAM );
|
||||
int float64_is_nan( float64 a );
|
||||
int float64_is_signaling_nan( float64 );
|
||||
float64 float64_scalbn( float64, int STATUS_PARAM );
|
||||
|
||||
INLINE float64 float64_abs(float64 a)
|
||||
{
|
||||
@ -339,6 +341,7 @@ int floatx80_le_quiet( floatx80, floatx80 STATUS_PARAM );
|
||||
int floatx80_lt_quiet( floatx80, floatx80 STATUS_PARAM );
|
||||
int floatx80_is_nan( floatx80 );
|
||||
int floatx80_is_signaling_nan( floatx80 );
|
||||
floatx80 floatx80_scalbn( floatx80, int STATUS_PARAM );
|
||||
|
||||
INLINE floatx80 floatx80_abs(floatx80 a)
|
||||
{
|
||||
@ -387,6 +390,7 @@ int float128_le_quiet( float128, float128 STATUS_PARAM );
|
||||
int float128_lt_quiet( float128, float128 STATUS_PARAM );
|
||||
int float128_is_nan( float128 );
|
||||
int float128_is_signaling_nan( float128 );
|
||||
float128 float128_scalbn( float128, int STATUS_PARAM );
|
||||
|
||||
INLINE float128 float128_abs(float128 a)
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* ARM kernel loader.
|
||||
*
|
||||
* Copyright (c) 2006 CodeSourcery.
|
||||
* Copyright (c) 2006-2007 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
@ -24,6 +24,22 @@ static uint32_t bootloader[] = {
|
||||
0 /* Kernel entry point. Set by integratorcp_init. */
|
||||
};
|
||||
|
||||
/* Entry point for secondary CPUs. Enable interrupt controller and
|
||||
Issue WFI until start address is written to system controller. */
|
||||
static uint32_t smpboot[] = {
|
||||
0xe3a00201, /* mov r0, #0x10000000 */
|
||||
0xe3800601, /* orr r0, r0, #0x001000000 */
|
||||
0xe3a01001, /* mov r1, #1 */
|
||||
0xe5801100, /* str r1, [r0, #0x100] */
|
||||
0xe3a00201, /* mov r0, #0x10000000 */
|
||||
0xe3800030, /* orr r0, #0x30 */
|
||||
0xe320f003, /* wfi */
|
||||
0xe5901000, /* ldr r1, [r0] */
|
||||
0xe3110003, /* tst r1, #3 */
|
||||
0x1afffffb, /* bne <wfi> */
|
||||
0xe12fff11 /* bx r1 */
|
||||
};
|
||||
|
||||
static void main_cpu_reset(void *opaque)
|
||||
{
|
||||
CPUState *env = opaque;
|
||||
@ -33,6 +49,8 @@ static void main_cpu_reset(void *opaque)
|
||||
arm_load_kernel(env, env->ram_size, env->kernel_filename,
|
||||
env->kernel_cmdline, env->initrd_filename,
|
||||
env->board_id, env->loader_start);
|
||||
|
||||
/* TODO: Reset secondary CPUs. */
|
||||
}
|
||||
|
||||
static void set_kernel_args(uint32_t ram_size, int initrd_size,
|
||||
@ -211,6 +229,8 @@ void arm_load_kernel(CPUState *env, int ram_size, const char *kernel_filename,
|
||||
bootloader[6] = entry;
|
||||
for (n = 0; n < sizeof(bootloader) / 4; n++)
|
||||
stl_raw(phys_ram_base + (n * 4), bootloader[n]);
|
||||
for (n = 0; n < sizeof(smpboot) / 4; n++)
|
||||
stl_raw(phys_ram_base + ram_size + (n * 4), smpboot[n]);
|
||||
if (old_param)
|
||||
set_kernel_args_old(ram_size, initrd_size,
|
||||
kernel_cmdline, loader_start);
|
||||
|
456
hw/arm_gic.c
456
hw/arm_gic.c
@ -1,17 +1,15 @@
|
||||
/*
|
||||
* ARM AMBA Generic/Distributed Interrupt Controller
|
||||
* ARM Generic/Distributed Interrupt Controller
|
||||
*
|
||||
* Copyright (c) 2006 CodeSourcery.
|
||||
* Copyright (c) 2006-2007 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
*/
|
||||
|
||||
/* TODO: Some variants of this controller can handle multiple CPUs.
|
||||
Currently only single CPU operation is implemented. */
|
||||
|
||||
#include "vl.h"
|
||||
#include "arm_pic.h"
|
||||
/* This file contains implementation code for the RealView EB interrupt
|
||||
controller, MPCore distributed interrupt controller and ARMv7-M
|
||||
Nested Vectored Interrupt Controller. */
|
||||
|
||||
//#define DEBUG_GIC
|
||||
|
||||
@ -22,58 +20,84 @@ do { printf("arm_gic: " fmt , ##args); } while (0)
|
||||
#define DPRINTF(fmt, args...) do {} while(0)
|
||||
#endif
|
||||
|
||||
/* Distributed interrupt controller. */
|
||||
|
||||
#ifdef NVIC
|
||||
static const uint8_t gic_id[] =
|
||||
{ 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 };
|
||||
#define GIC_DIST_OFFSET 0
|
||||
/* The NVIC has 16 internal vectors. However these are not exposed
|
||||
through the normal GIC interface. */
|
||||
#define GIC_BASE_IRQ 32
|
||||
#else
|
||||
static const uint8_t gic_id[] =
|
||||
{ 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
|
||||
|
||||
#define GIC_NIRQ 96
|
||||
#define GIC_DIST_OFFSET 0x1000
|
||||
#define GIC_BASE_IRQ 0
|
||||
#endif
|
||||
|
||||
typedef struct gic_irq_state
|
||||
{
|
||||
/* ??? The documentation seems to imply the enable bits are global, even
|
||||
for per-cpu interrupts. This seems strange. */
|
||||
unsigned enabled:1;
|
||||
unsigned pending:1;
|
||||
unsigned active:1;
|
||||
unsigned pending:NCPU;
|
||||
unsigned active:NCPU;
|
||||
unsigned level:1;
|
||||
unsigned model:1; /* 0 = 1:N, 1 = N:N */
|
||||
unsigned model:1; /* 0 = N:N, 1 = 1:N */
|
||||
unsigned trigger:1; /* nonzero = edge triggered. */
|
||||
} gic_irq_state;
|
||||
|
||||
#define ALL_CPU_MASK ((1 << NCPU) - 1)
|
||||
|
||||
#define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1
|
||||
#define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0
|
||||
#define GIC_TEST_ENABLED(irq) s->irq_state[irq].enabled
|
||||
#define GIC_SET_PENDING(irq) s->irq_state[irq].pending = 1
|
||||
#define GIC_CLEAR_PENDING(irq) s->irq_state[irq].pending = 0
|
||||
#define GIC_TEST_PENDING(irq) s->irq_state[irq].pending
|
||||
#define GIC_SET_ACTIVE(irq) s->irq_state[irq].active = 1
|
||||
#define GIC_CLEAR_ACTIVE(irq) s->irq_state[irq].active = 0
|
||||
#define GIC_TEST_ACTIVE(irq) s->irq_state[irq].active
|
||||
#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm)
|
||||
#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm)
|
||||
#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0)
|
||||
#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm)
|
||||
#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm)
|
||||
#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0)
|
||||
#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1
|
||||
#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0
|
||||
#define GIC_TEST_MODEL(irq) s->irq_state[irq].model
|
||||
#define GIC_SET_LEVEL(irq) s->irq_state[irq].level = 1
|
||||
#define GIC_CLEAR_LEVEL(irq) s->irq_state[irq].level = 0
|
||||
#define GIC_TEST_LEVEL(irq) s->irq_state[irq].level
|
||||
#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm)
|
||||
#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm)
|
||||
#define GIC_TEST_LEVEL(irq, cm) (s->irq_state[irq].level & (cm)) != 0
|
||||
#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1
|
||||
#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0
|
||||
#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger
|
||||
#define GIC_GET_PRIORITY(irq, cpu) \
|
||||
(((irq) < 32) ? s->priority1[irq][cpu] : s->priority2[(irq) - 32])
|
||||
#ifdef NVIC
|
||||
#define GIC_TARGET(irq) 1
|
||||
#else
|
||||
#define GIC_TARGET(irq) s->irq_target[irq]
|
||||
#endif
|
||||
|
||||
typedef struct gic_state
|
||||
{
|
||||
uint32_t base;
|
||||
qemu_irq parent_irq;
|
||||
qemu_irq parent_irq[NCPU];
|
||||
int enabled;
|
||||
int cpu_enabled;
|
||||
int cpu_enabled[NCPU];
|
||||
|
||||
gic_irq_state irq_state[GIC_NIRQ];
|
||||
#ifndef NVIC
|
||||
int irq_target[GIC_NIRQ];
|
||||
int priority[GIC_NIRQ];
|
||||
int last_active[GIC_NIRQ];
|
||||
#endif
|
||||
int priority1[32][NCPU];
|
||||
int priority2[GIC_NIRQ - 32];
|
||||
int last_active[GIC_NIRQ][NCPU];
|
||||
|
||||
int priority_mask;
|
||||
int running_irq;
|
||||
int running_priority;
|
||||
int current_pending;
|
||||
int priority_mask[NCPU];
|
||||
int running_irq[NCPU];
|
||||
int running_priority[NCPU];
|
||||
int current_pending[NCPU];
|
||||
|
||||
qemu_irq *in;
|
||||
#ifdef NVIC
|
||||
void *nvic;
|
||||
#endif
|
||||
} gic_state;
|
||||
|
||||
/* TODO: Many places that call this routine could be optimized. */
|
||||
@ -83,112 +107,136 @@ static void gic_update(gic_state *s)
|
||||
int best_irq;
|
||||
int best_prio;
|
||||
int irq;
|
||||
int level;
|
||||
int cpu;
|
||||
int cm;
|
||||
|
||||
s->current_pending = 1023;
|
||||
if (!s->enabled || !s->cpu_enabled) {
|
||||
qemu_irq_lower(s->parent_irq);
|
||||
return;
|
||||
}
|
||||
best_prio = 0x100;
|
||||
best_irq = 1023;
|
||||
for (irq = 0; irq < 96; irq++) {
|
||||
if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq)) {
|
||||
if (s->priority[irq] < best_prio) {
|
||||
best_prio = s->priority[irq];
|
||||
best_irq = irq;
|
||||
for (cpu = 0; cpu < NCPU; cpu++) {
|
||||
cm = 1 << cpu;
|
||||
s->current_pending[cpu] = 1023;
|
||||
if (!s->enabled || !s->cpu_enabled[cpu]) {
|
||||
qemu_irq_lower(s->parent_irq[cpu]);
|
||||
return;
|
||||
}
|
||||
best_prio = 0x100;
|
||||
best_irq = 1023;
|
||||
for (irq = 0; irq < GIC_NIRQ; irq++) {
|
||||
if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq, cm)) {
|
||||
if (GIC_GET_PRIORITY(irq, cpu) < best_prio) {
|
||||
best_prio = GIC_GET_PRIORITY(irq, cpu);
|
||||
best_irq = irq;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (best_prio > s->priority_mask) {
|
||||
qemu_irq_lower(s->parent_irq);
|
||||
} else {
|
||||
s->current_pending = best_irq;
|
||||
if (best_prio < s->running_priority) {
|
||||
DPRINTF("Raised pending IRQ %d\n", best_irq);
|
||||
qemu_irq_raise(s->parent_irq);
|
||||
level = 0;
|
||||
if (best_prio <= s->priority_mask[cpu]) {
|
||||
s->current_pending[cpu] = best_irq;
|
||||
if (best_prio < s->running_priority[cpu]) {
|
||||
DPRINTF("Raised pending IRQ %d\n", best_irq);
|
||||
level = 1;
|
||||
}
|
||||
}
|
||||
qemu_set_irq(s->parent_irq[cpu], level);
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((unused))
|
||||
gic_set_pending_private(gic_state *s, int cpu, int irq)
|
||||
{
|
||||
int cm = 1 << cpu;
|
||||
|
||||
if (GIC_TEST_PENDING(irq, cm))
|
||||
return;
|
||||
|
||||
DPRINTF("Set %d pending cpu %d\n", irq, cpu);
|
||||
GIC_SET_PENDING(irq, cm);
|
||||
gic_update(s);
|
||||
}
|
||||
|
||||
/* Process a change in an external IRQ input. */
|
||||
static void gic_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
gic_state *s = (gic_state *)opaque;
|
||||
/* The first external input line is internal interrupt 32. */
|
||||
irq += 32;
|
||||
if (level == GIC_TEST_LEVEL(irq))
|
||||
if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK))
|
||||
return;
|
||||
|
||||
if (level) {
|
||||
GIC_SET_LEVEL(irq);
|
||||
GIC_SET_LEVEL(irq, ALL_CPU_MASK);
|
||||
if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) {
|
||||
DPRINTF("Set %d pending\n", irq);
|
||||
GIC_SET_PENDING(irq);
|
||||
DPRINTF("Set %d pending mask %x\n", irq, GIC_TARGET(irq));
|
||||
GIC_SET_PENDING(irq, GIC_TARGET(irq));
|
||||
}
|
||||
} else {
|
||||
GIC_CLEAR_LEVEL(irq);
|
||||
GIC_CLEAR_LEVEL(irq, ALL_CPU_MASK);
|
||||
}
|
||||
gic_update(s);
|
||||
}
|
||||
|
||||
static void gic_set_running_irq(gic_state *s, int irq)
|
||||
static void gic_set_running_irq(gic_state *s, int cpu, int irq)
|
||||
{
|
||||
s->running_irq = irq;
|
||||
if (irq == 1023)
|
||||
s->running_priority = 0x100;
|
||||
else
|
||||
s->running_priority = s->priority[irq];
|
||||
s->running_irq[cpu] = irq;
|
||||
if (irq == 1023) {
|
||||
s->running_priority[cpu] = 0x100;
|
||||
} else {
|
||||
s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu);
|
||||
}
|
||||
gic_update(s);
|
||||
}
|
||||
|
||||
static uint32_t gic_acknowledge_irq(gic_state *s)
|
||||
static uint32_t gic_acknowledge_irq(gic_state *s, int cpu)
|
||||
{
|
||||
int new_irq;
|
||||
new_irq = s->current_pending;
|
||||
if (new_irq == 1023 || s->priority[new_irq] >= s->running_priority) {
|
||||
int cm = 1 << cpu;
|
||||
new_irq = s->current_pending[cpu];
|
||||
if (new_irq == 1023
|
||||
|| GIC_GET_PRIORITY(new_irq, cpu) >= s->running_priority[cpu]) {
|
||||
DPRINTF("ACK no pending IRQ\n");
|
||||
return 1023;
|
||||
}
|
||||
qemu_irq_lower(s->parent_irq);
|
||||
s->last_active[new_irq] = s->running_irq;
|
||||
/* For level triggered interrupts we clear the pending bit while
|
||||
the interrupt is active. */
|
||||
GIC_CLEAR_PENDING(new_irq);
|
||||
gic_set_running_irq(s, new_irq);
|
||||
s->last_active[new_irq][cpu] = s->running_irq[cpu];
|
||||
/* Clear pending flags for both level and edge triggered interrupts.
|
||||
Level triggered IRQs will be reasserted once they become inactive. */
|
||||
GIC_CLEAR_PENDING(new_irq, GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm);
|
||||
gic_set_running_irq(s, cpu, new_irq);
|
||||
DPRINTF("ACK %d\n", new_irq);
|
||||
return new_irq;
|
||||
}
|
||||
|
||||
static void gic_complete_irq(gic_state * s, int irq)
|
||||
static void gic_complete_irq(gic_state * s, int cpu, int irq)
|
||||
{
|
||||
int update = 0;
|
||||
int cm = 1 << cpu;
|
||||
DPRINTF("EOI %d\n", irq);
|
||||
if (s->running_irq == 1023)
|
||||
if (s->running_irq[cpu] == 1023)
|
||||
return; /* No active IRQ. */
|
||||
if (irq != 1023) {
|
||||
/* Mark level triggered interrupts as pending if they are still
|
||||
raised. */
|
||||
if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq)
|
||||
&& GIC_TEST_LEVEL(irq)) {
|
||||
GIC_SET_PENDING(irq);
|
||||
&& GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
|
||||
DPRINTF("Set %d pending mask %x\n", irq, cm);
|
||||
GIC_SET_PENDING(irq, cm);
|
||||
update = 1;
|
||||
}
|
||||
}
|
||||
if (irq != s->running_irq) {
|
||||
if (irq != s->running_irq[cpu]) {
|
||||
/* Complete an IRQ that is not currently running. */
|
||||
int tmp = s->running_irq;
|
||||
while (s->last_active[tmp] != 1023) {
|
||||
if (s->last_active[tmp] == irq) {
|
||||
s->last_active[tmp] = s->last_active[irq];
|
||||
int tmp = s->running_irq[cpu];
|
||||
while (s->last_active[tmp][cpu] != 1023) {
|
||||
if (s->last_active[tmp][cpu] == irq) {
|
||||
s->last_active[tmp][cpu] = s->last_active[irq][cpu];
|
||||
break;
|
||||
}
|
||||
tmp = s->last_active[tmp];
|
||||
tmp = s->last_active[tmp][cpu];
|
||||
}
|
||||
if (update) {
|
||||
gic_update(s);
|
||||
}
|
||||
} else {
|
||||
/* Complete the current running IRQ. */
|
||||
gic_set_running_irq(s, s->last_active[s->running_irq]);
|
||||
gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,15 +246,22 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
|
||||
uint32_t res;
|
||||
int irq;
|
||||
int i;
|
||||
int cpu;
|
||||
int cm;
|
||||
int mask;
|
||||
|
||||
offset -= s->base + 0x1000;
|
||||
cpu = gic_get_current_cpu();
|
||||
cm = 1 << cpu;
|
||||
offset -= s->base + GIC_DIST_OFFSET;
|
||||
if (offset < 0x100) {
|
||||
#ifndef NVIC
|
||||
if (offset == 0)
|
||||
return s->enabled;
|
||||
if (offset == 4)
|
||||
return (GIC_NIRQ / 32) - 1;
|
||||
return ((GIC_NIRQ / 32) - 1) | ((NCPU - 1) << 5);
|
||||
if (offset < 0x08)
|
||||
return 0;
|
||||
#endif
|
||||
goto bad_reg;
|
||||
} else if (offset < 0x200) {
|
||||
/* Interrupt Set/Clear Enable. */
|
||||
@ -214,6 +269,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
|
||||
irq = (offset - 0x100) * 8;
|
||||
else
|
||||
irq = (offset - 0x180) * 8;
|
||||
irq += GIC_BASE_IRQ;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
res = 0;
|
||||
@ -228,40 +284,48 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
|
||||
irq = (offset - 0x200) * 8;
|
||||
else
|
||||
irq = (offset - 0x280) * 8;
|
||||
irq += GIC_BASE_IRQ;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
res = 0;
|
||||
mask = (irq < 32) ? cm : ALL_CPU_MASK;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (GIC_TEST_PENDING(irq + i)) {
|
||||
if (GIC_TEST_PENDING(irq + i, mask)) {
|
||||
res |= (1 << i);
|
||||
}
|
||||
}
|
||||
} else if (offset < 0x400) {
|
||||
/* Interrupt Active. */
|
||||
irq = (offset - 0x300) * 8;
|
||||
irq = (offset - 0x300) * 8 + GIC_BASE_IRQ;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
res = 0;
|
||||
mask = (irq < 32) ? cm : ALL_CPU_MASK;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (GIC_TEST_ACTIVE(irq + i)) {
|
||||
if (GIC_TEST_ACTIVE(irq + i, mask)) {
|
||||
res |= (1 << i);
|
||||
}
|
||||
}
|
||||
} else if (offset < 0x800) {
|
||||
/* Interrupt Priority. */
|
||||
irq = offset - 0x400;
|
||||
irq = (offset - 0x400) + GIC_BASE_IRQ;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
res = s->priority[irq];
|
||||
res = GIC_GET_PRIORITY(irq, cpu);
|
||||
#ifndef NVIC
|
||||
} else if (offset < 0xc00) {
|
||||
/* Interrupt CPU Target. */
|
||||
irq = offset - 0x800;
|
||||
irq = (offset - 0x800) + GIC_BASE_IRQ;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
res = s->irq_target[irq];
|
||||
if (irq >= 29 && irq <= 31) {
|
||||
res = cm;
|
||||
} else {
|
||||
res = GIC_TARGET(irq);
|
||||
}
|
||||
} else if (offset < 0xf00) {
|
||||
/* Interrupt Configuration. */
|
||||
irq = (offset - 0xc00) * 2;
|
||||
irq = (offset - 0xc00) * 2 + GIC_BASE_IRQ;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
res = 0;
|
||||
@ -271,6 +335,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
|
||||
if (GIC_TEST_TRIGGER(irq + i))
|
||||
res |= (2 << (i * 2));
|
||||
}
|
||||
#endif
|
||||
} else if (offset < 0xfe0) {
|
||||
goto bad_reg;
|
||||
} else /* offset >= 0xfe0 */ {
|
||||
@ -282,7 +347,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
|
||||
}
|
||||
return res;
|
||||
bad_reg:
|
||||
cpu_abort (cpu_single_env, "gic_dist_readb: Bad offset %x\n", offset);
|
||||
cpu_abort(cpu_single_env, "gic_dist_readb: Bad offset %x\n", (int)offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -297,6 +362,13 @@ static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset)
|
||||
static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
uint32_t val;
|
||||
#ifdef NVIC
|
||||
gic_state *s = (gic_state *)opaque;
|
||||
uint32_t addr;
|
||||
addr = offset - s->base;
|
||||
if (addr < 0x100 || addr > 0xd00)
|
||||
return nvic_readl(s->nvic, addr);
|
||||
#endif
|
||||
val = gic_dist_readw(opaque, offset);
|
||||
val |= gic_dist_readw(opaque, offset + 2) << 16;
|
||||
return val;
|
||||
@ -308,9 +380,14 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
|
||||
gic_state *s = (gic_state *)opaque;
|
||||
int irq;
|
||||
int i;
|
||||
int cpu;
|
||||
|
||||
offset -= s->base + 0x1000;
|
||||
cpu = gic_get_current_cpu();
|
||||
offset -= s->base + GIC_DIST_OFFSET;
|
||||
if (offset < 0x100) {
|
||||
#ifdef NVIC
|
||||
goto bad_reg;
|
||||
#else
|
||||
if (offset == 0) {
|
||||
s->enabled = (value & 1);
|
||||
DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis");
|
||||
@ -319,27 +396,36 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
|
||||
} else {
|
||||
goto bad_reg;
|
||||
}
|
||||
#endif
|
||||
} else if (offset < 0x180) {
|
||||
/* Interrupt Set Enable. */
|
||||
irq = (offset - 0x100) * 8;
|
||||
irq = (offset - 0x100) * 8 + GIC_BASE_IRQ;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
if (irq < 16)
|
||||
value = 0xff;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (value & (1 << i)) {
|
||||
int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq);
|
||||
if (!GIC_TEST_ENABLED(irq + i))
|
||||
DPRINTF("Enabled IRQ %d\n", irq + i);
|
||||
GIC_SET_ENABLED(irq + i);
|
||||
/* If a raised level triggered IRQ enabled then mark
|
||||
is as pending. */
|
||||
if (GIC_TEST_LEVEL(irq + i) && !GIC_TEST_TRIGGER(irq + i))
|
||||
GIC_SET_PENDING(irq + i);
|
||||
if (GIC_TEST_LEVEL(irq + i, mask)
|
||||
&& !GIC_TEST_TRIGGER(irq + i)) {
|
||||
DPRINTF("Set %d pending mask %x\n", irq + i, mask);
|
||||
GIC_SET_PENDING(irq + i, mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (offset < 0x200) {
|
||||
/* Interrupt Clear Enable. */
|
||||
irq = (offset - 0x180) * 8;
|
||||
irq = (offset - 0x180) * 8 + GIC_BASE_IRQ;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
if (irq < 16)
|
||||
value = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (value & (1 << i)) {
|
||||
if (GIC_TEST_ENABLED(irq + i))
|
||||
@ -349,22 +435,28 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
|
||||
}
|
||||
} else if (offset < 0x280) {
|
||||
/* Interrupt Set Pending. */
|
||||
irq = (offset - 0x200) * 8;
|
||||
irq = (offset - 0x200) * 8 + GIC_BASE_IRQ;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
if (irq < 16)
|
||||
irq = 0;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (value & (1 << i)) {
|
||||
GIC_SET_PENDING(irq + i);
|
||||
GIC_SET_PENDING(irq + i, GIC_TARGET(irq));
|
||||
}
|
||||
}
|
||||
} else if (offset < 0x300) {
|
||||
/* Interrupt Clear Pending. */
|
||||
irq = (offset - 0x280) * 8;
|
||||
irq = (offset - 0x280) * 8 + GIC_BASE_IRQ;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
for (i = 0; i < 8; i++) {
|
||||
/* ??? This currently clears the pending bit for all CPUs, even
|
||||
for per-CPU interrupts. It's unclear whether this is the
|
||||
corect behavior. */
|
||||
if (value & (1 << i)) {
|
||||
GIC_CLEAR_PENDING(irq + i);
|
||||
GIC_CLEAR_PENDING(irq + i, ALL_CPU_MASK);
|
||||
}
|
||||
}
|
||||
} else if (offset < 0x400) {
|
||||
@ -372,21 +464,32 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
|
||||
goto bad_reg;
|
||||
} else if (offset < 0x800) {
|
||||
/* Interrupt Priority. */
|
||||
irq = offset - 0x400;
|
||||
irq = (offset - 0x400) + GIC_BASE_IRQ;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
s->priority[irq] = value;
|
||||
if (irq < 32) {
|
||||
s->priority1[irq][cpu] = value;
|
||||
} else {
|
||||
s->priority2[irq - 32] = value;
|
||||
}
|
||||
#ifndef NVIC
|
||||
} else if (offset < 0xc00) {
|
||||
/* Interrupt CPU Target. */
|
||||
irq = offset - 0x800;
|
||||
irq = (offset - 0x800) + GIC_BASE_IRQ;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
s->irq_target[irq] = value;
|
||||
if (irq < 29)
|
||||
value = 0;
|
||||
else if (irq < 32)
|
||||
value = ALL_CPU_MASK;
|
||||
s->irq_target[irq] = value & ALL_CPU_MASK;
|
||||
} else if (offset < 0xf00) {
|
||||
/* Interrupt Configuration. */
|
||||
irq = (offset - 0xc00) * 4;
|
||||
irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
|
||||
if (irq >= GIC_NIRQ)
|
||||
goto bad_reg;
|
||||
if (irq < 32)
|
||||
value |= 0xaa;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (value & (1 << (i * 2))) {
|
||||
GIC_SET_MODEL(irq + i);
|
||||
@ -399,25 +502,20 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
|
||||
GIC_CLEAR_TRIGGER(irq + i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
/* 0xf00 is only handled for word writes. */
|
||||
/* 0xf00 is only handled for 32-bit writes. */
|
||||
goto bad_reg;
|
||||
}
|
||||
gic_update(s);
|
||||
return;
|
||||
bad_reg:
|
||||
cpu_abort (cpu_single_env, "gic_dist_writeb: Bad offset %x\n", offset);
|
||||
cpu_abort(cpu_single_env, "gic_dist_writeb: Bad offset %x\n", (int)offset);
|
||||
}
|
||||
|
||||
static void gic_dist_writew(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
gic_state *s = (gic_state *)opaque;
|
||||
if (offset - s->base == 0xf00) {
|
||||
GIC_SET_PENDING(value & 0x3ff);
|
||||
gic_update(s);
|
||||
return;
|
||||
}
|
||||
gic_dist_writeb(opaque, offset, value & 0xff);
|
||||
gic_dist_writeb(opaque, offset + 1, value >> 8);
|
||||
}
|
||||
@ -425,6 +523,41 @@ static void gic_dist_writew(void *opaque, target_phys_addr_t offset,
|
||||
static void gic_dist_writel(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
gic_state *s = (gic_state *)opaque;
|
||||
#ifdef NVIC
|
||||
uint32_t addr;
|
||||
addr = offset - s->base;
|
||||
if (addr < 0x100 || (addr > 0xd00 && addr != 0xf00)) {
|
||||
nvic_writel(s->nvic, addr, value);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (offset - s->base == GIC_DIST_OFFSET + 0xf00) {
|
||||
int cpu;
|
||||
int irq;
|
||||
int mask;
|
||||
|
||||
cpu = gic_get_current_cpu();
|
||||
irq = value & 0x3ff;
|
||||
switch ((value >> 24) & 3) {
|
||||
case 0:
|
||||
mask = (value >> 16) & ALL_CPU_MASK;
|
||||
break;
|
||||
case 1:
|
||||
mask = 1 << cpu;
|
||||
break;
|
||||
case 2:
|
||||
mask = ALL_CPU_MASK ^ (1 << cpu);
|
||||
break;
|
||||
default:
|
||||
DPRINTF("Bad Soft Int target filter\n");
|
||||
mask = ALL_CPU_MASK;
|
||||
break;
|
||||
}
|
||||
GIC_SET_PENDING(irq, mask);
|
||||
gic_update(s);
|
||||
return;
|
||||
}
|
||||
gic_dist_writew(opaque, offset, value & 0xffff);
|
||||
gic_dist_writew(opaque, offset + 2, value >> 16);
|
||||
}
|
||||
@ -441,105 +574,100 @@ static CPUWriteMemoryFunc *gic_dist_writefn[] = {
|
||||
gic_dist_writel
|
||||
};
|
||||
|
||||
static uint32_t gic_cpu_read(void *opaque, target_phys_addr_t offset)
|
||||
#ifndef NVIC
|
||||
static uint32_t gic_cpu_read(gic_state *s, int cpu, int offset)
|
||||
{
|
||||
gic_state *s = (gic_state *)opaque;
|
||||
offset -= s->base;
|
||||
switch (offset) {
|
||||
case 0x00: /* Control */
|
||||
return s->cpu_enabled;
|
||||
return s->cpu_enabled[cpu];
|
||||
case 0x04: /* Priority mask */
|
||||
return s->priority_mask;
|
||||
return s->priority_mask[cpu];
|
||||
case 0x08: /* Binary Point */
|
||||
/* ??? Not implemented. */
|
||||
return 0;
|
||||
case 0x0c: /* Acknowledge */
|
||||
return gic_acknowledge_irq(s);
|
||||
return gic_acknowledge_irq(s, cpu);
|
||||
case 0x14: /* Runing Priority */
|
||||
return s->running_priority;
|
||||
return s->running_priority[cpu];
|
||||
case 0x18: /* Highest Pending Interrupt */
|
||||
return s->current_pending;
|
||||
return s->current_pending[cpu];
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "gic_cpu_read: Bad offset %x\n", offset);
|
||||
cpu_abort(cpu_single_env, "gic_cpu_read: Bad offset %x\n",
|
||||
(int)offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void gic_cpu_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
static void gic_cpu_write(gic_state *s, int cpu, int offset, uint32_t value)
|
||||
{
|
||||
gic_state *s = (gic_state *)opaque;
|
||||
offset -= s->base;
|
||||
switch (offset) {
|
||||
case 0x00: /* Control */
|
||||
s->cpu_enabled = (value & 1);
|
||||
s->cpu_enabled[cpu] = (value & 1);
|
||||
DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis");
|
||||
break;
|
||||
case 0x04: /* Priority mask */
|
||||
s->priority_mask = (value & 0x3ff);
|
||||
s->priority_mask[cpu] = (value & 0xff);
|
||||
break;
|
||||
case 0x08: /* Binary Point */
|
||||
/* ??? Not implemented. */
|
||||
break;
|
||||
case 0x10: /* End Of Interrupt */
|
||||
return gic_complete_irq(s, value & 0x3ff);
|
||||
return gic_complete_irq(s, cpu, value & 0x3ff);
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "gic_cpu_write: Bad offset %x\n", offset);
|
||||
cpu_abort(cpu_single_env, "gic_cpu_write: Bad offset %x\n",
|
||||
(int)offset);
|
||||
return;
|
||||
}
|
||||
gic_update(s);
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *gic_cpu_readfn[] = {
|
||||
gic_cpu_read,
|
||||
gic_cpu_read,
|
||||
gic_cpu_read
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *gic_cpu_writefn[] = {
|
||||
gic_cpu_write,
|
||||
gic_cpu_write,
|
||||
gic_cpu_write
|
||||
};
|
||||
#endif
|
||||
|
||||
static void gic_reset(gic_state *s)
|
||||
{
|
||||
int i;
|
||||
memset(s->irq_state, 0, GIC_NIRQ * sizeof(gic_irq_state));
|
||||
s->priority_mask = 0xf0;
|
||||
s->current_pending = 1023;
|
||||
s->running_irq = 1023;
|
||||
s->running_priority = 0x100;
|
||||
for (i = 0 ; i < NCPU; i++) {
|
||||
s->priority_mask[i] = 0xf0;
|
||||
s->current_pending[i] = 1023;
|
||||
s->running_irq[i] = 1023;
|
||||
s->running_priority[i] = 0x100;
|
||||
#ifdef NVIC
|
||||
/* The NVIC doesn't have per-cpu interfaces, so enable by default. */
|
||||
s->cpu_enabled[i] = 1;
|
||||
#else
|
||||
s->cpu_enabled[i] = 0;
|
||||
#endif
|
||||
}
|
||||
for (i = 0; i < 15; i++) {
|
||||
GIC_SET_ENABLED(i);
|
||||
GIC_SET_TRIGGER(i);
|
||||
}
|
||||
#ifdef NVIC
|
||||
/* The NVIC is always enabled. */
|
||||
s->enabled = 1;
|
||||
#else
|
||||
s->enabled = 0;
|
||||
s->cpu_enabled = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
qemu_irq *arm_gic_init(uint32_t base, qemu_irq parent_irq)
|
||||
static gic_state *gic_init(uint32_t base, qemu_irq *parent_irq)
|
||||
{
|
||||
gic_state *s;
|
||||
qemu_irq *qi;
|
||||
int iomemtype;
|
||||
int i;
|
||||
|
||||
s = (gic_state *)qemu_mallocz(sizeof(gic_state));
|
||||
if (!s)
|
||||
return NULL;
|
||||
qi = qemu_allocate_irqs(gic_set_irq, s, GIC_NIRQ);
|
||||
s->parent_irq = parent_irq;
|
||||
if (base != 0xffffffff) {
|
||||
iomemtype = cpu_register_io_memory(0, gic_cpu_readfn,
|
||||
gic_cpu_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x00001000, iomemtype);
|
||||
iomemtype = cpu_register_io_memory(0, gic_dist_readfn,
|
||||
gic_dist_writefn, s);
|
||||
cpu_register_physical_memory(base + 0x1000, 0x00001000, iomemtype);
|
||||
s->base = base;
|
||||
} else {
|
||||
s->base = 0;
|
||||
s->in = qemu_allocate_irqs(gic_set_irq, s, GIC_NIRQ);
|
||||
for (i = 0; i < NCPU; i++) {
|
||||
s->parent_irq[i] = parent_irq[i];
|
||||
}
|
||||
iomemtype = cpu_register_io_memory(0, gic_dist_readfn,
|
||||
gic_dist_writefn, s);
|
||||
cpu_register_physical_memory(base + GIC_DIST_OFFSET, 0x00001000,
|
||||
iomemtype);
|
||||
s->base = base;
|
||||
gic_reset(s);
|
||||
return qi;
|
||||
return s;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Status and system control registers for ARM RealView/Versatile boards.
|
||||
*
|
||||
* Copyright (c) 2006 CodeSourcery.
|
||||
* Copyright (c) 2006-2007 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
@ -200,6 +200,9 @@ void arm_sysctl_init(uint32_t base, uint32_t sys_id)
|
||||
return;
|
||||
s->base = base;
|
||||
s->sys_id = sys_id;
|
||||
/* The MPcore bootloader uses these flags to start secondary CPUs.
|
||||
We don't use a bootloader, so do this here. */
|
||||
s->flags = 3;
|
||||
iomemtype = cpu_register_io_memory(0, arm_sysctl_readfn,
|
||||
arm_sysctl_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x00001000, iomemtype);
|
||||
|
204
hw/armv7m.c
Normal file
204
hw/armv7m.c
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* ARMV7M System emulation.
|
||||
*
|
||||
* Copyright (c) 2006-2007 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
|
||||
/* Bitbanded IO. Each word corresponds to a single bit. */
|
||||
|
||||
/* Get the byte address of the real memory for a bitband acess. */
|
||||
static inline uint32_t bitband_addr(uint32_t addr)
|
||||
{
|
||||
uint32_t res;
|
||||
|
||||
res = addr & 0xe0000000;
|
||||
res |= (addr & 0x1ffffff) >> 5;
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
static uint32_t bitband_readb(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
uint8_t v;
|
||||
cpu_physical_memory_read(bitband_addr(offset), &v, 1);
|
||||
return (v & (1 << ((offset >> 2) & 7))) != 0;
|
||||
}
|
||||
|
||||
static void bitband_writeb(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
uint32_t addr;
|
||||
uint8_t mask;
|
||||
uint8_t v;
|
||||
addr = bitband_addr(offset);
|
||||
mask = (1 << ((offset >> 2) & 7));
|
||||
cpu_physical_memory_read(addr, &v, 1);
|
||||
if (value & 1)
|
||||
v |= mask;
|
||||
else
|
||||
v &= ~mask;
|
||||
cpu_physical_memory_write(addr, &v, 1);
|
||||
}
|
||||
|
||||
static uint32_t bitband_readw(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
uint32_t addr;
|
||||
uint16_t mask;
|
||||
uint16_t v;
|
||||
addr = bitband_addr(offset) & ~1;
|
||||
mask = (1 << ((offset >> 2) & 15));
|
||||
mask = tswap16(mask);
|
||||
cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
|
||||
return (v & mask) != 0;
|
||||
}
|
||||
|
||||
static void bitband_writew(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
uint32_t addr;
|
||||
uint16_t mask;
|
||||
uint16_t v;
|
||||
addr = bitband_addr(offset) & ~1;
|
||||
mask = (1 << ((offset >> 2) & 15));
|
||||
mask = tswap16(mask);
|
||||
cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
|
||||
if (value & 1)
|
||||
v |= mask;
|
||||
else
|
||||
v &= ~mask;
|
||||
cpu_physical_memory_write(addr, (uint8_t *)&v, 2);
|
||||
}
|
||||
|
||||
static uint32_t bitband_readl(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
uint32_t addr;
|
||||
uint32_t mask;
|
||||
uint32_t v;
|
||||
addr = bitband_addr(offset) & ~3;
|
||||
mask = (1 << ((offset >> 2) & 31));
|
||||
mask = tswap32(mask);
|
||||
cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
|
||||
return (v & mask) != 0;
|
||||
}
|
||||
|
||||
static void bitband_writel(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
uint32_t addr;
|
||||
uint32_t mask;
|
||||
uint32_t v;
|
||||
addr = bitband_addr(offset) & ~3;
|
||||
mask = (1 << ((offset >> 2) & 31));
|
||||
mask = tswap32(mask);
|
||||
cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
|
||||
if (value & 1)
|
||||
v |= mask;
|
||||
else
|
||||
v &= ~mask;
|
||||
cpu_physical_memory_write(addr, (uint8_t *)&v, 4);
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *bitband_readfn[] = {
|
||||
bitband_readb,
|
||||
bitband_readw,
|
||||
bitband_readl
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *bitband_writefn[] = {
|
||||
bitband_writeb,
|
||||
bitband_writew,
|
||||
bitband_writel
|
||||
};
|
||||
|
||||
static void armv7m_bitband_init(void)
|
||||
{
|
||||
int iomemtype;
|
||||
|
||||
iomemtype = cpu_register_io_memory(0, bitband_readfn, bitband_writefn,
|
||||
NULL);
|
||||
cpu_register_physical_memory(0x22000000, 0x02000000, iomemtype);
|
||||
cpu_register_physical_memory(0x42000000, 0x02000000, iomemtype);
|
||||
}
|
||||
|
||||
/* Board init. */
|
||||
/* Init CPU and memory for a v7-M based board.
|
||||
flash_size and sram_size are in kb.
|
||||
Returns the NVIC array. */
|
||||
|
||||
qemu_irq *armv7m_init(int flash_size, int sram_size,
|
||||
const char *kernel_filename, const char *cpu_model)
|
||||
{
|
||||
CPUState *env;
|
||||
qemu_irq *pic;
|
||||
uint32_t pc;
|
||||
int image_size;
|
||||
uint64_t entry;
|
||||
uint64_t lowaddr;
|
||||
|
||||
flash_size *= 1024;
|
||||
sram_size *= 1024;
|
||||
|
||||
if (!cpu_model)
|
||||
cpu_model = "cortex-m3";
|
||||
env = cpu_init(cpu_model);
|
||||
if (!env) {
|
||||
fprintf(stderr, "Unable to find CPU definition\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* > 32Mb SRAM gets complicated because it overlaps the bitband area.
|
||||
We don't have proper commandline options, so allocate half of memory
|
||||
as SRAM, up to a maximum of 32Mb, and the rest as code. */
|
||||
if (ram_size > (512 + 32) * 1024 * 1024)
|
||||
ram_size = (512 + 32) * 1024 * 1024;
|
||||
sram_size = (ram_size / 2) & TARGET_PAGE_MASK;
|
||||
if (sram_size > 32 * 1024 * 1024)
|
||||
sram_size = 32 * 1024 * 1024;
|
||||
code_size = ram_size - sram_size;
|
||||
#endif
|
||||
|
||||
/* Flash programming is done via the SCU, so pretend it is ROM. */
|
||||
cpu_register_physical_memory(0, flash_size, IO_MEM_ROM);
|
||||
cpu_register_physical_memory(0x20000000, sram_size,
|
||||
flash_size + IO_MEM_RAM);
|
||||
armv7m_bitband_init();
|
||||
|
||||
pic = armv7m_nvic_init(env);
|
||||
|
||||
image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL);
|
||||
if (image_size < 0) {
|
||||
image_size = load_image(kernel_filename, phys_ram_base);
|
||||
lowaddr = 0;
|
||||
}
|
||||
if (image_size < 0) {
|
||||
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
||||
kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* If the image was loaded at address zero then assume it is a
|
||||
regular ROM image and perform the normal CPU reset sequence.
|
||||
Otherwise jump directly to the entry point. */
|
||||
if (lowaddr == 0) {
|
||||
env->regs[13] = tswap32(*(uint32_t *)phys_ram_base);
|
||||
pc = tswap32(*(uint32_t *)(phys_ram_base + 4));
|
||||
} else {
|
||||
pc = entry;
|
||||
}
|
||||
env->thumb = pc & 1;
|
||||
env->regs[15] = pc & ~1;
|
||||
|
||||
/* Hack to map an additional page of ram at the top of the address
|
||||
space. This stops qemu complaining about executing code outside RAM
|
||||
when returning from an exception. */
|
||||
cpu_register_physical_memory(0xfffff000, 0x1000, IO_MEM_RAM + ram_size);
|
||||
|
||||
return pic;
|
||||
}
|
||||
|
381
hw/armv7m_nvic.c
Normal file
381
hw/armv7m_nvic.c
Normal file
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* ARM Nested Vectored Interrupt Controller
|
||||
*
|
||||
* Copyright (c) 2006-2007 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
*
|
||||
* The ARMv7M System controller is fairly tightly tied in with the
|
||||
* NVIC. Much of that is also implemented here.
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
#include "arm_pic.h"
|
||||
|
||||
#define GIC_NIRQ 64
|
||||
#define NCPU 1
|
||||
#define NVIC 1
|
||||
|
||||
/* Only a single "CPU" interface is present. */
|
||||
static inline int
|
||||
gic_get_current_cpu(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t nvic_readl(void *opaque, uint32_t offset);
|
||||
static void nvic_writel(void *opaque, uint32_t offset, uint32_t value);
|
||||
|
||||
#include "arm_gic.c"
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
uint32_t control;
|
||||
uint32_t reload;
|
||||
int64_t tick;
|
||||
QEMUTimer *timer;
|
||||
} systick;
|
||||
gic_state *gic;
|
||||
} nvic_state;
|
||||
|
||||
/* qemu timers run at 1GHz. We want something closer to 1MHz. */
|
||||
#define SYSTICK_SCALE 1000ULL
|
||||
|
||||
#define SYSTICK_ENABLE (1 << 0)
|
||||
#define SYSTICK_TICKINT (1 << 1)
|
||||
#define SYSTICK_CLKSOURCE (1 << 2)
|
||||
#define SYSTICK_COUNTFLAG (1 << 16)
|
||||
|
||||
/* Conversion factor from qemu timer to SysTick frequencies.
|
||||
QEMU uses a base of 1GHz, so these give 20MHz and 1MHz for core and
|
||||
reference frequencies. */
|
||||
|
||||
static inline int64_t systick_scale(nvic_state *s)
|
||||
{
|
||||
if (s->systick.control & SYSTICK_CLKSOURCE)
|
||||
return 50;
|
||||
else
|
||||
return 1000;
|
||||
}
|
||||
|
||||
static void systick_reload(nvic_state *s, int reset)
|
||||
{
|
||||
if (reset)
|
||||
s->systick.tick = qemu_get_clock(vm_clock);
|
||||
s->systick.tick += (s->systick.reload + 1) * systick_scale(s);
|
||||
qemu_mod_timer(s->systick.timer, s->systick.tick);
|
||||
}
|
||||
|
||||
static void systick_timer_tick(void * opaque)
|
||||
{
|
||||
nvic_state *s = (nvic_state *)opaque;
|
||||
s->systick.control |= SYSTICK_COUNTFLAG;
|
||||
if (s->systick.control & SYSTICK_TICKINT) {
|
||||
/* Trigger the interrupt. */
|
||||
armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
|
||||
}
|
||||
if (s->systick.reload == 0) {
|
||||
s->systick.control &= ~SYSTICK_ENABLE;
|
||||
} else {
|
||||
systick_reload(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* The external routines use the hardware vector numbering, ie. the first
|
||||
IRQ is #16. The internal GIC routines use #32 as the first IRQ. */
|
||||
void armv7m_nvic_set_pending(void *opaque, int irq)
|
||||
{
|
||||
nvic_state *s = (nvic_state *)opaque;
|
||||
if (irq >= 16)
|
||||
irq += 16;
|
||||
gic_set_pending_private(s->gic, 0, irq);
|
||||
}
|
||||
|
||||
/* Make pending IRQ active. */
|
||||
int armv7m_nvic_acknowledge_irq(void *opaque)
|
||||
{
|
||||
nvic_state *s = (nvic_state *)opaque;
|
||||
uint32_t irq;
|
||||
|
||||
irq = gic_acknowledge_irq(s->gic, 0);
|
||||
if (irq == 1023)
|
||||
cpu_abort(cpu_single_env, "Interrupt but no vector\n");
|
||||
if (irq >= 32)
|
||||
irq -= 16;
|
||||
return irq;
|
||||
}
|
||||
|
||||
void armv7m_nvic_complete_irq(void *opaque, int irq)
|
||||
{
|
||||
nvic_state *s = (nvic_state *)opaque;
|
||||
if (irq >= 16)
|
||||
irq += 16;
|
||||
gic_complete_irq(s->gic, 0, irq);
|
||||
}
|
||||
|
||||
static uint32_t nvic_readl(void *opaque, uint32_t offset)
|
||||
{
|
||||
nvic_state *s = (nvic_state *)opaque;
|
||||
uint32_t val;
|
||||
int irq;
|
||||
|
||||
switch (offset) {
|
||||
case 4: /* Interrupt Control Type. */
|
||||
return (GIC_NIRQ / 32) - 1;
|
||||
case 0x10: /* SysTick Control and Status. */
|
||||
val = s->systick.control;
|
||||
s->systick.control &= ~SYSTICK_COUNTFLAG;
|
||||
return val;
|
||||
case 0x14: /* SysTick Reload Value. */
|
||||
return s->systick.reload;
|
||||
case 0x18: /* SysTick Current Value. */
|
||||
{
|
||||
int64_t t;
|
||||
if ((s->systick.control & SYSTICK_ENABLE) == 0)
|
||||
return 0;
|
||||
t = qemu_get_clock(vm_clock);
|
||||
if (t >= s->systick.tick)
|
||||
return 0;
|
||||
val = ((s->systick.tick - (t + 1)) / systick_scale(s)) + 1;
|
||||
/* The interrupt in triggered when the timer reaches zero.
|
||||
However the counter is not reloaded until the next clock
|
||||
tick. This is a hack to return zero during the first tick. */
|
||||
if (val > s->systick.reload)
|
||||
val = 0;
|
||||
return val;
|
||||
}
|
||||
case 0x1c: /* SysTick Calibration Value. */
|
||||
return 10000;
|
||||
case 0xd00: /* CPUID Base. */
|
||||
return cpu_single_env->cp15.c0_cpuid;
|
||||
case 0xd04: /* Interrypt Control State. */
|
||||
/* VECTACTIVE */
|
||||
val = s->gic->running_irq[0];
|
||||
if (val == 1023) {
|
||||
val = 0;
|
||||
} else if (val >= 32) {
|
||||
val -= 16;
|
||||
}
|
||||
/* RETTOBASE */
|
||||
if (s->gic->running_irq[0] == 1023
|
||||
|| s->gic->last_active[s->gic->running_irq[0]][0] == 1023) {
|
||||
val |= (1 << 11);
|
||||
}
|
||||
/* VECTPENDING */
|
||||
if (s->gic->current_pending[0] != 1023)
|
||||
val |= (s->gic->current_pending[0] << 12);
|
||||
/* ISRPENDING */
|
||||
for (irq = 32; irq < GIC_NIRQ; irq++) {
|
||||
if (s->gic->irq_state[irq].pending) {
|
||||
val |= (1 << 22);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* PENDSTSET */
|
||||
if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending)
|
||||
val |= (1 << 26);
|
||||
/* PENDSVSET */
|
||||
if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending)
|
||||
val |= (1 << 28);
|
||||
/* NMIPENDSET */
|
||||
if (s->gic->irq_state[ARMV7M_EXCP_NMI].pending)
|
||||
val |= (1 << 31);
|
||||
return val;
|
||||
case 0xd08: /* Vector Table Offset. */
|
||||
return cpu_single_env->v7m.vecbase;
|
||||
case 0xd0c: /* Application Interrupt/Reset Control. */
|
||||
return 0xfa05000;
|
||||
case 0xd10: /* System Control. */
|
||||
/* TODO: Implement SLEEPONEXIT. */
|
||||
return 0;
|
||||
case 0xd14: /* Configuration Control. */
|
||||
/* TODO: Implement Configuration Control bits. */
|
||||
return 0;
|
||||
case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority. */
|
||||
irq = offset - 0xd14;
|
||||
val = 0;
|
||||
val = s->gic->priority1[irq++][0];
|
||||
val = s->gic->priority1[irq++][0] << 8;
|
||||
val = s->gic->priority1[irq++][0] << 16;
|
||||
val = s->gic->priority1[irq][0] << 24;
|
||||
return val;
|
||||
case 0xd24: /* System Handler Status. */
|
||||
val = 0;
|
||||
if (s->gic->irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0);
|
||||
if (s->gic->irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1);
|
||||
if (s->gic->irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3);
|
||||
if (s->gic->irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7);
|
||||
if (s->gic->irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8);
|
||||
if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10);
|
||||
if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11);
|
||||
if (s->gic->irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12);
|
||||
if (s->gic->irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13);
|
||||
if (s->gic->irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14);
|
||||
if (s->gic->irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15);
|
||||
if (s->gic->irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16);
|
||||
if (s->gic->irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17);
|
||||
if (s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18);
|
||||
return val;
|
||||
case 0xd28: /* Configurable Fault Status. */
|
||||
/* TODO: Implement Fault Status. */
|
||||
cpu_abort(cpu_single_env,
|
||||
"Not implemented: Configurable Fault Status.");
|
||||
return 0;
|
||||
case 0xd2c: /* Hard Fault Status. */
|
||||
case 0xd30: /* Debug Fault Status. */
|
||||
case 0xd34: /* Mem Manage Address. */
|
||||
case 0xd38: /* Bus Fault Address. */
|
||||
case 0xd3c: /* Aux Fault Status. */
|
||||
/* TODO: Implement fault status registers. */
|
||||
goto bad_reg;
|
||||
case 0xd40: /* PFR0. */
|
||||
return 0x00000030;
|
||||
case 0xd44: /* PRF1. */
|
||||
return 0x00000200;
|
||||
case 0xd48: /* DFR0. */
|
||||
return 0x00100000;
|
||||
case 0xd4c: /* AFR0. */
|
||||
return 0x00000000;
|
||||
case 0xd50: /* MMFR0. */
|
||||
return 0x00000030;
|
||||
case 0xd54: /* MMFR1. */
|
||||
return 0x00000000;
|
||||
case 0xd58: /* MMFR2. */
|
||||
return 0x00000000;
|
||||
case 0xd5c: /* MMFR3. */
|
||||
return 0x00000000;
|
||||
case 0xd60: /* ISAR0. */
|
||||
return 0x01141110;
|
||||
case 0xd64: /* ISAR1. */
|
||||
return 0x02111000;
|
||||
case 0xd68: /* ISAR2. */
|
||||
return 0x21112231;
|
||||
case 0xd6c: /* ISAR3. */
|
||||
return 0x01111110;
|
||||
case 0xd70: /* ISAR4. */
|
||||
return 0x01310102;
|
||||
/* TODO: Implement debug registers. */
|
||||
default:
|
||||
bad_reg:
|
||||
cpu_abort(cpu_single_env, "NVIC: Bad read offset 0x%x\n", offset);
|
||||
}
|
||||
}
|
||||
|
||||
static void nvic_writel(void *opaque, uint32_t offset, uint32_t value)
|
||||
{
|
||||
nvic_state *s = (nvic_state *)opaque;
|
||||
uint32_t oldval;
|
||||
switch (offset) {
|
||||
case 0x10: /* SysTick Control and Status. */
|
||||
oldval = s->systick.control;
|
||||
s->systick.control &= 0xfffffff8;
|
||||
s->systick.control |= value & 7;
|
||||
if ((oldval ^ value) & SYSTICK_ENABLE) {
|
||||
int64_t now = qemu_get_clock(vm_clock);
|
||||
if (value & SYSTICK_ENABLE) {
|
||||
if (s->systick.tick) {
|
||||
s->systick.tick += now;
|
||||
qemu_mod_timer(s->systick.timer, s->systick.tick);
|
||||
} else {
|
||||
systick_reload(s, 1);
|
||||
}
|
||||
} else {
|
||||
qemu_del_timer(s->systick.timer);
|
||||
s->systick.tick -= now;
|
||||
if (s->systick.tick < 0)
|
||||
s->systick.tick = 0;
|
||||
}
|
||||
} else if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
|
||||
/* This is a hack. Force the timer to be reloaded
|
||||
when the reference clock is changed. */
|
||||
systick_reload(s, 1);
|
||||
}
|
||||
break;
|
||||
case 0x14: /* SysTick Reload Value. */
|
||||
s->systick.reload = value;
|
||||
break;
|
||||
case 0x18: /* SysTick Current Value. Writes reload the timer. */
|
||||
systick_reload(s, 1);
|
||||
s->systick.control &= ~SYSTICK_COUNTFLAG;
|
||||
break;
|
||||
case 0xd04: /* Interrupt Control State. */
|
||||
if (value & (1 << 31)) {
|
||||
armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI);
|
||||
}
|
||||
if (value & (1 << 28)) {
|
||||
armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV);
|
||||
} else if (value & (1 << 27)) {
|
||||
s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending = 0;
|
||||
gic_update(s->gic);
|
||||
}
|
||||
if (value & (1 << 26)) {
|
||||
armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
|
||||
} else if (value & (1 << 25)) {
|
||||
s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending = 0;
|
||||
gic_update(s->gic);
|
||||
}
|
||||
break;
|
||||
case 0xd08: /* Vector Table Offset. */
|
||||
cpu_single_env->v7m.vecbase = value & 0xffffff80;
|
||||
break;
|
||||
case 0xd0c: /* Application Interrupt/Reset Control. */
|
||||
if ((value >> 16) == 0x05fa) {
|
||||
if (value & 2) {
|
||||
cpu_abort(cpu_single_env, "VECTCLRACTIVE not implemented");
|
||||
}
|
||||
if (value & 5) {
|
||||
cpu_abort(cpu_single_env, "System reset");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xd10: /* System Control. */
|
||||
case 0xd14: /* Configuration Control. */
|
||||
/* TODO: Implement control registers. */
|
||||
goto bad_reg;
|
||||
case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority. */
|
||||
{
|
||||
int irq;
|
||||
irq = offset - 0xd14;
|
||||
s->gic->priority1[irq++][0] = value & 0xff;
|
||||
s->gic->priority1[irq++][0] = (value >> 8) & 0xff;
|
||||
s->gic->priority1[irq++][0] = (value >> 16) & 0xff;
|
||||
s->gic->priority1[irq][0] = (value >> 24) & 0xff;
|
||||
gic_update(s->gic);
|
||||
}
|
||||
break;
|
||||
case 0xd24: /* System Handler Control. */
|
||||
/* TODO: Real hardware allows you to set/clear the active bits
|
||||
under some circumstances. We don't implement this. */
|
||||
s->gic->irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0;
|
||||
s->gic->irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0;
|
||||
s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0;
|
||||
break;
|
||||
case 0xd28: /* Configurable Fault Status. */
|
||||
case 0xd2c: /* Hard Fault Status. */
|
||||
case 0xd30: /* Debug Fault Status. */
|
||||
case 0xd34: /* Mem Manage Address. */
|
||||
case 0xd38: /* Bus Fault Address. */
|
||||
case 0xd3c: /* Aux Fault Status. */
|
||||
goto bad_reg;
|
||||
default:
|
||||
bad_reg:
|
||||
cpu_abort(cpu_single_env, "NVIC: Bad write offset 0x%x\n", offset);
|
||||
}
|
||||
}
|
||||
|
||||
qemu_irq *armv7m_nvic_init(CPUState *env)
|
||||
{
|
||||
nvic_state *s;
|
||||
qemu_irq *parent;
|
||||
|
||||
parent = arm_pic_init_cpu(env);
|
||||
s = (nvic_state *)qemu_mallocz(sizeof(nvic_state));
|
||||
s->gic = gic_init(0xe000e000, &parent[ARM_PIC_CPU_IRQ]);
|
||||
s->gic->nvic = s;
|
||||
s->systick.timer = qemu_new_timer(vm_clock, systick_timer_tick, s);
|
||||
if (env->v7m.nvic)
|
||||
cpu_abort(env, "CPU can only have one NVIC\n");
|
||||
env->v7m.nvic = s;
|
||||
return s->gic->in;
|
||||
}
|
@ -497,8 +497,8 @@ static void integratorcp_init(int ram_size, int vga_ram_size,
|
||||
icp_pic_init(0xca000000, pic[26], NULL);
|
||||
icp_pit_init(0x13000000, pic, 5);
|
||||
pl031_init(0x15000000, pic[8]);
|
||||
pl011_init(0x16000000, pic[1], serial_hds[0]);
|
||||
pl011_init(0x17000000, pic[2], serial_hds[1]);
|
||||
pl011_init(0x16000000, pic[1], serial_hds[0], PL011_ARM);
|
||||
pl011_init(0x17000000, pic[2], serial_hds[1], PL011_ARM);
|
||||
icp_control_init(0xcb000000);
|
||||
pl050_init(0x18000000, pic[3], 0);
|
||||
pl050_init(0x19000000, pic[4], 1);
|
||||
|
323
hw/mpcore.c
Normal file
323
hw/mpcore.c
Normal file
@ -0,0 +1,323 @@
|
||||
/*
|
||||
* ARM MPCore internal peripheral emulation.
|
||||
*
|
||||
* Copyright (c) 2006-2007 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
|
||||
#define MPCORE_PRIV_BASE 0x10100000
|
||||
#define NCPU 4
|
||||
/* ??? The MPCore TRM says the on-chip controller has 224 external IRQ lines
|
||||
(+ 32 internal). However my test chip only exposes/reports 32.
|
||||
More importantly Linux falls over if more than 32 are present! */
|
||||
#define GIC_NIRQ 64
|
||||
|
||||
static inline int
|
||||
gic_get_current_cpu(void)
|
||||
{
|
||||
return cpu_single_env->cpu_index;
|
||||
}
|
||||
|
||||
#include "arm_gic.c"
|
||||
|
||||
/* MPCore private memory region. */
|
||||
|
||||
typedef struct {
|
||||
uint32_t count;
|
||||
uint32_t load;
|
||||
uint32_t control;
|
||||
uint32_t status;
|
||||
uint32_t old_status;
|
||||
int64_t tick;
|
||||
QEMUTimer *timer;
|
||||
struct mpcore_priv_state *mpcore;
|
||||
int id; /* Encodes both timer/watchdog and CPU. */
|
||||
} mpcore_timer_state;
|
||||
|
||||
typedef struct mpcore_priv_state {
|
||||
gic_state *gic;
|
||||
uint32_t scu_control;
|
||||
mpcore_timer_state timer[8];
|
||||
} mpcore_priv_state;
|
||||
|
||||
/* Per-CPU Timers. */
|
||||
|
||||
static inline void mpcore_timer_update_irq(mpcore_timer_state *s)
|
||||
{
|
||||
if (s->status & ~s->old_status) {
|
||||
gic_set_pending_private(s->mpcore->gic, s->id >> 1, 29 + (s->id & 1));
|
||||
}
|
||||
s->old_status = s->status;
|
||||
}
|
||||
|
||||
/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
|
||||
static inline uint32_t mpcore_timer_scale(mpcore_timer_state *s)
|
||||
{
|
||||
return (((s->control >> 8) & 0xff) + 1) * 10;
|
||||
}
|
||||
|
||||
static void mpcore_timer_reload(mpcore_timer_state *s, int restart)
|
||||
{
|
||||
if (s->count == 0)
|
||||
return;
|
||||
if (restart)
|
||||
s->tick = qemu_get_clock(vm_clock);
|
||||
s->tick += (int64_t)s->count * mpcore_timer_scale(s);
|
||||
qemu_mod_timer(s->timer, s->tick);
|
||||
}
|
||||
|
||||
static void mpcore_timer_tick(void *opaque)
|
||||
{
|
||||
mpcore_timer_state *s = (mpcore_timer_state *)opaque;
|
||||
s->status = 1;
|
||||
if (s->control & 2) {
|
||||
s->count = s->load;
|
||||
mpcore_timer_reload(s, 0);
|
||||
} else {
|
||||
s->count = 0;
|
||||
}
|
||||
mpcore_timer_update_irq(s);
|
||||
}
|
||||
|
||||
static uint32_t mpcore_timer_read(mpcore_timer_state *s, int offset)
|
||||
{
|
||||
int64_t val;
|
||||
switch (offset) {
|
||||
case 0: /* Load */
|
||||
return s->load;
|
||||
/* Fall through. */
|
||||
case 4: /* Counter. */
|
||||
if (((s->control & 1) == 0) || (s->count == 0))
|
||||
return 0;
|
||||
/* Slow and ugly, but hopefully won't happen too often. */
|
||||
val = s->tick - qemu_get_clock(vm_clock);
|
||||
val /= mpcore_timer_scale(s);
|
||||
if (val < 0)
|
||||
val = 0;
|
||||
return val;
|
||||
case 8: /* Control. */
|
||||
return s->control;
|
||||
case 12: /* Interrupt status. */
|
||||
return s->status;
|
||||
}
|
||||
}
|
||||
|
||||
static void mpcore_timer_write(mpcore_timer_state *s, int offset,
|
||||
uint32_t value)
|
||||
{
|
||||
int64_t old;
|
||||
switch (offset) {
|
||||
case 0: /* Load */
|
||||
s->load = value;
|
||||
/* Fall through. */
|
||||
case 4: /* Counter. */
|
||||
if ((s->control & 1) && s->count) {
|
||||
/* Cancel the previous timer. */
|
||||
qemu_del_timer(s->timer);
|
||||
}
|
||||
s->count = value;
|
||||
if (s->control & 1) {
|
||||
mpcore_timer_reload(s, 1);
|
||||
}
|
||||
break;
|
||||
case 8: /* Control. */
|
||||
old = s->control;
|
||||
s->control = value;
|
||||
if (((old & 1) == 0) && (value & 1)) {
|
||||
if (s->count == 0 && (s->control & 2))
|
||||
s->count = s->load;
|
||||
mpcore_timer_reload(s, 1);
|
||||
}
|
||||
break;
|
||||
case 12: /* Interrupt status. */
|
||||
s->status &= ~value;
|
||||
mpcore_timer_update_irq(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void mpcore_timer_init(mpcore_priv_state *mpcore,
|
||||
mpcore_timer_state *s, int id)
|
||||
{
|
||||
s->id = id;
|
||||
s->mpcore = mpcore;
|
||||
s->timer = qemu_new_timer(vm_clock, mpcore_timer_tick, s);
|
||||
}
|
||||
|
||||
|
||||
/* Per-CPU private memory mapped IO. */
|
||||
|
||||
static uint32_t mpcore_priv_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
mpcore_priv_state *s = (mpcore_priv_state *)opaque;
|
||||
int id;
|
||||
offset &= 0xfff;
|
||||
if (offset < 0x100) {
|
||||
/* SCU */
|
||||
switch (offset) {
|
||||
case 0x00: /* Control. */
|
||||
return s->scu_control;
|
||||
case 0x04: /* Configuration. */
|
||||
return 0xf3;
|
||||
case 0x08: /* CPU status. */
|
||||
return 0;
|
||||
case 0x0c: /* Invalidate all. */
|
||||
return 0;
|
||||
default:
|
||||
goto bad_reg;
|
||||
}
|
||||
} else if (offset < 0x600) {
|
||||
/* Interrupt controller. */
|
||||
if (offset < 0x200) {
|
||||
id = gic_get_current_cpu();
|
||||
} else {
|
||||
id = (offset - 0x200) >> 8;
|
||||
}
|
||||
return gic_cpu_read(s->gic, id, offset & 0xff);
|
||||
} else if (offset < 0xb00) {
|
||||
/* Timers. */
|
||||
if (offset < 0x700) {
|
||||
id = gic_get_current_cpu();
|
||||
} else {
|
||||
id = (offset - 0x700) >> 8;
|
||||
}
|
||||
id <<= 1;
|
||||
if (offset & 0x20)
|
||||
id++;
|
||||
return mpcore_timer_read(&s->timer[id], offset & 0xf);
|
||||
}
|
||||
bad_reg:
|
||||
cpu_abort(cpu_single_env, "mpcore_priv_read: Bad offset %x\n",
|
||||
(int)offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mpcore_priv_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
mpcore_priv_state *s = (mpcore_priv_state *)opaque;
|
||||
int id;
|
||||
offset &= 0xfff;
|
||||
if (offset < 0x100) {
|
||||
/* SCU */
|
||||
switch (offset) {
|
||||
case 0: /* Control register. */
|
||||
s->scu_control = value & 1;
|
||||
break;
|
||||
case 0x0c: /* Invalidate all. */
|
||||
/* This is a no-op as cache is not emulated. */
|
||||
break;
|
||||
default:
|
||||
goto bad_reg;
|
||||
}
|
||||
} else if (offset < 0x600) {
|
||||
/* Interrupt controller. */
|
||||
if (offset < 0x200) {
|
||||
id = gic_get_current_cpu();
|
||||
} else {
|
||||
id = (offset - 0x200) >> 8;
|
||||
}
|
||||
gic_cpu_write(s->gic, id, offset & 0xff, value);
|
||||
} else if (offset < 0xb00) {
|
||||
/* Timers. */
|
||||
if (offset < 0x700) {
|
||||
id = gic_get_current_cpu();
|
||||
} else {
|
||||
id = (offset - 0x700) >> 8;
|
||||
}
|
||||
id <<= 1;
|
||||
if (offset & 0x20)
|
||||
id++;
|
||||
mpcore_timer_write(&s->timer[id], offset & 0xf, value);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
bad_reg:
|
||||
cpu_abort(cpu_single_env, "mpcore_priv_read: Bad offset %x\n",
|
||||
(int)offset);
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *mpcore_priv_readfn[] = {
|
||||
mpcore_priv_read,
|
||||
mpcore_priv_read,
|
||||
mpcore_priv_read
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *mpcore_priv_writefn[] = {
|
||||
mpcore_priv_write,
|
||||
mpcore_priv_write,
|
||||
mpcore_priv_write
|
||||
};
|
||||
|
||||
|
||||
static qemu_irq *mpcore_priv_init(uint32_t base, qemu_irq *pic_irq)
|
||||
{
|
||||
mpcore_priv_state *s;
|
||||
int iomemtype;
|
||||
int i;
|
||||
|
||||
s = (mpcore_priv_state *)qemu_mallocz(sizeof(mpcore_priv_state));
|
||||
if (!s)
|
||||
return NULL;
|
||||
s->gic = gic_init(base, pic_irq);
|
||||
if (!s->gic)
|
||||
return NULL;
|
||||
iomemtype = cpu_register_io_memory(0, mpcore_priv_readfn,
|
||||
mpcore_priv_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x00001000, iomemtype);
|
||||
for (i = 0; i < 8; i++) {
|
||||
mpcore_timer_init(s, &s->timer[i], i);
|
||||
}
|
||||
return s->gic->in;
|
||||
}
|
||||
|
||||
/* Dummy PIC to route IRQ lines. The baseboard has 4 independent IRQ
|
||||
controllers. The output of these, plus some of the raw input lines
|
||||
are fed into a single SMP-aware interrupt controller on the CPU. */
|
||||
typedef struct {
|
||||
qemu_irq *cpuic;
|
||||
qemu_irq *rvic[4];
|
||||
} mpcore_rirq_state;
|
||||
|
||||
/* Map baseboard IRQs onto CPU IRQ lines. */
|
||||
static const int mpcore_irq_map[32] = {
|
||||
-1, -1, -1, -1, 1, 2, -1, -1,
|
||||
-1, -1, 6, -1, 4, 5, -1, -1,
|
||||
-1, 14, 15, 0, 7, 8, -1, -1,
|
||||
-1, -1, -1, -1, 9, 3, -1, -1,
|
||||
};
|
||||
|
||||
static void mpcore_rirq_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
mpcore_rirq_state *s = (mpcore_rirq_state *)opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
qemu_set_irq(s->rvic[i][irq], level);
|
||||
}
|
||||
if (irq < 32) {
|
||||
irq = mpcore_irq_map[irq];
|
||||
if (irq >= 0) {
|
||||
qemu_set_irq(s->cpuic[irq], level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qemu_irq *mpcore_irq_init(qemu_irq *cpu_irq)
|
||||
{
|
||||
mpcore_rirq_state *s;
|
||||
int n;
|
||||
|
||||
/* ??? IRQ routing is hardcoded to "normal" mode. */
|
||||
s = qemu_mallocz(sizeof(mpcore_rirq_state));
|
||||
s->cpuic = mpcore_priv_init(MPCORE_PRIV_BASE, cpu_irq);
|
||||
for (n = 0; n < 4; n++) {
|
||||
s->rvic[n] = realview_gic_init(0x10040000 + n * 0x10000,
|
||||
s->cpuic[10 + n]);
|
||||
}
|
||||
return qemu_allocate_irqs(mpcore_rirq_set_irq, s, 64);
|
||||
}
|
15
hw/pl011.c
15
hw/pl011.c
@ -28,6 +28,7 @@ typedef struct {
|
||||
int read_trigger;
|
||||
CharDriverState *chr;
|
||||
qemu_irq irq;
|
||||
enum pl011_type type;
|
||||
} pl011_state;
|
||||
|
||||
#define PL011_INT_TX 0x20
|
||||
@ -38,8 +39,10 @@ typedef struct {
|
||||
#define PL011_FLAG_TXFF 0x20
|
||||
#define PL011_FLAG_RXFE 0x10
|
||||
|
||||
static const unsigned char pl011_id[] =
|
||||
{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
|
||||
static const unsigned char pl011_id[2][8] = {
|
||||
{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_ARM */
|
||||
{ 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_LUMINARY */
|
||||
};
|
||||
|
||||
static void pl011_update(pl011_state *s)
|
||||
{
|
||||
@ -56,7 +59,7 @@ static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
|
||||
|
||||
offset -= s->base;
|
||||
if (offset >= 0xfe0 && offset < 0x1000) {
|
||||
return pl011_id[(offset - 0xfe0) >> 2];
|
||||
return pl011_id[s->type][(offset - 0xfe0) >> 2];
|
||||
}
|
||||
switch (offset >> 2) {
|
||||
case 0: /* UARTDR */
|
||||
@ -137,6 +140,9 @@ static void pl011_write(void *opaque, target_phys_addr_t offset,
|
||||
case 1: /* UARTCR */
|
||||
s->cr = value;
|
||||
break;
|
||||
case 6: /* UARTFR */
|
||||
/* Writes to Flag register are ignored. */
|
||||
break;
|
||||
case 8: /* UARTUARTILPR */
|
||||
s->ilpr = value;
|
||||
break;
|
||||
@ -224,7 +230,7 @@ static CPUWriteMemoryFunc *pl011_writefn[] = {
|
||||
};
|
||||
|
||||
void pl011_init(uint32_t base, qemu_irq irq,
|
||||
CharDriverState *chr)
|
||||
CharDriverState *chr, enum pl011_type type)
|
||||
{
|
||||
int iomemtype;
|
||||
pl011_state *s;
|
||||
@ -235,6 +241,7 @@ void pl011_init(uint32_t base, qemu_irq irq,
|
||||
cpu_register_physical_memory(base, 0x00001000, iomemtype);
|
||||
s->base = base;
|
||||
s->irq = irq;
|
||||
s->type = type;
|
||||
s->chr = chr;
|
||||
s->read_trigger = 1;
|
||||
s->ifl = 0x12;
|
||||
|
264
hw/pl022.c
Normal file
264
hw/pl022.c
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Arm PrimeCell PL022 Synchronous Serial Port
|
||||
*
|
||||
* Copyright (c) 2007 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
|
||||
//#define DEBUG_PL022 1
|
||||
|
||||
#ifdef DEBUG_PL022
|
||||
#define DPRINTF(fmt, args...) \
|
||||
do { printf("pl022: " fmt , ##args); } while (0)
|
||||
#define BADF(fmt, args...) \
|
||||
do { fprintf(stderr, "pl022: error: " fmt , ##args); exit(1);} while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, args...) do {} while(0)
|
||||
#define BADF(fmt, args...) \
|
||||
do { fprintf(stderr, "pl022: error: " fmt , ##args);} while (0)
|
||||
#endif
|
||||
|
||||
#define PL022_CR1_LBM 0x01
|
||||
#define PL022_CR1_SSE 0x02
|
||||
#define PL022_CR1_MS 0x04
|
||||
#define PL022_CR1_SDO 0x08
|
||||
|
||||
#define PL022_SR_TFE 0x01
|
||||
#define PL022_SR_TNF 0x02
|
||||
#define PL022_SR_RNE 0x04
|
||||
#define PL022_SR_RFF 0x08
|
||||
#define PL022_SR_BSY 0x10
|
||||
|
||||
#define PL022_INT_ROR 0x01
|
||||
#define PL022_INT_RT 0x04
|
||||
#define PL022_INT_RX 0x04
|
||||
#define PL022_INT_TX 0x08
|
||||
|
||||
typedef struct {
|
||||
uint32_t base;
|
||||
uint32_t cr0;
|
||||
uint32_t cr1;
|
||||
uint32_t bitmask;
|
||||
uint32_t sr;
|
||||
uint32_t cpsr;
|
||||
uint32_t is;
|
||||
uint32_t im;
|
||||
/* The FIFO head points to the next empty entry. */
|
||||
int tx_fifo_head;
|
||||
int rx_fifo_head;
|
||||
int tx_fifo_len;
|
||||
int rx_fifo_len;
|
||||
uint16_t tx_fifo[8];
|
||||
uint16_t rx_fifo[8];
|
||||
qemu_irq irq;
|
||||
int (*xfer_cb)(void *, int);
|
||||
void *opaque;
|
||||
} pl022_state;
|
||||
|
||||
static const unsigned char pl022_id[8] =
|
||||
{ 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
|
||||
|
||||
static void pl022_update(pl022_state *s)
|
||||
{
|
||||
s->sr = 0;
|
||||
if (s->tx_fifo_len == 0)
|
||||
s->sr |= PL022_SR_TFE;
|
||||
if (s->tx_fifo_len != 8)
|
||||
s->sr |= PL022_SR_TNF;
|
||||
if (s->rx_fifo_len != 0)
|
||||
s->sr |= PL022_SR_RNE;
|
||||
if (s->rx_fifo_len == 8)
|
||||
s->sr |= PL022_SR_RFF;
|
||||
if (s->tx_fifo_len)
|
||||
s->sr |= PL022_SR_BSY;
|
||||
s->is = 0;
|
||||
if (s->rx_fifo_len >= 4)
|
||||
s->is |= PL022_INT_RX;
|
||||
if (s->tx_fifo_len <= 4)
|
||||
s->is |= PL022_INT_TX;
|
||||
|
||||
qemu_set_irq(s->irq, (s->is & s->im) != 0);
|
||||
}
|
||||
|
||||
static void pl022_xfer(pl022_state *s)
|
||||
{
|
||||
int i;
|
||||
int o;
|
||||
int val;
|
||||
|
||||
if ((s->cr1 & PL022_CR1_SSE) == 0) {
|
||||
pl022_update(s);
|
||||
DPRINTF("Disabled\n");
|
||||
return;
|
||||
}
|
||||
|
||||
DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len);
|
||||
i = (s->tx_fifo_head - s->tx_fifo_len) & 7;
|
||||
o = s->rx_fifo_head;
|
||||
/* ??? We do not emulate the line speed.
|
||||
This may break some applications. The are two problematic cases:
|
||||
(a) A driver feeds data into the TX FIFO until it is full,
|
||||
and only then drains the RX FIFO. On real hardware the CPU can
|
||||
feed data fast enough that the RX fifo never gets chance to overflow.
|
||||
(b) A driver transmits data, deliberately allowing the RX FIFO to
|
||||
overflow because it ignores the RX data anyway.
|
||||
|
||||
We choose to support (a) by stalling the transmit engine if it would
|
||||
cause the RX FIFO to overflow. In practice much transmit-only code
|
||||
falls into (a) because it flushes the RX FIFO to determine when
|
||||
the transfer has completed. */
|
||||
while (s->tx_fifo_len && s->rx_fifo_len < 8) {
|
||||
DPRINTF("xfer\n");
|
||||
val = s->tx_fifo[i];
|
||||
if (s->cr1 & PL022_CR1_LBM) {
|
||||
/* Loopback mode. */
|
||||
} else if (s->xfer_cb) {
|
||||
val = s->xfer_cb(s->opaque, val);
|
||||
} else {
|
||||
val = 0;
|
||||
}
|
||||
s->rx_fifo[o] = val & s->bitmask;
|
||||
i = (i + 1) & 7;
|
||||
o = (o + 1) & 7;
|
||||
s->tx_fifo_len--;
|
||||
s->rx_fifo_len++;
|
||||
}
|
||||
s->rx_fifo_head = o;
|
||||
pl022_update(s);
|
||||
}
|
||||
|
||||
static uint32_t pl022_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
pl022_state *s = (pl022_state *)opaque;
|
||||
int val;
|
||||
|
||||
offset -= s->base;
|
||||
if (offset >= 0xfe0 && offset < 0x1000) {
|
||||
return pl022_id[(offset - 0xfe0) >> 2];
|
||||
}
|
||||
switch (offset) {
|
||||
case 0x00: /* CR0 */
|
||||
return s->cr0;
|
||||
case 0x04: /* CR1 */
|
||||
return s->cr1;
|
||||
case 0x08: /* DR */
|
||||
if (s->rx_fifo_len) {
|
||||
val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7];
|
||||
DPRINTF("RX %02x\n", val);
|
||||
s->rx_fifo_len--;
|
||||
pl022_xfer(s);
|
||||
} else {
|
||||
val = 0;
|
||||
}
|
||||
return val;
|
||||
case 0x0c: /* SR */
|
||||
return s->sr;
|
||||
case 0x10: /* CPSR */
|
||||
return s->cpsr;
|
||||
case 0x14: /* IMSC */
|
||||
return s->im;
|
||||
case 0x18: /* RIS */
|
||||
return s->is;
|
||||
case 0x1c: /* MIS */
|
||||
return s->im & s->is;
|
||||
case 0x20: /* DMACR */
|
||||
/* Not implemented. */
|
||||
return 0;
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "pl022_read: Bad offset %x\n",
|
||||
(int)offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void pl022_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
pl022_state *s = (pl022_state *)opaque;
|
||||
|
||||
offset -= s->base;
|
||||
switch (offset) {
|
||||
case 0x00: /* CR0 */
|
||||
s->cr0 = value;
|
||||
/* Clock rate and format are ignored. */
|
||||
s->bitmask = (1 << ((value & 15) + 1)) - 1;
|
||||
break;
|
||||
case 0x04: /* CR1 */
|
||||
s->cr1 = value;
|
||||
if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
|
||||
== (PL022_CR1_MS | PL022_CR1_SSE)) {
|
||||
BADF("SPI slave mode not implemented\n");
|
||||
}
|
||||
pl022_xfer(s);
|
||||
break;
|
||||
case 0x08: /* DR */
|
||||
if (s->tx_fifo_len < 8) {
|
||||
DPRINTF("TX %02x\n", value);
|
||||
s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
|
||||
s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
|
||||
s->tx_fifo_len++;
|
||||
pl022_xfer(s);
|
||||
}
|
||||
break;
|
||||
case 0x10: /* CPSR */
|
||||
/* Prescaler. Ignored. */
|
||||
s->cpsr = value & 0xff;
|
||||
break;
|
||||
case 0x14: /* IMSC */
|
||||
s->im = value;
|
||||
pl022_update(s);
|
||||
break;
|
||||
case 0x20: /* DMACR */
|
||||
if (value)
|
||||
cpu_abort (cpu_single_env, "pl022: DMA not implemented\n");
|
||||
break;
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "pl022_write: Bad offset %x\n",
|
||||
(int)offset);
|
||||
}
|
||||
}
|
||||
|
||||
static void pl022_reset(pl022_state *s)
|
||||
{
|
||||
s->rx_fifo_len = 0;
|
||||
s->tx_fifo_len = 0;
|
||||
s->im = 0;
|
||||
s->is = PL022_INT_TX;
|
||||
s->sr = PL022_SR_TFE | PL022_SR_TNF;
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *pl022_readfn[] = {
|
||||
pl022_read,
|
||||
pl022_read,
|
||||
pl022_read
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *pl022_writefn[] = {
|
||||
pl022_write,
|
||||
pl022_write,
|
||||
pl022_write
|
||||
};
|
||||
|
||||
void pl022_init(uint32_t base, qemu_irq irq, int (*xfer_cb)(void *, int),
|
||||
void * opaque)
|
||||
{
|
||||
int iomemtype;
|
||||
pl022_state *s;
|
||||
|
||||
s = (pl022_state *)qemu_mallocz(sizeof(pl022_state));
|
||||
iomemtype = cpu_register_io_memory(0, pl022_readfn,
|
||||
pl022_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x00001000, iomemtype);
|
||||
s->base = base;
|
||||
s->irq = irq;
|
||||
s->xfer_cb = xfer_cb;
|
||||
s->opaque = opaque;
|
||||
pl022_reset(s);
|
||||
/* ??? Save/restore. */
|
||||
}
|
||||
|
||||
|
256
hw/pl061.c
Normal file
256
hw/pl061.c
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Arm PrimeCell PL061 General Purpose IO with additional
|
||||
* Luminary Micro Stellaris bits.
|
||||
*
|
||||
* Copyright (c) 2007 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
|
||||
//#define DEBUG_PL061 1
|
||||
|
||||
#ifdef DEBUG_PL061
|
||||
#define DPRINTF(fmt, args...) \
|
||||
do { printf("pl061: " fmt , ##args); } while (0)
|
||||
#define BADF(fmt, args...) \
|
||||
do { fprintf(stderr, "pl061: error: " fmt , ##args); exit(1);} while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, args...) do {} while(0)
|
||||
#define BADF(fmt, args...) \
|
||||
do { fprintf(stderr, "pl061: error: " fmt , ##args);} while (0)
|
||||
#endif
|
||||
|
||||
static const uint8_t pl061_id[12] =
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
|
||||
|
||||
typedef struct {
|
||||
uint32_t base;
|
||||
int locked;
|
||||
uint8_t data;
|
||||
uint8_t old_data;
|
||||
uint8_t dir;
|
||||
uint8_t isense;
|
||||
uint8_t ibe;
|
||||
uint8_t iev;
|
||||
uint8_t im;
|
||||
uint8_t istate;
|
||||
uint8_t afsel;
|
||||
uint8_t dr2r;
|
||||
uint8_t dr4r;
|
||||
uint8_t dr8r;
|
||||
uint8_t odr;
|
||||
uint8_t pur;
|
||||
uint8_t pdr;
|
||||
uint8_t slr;
|
||||
uint8_t den;
|
||||
uint8_t cr;
|
||||
qemu_irq irq;
|
||||
qemu_irq out[8];
|
||||
} pl061_state;
|
||||
|
||||
static void pl061_update(pl061_state *s)
|
||||
{
|
||||
uint8_t changed;
|
||||
uint8_t mask;
|
||||
int i;
|
||||
|
||||
changed = s->old_data ^ s->data;
|
||||
if (!changed)
|
||||
return;
|
||||
|
||||
s->old_data = s->data;
|
||||
for (i = 0; i < 8; i++) {
|
||||
mask = 1 << i;
|
||||
if ((changed & mask & s->dir) && s->out) {
|
||||
DPRINTF("Set output %d = %d\n", i, (s->data & mask) != 0);
|
||||
qemu_set_irq(s->out[i], (s->data & mask) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: Implement input interrupts. */
|
||||
}
|
||||
|
||||
static uint32_t pl061_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
pl061_state *s = (pl061_state *)opaque;
|
||||
|
||||
offset -= s->base;
|
||||
if (offset >= 0xfd0 && offset < 0x1000) {
|
||||
return pl061_id[(offset - 0xfd0) >> 2];
|
||||
}
|
||||
if (offset < 0x400) {
|
||||
return s->data & (offset >> 2);
|
||||
}
|
||||
switch (offset) {
|
||||
case 0x400: /* Direction */
|
||||
return s->dir;
|
||||
case 0x404: /* Interrupt sense */
|
||||
return s->isense;
|
||||
case 0x408: /* Interrupt both edges */
|
||||
return s->ibe;
|
||||
case 0x40c: /* Interupt event */
|
||||
return s->iev;
|
||||
case 0x410: /* Interrupt mask */
|
||||
return s->im;
|
||||
case 0x414: /* Raw interrupt status */
|
||||
return s->istate;
|
||||
case 0x418: /* Masked interrupt status */
|
||||
return s->istate | s->im;
|
||||
case 0x420: /* Alternate function select */
|
||||
return s->afsel;
|
||||
case 0x500: /* 2mA drive */
|
||||
return s->dr2r;
|
||||
case 0x504: /* 4mA drive */
|
||||
return s->dr4r;
|
||||
case 0x508: /* 8mA drive */
|
||||
return s->dr8r;
|
||||
case 0x50c: /* Open drain */
|
||||
return s->odr;
|
||||
case 0x510: /* Pull-up */
|
||||
return s->pur;
|
||||
case 0x514: /* Pull-down */
|
||||
return s->pdr;
|
||||
case 0x518: /* Slew rate control */
|
||||
return s->slr;
|
||||
case 0x51c: /* Digital enable */
|
||||
return s->den;
|
||||
case 0x520: /* Lock */
|
||||
return s->locked;
|
||||
case 0x524: /* Commit */
|
||||
return s->cr;
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "pl061_read: Bad offset %x\n",
|
||||
(int)offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void pl061_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
pl061_state *s = (pl061_state *)opaque;
|
||||
uint8_t mask;
|
||||
|
||||
offset -= s->base;
|
||||
if (offset < 0x400) {
|
||||
mask = (offset >> 2) & s->dir;
|
||||
s->data = (s->data & ~mask) | (value & mask);
|
||||
pl061_update(s);
|
||||
return;
|
||||
}
|
||||
switch (offset) {
|
||||
case 0x400: /* Direction */
|
||||
s->dir = value;
|
||||
break;
|
||||
case 0x404: /* Interrupt sense */
|
||||
s->isense = value;
|
||||
break;
|
||||
case 0x408: /* Interrupt both edges */
|
||||
s->ibe = value;
|
||||
break;
|
||||
case 0x40c: /* Interupt event */
|
||||
s->iev = value;
|
||||
break;
|
||||
case 0x410: /* Interrupt mask */
|
||||
s->im = value;
|
||||
break;
|
||||
case 0x41c: /* Interrupt clear */
|
||||
s->istate &= ~value;
|
||||
break;
|
||||
case 0x420: /* Alternate function select */
|
||||
mask = s->cr;
|
||||
s->afsel = (s->afsel & ~mask) | (value & mask);
|
||||
break;
|
||||
case 0x500: /* 2mA drive */
|
||||
s->dr2r = value;
|
||||
break;
|
||||
case 0x504: /* 4mA drive */
|
||||
s->dr4r = value;
|
||||
break;
|
||||
case 0x508: /* 8mA drive */
|
||||
s->dr8r = value;
|
||||
break;
|
||||
case 0x50c: /* Open drain */
|
||||
s->odr = value;
|
||||
break;
|
||||
case 0x510: /* Pull-up */
|
||||
s->pur = value;
|
||||
break;
|
||||
case 0x514: /* Pull-down */
|
||||
s->pdr = value;
|
||||
break;
|
||||
case 0x518: /* Slew rate control */
|
||||
s->slr = value;
|
||||
break;
|
||||
case 0x51c: /* Digital enable */
|
||||
s->den = value;
|
||||
break;
|
||||
case 0x520: /* Lock */
|
||||
s->locked = (value != 0xacce551);
|
||||
break;
|
||||
case 0x524: /* Commit */
|
||||
if (!s->locked)
|
||||
s->cr = value;
|
||||
break;
|
||||
default:
|
||||
cpu_abort (cpu_single_env, "pl061_write: Bad offset %x\n",
|
||||
(int)offset);
|
||||
}
|
||||
pl061_update(s);
|
||||
}
|
||||
|
||||
static void pl061_reset(pl061_state *s)
|
||||
{
|
||||
s->locked = 1;
|
||||
s->cr = 0xff;
|
||||
}
|
||||
|
||||
void pl061_set_irq(void * opaque, int irq, int level)
|
||||
{
|
||||
pl061_state *s = (pl061_state *)opaque;
|
||||
uint8_t mask;
|
||||
|
||||
mask = 1 << irq;
|
||||
if ((s->dir & mask) == 0) {
|
||||
s->data &= ~mask;
|
||||
if (level)
|
||||
s->data |= mask;
|
||||
pl061_update(s);
|
||||
}
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *pl061_readfn[] = {
|
||||
pl061_read,
|
||||
pl061_read,
|
||||
pl061_read
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *pl061_writefn[] = {
|
||||
pl061_write,
|
||||
pl061_write,
|
||||
pl061_write
|
||||
};
|
||||
|
||||
/* Returns an array of inputs. */
|
||||
qemu_irq *pl061_init(uint32_t base, qemu_irq irq, qemu_irq **out)
|
||||
{
|
||||
int iomemtype;
|
||||
pl061_state *s;
|
||||
|
||||
s = (pl061_state *)qemu_mallocz(sizeof(pl061_state));
|
||||
iomemtype = cpu_register_io_memory(0, pl061_readfn,
|
||||
pl061_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x00001000, iomemtype);
|
||||
s->base = base;
|
||||
s->irq = irq;
|
||||
pl061_reset(s);
|
||||
if (out)
|
||||
*out = s->out;
|
||||
|
||||
/* ??? Save/restore. */
|
||||
return qemu_allocate_irqs(pl061_set_irq, s, 8);
|
||||
}
|
||||
|
@ -297,7 +297,7 @@ static void pxa2xx_clkpwr_write(void *opaque, int op2, int reg, int crm,
|
||||
ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
|
||||
s->env->cp15.c1_sys = 0;
|
||||
s->env->cp15.c1_coproc = 0;
|
||||
s->env->cp15.c2_base = 0;
|
||||
s->env->cp15.c2_base0 = 0;
|
||||
s->env->cp15.c3 = 0;
|
||||
s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */
|
||||
s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */
|
||||
@ -2031,7 +2031,8 @@ struct pxa2xx_state_s *pxa270_init(unsigned int sdram_size,
|
||||
fprintf(stderr, "Unable to find CPU definition\n");
|
||||
exit(1);
|
||||
}
|
||||
register_savevm("cpu", 0, 0, cpu_save, cpu_load, s->env);
|
||||
register_savevm("cpu", 0, ARM_CPU_SAVE_VERSION, cpu_save, cpu_load,
|
||||
s->env);
|
||||
|
||||
/* SDRAM & Internal Memory Storage */
|
||||
cpu_register_physical_memory(PXA2XX_SDRAM_BASE,
|
||||
@ -2145,7 +2146,8 @@ struct pxa2xx_state_s *pxa255_init(unsigned int sdram_size,
|
||||
fprintf(stderr, "Unable to find CPU definition\n");
|
||||
exit(1);
|
||||
}
|
||||
register_savevm("cpu", 0, 0, cpu_save, cpu_load, s->env);
|
||||
register_savevm("cpu", 0, ARM_CPU_SAVE_VERSION, cpu_save, cpu_load,
|
||||
s->env);
|
||||
|
||||
/* SDRAM & Internal Memory Storage */
|
||||
cpu_register_physical_memory(PXA2XX_SDRAM_BASE, sdram_size,
|
||||
|
@ -25,13 +25,32 @@ static void realview_init(int ram_size, int vga_ram_size,
|
||||
NICInfo *nd;
|
||||
int n;
|
||||
int done_smc = 0;
|
||||
qemu_irq cpu_irq[4];
|
||||
int ncpu;
|
||||
|
||||
if (!cpu_model)
|
||||
cpu_model = "arm926";
|
||||
env = cpu_init(cpu_model);
|
||||
if (!env) {
|
||||
fprintf(stderr, "Unable to find CPU definition\n");
|
||||
exit(1);
|
||||
/* FIXME: obey smp_cpus. */
|
||||
if (strcmp(cpu_model, "arm11mpcore") == 0) {
|
||||
ncpu = 4;
|
||||
} else {
|
||||
ncpu = 1;
|
||||
}
|
||||
|
||||
for (n = 0; n < ncpu; n++) {
|
||||
env = cpu_init(cpu_model);
|
||||
if (!env) {
|
||||
fprintf(stderr, "Unable to find CPU definition\n");
|
||||
exit(1);
|
||||
}
|
||||
pic = arm_pic_init_cpu(env);
|
||||
cpu_irq[n] = pic[ARM_PIC_CPU_IRQ];
|
||||
if (n > 0) {
|
||||
/* Set entry point for secondary CPUs. This assumes we're using
|
||||
the init code from arm_boot.c. Real hardware resets all CPUs
|
||||
the same. */
|
||||
env->regs[15] = 0x80000000;
|
||||
}
|
||||
}
|
||||
|
||||
/* ??? RAM shoud repeat to fill physical memory space. */
|
||||
@ -39,18 +58,23 @@ static void realview_init(int ram_size, int vga_ram_size,
|
||||
cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
|
||||
|
||||
arm_sysctl_init(0x10000000, 0xc1400400);
|
||||
pic = arm_pic_init_cpu(env);
|
||||
/* ??? The documentation says GIC1 is nFIQ and either GIC2 or GIC3
|
||||
is nIRQ (there are inconsistencies). However Linux 2.6.17 expects
|
||||
GIC1 to be nIRQ and ignores all the others, so do that for now. */
|
||||
pic = arm_gic_init(0x10040000, pic[ARM_PIC_CPU_IRQ]);
|
||||
|
||||
if (ncpu == 1) {
|
||||
/* ??? The documentation says GIC1 is nFIQ and either GIC2 or GIC3
|
||||
is nIRQ (there are inconsistencies). However Linux 2.6.17 expects
|
||||
GIC1 to be nIRQ and ignores all the others, so do that for now. */
|
||||
pic = realview_gic_init(0x10040000, cpu_irq[0]);
|
||||
} else {
|
||||
pic = mpcore_irq_init(cpu_irq);
|
||||
}
|
||||
|
||||
pl050_init(0x10006000, pic[20], 0);
|
||||
pl050_init(0x10007000, pic[21], 1);
|
||||
|
||||
pl011_init(0x10009000, pic[12], serial_hds[0]);
|
||||
pl011_init(0x1000a000, pic[13], serial_hds[1]);
|
||||
pl011_init(0x1000b000, pic[14], serial_hds[2]);
|
||||
pl011_init(0x1000c000, pic[15], serial_hds[3]);
|
||||
pl011_init(0x10009000, pic[12], serial_hds[0], PL011_ARM);
|
||||
pl011_init(0x1000a000, pic[13], serial_hds[1], PL011_ARM);
|
||||
pl011_init(0x1000b000, pic[14], serial_hds[2], PL011_ARM);
|
||||
pl011_init(0x1000c000, pic[15], serial_hds[3], PL011_ARM);
|
||||
|
||||
/* DMA controller is optional, apparently. */
|
||||
pl080_init(0x10030000, pic[24], 2);
|
||||
@ -114,10 +138,10 @@ static void realview_init(int ram_size, int vga_ram_size,
|
||||
/* 0x10019000 PCI controller config. */
|
||||
/* 0x10020000 CLCD. */
|
||||
/* 0x10030000 DMA Controller. */
|
||||
/* 0x10040000 GIC1 (FIQ1). */
|
||||
/* 0x10050000 GIC2 (IRQ1). */
|
||||
/* 0x10060000 GIC3 (FIQ2). */
|
||||
/* 0x10070000 GIC4 (IRQ2). */
|
||||
/* 0x10040000 GIC1. */
|
||||
/* 0x10050000 GIC2. */
|
||||
/* 0x10060000 GIC3. */
|
||||
/* 0x10070000 GIC4. */
|
||||
/* 0x10080000 SMC. */
|
||||
/* 0x40000000 NOR flash. */
|
||||
/* 0x44000000 DoC flash. */
|
||||
@ -137,8 +161,14 @@ static void realview_init(int ram_size, int vga_ram_size,
|
||||
/* 0x68000000 PCI mem 1. */
|
||||
/* 0x6c000000 PCI mem 2. */
|
||||
|
||||
arm_load_kernel(env, ram_size, kernel_filename, kernel_cmdline,
|
||||
arm_load_kernel(first_cpu, ram_size, kernel_filename, kernel_cmdline,
|
||||
initrd_filename, 0x33b, 0x0);
|
||||
|
||||
/* ??? Hack to map an additional page of ram for the secondary CPU
|
||||
startup code. I guess this works on real hardware because the
|
||||
BootROM happens to be in ROM/flash or in memory that isn't clobbered
|
||||
until after Linux boots the secondary CPUs. */
|
||||
cpu_register_physical_memory(0x80000000, 0x1000, IO_MEM_RAM + ram_size);
|
||||
}
|
||||
|
||||
QEMUMachine realview_machine = {
|
||||
|
64
hw/realview_gic.c
Normal file
64
hw/realview_gic.c
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* ARM RealView Emulation Baseboard Interrupt Controller
|
||||
*
|
||||
* Copyright (c) 2006-2007 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
#include "arm_pic.h"
|
||||
|
||||
#define GIC_NIRQ 96
|
||||
#define NCPU 1
|
||||
|
||||
/* Only a single "CPU" interface is present. */
|
||||
static inline int
|
||||
gic_get_current_cpu(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "arm_gic.c"
|
||||
|
||||
static uint32_t realview_gic_cpu_read(void *opaque, target_phys_addr_t offset)
|
||||
{
|
||||
gic_state *s = (gic_state *)opaque;
|
||||
offset -= s->base;
|
||||
return gic_cpu_read(s, gic_get_current_cpu(), offset);
|
||||
}
|
||||
|
||||
static void realview_gic_cpu_write(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t value)
|
||||
{
|
||||
gic_state *s = (gic_state *)opaque;
|
||||
offset -= s->base;
|
||||
gic_cpu_write(s, gic_get_current_cpu(), offset, value);
|
||||
}
|
||||
|
||||
static CPUReadMemoryFunc *realview_gic_cpu_readfn[] = {
|
||||
realview_gic_cpu_read,
|
||||
realview_gic_cpu_read,
|
||||
realview_gic_cpu_read
|
||||
};
|
||||
|
||||
static CPUWriteMemoryFunc *realview_gic_cpu_writefn[] = {
|
||||
realview_gic_cpu_write,
|
||||
realview_gic_cpu_write,
|
||||
realview_gic_cpu_write
|
||||
};
|
||||
|
||||
qemu_irq *realview_gic_init(uint32_t base, qemu_irq parent_irq)
|
||||
{
|
||||
gic_state *s;
|
||||
int iomemtype;
|
||||
|
||||
s = gic_init(base, &parent_irq);
|
||||
if (!s)
|
||||
return NULL;
|
||||
iomemtype = cpu_register_io_memory(0, realview_gic_cpu_readfn,
|
||||
realview_gic_cpu_writefn, s);
|
||||
cpu_register_physical_memory(base, 0x00001000, iomemtype);
|
||||
return s->in;
|
||||
}
|
273
hw/ssd0303.c
Normal file
273
hw/ssd0303.c
Normal file
@ -0,0 +1,273 @@
|
||||
/*
|
||||
* SSD0303 OLED controller with OSRAM Pictiva 96x16 display.
|
||||
*
|
||||
* Copyright (c) 2006-2007 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
*/
|
||||
|
||||
/* The controller can support a variety of different displays, but we only
|
||||
implement one. Most of the commends relating to brightness and geometry
|
||||
setup are ignored. */
|
||||
#include "vl.h"
|
||||
|
||||
//#define DEBUG_SSD0303 1
|
||||
|
||||
#ifdef DEBUG_SSD0303
|
||||
#define DPRINTF(fmt, args...) \
|
||||
do { printf("ssd0303: " fmt , ##args); } while (0)
|
||||
#define BADF(fmt, args...) \
|
||||
do { fprintf(stderr, "ssd0303: error: " fmt , ##args); exit(1);} while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, args...) do {} while(0)
|
||||
#define BADF(fmt, args...) \
|
||||
do { fprintf(stderr, "ssd0303: error: " fmt , ##args);} while (0)
|
||||
#endif
|
||||
|
||||
/* Scaling factor for pixels. */
|
||||
#define MAGNIFY 4
|
||||
|
||||
enum ssd0303_mode
|
||||
{
|
||||
SSD0303_IDLE,
|
||||
SSD0303_DATA,
|
||||
SSD0303_CMD
|
||||
};
|
||||
|
||||
enum ssd0303_cmd {
|
||||
SSD0303_CMD_NONE,
|
||||
SSD0303_CMD_SKIP1
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
i2c_slave i2c;
|
||||
DisplayState *ds;
|
||||
int row;
|
||||
int col;
|
||||
int start_line;
|
||||
int mirror;
|
||||
int flash;
|
||||
int enabled;
|
||||
int inverse;
|
||||
int redraw;
|
||||
enum ssd0303_mode mode;
|
||||
enum ssd0303_cmd cmd_state;
|
||||
uint8_t framebuffer[132*8];
|
||||
} ssd0303_state;
|
||||
|
||||
static int ssd0303_recv(i2c_slave *i2c)
|
||||
{
|
||||
BADF("Reads not implemented\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ssd0303_send(i2c_slave *i2c, uint8_t data)
|
||||
{
|
||||
ssd0303_state *s = (ssd0303_state *)i2c;
|
||||
enum ssd0303_cmd old_cmd_state;
|
||||
switch (s->mode) {
|
||||
case SSD0303_IDLE:
|
||||
DPRINTF("byte 0x%02x\n", data);
|
||||
if (data == 0x80)
|
||||
s->mode = SSD0303_CMD;
|
||||
else if (data == 0x40)
|
||||
s->mode = SSD0303_DATA;
|
||||
else
|
||||
BADF("Unexpected byte 0x%x\n", data);
|
||||
break;
|
||||
case SSD0303_DATA:
|
||||
DPRINTF("data 0x%02x\n", data);
|
||||
if (s->col < 132) {
|
||||
s->framebuffer[s->col + s->row * 132] = data;
|
||||
s->col++;
|
||||
s->redraw = 1;
|
||||
}
|
||||
break;
|
||||
case SSD0303_CMD:
|
||||
old_cmd_state = s->cmd_state;
|
||||
s->cmd_state = SSD0303_CMD_NONE;
|
||||
switch (old_cmd_state) {
|
||||
case SSD0303_CMD_NONE:
|
||||
DPRINTF("cmd 0x%02x\n", data);
|
||||
s->mode = SSD0303_IDLE;
|
||||
switch (data) {
|
||||
case 0x00 ... 0x0f: /* Set lower colum address. */
|
||||
s->col = (s->col & 0xf0) | (data & 0xf);
|
||||
break;
|
||||
case 0x10 ... 0x20: /* Set higher column address. */
|
||||
s->col = (s->col & 0x0f) | ((data & 0xf) << 4);
|
||||
break;
|
||||
case 0x40 ... 0x7f: /* Set start line. */
|
||||
s->start_line = 0;
|
||||
break;
|
||||
case 0x81: /* Set contrast (Ignored). */
|
||||
s->cmd_state = SSD0303_CMD_SKIP1;
|
||||
break;
|
||||
case 0xa0: /* Mirror off. */
|
||||
s->mirror = 0;
|
||||
break;
|
||||
case 0xa1: /* Mirror off. */
|
||||
s->mirror = 1;
|
||||
break;
|
||||
case 0xa4: /* Entire display off. */
|
||||
s->flash = 0;
|
||||
break;
|
||||
case 0xa5: /* Entire display on. */
|
||||
s->flash = 1;
|
||||
break;
|
||||
case 0xa6: /* Inverse off. */
|
||||
s->inverse = 0;
|
||||
break;
|
||||
case 0xa7: /* Inverse on. */
|
||||
s->inverse = 1;
|
||||
break;
|
||||
case 0xa8: /* Set multipled ratio (Ignored). */
|
||||
s->cmd_state = SSD0303_CMD_SKIP1;
|
||||
break;
|
||||
case 0xad: /* DC-DC power control. */
|
||||
s->cmd_state = SSD0303_CMD_SKIP1;
|
||||
break;
|
||||
case 0xae: /* Display off. */
|
||||
s->enabled = 0;
|
||||
break;
|
||||
case 0xaf: /* Display on. */
|
||||
s->enabled = 1;
|
||||
break;
|
||||
case 0xb0 ... 0xbf: /* Set Page address. */
|
||||
s->row = data & 7;
|
||||
break;
|
||||
case 0xc0 ... 0xc8: /* Set COM output direction (Ignored). */
|
||||
break;
|
||||
case 0xd3: /* Set display offset (Ignored). */
|
||||
s->cmd_state = SSD0303_CMD_SKIP1;
|
||||
break;
|
||||
case 0xd5: /* Set display clock (Ignored). */
|
||||
s->cmd_state = SSD0303_CMD_SKIP1;
|
||||
break;
|
||||
case 0xd8: /* Set color and power mode (Ignored). */
|
||||
s->cmd_state = SSD0303_CMD_SKIP1;
|
||||
break;
|
||||
case 0xd9: /* Set pre-charge period (Ignored). */
|
||||
s->cmd_state = SSD0303_CMD_SKIP1;
|
||||
break;
|
||||
case 0xda: /* Set COM pin configuration (Ignored). */
|
||||
s->cmd_state = SSD0303_CMD_SKIP1;
|
||||
break;
|
||||
case 0xdb: /* Set VCOM dselect level (Ignored). */
|
||||
s->cmd_state = SSD0303_CMD_SKIP1;
|
||||
break;
|
||||
case 0xe3: /* no-op. */
|
||||
break;
|
||||
default:
|
||||
BADF("Unknown command: 0x%x\n", data);
|
||||
}
|
||||
break;
|
||||
case SSD0303_CMD_SKIP1:
|
||||
DPRINTF("skip 0x%02x\n", data);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ssd0303_event(i2c_slave *i2c, enum i2c_event event)
|
||||
{
|
||||
ssd0303_state *s = (ssd0303_state *)i2c;
|
||||
switch (event) {
|
||||
case I2C_FINISH:
|
||||
s->mode = SSD0303_IDLE;
|
||||
break;
|
||||
case I2C_START_RECV:
|
||||
case I2C_START_SEND:
|
||||
case I2C_NACK:
|
||||
/* Nothing to do. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ssd0303_update_display(void *opaque)
|
||||
{
|
||||
ssd0303_state *s = (ssd0303_state *)opaque;
|
||||
uint8_t *dest;
|
||||
uint8_t *src;
|
||||
int x;
|
||||
int y;
|
||||
int line;
|
||||
char *colors[2];
|
||||
char colortab[MAGNIFY * 8];
|
||||
int dest_width;
|
||||
uint8_t mask;
|
||||
|
||||
if (s->redraw) {
|
||||
switch (s->ds->depth) {
|
||||
case 0:
|
||||
return;
|
||||
case 15:
|
||||
dest_width = 2;
|
||||
break;
|
||||
case 16:
|
||||
dest_width = 2;
|
||||
break;
|
||||
case 24:
|
||||
dest_width = 3;
|
||||
break;
|
||||
case 32:
|
||||
dest_width = 4;
|
||||
break;
|
||||
default:
|
||||
BADF("Bad color depth\n");
|
||||
return;
|
||||
}
|
||||
dest_width *= MAGNIFY;
|
||||
memset(colortab, 0xff, dest_width);
|
||||
memset(colortab + dest_width, 0, dest_width);
|
||||
if (s->flash) {
|
||||
colors[0] = colortab;
|
||||
colors[1] = colortab;
|
||||
} else if (s->inverse) {
|
||||
colors[0] = colortab;
|
||||
colors[1] = colortab + dest_width;
|
||||
} else {
|
||||
colors[0] = colortab + dest_width;
|
||||
colors[1] = colortab;
|
||||
}
|
||||
dest = s->ds->data;
|
||||
for (y = 0; y < 16; y++) {
|
||||
line = (y + s->start_line) & 63;
|
||||
src = s->framebuffer + 132 * (line >> 3) + 36;
|
||||
mask = 1 << (line & 7);
|
||||
for (x = 0; x < 96; x++) {
|
||||
memcpy(dest, colors[(*src & mask) != 0], dest_width);
|
||||
dest += dest_width;
|
||||
src++;
|
||||
}
|
||||
for (x = 1; x < MAGNIFY; x++) {
|
||||
memcpy(dest, dest - dest_width * 96, dest_width * 96);
|
||||
dest += dest_width * 96;
|
||||
}
|
||||
}
|
||||
}
|
||||
dpy_update(s->ds, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY);
|
||||
}
|
||||
|
||||
static void ssd0303_invalidate_display(void * opaque)
|
||||
{
|
||||
ssd0303_state *s = (ssd0303_state *)opaque;
|
||||
s->redraw = 1;
|
||||
}
|
||||
|
||||
void ssd0303_init(DisplayState *ds, i2c_bus *bus, int address)
|
||||
{
|
||||
ssd0303_state *s;
|
||||
|
||||
s = (ssd0303_state *)i2c_slave_init(bus, address, sizeof(ssd0303_state));
|
||||
s->ds = ds;
|
||||
s->i2c.event = ssd0303_event;
|
||||
s->i2c.recv = ssd0303_recv;
|
||||
s->i2c.send = ssd0303_send;
|
||||
graphic_console_init(ds, ssd0303_update_display, ssd0303_invalidate_display,
|
||||
NULL, s);
|
||||
dpy_resize(s->ds, 96 * MAGNIFY, 16 * MAGNIFY);
|
||||
}
|
267
hw/ssd0323.c
Normal file
267
hw/ssd0323.c
Normal file
@ -0,0 +1,267 @@
|
||||
/*
|
||||
* SSD0323 OLED controller with OSRAM Pictiva 128x64 display.
|
||||
*
|
||||
* Copyright (c) 2006-2007 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
*/
|
||||
|
||||
/* The controller can support a variety of different displays, but we only
|
||||
implement one. Most of the commends relating to brightness and geometry
|
||||
setup are ignored. */
|
||||
#include "vl.h"
|
||||
|
||||
//#define DEBUG_SSD0323 1
|
||||
|
||||
#ifdef DEBUG_SSD0323
|
||||
#define DPRINTF(fmt, args...) \
|
||||
do { printf("ssd0323: " fmt , ##args); } while (0)
|
||||
#define BADF(fmt, args...) \
|
||||
do { fprintf(stderr, "ssd0323: error: " fmt , ##args); exit(1);} while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, args...) do {} while(0)
|
||||
#define BADF(fmt, args...) \
|
||||
do { fprintf(stderr, "ssd0323: error: " fmt , ##args);} while (0)
|
||||
#endif
|
||||
|
||||
/* Scaling factor for pixels. */
|
||||
#define MAGNIFY 4
|
||||
|
||||
enum ssd0323_mode
|
||||
{
|
||||
SSD0323_CMD,
|
||||
SSD0323_DATA
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
DisplayState *ds;
|
||||
|
||||
int cmd_len;
|
||||
int cmd;
|
||||
int cmd_data[8];
|
||||
int row;
|
||||
int row_start;
|
||||
int row_end;
|
||||
int col;
|
||||
int col_start;
|
||||
int col_end;
|
||||
int redraw;
|
||||
enum ssd0323_mode mode;
|
||||
uint8_t framebuffer[128 * 80 / 2];
|
||||
} ssd0323_state;
|
||||
|
||||
int ssd0323_xfer_ssi(void *opaque, int data)
|
||||
{
|
||||
ssd0323_state *s = (ssd0323_state *)opaque;
|
||||
switch (s->mode) {
|
||||
case SSD0323_DATA:
|
||||
DPRINTF("data 0x%02x\n", data);
|
||||
s->framebuffer[s->col + s->row * 64] = data;
|
||||
s->col++;
|
||||
if (s->col > s->col_end) {
|
||||
s->row++;
|
||||
s->col = s->col_start;
|
||||
}
|
||||
if (s->row > s->row_end) {
|
||||
s->row = s->row_start;
|
||||
}
|
||||
s->redraw = 1;
|
||||
break;
|
||||
case SSD0323_CMD:
|
||||
DPRINTF("cmd 0x%02x\n", data);
|
||||
if (s->cmd_len == 0) {
|
||||
s->cmd = data;
|
||||
} else {
|
||||
s->cmd_data[s->cmd_len - 1] = data;
|
||||
}
|
||||
s->cmd_len++;
|
||||
switch (s->cmd) {
|
||||
#define DATA(x) if (s->cmd_len <= (x)) return 0
|
||||
case 0x15: /* Set column. */
|
||||
DATA(2);
|
||||
s->col_start = s->cmd_data[0] % 64;
|
||||
s->col_end = s->cmd_data[1] % 64;
|
||||
break;
|
||||
case 0x75: /* Set row. */
|
||||
DATA(2);
|
||||
s->row_start = s->cmd_data[0] % 80;
|
||||
s->row_end = s->cmd_data[1] % 80;
|
||||
break;
|
||||
case 0x81: /* Set contrast */
|
||||
DATA(1);
|
||||
break;
|
||||
case 0x84: case 0x85: case 0x86: /* Max current. */
|
||||
DATA(0);
|
||||
break;
|
||||
case 0xa0: /* Set remapping. */
|
||||
/* FIXME: Implement this. */
|
||||
DATA(1);
|
||||
break;
|
||||
case 0xa1: /* Set display start line. */
|
||||
case 0xa2: /* Set display offset. */
|
||||
/* FIXME: Implement these. */
|
||||
DATA(1);
|
||||
break;
|
||||
case 0xa4: /* Normal mode. */
|
||||
case 0xa5: /* All on. */
|
||||
case 0xa6: /* All off. */
|
||||
case 0xa7: /* Inverse. */
|
||||
/* FIXME: Implement these. */
|
||||
DATA(0);
|
||||
break;
|
||||
case 0xa8: /* Set multiplex ratio. */
|
||||
case 0xad: /* Set DC-DC converter. */
|
||||
DATA(1);
|
||||
/* Ignored. Don't care. */
|
||||
break;
|
||||
case 0xae: /* Display off. */
|
||||
case 0xaf: /* Display on. */
|
||||
DATA(0);
|
||||
/* TODO: Implement power control. */
|
||||
break;
|
||||
case 0xb1: /* Set phase length. */
|
||||
case 0xb2: /* Set row period. */
|
||||
case 0xb3: /* Set clock rate. */
|
||||
case 0xbc: /* Set precharge. */
|
||||
case 0xbe: /* Set VCOMH. */
|
||||
case 0xbf: /* Set segment low. */
|
||||
DATA(1);
|
||||
/* Ignored. Don't care. */
|
||||
break;
|
||||
case 0xb8: /* Set grey scale table. */
|
||||
/* FIXME: Implement this. */
|
||||
DATA(8);
|
||||
break;
|
||||
case 0xe3: /* NOP. */
|
||||
DATA(0);
|
||||
break;
|
||||
default:
|
||||
BADF("Unknown command: 0x%x\n", data);
|
||||
}
|
||||
s->cmd_len = 0;
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ssd0323_update_display(void *opaque)
|
||||
{
|
||||
ssd0323_state *s = (ssd0323_state *)opaque;
|
||||
uint8_t *dest;
|
||||
uint8_t *src;
|
||||
int x;
|
||||
int y;
|
||||
int i;
|
||||
int line;
|
||||
char *colors[16];
|
||||
char colortab[MAGNIFY * 64];
|
||||
char *p;
|
||||
int dest_width;
|
||||
|
||||
if (s->redraw) {
|
||||
switch (s->ds->depth) {
|
||||
case 0:
|
||||
return;
|
||||
case 15:
|
||||
dest_width = 2;
|
||||
break;
|
||||
case 16:
|
||||
dest_width = 2;
|
||||
break;
|
||||
case 24:
|
||||
dest_width = 3;
|
||||
break;
|
||||
case 32:
|
||||
dest_width = 4;
|
||||
break;
|
||||
default:
|
||||
BADF("Bad color depth\n");
|
||||
return;
|
||||
}
|
||||
p = colortab;
|
||||
for (i = 0; i < 16; i++) {
|
||||
int n;
|
||||
colors[i] = p;
|
||||
switch (s->ds->depth) {
|
||||
case 15:
|
||||
n = i * 2 + (i >> 3);
|
||||
p[0] = n | (n << 5);
|
||||
p[1] = (n << 2) | (n >> 3);
|
||||
break;
|
||||
case 16:
|
||||
n = i * 2 + (i >> 3);
|
||||
p[0] = n | (n << 6) | ((n << 1) & 0x20);
|
||||
p[1] = (n << 3) | (n >> 2);
|
||||
break;
|
||||
case 24:
|
||||
case 32:
|
||||
n = (i << 4) | i;
|
||||
p[0] = p[1] = p[2] = n;
|
||||
break;
|
||||
default:
|
||||
BADF("Bad color depth\n");
|
||||
return;
|
||||
}
|
||||
p += dest_width;
|
||||
}
|
||||
dest = s->ds->data;
|
||||
for (y = 0; y < 64; y++) {
|
||||
line = y;
|
||||
src = s->framebuffer + 64 * line;
|
||||
for (x = 0; x < 64; x++) {
|
||||
int val;
|
||||
val = *src >> 4;
|
||||
for (i = 0; i < MAGNIFY; i++) {
|
||||
memcpy(dest, colors[val], dest_width);
|
||||
dest += dest_width;
|
||||
}
|
||||
val = *src & 0xf;
|
||||
for (i = 0; i < MAGNIFY; i++) {
|
||||
memcpy(dest, colors[val], dest_width);
|
||||
dest += dest_width;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
for (i = 1; i < MAGNIFY; i++) {
|
||||
memcpy(dest, dest - dest_width * MAGNIFY * 128,
|
||||
dest_width * 128 * MAGNIFY);
|
||||
dest += dest_width * 128 * MAGNIFY;
|
||||
}
|
||||
}
|
||||
}
|
||||
dpy_update(s->ds, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY);
|
||||
}
|
||||
|
||||
static void ssd0323_invalidate_display(void * opaque)
|
||||
{
|
||||
ssd0323_state *s = (ssd0323_state *)opaque;
|
||||
s->redraw = 1;
|
||||
}
|
||||
|
||||
/* Command/data input. */
|
||||
static void ssd0323_cd(void *opaque, int n, int level)
|
||||
{
|
||||
ssd0323_state *s = (ssd0323_state *)opaque;
|
||||
DPRINTF("%s mode\n", level ? "Data" : "Command");
|
||||
s->mode = level ? SSD0323_DATA : SSD0323_CMD;
|
||||
}
|
||||
|
||||
void *ssd0323_init(DisplayState *ds, qemu_irq *cmd_p)
|
||||
{
|
||||
ssd0323_state *s;
|
||||
qemu_irq *cmd;
|
||||
|
||||
s = (ssd0323_state *)qemu_mallocz(sizeof(ssd0323_state));
|
||||
s->ds = ds;
|
||||
graphic_console_init(ds, ssd0323_update_display, ssd0323_invalidate_display,
|
||||
NULL, s);
|
||||
dpy_resize(s->ds, 128 * MAGNIFY, 64 * MAGNIFY);
|
||||
s->col_end = 63;
|
||||
s->row_end = 79;
|
||||
|
||||
cmd = qemu_allocate_irqs(ssd0323_cd, s, 1);
|
||||
*cmd_p = *cmd;
|
||||
|
||||
return s;
|
||||
}
|
1101
hw/stellaris.c
Normal file
1101
hw/stellaris.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -208,10 +208,10 @@ static void versatile_init(int ram_size, int vga_ram_size,
|
||||
}
|
||||
}
|
||||
|
||||
pl011_init(0x101f1000, pic[12], serial_hds[0]);
|
||||
pl011_init(0x101f2000, pic[13], serial_hds[1]);
|
||||
pl011_init(0x101f3000, pic[14], serial_hds[2]);
|
||||
pl011_init(0x10009000, sic[6], serial_hds[3]);
|
||||
pl011_init(0x101f1000, pic[12], serial_hds[0], PL011_ARM);
|
||||
pl011_init(0x101f2000, pic[13], serial_hds[1], PL011_ARM);
|
||||
pl011_init(0x101f3000, pic[14], serial_hds[2], PL011_ARM);
|
||||
pl011_init(0x10009000, sic[6], serial_hds[3], PL011_ARM);
|
||||
|
||||
pl080_init(0x10130000, pic[17], 8);
|
||||
sp804_init(0x101e2000, pic[4]);
|
||||
|
@ -77,10 +77,12 @@ For system emulation, the following hardware targets are supported:
|
||||
@item Sun4m (32-bit Sparc processor)
|
||||
@item Sun4u (64-bit Sparc processor, in progress)
|
||||
@item Malta board (32-bit MIPS processor)
|
||||
@item ARM Integrator/CP (ARM926E, 1026E or 946E processor)
|
||||
@item ARM Versatile baseboard (ARM926E)
|
||||
@item ARM RealView Emulation baseboard (ARM926EJ-S)
|
||||
@item ARM Integrator/CP (ARM)
|
||||
@item ARM Versatile baseboard (ARM)
|
||||
@item ARM RealView Emulation baseboard (ARM)
|
||||
@item Spitz, Akita, Borzoi and Terrier PDAs (PXA270 processor)
|
||||
@item Luminary Micro LM3S811EVB (ARM Cortex-M3)
|
||||
@item Luminary Micro LM3S6965EVB (ARM Cortex-M3)
|
||||
@item Freescale MCF5208EVB (ColdFire V2).
|
||||
@item Arnewsh MCF5206 evaluation board (ColdFire V2).
|
||||
@item Palm Tungsten|E PDA (OMAP310 processor)
|
||||
@ -2117,7 +2119,7 @@ devices:
|
||||
|
||||
@itemize @minus
|
||||
@item
|
||||
ARM926E, ARM1026E or ARM946E CPU
|
||||
ARM926E, ARM1026E, ARM946E, ARM1136 or Cortex-A8 CPU
|
||||
@item
|
||||
Two PL011 UARTs
|
||||
@item
|
||||
@ -2134,7 +2136,7 @@ The ARM Versatile baseboard is emulated with the following devices:
|
||||
|
||||
@itemize @minus
|
||||
@item
|
||||
ARM926E CPU
|
||||
ARM926E, ARM1136 or Cortex-A8 CPU
|
||||
@item
|
||||
PL190 Vectored Interrupt Controller
|
||||
@item
|
||||
@ -2163,7 +2165,7 @@ The ARM RealView Emulation baseboard is emulated with the following devices:
|
||||
|
||||
@itemize @minus
|
||||
@item
|
||||
ARM926E CPU
|
||||
ARM926E, ARM1136, ARM11MPCORE(x4) or Cortex-A8 CPU
|
||||
@item
|
||||
ARM AMBA Generic/Distributed Interrupt Controller
|
||||
@item
|
||||
@ -2237,6 +2239,34 @@ Secure Digital card connected to OMAP MMC/SD host
|
||||
Three on-chip UARTs
|
||||
@end itemize
|
||||
|
||||
The Luminary Micro Stellaris LM3S811EVB emulation includes the following
|
||||
devices:
|
||||
|
||||
@itemize @minus
|
||||
@item
|
||||
Cortex-M3 CPU core.
|
||||
@item
|
||||
64k Flash and 8k SRAM.
|
||||
@item
|
||||
Timers, UARTs, ADC and I@math{^2}C interface.
|
||||
@item
|
||||
OSRAM Pictiva 96x16 OLED with SSD0303 controller on I@math{^2}C bus.
|
||||
@end itemize
|
||||
|
||||
The Luminary Micro Stellaris LM3S6965EVB emulation includes the following
|
||||
devices:
|
||||
|
||||
@itemize @minus
|
||||
@item
|
||||
Cortex-M3 CPU core.
|
||||
@item
|
||||
256k Flash and 64k SRAM.
|
||||
@item
|
||||
Timers, UARTs, ADC, I@math{^2}C and SSI interfaces.
|
||||
@item
|
||||
OSRAM Pictiva 128x64 OLED with SSD0323 controller connected via SSI.
|
||||
@end itemize
|
||||
|
||||
A Linux 2.6 test image is available on the QEMU web site. More
|
||||
information is available in the QEMU mailing-list archive.
|
||||
|
||||
|
187
target-arm/cpu.h
187
target-arm/cpu.h
@ -37,6 +37,18 @@
|
||||
#define EXCP_IRQ 5
|
||||
#define EXCP_FIQ 6
|
||||
#define EXCP_BKPT 7
|
||||
#define EXCP_EXCEPTION_EXIT 8 /* Return from v7M exception. */
|
||||
|
||||
#define ARMV7M_EXCP_RESET 1
|
||||
#define ARMV7M_EXCP_NMI 2
|
||||
#define ARMV7M_EXCP_HARD 3
|
||||
#define ARMV7M_EXCP_MEM 4
|
||||
#define ARMV7M_EXCP_BUS 5
|
||||
#define ARMV7M_EXCP_USAGE 6
|
||||
#define ARMV7M_EXCP_SVC 11
|
||||
#define ARMV7M_EXCP_DEBUG 12
|
||||
#define ARMV7M_EXCP_PENDSV 14
|
||||
#define ARMV7M_EXCP_SYSTICK 15
|
||||
|
||||
typedef void ARMWriteCPFunc(void *opaque, int cp_info,
|
||||
int srcreg, int operand, uint32_t value);
|
||||
@ -76,17 +88,22 @@ typedef struct CPUARMState {
|
||||
uint32_t VF; /* V is the bit 31. All other bits are undefined */
|
||||
uint32_t NZF; /* N is bit 31. Z is computed from NZF */
|
||||
uint32_t QF; /* 0 or 1 */
|
||||
|
||||
int thumb; /* 0 = arm mode, 1 = thumb mode */
|
||||
uint32_t GE; /* cpsr[19:16] */
|
||||
int thumb; /* cprs[5]. 0 = arm mode, 1 = thumb mode. */
|
||||
uint32_t condexec_bits; /* IT bits. cpsr[15:10,26:25]. */
|
||||
|
||||
/* System control coprocessor (cp15) */
|
||||
struct {
|
||||
uint32_t c0_cpuid;
|
||||
uint32_t c0_cachetype;
|
||||
uint32_t c0_c1[8]; /* Feature registers. */
|
||||
uint32_t c0_c2[8]; /* Instruction set registers. */
|
||||
uint32_t c1_sys; /* System control register. */
|
||||
uint32_t c1_coproc; /* Coprocessor access register. */
|
||||
uint32_t c1_xscaleauxcr; /* XScale auxiliary control register. */
|
||||
uint32_t c2_base; /* MMU translation table base. */
|
||||
uint32_t c2_base0; /* MMU translation table base 0. */
|
||||
uint32_t c2_base1; /* MMU translation table base 1. */
|
||||
uint32_t c2_mask; /* MMU translation table base mask. */
|
||||
uint32_t c2_data; /* MPU data cachable bits. */
|
||||
uint32_t c2_insn; /* MPU instruction cachable bits. */
|
||||
uint32_t c3; /* MMU domain access control register
|
||||
@ -100,6 +117,9 @@ typedef struct CPUARMState {
|
||||
uint32_t c9_data;
|
||||
uint32_t c13_fcse; /* FCSE PID. */
|
||||
uint32_t c13_context; /* Context ID. */
|
||||
uint32_t c13_tls1; /* User RW Thread register. */
|
||||
uint32_t c13_tls2; /* User RO Thread register. */
|
||||
uint32_t c13_tls3; /* Privileged Thread register. */
|
||||
uint32_t c15_cpar; /* XScale Coprocessor Access Register */
|
||||
uint32_t c15_ticonfig; /* TI925T configuration byte. */
|
||||
uint32_t c15_i_max; /* Maximum D-cache dirty line index. */
|
||||
@ -107,6 +127,17 @@ typedef struct CPUARMState {
|
||||
uint32_t c15_threadid; /* TI debugger thread-ID. */
|
||||
} cp15;
|
||||
|
||||
struct {
|
||||
uint32_t other_sp;
|
||||
uint32_t vecbase;
|
||||
uint32_t basepri;
|
||||
uint32_t control;
|
||||
int current_sp;
|
||||
int exception;
|
||||
int pending_exception;
|
||||
void *nvic;
|
||||
} v7m;
|
||||
|
||||
/* Coprocessor IO used by peripherals */
|
||||
struct {
|
||||
ARMReadCPFunc *cp_read;
|
||||
@ -117,6 +148,10 @@ typedef struct CPUARMState {
|
||||
/* Internal CPU feature flags. */
|
||||
uint32_t features;
|
||||
|
||||
/* Callback for vectored interrupt controller. */
|
||||
int (*get_irq_vector)(struct CPUARMState *);
|
||||
void *irq_opaque;
|
||||
|
||||
/* exception/interrupt handling */
|
||||
jmp_buf jmp_env;
|
||||
int exception_index;
|
||||
@ -126,7 +161,7 @@ typedef struct CPUARMState {
|
||||
|
||||
/* VFP coprocessor state. */
|
||||
struct {
|
||||
float64 regs[16];
|
||||
float64 regs[32];
|
||||
|
||||
uint32_t xregs[16];
|
||||
/* We store these fpcsr fields separately for convenience. */
|
||||
@ -136,9 +171,16 @@ typedef struct CPUARMState {
|
||||
/* Temporary variables if we don't have spare fp regs. */
|
||||
float32 tmp0s, tmp1s;
|
||||
float64 tmp0d, tmp1d;
|
||||
/* scratch space when Tn are not sufficient. */
|
||||
uint32_t scratch[8];
|
||||
|
||||
float_status fp_status;
|
||||
} vfp;
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
struct mmon_state *mmon_entry;
|
||||
#else
|
||||
uint32_t mmon_addr;
|
||||
#endif
|
||||
|
||||
/* iwMMXt coprocessor state. */
|
||||
struct {
|
||||
@ -169,6 +211,7 @@ int cpu_arm_exec(CPUARMState *s);
|
||||
void cpu_arm_close(CPUARMState *s);
|
||||
void do_interrupt(CPUARMState *);
|
||||
void switch_mode(CPUARMState *, int);
|
||||
uint32_t do_arm_semihosting(CPUARMState *env);
|
||||
|
||||
/* you can call this signal handler from your SIGBUS and SIGSEGV
|
||||
signal handlers to inform the virtual CPU of exceptions. non zero
|
||||
@ -176,6 +219,9 @@ void switch_mode(CPUARMState *, int);
|
||||
int cpu_arm_signal_handler(int host_signum, void *pinfo,
|
||||
void *puc);
|
||||
|
||||
void cpu_lock(void);
|
||||
void cpu_unlock(void);
|
||||
|
||||
#define CPSR_M (0x1f)
|
||||
#define CPSR_T (1 << 5)
|
||||
#define CPSR_F (1 << 6)
|
||||
@ -183,13 +229,24 @@ int cpu_arm_signal_handler(int host_signum, void *pinfo,
|
||||
#define CPSR_A (1 << 8)
|
||||
#define CPSR_E (1 << 9)
|
||||
#define CPSR_IT_2_7 (0xfc00)
|
||||
/* Bits 20-23 reserved. */
|
||||
#define CPSR_GE (0xf << 16)
|
||||
#define CPSR_RESERVED (0xf << 20)
|
||||
#define CPSR_J (1 << 24)
|
||||
#define CPSR_IT_0_1 (3 << 25)
|
||||
#define CPSR_Q (1 << 27)
|
||||
#define CPSR_NZCV (0xf << 28)
|
||||
#define CPSR_V (1 << 28)
|
||||
#define CPSR_C (1 << 29)
|
||||
#define CPSR_Z (1 << 30)
|
||||
#define CPSR_N (1 << 31)
|
||||
#define CPSR_NZCV (CPSR_N | CPSR_Z | CPSR_C | CPSR_V)
|
||||
|
||||
#define CPSR_IT (CPSR_IT_0_1 | CPSR_IT_2_7)
|
||||
#define CACHED_CPSR_BITS (CPSR_T | CPSR_GE | CPSR_IT | CPSR_Q | CPSR_NZCV)
|
||||
/* Bits writable in user mode. */
|
||||
#define CPSR_USER (CPSR_NZCV | CPSR_Q | CPSR_GE)
|
||||
/* Execution state bits. MRS read as zero, MSR writes ignored. */
|
||||
#define CPSR_EXEC (CPSR_T | CPSR_IT | CPSR_J)
|
||||
|
||||
#define CACHED_CPSR_BITS (CPSR_T | CPSR_Q | CPSR_NZCV)
|
||||
/* Return the current CPSR value. */
|
||||
static inline uint32_t cpsr_read(CPUARMState *env)
|
||||
{
|
||||
@ -197,7 +254,21 @@ static inline uint32_t cpsr_read(CPUARMState *env)
|
||||
ZF = (env->NZF == 0);
|
||||
return env->uncached_cpsr | (env->NZF & 0x80000000) | (ZF << 30) |
|
||||
(env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27)
|
||||
| (env->thumb << 5);
|
||||
| (env->thumb << 5) | ((env->condexec_bits & 3) << 25)
|
||||
| ((env->condexec_bits & 0xfc) << 8)
|
||||
| (env->GE << 16);
|
||||
}
|
||||
|
||||
/* Return the current xPSR value. */
|
||||
static inline uint32_t xpsr_read(CPUARMState *env)
|
||||
{
|
||||
int ZF;
|
||||
ZF = (env->NZF == 0);
|
||||
return (env->NZF & 0x80000000) | (ZF << 30)
|
||||
| (env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27)
|
||||
| (env->thumb << 24) | ((env->condexec_bits & 3) << 25)
|
||||
| ((env->condexec_bits & 0xfc) << 8)
|
||||
| env->v7m.exception;
|
||||
}
|
||||
|
||||
/* Set the CPSR. Note that some bits of mask must be all-set or all-clear. */
|
||||
@ -213,6 +284,17 @@ static inline void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
|
||||
env->QF = ((val & CPSR_Q) != 0);
|
||||
if (mask & CPSR_T)
|
||||
env->thumb = ((val & CPSR_T) != 0);
|
||||
if (mask & CPSR_IT_0_1) {
|
||||
env->condexec_bits &= ~3;
|
||||
env->condexec_bits |= (val >> 25) & 3;
|
||||
}
|
||||
if (mask & CPSR_IT_2_7) {
|
||||
env->condexec_bits &= 3;
|
||||
env->condexec_bits |= (val >> 8) & 0xfc;
|
||||
}
|
||||
if (mask & CPSR_GE) {
|
||||
env->GE = (val >> 16) & 0xf;
|
||||
}
|
||||
|
||||
if ((env->uncached_cpsr ^ val) & mask & CPSR_M) {
|
||||
switch_mode(env, val & CPSR_M);
|
||||
@ -221,6 +303,32 @@ static inline void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
|
||||
env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask);
|
||||
}
|
||||
|
||||
/* Set the xPSR. Note that some bits of mask must be all-set or all-clear. */
|
||||
static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
|
||||
{
|
||||
/* NOTE: N = 1 and Z = 1 cannot be stored currently */
|
||||
if (mask & CPSR_NZCV) {
|
||||
env->NZF = (val & 0xc0000000) ^ 0x40000000;
|
||||
env->CF = (val >> 29) & 1;
|
||||
env->VF = (val << 3) & 0x80000000;
|
||||
}
|
||||
if (mask & CPSR_Q)
|
||||
env->QF = ((val & CPSR_Q) != 0);
|
||||
if (mask & (1 << 24))
|
||||
env->thumb = ((val & (1 << 24)) != 0);
|
||||
if (mask & CPSR_IT_0_1) {
|
||||
env->condexec_bits &= ~3;
|
||||
env->condexec_bits |= (val >> 25) & 3;
|
||||
}
|
||||
if (mask & CPSR_IT_2_7) {
|
||||
env->condexec_bits &= 3;
|
||||
env->condexec_bits |= (val >> 8) & 0xfc;
|
||||
}
|
||||
if (mask & 0x1ff) {
|
||||
env->v7m.exception = val & 0x1ff;
|
||||
}
|
||||
}
|
||||
|
||||
enum arm_cpu_mode {
|
||||
ARM_CPU_MODE_USR = 0x10,
|
||||
ARM_CPU_MODE_FIQ = 0x11,
|
||||
@ -234,6 +342,8 @@ enum arm_cpu_mode {
|
||||
/* VFP system registers. */
|
||||
#define ARM_VFP_FPSID 0
|
||||
#define ARM_VFP_FPSCR 1
|
||||
#define ARM_VFP_MVFR1 6
|
||||
#define ARM_VFP_MVFR0 7
|
||||
#define ARM_VFP_FPEXC 8
|
||||
#define ARM_VFP_FPINST 9
|
||||
#define ARM_VFP_FPINST2 10
|
||||
@ -253,7 +363,15 @@ enum arm_features {
|
||||
ARM_FEATURE_AUXCR, /* ARM1026 Auxiliary control register. */
|
||||
ARM_FEATURE_XSCALE, /* Intel XScale extensions. */
|
||||
ARM_FEATURE_IWMMXT, /* Intel iwMMXt extension. */
|
||||
ARM_FEATURE_V6,
|
||||
ARM_FEATURE_V6K,
|
||||
ARM_FEATURE_V7,
|
||||
ARM_FEATURE_THUMB2,
|
||||
ARM_FEATURE_MPU, /* Only has Memory Protection Unit, not full MMU. */
|
||||
ARM_FEATURE_VFP3,
|
||||
ARM_FEATURE_NEON,
|
||||
ARM_FEATURE_DIV,
|
||||
ARM_FEATURE_M, /* Microcontroller profile. */
|
||||
ARM_FEATURE_OMAPCP /* OMAP specific CP15 ops handling. */
|
||||
};
|
||||
|
||||
@ -264,27 +382,44 @@ static inline int arm_feature(CPUARMState *env, int feature)
|
||||
|
||||
void arm_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
|
||||
|
||||
/* Interface between CPU and Interrupt controller. */
|
||||
void armv7m_nvic_set_pending(void *opaque, int irq);
|
||||
int armv7m_nvic_acknowledge_irq(void *opaque);
|
||||
void armv7m_nvic_complete_irq(void *opaque, int irq);
|
||||
|
||||
void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
|
||||
ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write,
|
||||
void *opaque);
|
||||
|
||||
#define ARM_CPUID_ARM1026 0x4106a262
|
||||
#define ARM_CPUID_ARM926 0x41069265
|
||||
#define ARM_CPUID_ARM946 0x41059461
|
||||
#define ARM_CPUID_TI915T 0x54029152
|
||||
#define ARM_CPUID_TI925T 0x54029252
|
||||
#define ARM_CPUID_PXA250 0x69052100
|
||||
#define ARM_CPUID_PXA255 0x69052d00
|
||||
#define ARM_CPUID_PXA260 0x69052903
|
||||
#define ARM_CPUID_PXA261 0x69052d05
|
||||
#define ARM_CPUID_PXA262 0x69052d06
|
||||
#define ARM_CPUID_PXA270 0x69054110
|
||||
#define ARM_CPUID_PXA270_A0 0x69054110
|
||||
#define ARM_CPUID_PXA270_A1 0x69054111
|
||||
#define ARM_CPUID_PXA270_B0 0x69054112
|
||||
#define ARM_CPUID_PXA270_B1 0x69054113
|
||||
#define ARM_CPUID_PXA270_C0 0x69054114
|
||||
#define ARM_CPUID_PXA270_C5 0x69054117
|
||||
/* Does the core conform to the the "MicroController" profile. e.g. Cortex-M3.
|
||||
Note the M in older cores (eg. ARM7TDMI) stands for Multiply. These are
|
||||
conventional cores (ie. Application or Realtime profile). */
|
||||
|
||||
#define IS_M(env) arm_feature(env, ARM_FEATURE_M)
|
||||
#define ARM_CPUID(env) (env->cp15.c0_cpuid)
|
||||
|
||||
#define ARM_CPUID_ARM1026 0x4106a262
|
||||
#define ARM_CPUID_ARM926 0x41069265
|
||||
#define ARM_CPUID_ARM946 0x41059461
|
||||
#define ARM_CPUID_TI915T 0x54029152
|
||||
#define ARM_CPUID_TI925T 0x54029252
|
||||
#define ARM_CPUID_PXA250 0x69052100
|
||||
#define ARM_CPUID_PXA255 0x69052d00
|
||||
#define ARM_CPUID_PXA260 0x69052903
|
||||
#define ARM_CPUID_PXA261 0x69052d05
|
||||
#define ARM_CPUID_PXA262 0x69052d06
|
||||
#define ARM_CPUID_PXA270 0x69054110
|
||||
#define ARM_CPUID_PXA270_A0 0x69054110
|
||||
#define ARM_CPUID_PXA270_A1 0x69054111
|
||||
#define ARM_CPUID_PXA270_B0 0x69054112
|
||||
#define ARM_CPUID_PXA270_B1 0x69054113
|
||||
#define ARM_CPUID_PXA270_C0 0x69054114
|
||||
#define ARM_CPUID_PXA270_C5 0x69054117
|
||||
#define ARM_CPUID_ARM1136 0x4117b363
|
||||
#define ARM_CPUID_ARM11MPCORE 0x410fb022
|
||||
#define ARM_CPUID_CORTEXA8 0x410fc080
|
||||
#define ARM_CPUID_CORTEXM3 0x410fc231
|
||||
#define ARM_CPUID_ANY 0xffffffff
|
||||
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
#define TARGET_PAGE_BITS 12
|
||||
@ -302,6 +437,8 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
|
||||
#define cpu_signal_handler cpu_arm_signal_handler
|
||||
#define cpu_list arm_cpu_list
|
||||
|
||||
#define ARM_CPU_SAVE_VERSION 1
|
||||
|
||||
/* MMU modes definitions */
|
||||
#define MMU_MODE0_SUFFIX _kernel
|
||||
#define MMU_MODE1_SUFFIX _user
|
||||
|
@ -68,12 +68,18 @@ static inline int cpu_halted(CPUState *env) {
|
||||
|
||||
/* In op_helper.c */
|
||||
|
||||
void cpu_lock(void);
|
||||
void cpu_unlock(void);
|
||||
void helper_set_cp(CPUState *, uint32_t, uint32_t);
|
||||
uint32_t helper_get_cp(CPUState *, uint32_t);
|
||||
void helper_set_cp15(CPUState *, uint32_t, uint32_t);
|
||||
uint32_t helper_get_cp15(CPUState *, uint32_t);
|
||||
void helper_set_r13_banked(CPUState *env, int mode, uint32_t val);
|
||||
uint32_t helper_get_r13_banked(CPUState *env, int mode);
|
||||
uint32_t helper_v7m_mrs(CPUState *env, int reg);
|
||||
void helper_v7m_msr(CPUState *env, int reg, uint32_t val);
|
||||
|
||||
void helper_mark_exclusive(CPUARMState *, uint32_t addr);
|
||||
int helper_test_exclusive(CPUARMState *, uint32_t addr);
|
||||
void helper_clrex(CPUARMState *env);
|
||||
|
||||
void cpu_loop_exit(void);
|
||||
|
||||
@ -91,4 +97,11 @@ void do_vfp_cmpes(void);
|
||||
void do_vfp_cmped(void);
|
||||
void do_vfp_set_fpscr(void);
|
||||
void do_vfp_get_fpscr(void);
|
||||
|
||||
float32 helper_recps_f32(float32, float32);
|
||||
float32 helper_rsqrts_f32(float32, float32);
|
||||
uint32_t helper_recpe_u32(uint32_t);
|
||||
uint32_t helper_rsqrte_u32(uint32_t);
|
||||
float32 helper_recpe_f32(float32);
|
||||
float32 helper_rsqrte_f32(float32);
|
||||
void helper_neon_tbl(int rn, int maxindex);
|
||||
uint32_t helper_neon_mul_p8(uint32_t op1, uint32_t op2);
|
||||
|
1110
target-arm/helper.c
1110
target-arm/helper.c
File diff suppressed because it is too large
Load Diff
731
target-arm/op.c
731
target-arm/op.c
@ -2,7 +2,7 @@
|
||||
* ARM micro operations
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
* Copyright (c) 2005 CodeSourcery, LLC
|
||||
* Copyright (c) 2005-2007 CodeSourcery, LLC
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -101,11 +101,6 @@ void OPPROTO op_movl_T0_im(void)
|
||||
T0 = PARAM1;
|
||||
}
|
||||
|
||||
void OPPROTO op_movl_T0_T1(void)
|
||||
{
|
||||
T0 = T1;
|
||||
}
|
||||
|
||||
void OPPROTO op_movl_T1_im(void)
|
||||
{
|
||||
T1 = PARAM1;
|
||||
@ -236,6 +231,11 @@ void OPPROTO op_bicl_T0_T1(void)
|
||||
T0 &= ~T1;
|
||||
}
|
||||
|
||||
void OPPROTO op_notl_T0(void)
|
||||
{
|
||||
T0 = ~T0;
|
||||
}
|
||||
|
||||
void OPPROTO op_notl_T1(void)
|
||||
{
|
||||
T1 = ~T1;
|
||||
@ -351,6 +351,19 @@ void OPPROTO op_test_le(void)
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO op_test_T0(void)
|
||||
{
|
||||
if (T0)
|
||||
GOTO_LABEL_PARAM(1);
|
||||
FORCE_RET();
|
||||
}
|
||||
void OPPROTO op_testn_T0(void)
|
||||
{
|
||||
if (!T0)
|
||||
GOTO_LABEL_PARAM(1);
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO op_goto_tb0(void)
|
||||
{
|
||||
GOTO_TB(op_goto_tb0, PARAM1, 0);
|
||||
@ -368,7 +381,8 @@ void OPPROTO op_exit_tb(void)
|
||||
|
||||
void OPPROTO op_movl_T0_cpsr(void)
|
||||
{
|
||||
T0 = cpsr_read(env);
|
||||
/* Execution state bits always read as zero. */
|
||||
T0 = cpsr_read(env) & ~CPSR_EXEC;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
@ -438,6 +452,28 @@ void OPPROTO op_addq_lo_T0_T1(void)
|
||||
T0 = res;
|
||||
}
|
||||
|
||||
/* Dual 16-bit accumulate. */
|
||||
void OPPROTO op_addq_T0_T1_dual(void)
|
||||
{
|
||||
uint64_t res;
|
||||
res = ((uint64_t)(env->regs[PARAM2]) << 32) | (env->regs[PARAM1]);
|
||||
res += (int32_t)T0;
|
||||
res += (int32_t)T1;
|
||||
env->regs[PARAM1] = (uint32_t)res;
|
||||
env->regs[PARAM2] = res >> 32;
|
||||
}
|
||||
|
||||
/* Dual 16-bit subtract accumulate. */
|
||||
void OPPROTO op_subq_T0_T1_dual(void)
|
||||
{
|
||||
uint64_t res;
|
||||
res = ((uint64_t)(env->regs[PARAM2]) << 32) | (env->regs[PARAM1]);
|
||||
res += (int32_t)T0;
|
||||
res -= (int32_t)T1;
|
||||
env->regs[PARAM1] = (uint32_t)res;
|
||||
env->regs[PARAM2] = res >> 32;
|
||||
}
|
||||
|
||||
void OPPROTO op_logicq_cc(void)
|
||||
{
|
||||
env->NZF = (T1 & 0x80000000) | ((T0 | T1) != 0);
|
||||
@ -455,8 +491,21 @@ void OPPROTO op_logicq_cc(void)
|
||||
#include "op_mem.h"
|
||||
#endif
|
||||
|
||||
void OPPROTO op_clrex(void)
|
||||
{
|
||||
cpu_lock();
|
||||
helper_clrex(env);
|
||||
cpu_unlock();
|
||||
}
|
||||
|
||||
/* shifts */
|
||||
|
||||
/* Used by NEON. */
|
||||
void OPPROTO op_shll_T0_im(void)
|
||||
{
|
||||
T1 = T1 << PARAM1;
|
||||
}
|
||||
|
||||
/* T1 based */
|
||||
|
||||
void OPPROTO op_shll_T1_im(void)
|
||||
@ -813,8 +862,39 @@ void OPPROTO op_double_T1_saturate(void)
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
/* thumb shift by immediate */
|
||||
void OPPROTO op_shll_T0_im_thumb(void)
|
||||
/* Unsigned saturating arithmetic for NEON. */
|
||||
void OPPROTO op_addl_T0_T1_usaturate(void)
|
||||
{
|
||||
uint32_t res;
|
||||
|
||||
res = T0 + T1;
|
||||
if (res < T0) {
|
||||
env->QF = 1;
|
||||
T0 = 0xffffffff;
|
||||
} else {
|
||||
T0 = res;
|
||||
}
|
||||
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO op_subl_T0_T1_usaturate(void)
|
||||
{
|
||||
uint32_t res;
|
||||
|
||||
res = T0 - T1;
|
||||
if (res > T0) {
|
||||
env->QF = 1;
|
||||
T0 = 0;
|
||||
} else {
|
||||
T0 = res;
|
||||
}
|
||||
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
/* Thumb shift by immediate */
|
||||
void OPPROTO op_shll_T0_im_thumb_cc(void)
|
||||
{
|
||||
int shift;
|
||||
shift = PARAM1;
|
||||
@ -826,7 +906,13 @@ void OPPROTO op_shll_T0_im_thumb(void)
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO op_shrl_T0_im_thumb(void)
|
||||
void OPPROTO op_shll_T0_im_thumb(void)
|
||||
{
|
||||
T0 = T0 << PARAM1;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO op_shrl_T0_im_thumb_cc(void)
|
||||
{
|
||||
int shift;
|
||||
|
||||
@ -842,7 +928,20 @@ void OPPROTO op_shrl_T0_im_thumb(void)
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO op_sarl_T0_im_thumb(void)
|
||||
void OPPROTO op_shrl_T0_im_thumb(void)
|
||||
{
|
||||
int shift;
|
||||
|
||||
shift = PARAM1;
|
||||
if (shift == 0) {
|
||||
T0 = 0;
|
||||
} else {
|
||||
T0 = T0 >> shift;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO op_sarl_T0_im_thumb_cc(void)
|
||||
{
|
||||
int shift;
|
||||
|
||||
@ -858,6 +957,19 @@ void OPPROTO op_sarl_T0_im_thumb(void)
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO op_sarl_T0_im_thumb(void)
|
||||
{
|
||||
int shift;
|
||||
|
||||
shift = PARAM1;
|
||||
if (shift == 0) {
|
||||
env->CF = T0 & 1;
|
||||
} else {
|
||||
T0 = ((int32_t)T0) >> shift;
|
||||
}
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
/* exceptions */
|
||||
|
||||
void OPPROTO op_swi(void)
|
||||
@ -891,6 +1003,12 @@ void OPPROTO op_bkpt(void)
|
||||
cpu_loop_exit();
|
||||
}
|
||||
|
||||
void OPPROTO op_exception_exit(void)
|
||||
{
|
||||
env->exception_index = EXCP_EXCEPTION_EXIT;
|
||||
cpu_loop_exit();
|
||||
}
|
||||
|
||||
/* VFP support. We follow the convention used for VFP instrunctions:
|
||||
Single precition routines have a "s" suffix, double precision a
|
||||
"d" suffix. */
|
||||
@ -982,6 +1100,28 @@ static inline uint32_t vfp_stoi(float32 s)
|
||||
return v.i;
|
||||
}
|
||||
|
||||
static inline float64 vfp_itod(uint64_t i)
|
||||
{
|
||||
union {
|
||||
uint64_t i;
|
||||
float64 d;
|
||||
} v;
|
||||
|
||||
v.i = i;
|
||||
return v.d;
|
||||
}
|
||||
|
||||
static inline uint64_t vfp_dtoi(float64 d)
|
||||
{
|
||||
union {
|
||||
uint64_t i;
|
||||
float64 d;
|
||||
} v;
|
||||
|
||||
v.d = d;
|
||||
return v.i;
|
||||
}
|
||||
|
||||
/* Integer to float conversion. */
|
||||
VFP_OP(uito, s)
|
||||
{
|
||||
@ -1056,6 +1196,32 @@ VFP_OP(fcvts, d)
|
||||
FT0s = float64_to_float32(FT0d, &env->vfp.fp_status);
|
||||
}
|
||||
|
||||
/* VFP3 fixed point conversion. */
|
||||
#define VFP_CONV_FIX(name, p, ftype, itype, sign) \
|
||||
VFP_OP(name##to, p) \
|
||||
{ \
|
||||
ftype tmp; \
|
||||
tmp = sign##int32_to_##ftype ((itype)vfp_##p##toi(FT0##p), \
|
||||
&env->vfp.fp_status); \
|
||||
FT0##p = ftype##_scalbn(tmp, PARAM1, &env->vfp.fp_status); \
|
||||
} \
|
||||
VFP_OP(to##name, p) \
|
||||
{ \
|
||||
ftype tmp; \
|
||||
tmp = ftype##_scalbn(FT0##p, PARAM1, &env->vfp.fp_status); \
|
||||
FT0##p = vfp_ito##p((itype)ftype##_to_##sign##int32_round_to_zero(tmp, \
|
||||
&env->vfp.fp_status)); \
|
||||
}
|
||||
|
||||
VFP_CONV_FIX(sh, d, float64, int16, )
|
||||
VFP_CONV_FIX(sl, d, float64, int32, )
|
||||
VFP_CONV_FIX(uh, d, float64, uint16, u)
|
||||
VFP_CONV_FIX(ul, d, float64, uint32, u)
|
||||
VFP_CONV_FIX(sh, s, float32, int16, )
|
||||
VFP_CONV_FIX(sl, s, float32, int32, )
|
||||
VFP_CONV_FIX(uh, s, float32, uint16, u)
|
||||
VFP_CONV_FIX(ul, s, float32, uint32, u)
|
||||
|
||||
/* Get and Put values from registers. */
|
||||
VFP_OP(getreg_F0, d)
|
||||
{
|
||||
@ -1142,6 +1308,20 @@ void OPPROTO op_vfp_mdrr(void)
|
||||
FT0d = u.d;
|
||||
}
|
||||
|
||||
/* Load immediate. PARAM1 is the 32 most significant bits of the value. */
|
||||
void OPPROTO op_vfp_fconstd(void)
|
||||
{
|
||||
CPU_DoubleU u;
|
||||
u.l.upper = PARAM1;
|
||||
u.l.lower = 0;
|
||||
FT0d = u.d;
|
||||
}
|
||||
|
||||
void OPPROTO op_vfp_fconsts(void)
|
||||
{
|
||||
FT0s = vfp_itos(PARAM1);
|
||||
}
|
||||
|
||||
/* Copy the most significant bit of T0 to all bits of T1. */
|
||||
void OPPROTO op_signbit_T1_T0(void)
|
||||
{
|
||||
@ -1204,9 +1384,9 @@ void OPPROTO op_movl_user_T0(void)
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO op_movl_T2_T0(void)
|
||||
void OPPROTO op_movl_T0_T1(void)
|
||||
{
|
||||
T2 = T0;
|
||||
T0 = T1;
|
||||
}
|
||||
|
||||
void OPPROTO op_movl_T0_T2(void)
|
||||
@ -1214,5 +1394,530 @@ void OPPROTO op_movl_T0_T2(void)
|
||||
T0 = T2;
|
||||
}
|
||||
|
||||
void OPPROTO op_movl_T1_T0(void)
|
||||
{
|
||||
T1 = T0;
|
||||
}
|
||||
|
||||
void OPPROTO op_movl_T1_T2(void)
|
||||
{
|
||||
T1 = T2;
|
||||
}
|
||||
|
||||
void OPPROTO op_movl_T2_T0(void)
|
||||
{
|
||||
T2 = T0;
|
||||
}
|
||||
|
||||
/* ARMv6 Media instructions. */
|
||||
|
||||
/* Note that signed overflow is undefined in C. The following routines are
|
||||
careful to use unsigned types where modulo arithmetic is required.
|
||||
Failure to do so _will_ break on newer gcc. */
|
||||
|
||||
/* Signed saturating arithmetic. */
|
||||
|
||||
/* Perform 16-bit signed satruating addition. */
|
||||
static inline uint16_t add16_sat(uint16_t a, uint16_t b)
|
||||
{
|
||||
uint16_t res;
|
||||
|
||||
res = a + b;
|
||||
if (((res ^ a) & 0x8000) && !((a ^ b) & 0x8000)) {
|
||||
if (a & 0x8000)
|
||||
res = 0x8000;
|
||||
else
|
||||
res = 0x7fff;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Perform 8-bit signed satruating addition. */
|
||||
static inline uint8_t add8_sat(uint8_t a, uint8_t b)
|
||||
{
|
||||
uint8_t res;
|
||||
|
||||
res = a + b;
|
||||
if (((res ^ a) & 0x80) && !((a ^ b) & 0x80)) {
|
||||
if (a & 0x80)
|
||||
res = 0x80;
|
||||
else
|
||||
res = 0x7f;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Perform 16-bit signed satruating subtraction. */
|
||||
static inline uint16_t sub16_sat(uint16_t a, uint16_t b)
|
||||
{
|
||||
uint16_t res;
|
||||
|
||||
res = a - b;
|
||||
if (((res ^ a) & 0x8000) && ((a ^ b) & 0x8000)) {
|
||||
if (a & 0x8000)
|
||||
res = 0x8000;
|
||||
else
|
||||
res = 0x7fff;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Perform 8-bit signed satruating subtraction. */
|
||||
static inline uint8_t sub8_sat(uint8_t a, uint8_t b)
|
||||
{
|
||||
uint8_t res;
|
||||
|
||||
res = a - b;
|
||||
if (((res ^ a) & 0x80) && ((a ^ b) & 0x80)) {
|
||||
if (a & 0x80)
|
||||
res = 0x80;
|
||||
else
|
||||
res = 0x7f;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#define ADD16(a, b, n) RESULT(add16_sat(a, b), n, 16);
|
||||
#define SUB16(a, b, n) RESULT(sub16_sat(a, b), n, 16);
|
||||
#define ADD8(a, b, n) RESULT(add8_sat(a, b), n, 8);
|
||||
#define SUB8(a, b, n) RESULT(sub8_sat(a, b), n, 8);
|
||||
#define PFX q
|
||||
|
||||
#include "op_addsub.h"
|
||||
|
||||
/* Unsigned saturating arithmetic. */
|
||||
static inline uint16_t add16_usat(uint16_t a, uint8_t b)
|
||||
{
|
||||
uint16_t res;
|
||||
res = a + b;
|
||||
if (res < a)
|
||||
res = 0xffff;
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline uint16_t sub16_usat(uint16_t a, uint8_t b)
|
||||
{
|
||||
if (a < b)
|
||||
return a - b;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline uint8_t add8_usat(uint8_t a, uint8_t b)
|
||||
{
|
||||
uint8_t res;
|
||||
res = a + b;
|
||||
if (res < a)
|
||||
res = 0xff;
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline uint8_t sub8_usat(uint8_t a, uint8_t b)
|
||||
{
|
||||
if (a < b)
|
||||
return a - b;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ADD16(a, b, n) RESULT(add16_usat(a, b), n, 16);
|
||||
#define SUB16(a, b, n) RESULT(sub16_usat(a, b), n, 16);
|
||||
#define ADD8(a, b, n) RESULT(add8_usat(a, b), n, 8);
|
||||
#define SUB8(a, b, n) RESULT(sub8_usat(a, b), n, 8);
|
||||
#define PFX uq
|
||||
|
||||
#include "op_addsub.h"
|
||||
|
||||
/* Signed modulo arithmetic. */
|
||||
#define SARITH16(a, b, n, op) do { \
|
||||
int32_t sum; \
|
||||
sum = (int16_t)((uint16_t)(a) op (uint16_t)(b)); \
|
||||
RESULT(sum, n, 16); \
|
||||
if (sum >= 0) \
|
||||
ge |= 3 << (n * 2); \
|
||||
} while(0)
|
||||
|
||||
#define SARITH8(a, b, n, op) do { \
|
||||
int32_t sum; \
|
||||
sum = (int8_t)((uint8_t)(a) op (uint8_t)(b)); \
|
||||
RESULT(sum, n, 8); \
|
||||
if (sum >= 0) \
|
||||
ge |= 1 << n; \
|
||||
} while(0)
|
||||
|
||||
|
||||
#define ADD16(a, b, n) SARITH16(a, b, n, +)
|
||||
#define SUB16(a, b, n) SARITH16(a, b, n, -)
|
||||
#define ADD8(a, b, n) SARITH8(a, b, n, +)
|
||||
#define SUB8(a, b, n) SARITH8(a, b, n, -)
|
||||
#define PFX s
|
||||
#define ARITH_GE
|
||||
|
||||
#include "op_addsub.h"
|
||||
|
||||
/* Unsigned modulo arithmetic. */
|
||||
#define ADD16(a, b, n) do { \
|
||||
uint32_t sum; \
|
||||
sum = (uint32_t)(uint16_t)(a) + (uint32_t)(uint16_t)(b); \
|
||||
RESULT(sum, n, 16); \
|
||||
if ((sum >> 16) == 0) \
|
||||
ge |= 3 << (n * 2); \
|
||||
} while(0)
|
||||
|
||||
#define ADD8(a, b, n) do { \
|
||||
uint32_t sum; \
|
||||
sum = (uint32_t)(uint8_t)(a) + (uint32_t)(uint8_t)(b); \
|
||||
RESULT(sum, n, 8); \
|
||||
if ((sum >> 8) == 0) \
|
||||
ge |= 3 << (n * 2); \
|
||||
} while(0)
|
||||
|
||||
#define SUB16(a, b, n) do { \
|
||||
uint32_t sum; \
|
||||
sum = (uint32_t)(uint16_t)(a) - (uint32_t)(uint16_t)(b); \
|
||||
RESULT(sum, n, 16); \
|
||||
if ((sum >> 16) == 0) \
|
||||
ge |= 3 << (n * 2); \
|
||||
} while(0)
|
||||
|
||||
#define SUB8(a, b, n) do { \
|
||||
uint32_t sum; \
|
||||
sum = (uint32_t)(uint8_t)(a) - (uint32_t)(uint8_t)(b); \
|
||||
RESULT(sum, n, 8); \
|
||||
if ((sum >> 8) == 0) \
|
||||
ge |= 3 << (n * 2); \
|
||||
} while(0)
|
||||
|
||||
#define PFX u
|
||||
#define ARITH_GE
|
||||
|
||||
#include "op_addsub.h"
|
||||
|
||||
/* Halved signed arithmetic. */
|
||||
#define ADD16(a, b, n) \
|
||||
RESULT(((int32_t)(int16_t)(a) + (int32_t)(int16_t)(b)) >> 1, n, 16)
|
||||
#define SUB16(a, b, n) \
|
||||
RESULT(((int32_t)(int16_t)(a) - (int32_t)(int16_t)(b)) >> 1, n, 16)
|
||||
#define ADD8(a, b, n) \
|
||||
RESULT(((int32_t)(int8_t)(a) + (int32_t)(int8_t)(b)) >> 1, n, 8)
|
||||
#define SUB8(a, b, n) \
|
||||
RESULT(((int32_t)(int8_t)(a) - (int32_t)(int8_t)(b)) >> 1, n, 8)
|
||||
#define PFX sh
|
||||
|
||||
#include "op_addsub.h"
|
||||
|
||||
/* Halved unsigned arithmetic. */
|
||||
#define ADD16(a, b, n) \
|
||||
RESULT(((uint32_t)(uint16_t)(a) + (uint32_t)(uint16_t)(b)) >> 1, n, 16)
|
||||
#define SUB16(a, b, n) \
|
||||
RESULT(((uint32_t)(uint16_t)(a) - (uint32_t)(uint16_t)(b)) >> 1, n, 16)
|
||||
#define ADD8(a, b, n) \
|
||||
RESULT(((uint32_t)(uint8_t)(a) + (uint32_t)(uint8_t)(b)) >> 1, n, 8)
|
||||
#define SUB8(a, b, n) \
|
||||
RESULT(((uint32_t)(uint8_t)(a) - (uint32_t)(uint8_t)(b)) >> 1, n, 8)
|
||||
#define PFX uh
|
||||
|
||||
#include "op_addsub.h"
|
||||
|
||||
void OPPROTO op_pkhtb_T0_T1(void)
|
||||
{
|
||||
T0 = (T0 & 0xffff0000) | (T1 & 0xffff);
|
||||
}
|
||||
|
||||
void OPPROTO op_pkhbt_T0_T1(void)
|
||||
{
|
||||
T0 = (T0 & 0xffff) | (T1 & 0xffff0000);
|
||||
}
|
||||
void OPPROTO op_rev_T0(void)
|
||||
{
|
||||
T0 = ((T0 & 0xff000000) >> 24)
|
||||
| ((T0 & 0x00ff0000) >> 8)
|
||||
| ((T0 & 0x0000ff00) << 8)
|
||||
| ((T0 & 0x000000ff) << 24);
|
||||
}
|
||||
|
||||
void OPPROTO op_revh_T0(void)
|
||||
{
|
||||
T0 = (T0 >> 16) | (T0 << 16);
|
||||
}
|
||||
|
||||
void OPPROTO op_rev16_T0(void)
|
||||
{
|
||||
T0 = ((T0 & 0xff000000) >> 8)
|
||||
| ((T0 & 0x00ff0000) << 8)
|
||||
| ((T0 & 0x0000ff00) >> 8)
|
||||
| ((T0 & 0x000000ff) << 8);
|
||||
}
|
||||
|
||||
void OPPROTO op_revsh_T0(void)
|
||||
{
|
||||
T0 = (int16_t)( ((T0 & 0x0000ff00) >> 8)
|
||||
| ((T0 & 0x000000ff) << 8));
|
||||
}
|
||||
|
||||
void OPPROTO op_rbit_T0(void)
|
||||
{
|
||||
T0 = ((T0 & 0xff000000) >> 24)
|
||||
| ((T0 & 0x00ff0000) >> 8)
|
||||
| ((T0 & 0x0000ff00) << 8)
|
||||
| ((T0 & 0x000000ff) << 24);
|
||||
T0 = ((T0 & 0xf0f0f0f0) >> 4)
|
||||
| ((T0 & 0x0f0f0f0f) << 4);
|
||||
T0 = ((T0 & 0x88888888) >> 3)
|
||||
| ((T0 & 0x44444444) >> 1)
|
||||
| ((T0 & 0x22222222) << 1)
|
||||
| ((T0 & 0x11111111) << 3);
|
||||
}
|
||||
|
||||
/* Swap low and high halfwords. */
|
||||
void OPPROTO op_swap_half_T1(void)
|
||||
{
|
||||
T1 = (T1 >> 16) | (T1 << 16);
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
/* Dual 16-bit signed multiply. */
|
||||
void OPPROTO op_mul_dual_T0_T1(void)
|
||||
{
|
||||
int32_t low;
|
||||
int32_t high;
|
||||
low = (int32_t)(int16_t)T0 * (int32_t)(int16_t)T1;
|
||||
high = (((int32_t)T0) >> 16) * (((int32_t)T1) >> 16);
|
||||
T0 = low;
|
||||
T1 = high;
|
||||
}
|
||||
|
||||
void OPPROTO op_sel_T0_T1(void)
|
||||
{
|
||||
uint32_t mask;
|
||||
uint32_t flags;
|
||||
|
||||
flags = env->GE;
|
||||
mask = 0;
|
||||
if (flags & 1)
|
||||
mask |= 0xff;
|
||||
if (flags & 2)
|
||||
mask |= 0xff00;
|
||||
if (flags & 4)
|
||||
mask |= 0xff0000;
|
||||
if (flags & 8)
|
||||
mask |= 0xff000000;
|
||||
T0 = (T0 & mask) | (T1 & ~mask);
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO op_roundqd_T0_T1(void)
|
||||
{
|
||||
T0 = T1 + ((uint32_t)T0 >> 31);
|
||||
}
|
||||
|
||||
/* Signed saturation. */
|
||||
static inline uint32_t do_ssat(int32_t val, int shift)
|
||||
{
|
||||
int32_t top;
|
||||
uint32_t mask;
|
||||
|
||||
shift = PARAM1;
|
||||
top = val >> shift;
|
||||
mask = (1u << shift) - 1;
|
||||
if (top > 0) {
|
||||
env->QF = 1;
|
||||
return mask;
|
||||
} else if (top < -1) {
|
||||
env->QF = 1;
|
||||
return ~mask;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Unsigned saturation. */
|
||||
static inline uint32_t do_usat(int32_t val, int shift)
|
||||
{
|
||||
uint32_t max;
|
||||
|
||||
shift = PARAM1;
|
||||
max = (1u << shift) - 1;
|
||||
if (val < 0) {
|
||||
env->QF = 1;
|
||||
return 0;
|
||||
} else if (val > max) {
|
||||
env->QF = 1;
|
||||
return max;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Signed saturate. */
|
||||
void OPPROTO op_ssat_T1(void)
|
||||
{
|
||||
T0 = do_ssat(T0, PARAM1);
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
/* Dual halfword signed saturate. */
|
||||
void OPPROTO op_ssat16_T1(void)
|
||||
{
|
||||
uint32_t res;
|
||||
|
||||
res = (uint16_t)do_ssat((int16_t)T0, PARAM1);
|
||||
res |= do_ssat(((int32_t)T0) >> 16, PARAM1) << 16;
|
||||
T0 = res;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
/* Unsigned saturate. */
|
||||
void OPPROTO op_usat_T1(void)
|
||||
{
|
||||
T0 = do_usat(T0, PARAM1);
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
/* Dual halfword unsigned saturate. */
|
||||
void OPPROTO op_usat16_T1(void)
|
||||
{
|
||||
uint32_t res;
|
||||
|
||||
res = (uint16_t)do_usat((int16_t)T0, PARAM1);
|
||||
res |= do_usat(((int32_t)T0) >> 16, PARAM1) << 16;
|
||||
T0 = res;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
/* Dual 16-bit add. */
|
||||
void OPPROTO op_add16_T1_T2(void)
|
||||
{
|
||||
uint32_t mask;
|
||||
mask = (T0 & T1) & 0x8000;
|
||||
T0 ^= ~0x8000;
|
||||
T1 ^= ~0x8000;
|
||||
T0 = (T0 + T1) ^ mask;
|
||||
}
|
||||
|
||||
static inline uint8_t do_usad(uint8_t a, uint8_t b)
|
||||
{
|
||||
if (a > b)
|
||||
return a - b;
|
||||
else
|
||||
return b - a;
|
||||
}
|
||||
|
||||
/* Unsigned sum of absolute byte differences. */
|
||||
void OPPROTO op_usad8_T0_T1(void)
|
||||
{
|
||||
uint32_t sum;
|
||||
sum = do_usad(T0, T1);
|
||||
sum += do_usad(T0 >> 8, T1 >> 8);
|
||||
sum += do_usad(T0 >> 16, T1 >>16);
|
||||
sum += do_usad(T0 >> 24, T1 >> 24);
|
||||
T0 = sum;
|
||||
}
|
||||
|
||||
/* Thumb-2 instructions. */
|
||||
|
||||
/* Insert T1 into T0. Result goes in T1. */
|
||||
void OPPROTO op_bfi_T1_T0(void)
|
||||
{
|
||||
int shift = PARAM1;
|
||||
uint32_t mask = PARAM2;
|
||||
uint32_t bits;
|
||||
|
||||
bits = (T1 << shift) & mask;
|
||||
T1 = (T0 & ~mask) | bits;
|
||||
}
|
||||
|
||||
/* Unsigned bitfield extract. */
|
||||
void OPPROTO op_ubfx_T1(void)
|
||||
{
|
||||
uint32_t shift = PARAM1;
|
||||
uint32_t mask = PARAM2;
|
||||
|
||||
T1 >>= shift;
|
||||
T1 &= mask;
|
||||
}
|
||||
|
||||
/* Signed bitfield extract. */
|
||||
void OPPROTO op_sbfx_T1(void)
|
||||
{
|
||||
uint32_t shift = PARAM1;
|
||||
uint32_t width = PARAM2;
|
||||
int32_t val;
|
||||
|
||||
val = T1 << (32 - (shift + width));
|
||||
T1 = val >> (32 - width);
|
||||
}
|
||||
|
||||
void OPPROTO op_movtop_T0_im(void)
|
||||
{
|
||||
T0 = (T0 & 0xffff) | PARAM1;
|
||||
}
|
||||
|
||||
/* Used by table branch instructions. */
|
||||
void OPPROTO op_jmp_T0_im(void)
|
||||
{
|
||||
env->regs[15] = PARAM1 + (T0 << 1);
|
||||
}
|
||||
|
||||
void OPPROTO op_set_condexec(void)
|
||||
{
|
||||
env->condexec_bits = PARAM1;
|
||||
}
|
||||
|
||||
void OPPROTO op_sdivl_T0_T1(void)
|
||||
{
|
||||
int32_t num;
|
||||
int32_t den;
|
||||
num = T0;
|
||||
den = T1;
|
||||
if (den == 0)
|
||||
T0 = 0;
|
||||
else
|
||||
T0 = num / den;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO op_udivl_T0_T1(void)
|
||||
{
|
||||
uint32_t num;
|
||||
uint32_t den;
|
||||
num = T0;
|
||||
den = T1;
|
||||
if (den == 0)
|
||||
T0 = 0;
|
||||
else
|
||||
T0 = num / den;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO op_movl_T1_r13_banked(void)
|
||||
{
|
||||
T1 = helper_get_r13_banked(env, PARAM1);
|
||||
}
|
||||
|
||||
void OPPROTO op_movl_r13_T1_banked(void)
|
||||
{
|
||||
helper_set_r13_banked(env, PARAM1, T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_v7m_mrs_T0(void)
|
||||
{
|
||||
T0 = helper_v7m_mrs(env, PARAM1);
|
||||
}
|
||||
|
||||
void OPPROTO op_v7m_msr_T0(void)
|
||||
{
|
||||
helper_v7m_msr(env, PARAM1, T0);
|
||||
}
|
||||
|
||||
void OPPROTO op_movl_T0_sp(void)
|
||||
{
|
||||
if (PARAM1 == env->v7m.current_sp)
|
||||
T0 = env->regs[13];
|
||||
else
|
||||
T0 = env->v7m.other_sp;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
#include "op_neon.h"
|
||||
|
||||
/* iwMMXt support */
|
||||
#include "op_iwmmxt.c"
|
||||
|
106
target-arm/op_addsub.h
Normal file
106
target-arm/op_addsub.h
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* ARMv6 integer SIMD operations.
|
||||
*
|
||||
* Copyright (c) 2007 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
*/
|
||||
|
||||
#ifdef ARITH_GE
|
||||
#define DECLARE_GE uint32_t ge = 0
|
||||
#define SET_GE env->GE = ge
|
||||
#else
|
||||
#define DECLARE_GE do{}while(0)
|
||||
#define SET_GE do{}while(0)
|
||||
#endif
|
||||
|
||||
#define RESULT(val, n, width) \
|
||||
res |= ((uint32_t)(glue(glue(uint,width),_t))(val)) << (n * width)
|
||||
|
||||
void OPPROTO glue(glue(op_,PFX),add16_T0_T1)(void)
|
||||
{
|
||||
uint32_t res = 0;
|
||||
DECLARE_GE;
|
||||
|
||||
ADD16(T0, T1, 0);
|
||||
ADD16(T0 >> 16, T1 >> 16, 1);
|
||||
SET_GE;
|
||||
T0 = res;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_,PFX),add8_T0_T1)(void)
|
||||
{
|
||||
uint32_t res = 0;
|
||||
DECLARE_GE;
|
||||
|
||||
ADD8(T0, T1, 0);
|
||||
ADD8(T0 >> 8, T1 >> 8, 1);
|
||||
ADD8(T0 >> 16, T1 >> 16, 2);
|
||||
ADD8(T0 >> 24, T1 >> 24, 3);
|
||||
SET_GE;
|
||||
T0 = res;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_,PFX),sub16_T0_T1)(void)
|
||||
{
|
||||
uint32_t res = 0;
|
||||
DECLARE_GE;
|
||||
|
||||
SUB16(T0, T1, 0);
|
||||
SUB16(T0 >> 16, T1 >> 16, 1);
|
||||
SET_GE;
|
||||
T0 = res;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_,PFX),sub8_T0_T1)(void)
|
||||
{
|
||||
uint32_t res = 0;
|
||||
DECLARE_GE;
|
||||
|
||||
SUB8(T0, T1, 0);
|
||||
SUB8(T0 >> 8, T1 >> 8, 1);
|
||||
SUB8(T0 >> 16, T1 >> 16, 2);
|
||||
SUB8(T0 >> 24, T1 >> 24, 3);
|
||||
SET_GE;
|
||||
T0 = res;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_,PFX),subaddx_T0_T1)(void)
|
||||
{
|
||||
uint32_t res = 0;
|
||||
DECLARE_GE;
|
||||
|
||||
ADD16(T0, T1, 0);
|
||||
SUB16(T0 >> 16, T1 >> 16, 1);
|
||||
SET_GE;
|
||||
T0 = res;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
void OPPROTO glue(glue(op_,PFX),addsubx_T0_T1)(void)
|
||||
{
|
||||
uint32_t res = 0;
|
||||
DECLARE_GE;
|
||||
|
||||
SUB16(T0, T1, 0);
|
||||
ADD16(T0 >> 16, T1 >> 16, 1);
|
||||
SET_GE;
|
||||
T0 = res;
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
#undef DECLARE_GE
|
||||
#undef SET_GE
|
||||
#undef RESULT
|
||||
|
||||
#undef ARITH_GE
|
||||
#undef PFX
|
||||
#undef ADD16
|
||||
#undef SUB16
|
||||
#undef ADD8
|
||||
#undef SUB8
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* ARM helper routines
|
||||
*
|
||||
* Copyright (c) 2005 CodeSourcery, LLC
|
||||
* Copyright (c) 2005-2007 CodeSourcery, LLC
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -175,6 +175,81 @@ void do_vfp_get_fpscr(void)
|
||||
T0 |= vfp_exceptbits_from_host(i);
|
||||
}
|
||||
|
||||
float32 helper_recps_f32(float32 a, float32 b)
|
||||
{
|
||||
float_status *s = &env->vfp.fp_status;
|
||||
float32 two = int32_to_float32(2, s);
|
||||
return float32_sub(two, float32_mul(a, b, s), s);
|
||||
}
|
||||
|
||||
float32 helper_rsqrts_f32(float32 a, float32 b)
|
||||
{
|
||||
float_status *s = &env->vfp.fp_status;
|
||||
float32 three = int32_to_float32(3, s);
|
||||
return float32_sub(three, float32_mul(a, b, s), s);
|
||||
}
|
||||
|
||||
/* TODO: The architecture specifies the value that the estimate functions
|
||||
should return. We return the exact reciprocal/root instead. */
|
||||
float32 helper_recpe_f32(float32 a)
|
||||
{
|
||||
float_status *s = &env->vfp.fp_status;
|
||||
float32 one = int32_to_float32(1, s);
|
||||
return float32_div(one, a, s);
|
||||
}
|
||||
|
||||
float32 helper_rsqrte_f32(float32 a)
|
||||
{
|
||||
float_status *s = &env->vfp.fp_status;
|
||||
float32 one = int32_to_float32(1, s);
|
||||
return float32_div(one, float32_sqrt(a, s), s);
|
||||
}
|
||||
|
||||
uint32_t helper_recpe_u32(uint32_t a)
|
||||
{
|
||||
float_status *s = &env->vfp.fp_status;
|
||||
float32 tmp;
|
||||
tmp = int32_to_float32(a, s);
|
||||
tmp = float32_scalbn(tmp, -32, s);
|
||||
tmp = helper_recpe_f32(tmp);
|
||||
tmp = float32_scalbn(tmp, 31, s);
|
||||
return float32_to_int32(tmp, s);
|
||||
}
|
||||
|
||||
uint32_t helper_rsqrte_u32(uint32_t a)
|
||||
{
|
||||
float_status *s = &env->vfp.fp_status;
|
||||
float32 tmp;
|
||||
tmp = int32_to_float32(a, s);
|
||||
tmp = float32_scalbn(tmp, -32, s);
|
||||
tmp = helper_rsqrte_f32(tmp);
|
||||
tmp = float32_scalbn(tmp, 31, s);
|
||||
return float32_to_int32(tmp, s);
|
||||
}
|
||||
|
||||
void helper_neon_tbl(int rn, int maxindex)
|
||||
{
|
||||
uint32_t val;
|
||||
uint32_t mask;
|
||||
uint32_t tmp;
|
||||
int index;
|
||||
int shift;
|
||||
uint64_t *table;
|
||||
table = (uint64_t *)&env->vfp.regs[rn];
|
||||
val = 0;
|
||||
mask = 0;
|
||||
for (shift = 0; shift < 32; shift += 8) {
|
||||
index = (T1 >> shift) & 0xff;
|
||||
if (index <= maxindex) {
|
||||
tmp = (table[index >> 3] >> (index & 7)) & 0xff;
|
||||
val |= tmp << shift;
|
||||
} else {
|
||||
val |= T0 & (0xff << shift);
|
||||
}
|
||||
}
|
||||
T0 = val;
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
|
||||
#define MMUSUFFIX _mmu
|
||||
@ -227,5 +302,4 @@ void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
|
||||
}
|
||||
env = saved_env;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* ARM memory operations. */
|
||||
|
||||
void helper_ld(uint32_t);
|
||||
/* Load from address T1 into T0. */
|
||||
#define MEM_LD_OP(name) \
|
||||
void OPPROTO glue(op_ld##name,MEMSUFFIX)(void) \
|
||||
@ -49,6 +50,64 @@ MEM_SWP_OP(l, l)
|
||||
|
||||
#undef MEM_SWP_OP
|
||||
|
||||
/* Load-locked, store exclusive. */
|
||||
#define EXCLUSIVE_OP(suffix, ldsuffix) \
|
||||
void OPPROTO glue(op_ld##suffix##ex,MEMSUFFIX)(void) \
|
||||
{ \
|
||||
cpu_lock(); \
|
||||
helper_mark_exclusive(env, T1); \
|
||||
T0 = glue(ld##ldsuffix,MEMSUFFIX)(T1); \
|
||||
cpu_unlock(); \
|
||||
FORCE_RET(); \
|
||||
} \
|
||||
\
|
||||
void OPPROTO glue(op_st##suffix##ex,MEMSUFFIX)(void) \
|
||||
{ \
|
||||
int failed; \
|
||||
cpu_lock(); \
|
||||
failed = helper_test_exclusive(env, T1); \
|
||||
/* ??? Is it safe to hold the cpu lock over a store? */ \
|
||||
if (!failed) { \
|
||||
glue(st##suffix,MEMSUFFIX)(T1, T0); \
|
||||
} \
|
||||
T0 = failed; \
|
||||
cpu_unlock(); \
|
||||
FORCE_RET(); \
|
||||
}
|
||||
|
||||
EXCLUSIVE_OP(b, ub)
|
||||
EXCLUSIVE_OP(w, uw)
|
||||
EXCLUSIVE_OP(l, l)
|
||||
|
||||
#undef EXCLUSIVE_OP
|
||||
|
||||
/* Load exclusive T0:T1 from address T1. */
|
||||
void OPPROTO glue(op_ldqex,MEMSUFFIX)(void)
|
||||
{
|
||||
cpu_lock();
|
||||
helper_mark_exclusive(env, T1);
|
||||
T0 = glue(ldl,MEMSUFFIX)(T1);
|
||||
T1 = glue(ldl,MEMSUFFIX)((T1 + 4));
|
||||
cpu_unlock();
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
/* Store exclusive T0:T2 to address T1. */
|
||||
void OPPROTO glue(op_stqex,MEMSUFFIX)(void)
|
||||
{
|
||||
int failed;
|
||||
cpu_lock();
|
||||
failed = helper_test_exclusive(env, T1);
|
||||
/* ??? Is it safe to hold the cpu lock over a store? */
|
||||
if (!failed) {
|
||||
glue(stl,MEMSUFFIX)(T1, T0);
|
||||
glue(stl,MEMSUFFIX)((T1 + 4), T2);
|
||||
}
|
||||
T0 = failed;
|
||||
cpu_unlock();
|
||||
FORCE_RET();
|
||||
}
|
||||
|
||||
/* Floating point load/store. Address is in T1 */
|
||||
#define VFP_MEM_OP(p, w) \
|
||||
void OPPROTO glue(op_vfp_ld##p,MEMSUFFIX)(void) \
|
||||
|
1754
target-arm/op_neon.h
Normal file
1754
target-arm/op_neon.h
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
54
vl.c
54
vl.c
@ -6126,7 +6126,9 @@ void cpu_save(QEMUFile *f, void *opaque)
|
||||
qemu_put_be32(f, env->cp15.c1_sys);
|
||||
qemu_put_be32(f, env->cp15.c1_coproc);
|
||||
qemu_put_be32(f, env->cp15.c1_xscaleauxcr);
|
||||
qemu_put_be32(f, env->cp15.c2_base);
|
||||
qemu_put_be32(f, env->cp15.c2_base0);
|
||||
qemu_put_be32(f, env->cp15.c2_base1);
|
||||
qemu_put_be32(f, env->cp15.c2_mask);
|
||||
qemu_put_be32(f, env->cp15.c2_data);
|
||||
qemu_put_be32(f, env->cp15.c2_insn);
|
||||
qemu_put_be32(f, env->cp15.c3);
|
||||
@ -6141,6 +6143,9 @@ void cpu_save(QEMUFile *f, void *opaque)
|
||||
qemu_put_be32(f, env->cp15.c9_data);
|
||||
qemu_put_be32(f, env->cp15.c13_fcse);
|
||||
qemu_put_be32(f, env->cp15.c13_context);
|
||||
qemu_put_be32(f, env->cp15.c13_tls1);
|
||||
qemu_put_be32(f, env->cp15.c13_tls2);
|
||||
qemu_put_be32(f, env->cp15.c13_tls3);
|
||||
qemu_put_be32(f, env->cp15.c15_cpar);
|
||||
|
||||
qemu_put_be32(f, env->features);
|
||||
@ -6159,6 +6164,15 @@ void cpu_save(QEMUFile *f, void *opaque)
|
||||
/* TODO: Should use proper FPSCR access functions. */
|
||||
qemu_put_be32(f, env->vfp.vec_len);
|
||||
qemu_put_be32(f, env->vfp.vec_stride);
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_VFP3)) {
|
||||
for (i = 16; i < 32; i++) {
|
||||
CPU_DoubleU u;
|
||||
u.d = env->vfp.regs[i];
|
||||
qemu_put_be32(f, u.l.upper);
|
||||
qemu_put_be32(f, u.l.lower);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
|
||||
@ -6169,6 +6183,15 @@ void cpu_save(QEMUFile *f, void *opaque)
|
||||
qemu_put_be32(f, env->iwmmxt.cregs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_M)) {
|
||||
qemu_put_be32(f, env->v7m.other_sp);
|
||||
qemu_put_be32(f, env->v7m.vecbase);
|
||||
qemu_put_be32(f, env->v7m.basepri);
|
||||
qemu_put_be32(f, env->v7m.control);
|
||||
qemu_put_be32(f, env->v7m.current_sp);
|
||||
qemu_put_be32(f, env->v7m.exception);
|
||||
}
|
||||
}
|
||||
|
||||
int cpu_load(QEMUFile *f, void *opaque, int version_id)
|
||||
@ -6176,7 +6199,7 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
|
||||
CPUARMState *env = (CPUARMState *)opaque;
|
||||
int i;
|
||||
|
||||
if (version_id != 0)
|
||||
if (version_id != ARM_CPU_SAVE_VERSION)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
@ -6198,7 +6221,9 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
|
||||
env->cp15.c1_sys = qemu_get_be32(f);
|
||||
env->cp15.c1_coproc = qemu_get_be32(f);
|
||||
env->cp15.c1_xscaleauxcr = qemu_get_be32(f);
|
||||
env->cp15.c2_base = qemu_get_be32(f);
|
||||
env->cp15.c2_base0 = qemu_get_be32(f);
|
||||
env->cp15.c2_base1 = qemu_get_be32(f);
|
||||
env->cp15.c2_mask = qemu_get_be32(f);
|
||||
env->cp15.c2_data = qemu_get_be32(f);
|
||||
env->cp15.c2_insn = qemu_get_be32(f);
|
||||
env->cp15.c3 = qemu_get_be32(f);
|
||||
@ -6213,6 +6238,9 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
|
||||
env->cp15.c9_data = qemu_get_be32(f);
|
||||
env->cp15.c13_fcse = qemu_get_be32(f);
|
||||
env->cp15.c13_context = qemu_get_be32(f);
|
||||
env->cp15.c13_tls1 = qemu_get_be32(f);
|
||||
env->cp15.c13_tls2 = qemu_get_be32(f);
|
||||
env->cp15.c13_tls3 = qemu_get_be32(f);
|
||||
env->cp15.c15_cpar = qemu_get_be32(f);
|
||||
|
||||
env->features = qemu_get_be32(f);
|
||||
@ -6231,6 +6259,15 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
|
||||
/* TODO: Should use proper FPSCR access functions. */
|
||||
env->vfp.vec_len = qemu_get_be32(f);
|
||||
env->vfp.vec_stride = qemu_get_be32(f);
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_VFP3)) {
|
||||
for (i = 0; i < 16; i++) {
|
||||
CPU_DoubleU u;
|
||||
u.l.upper = qemu_get_be32(f);
|
||||
u.l.lower = qemu_get_be32(f);
|
||||
env->vfp.regs[i] = u.d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
|
||||
@ -6242,6 +6279,15 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
|
||||
}
|
||||
}
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_M)) {
|
||||
env->v7m.other_sp = qemu_get_be32(f);
|
||||
env->v7m.vecbase = qemu_get_be32(f);
|
||||
env->v7m.basepri = qemu_get_be32(f);
|
||||
env->v7m.control = qemu_get_be32(f);
|
||||
env->v7m.current_sp = qemu_get_be32(f);
|
||||
env->v7m.exception = qemu_get_be32(f);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -7392,6 +7438,8 @@ void register_machines(void)
|
||||
qemu_register_machine(&borzoipda_machine);
|
||||
qemu_register_machine(&terrierpda_machine);
|
||||
qemu_register_machine(&palmte_machine);
|
||||
qemu_register_machine(&lm3s811evb_machine);
|
||||
qemu_register_machine(&lm3s6965evb_machine);
|
||||
#elif defined(TARGET_SH4)
|
||||
qemu_register_machine(&shix_machine);
|
||||
qemu_register_machine(&r2d_machine);
|
||||
|
40
vl.h
40
vl.h
@ -1482,6 +1482,14 @@ extern QEMUMachine terrierpda_machine;
|
||||
/* palm.c */
|
||||
extern QEMUMachine palmte_machine;
|
||||
|
||||
/* armv7m.c */
|
||||
qemu_irq *armv7m_init(int flash_size, int sram_size,
|
||||
const char *kernel_filename, const char *cpu_model);
|
||||
|
||||
/* stellaris.c */
|
||||
extern QEMUMachine lm3s811evb_machine;
|
||||
extern QEMUMachine lm3s6965evb_machine;
|
||||
|
||||
/* ps2.c */
|
||||
void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg);
|
||||
void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg);
|
||||
@ -1502,11 +1510,24 @@ void pl031_init(uint32_t base, qemu_irq irq);
|
||||
void *pl110_init(DisplayState *ds, uint32_t base, qemu_irq irq, int);
|
||||
|
||||
/* pl011.c */
|
||||
void pl011_init(uint32_t base, qemu_irq irq, CharDriverState *chr);
|
||||
enum pl011_type {
|
||||
PL011_ARM,
|
||||
PL011_LUMINARY
|
||||
};
|
||||
|
||||
void pl011_init(uint32_t base, qemu_irq irq, CharDriverState *chr,
|
||||
enum pl011_type type);
|
||||
|
||||
/* pl022.c */
|
||||
void pl022_init(uint32_t base, qemu_irq irq, int (*xfer_cb)(void *, int),
|
||||
void *opaque);
|
||||
|
||||
/* pl050.c */
|
||||
void pl050_init(uint32_t base, qemu_irq irq, int is_mouse);
|
||||
|
||||
/* pl061.c */
|
||||
qemu_irq *pl061_init(uint32_t base, qemu_irq irq, qemu_irq **out);
|
||||
|
||||
/* pl080.c */
|
||||
void *pl080_init(uint32_t base, qemu_irq irq, int nchannels);
|
||||
|
||||
@ -1524,8 +1545,11 @@ void icp_pit_init(uint32_t base, qemu_irq *pic, int irq);
|
||||
/* arm_sysctl.c */
|
||||
void arm_sysctl_init(uint32_t base, uint32_t sys_id);
|
||||
|
||||
/* arm_gic.c */
|
||||
qemu_irq *arm_gic_init(uint32_t base, qemu_irq parent_irq);
|
||||
/* realview_gic.c */
|
||||
qemu_irq *realview_gic_init(uint32_t base, qemu_irq parent_irq);
|
||||
|
||||
/* mpcore.c */
|
||||
extern qemu_irq *mpcore_irq_init(qemu_irq *cpu_irq);
|
||||
|
||||
/* arm_boot.c */
|
||||
|
||||
@ -1533,6 +1557,16 @@ void arm_load_kernel(CPUState *env, int ram_size, const char *kernel_filename,
|
||||
const char *kernel_cmdline, const char *initrd_filename,
|
||||
int board_id, target_phys_addr_t loader_start);
|
||||
|
||||
/* armv7m_nvic.c */
|
||||
qemu_irq *armv7m_nvic_init(CPUState *env);
|
||||
|
||||
/* ssd0303.c */
|
||||
void ssd0303_init(DisplayState *ds, i2c_bus *bus, int address);
|
||||
|
||||
/* ssd0323.c */
|
||||
int ssd0323_xfer_ssi(void *opaque, int data);
|
||||
void *ssd0323_init(DisplayState *ds, qemu_irq *cmd_p);
|
||||
|
||||
/* sh7750.c */
|
||||
struct SH7750State;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user