Commit 9ee6e8bb authored by pbrook's avatar pbrook

ARMv7 support.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3572 c046a42c-6fe2-441c-8c8c-71466251a162
parent ee4e83ed
......@@ -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
......
......@@ -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)
{
......
/*
* 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);
......
This diff is collapsed.
/*
* 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);
......
/*
* 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;
}
/*
* 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 */