e23a1b33b5
Like qdev_init(), but terminate program via hw_error() instead of returning an error value. Use it instead of qdev_init() where terminating the program on failure is okay, either because it's during machine construction, or because we know that failure can't happen. Because relying in the latter is somewhat unclean, and the former is not always obvious, it would be nice to go back to qdev_init() in the not-so-obvious cases, only with proper error handling. I'm leaving that for another day, because it involves making sure that error values are properly checked by all callers. Patchworks-ID: 35168 Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
267 lines
7.1 KiB
C
267 lines
7.1 KiB
C
/*
|
|
* ARMV7M System emulation.
|
|
*
|
|
* Copyright (c) 2006-2007 CodeSourcery.
|
|
* Written by Paul Brook
|
|
*
|
|
* This code is licenced under the GPL.
|
|
*/
|
|
|
|
#include "sysbus.h"
|
|
#include "arm-misc.h"
|
|
#include "sysemu.h"
|
|
#include "loader.h"
|
|
#include "elf.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(void * opaque, uint32_t addr)
|
|
{
|
|
uint32_t res;
|
|
|
|
res = *(uint32_t *)opaque;
|
|
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(opaque, 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(opaque, 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(opaque, 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(opaque, 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(opaque, 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(opaque, 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 * const bitband_readfn[] = {
|
|
bitband_readb,
|
|
bitband_readw,
|
|
bitband_readl
|
|
};
|
|
|
|
static CPUWriteMemoryFunc * const bitband_writefn[] = {
|
|
bitband_writeb,
|
|
bitband_writew,
|
|
bitband_writel
|
|
};
|
|
|
|
typedef struct {
|
|
SysBusDevice busdev;
|
|
uint32_t base;
|
|
} BitBandState;
|
|
|
|
static int bitband_init(SysBusDevice *dev)
|
|
{
|
|
BitBandState *s = FROM_SYSBUS(BitBandState, dev);
|
|
int iomemtype;
|
|
|
|
iomemtype = cpu_register_io_memory(bitband_readfn, bitband_writefn,
|
|
&s->base);
|
|
sysbus_init_mmio(dev, 0x02000000, iomemtype);
|
|
return 0;
|
|
}
|
|
|
|
static void armv7m_bitband_init(void)
|
|
{
|
|
DeviceState *dev;
|
|
|
|
dev = qdev_create(NULL, "ARM,bitband-memory");
|
|
qdev_prop_set_uint32(dev, "base", 0x20000000);
|
|
qdev_init_nofail(dev);
|
|
sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0x22000000);
|
|
|
|
dev = qdev_create(NULL, "ARM,bitband-memory");
|
|
qdev_prop_set_uint32(dev, "base", 0x40000000);
|
|
qdev_init_nofail(dev);
|
|
sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0x42000000);
|
|
}
|
|
|
|
/* 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;
|
|
DeviceState *nvic;
|
|
/* FIXME: make this local state. */
|
|
static qemu_irq pic[64];
|
|
qemu_irq *cpu_pic;
|
|
uint32_t pc;
|
|
int image_size;
|
|
uint64_t entry;
|
|
uint64_t lowaddr;
|
|
int i;
|
|
int big_endian;
|
|
|
|
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,
|
|
qemu_ram_alloc(flash_size) | IO_MEM_ROM);
|
|
cpu_register_physical_memory(0x20000000, sram_size,
|
|
qemu_ram_alloc(sram_size) | IO_MEM_RAM);
|
|
armv7m_bitband_init();
|
|
|
|
nvic = qdev_create(NULL, "armv7m_nvic");
|
|
env->v7m.nvic = nvic;
|
|
qdev_init_nofail(nvic);
|
|
cpu_pic = arm_pic_init_cpu(env);
|
|
sysbus_connect_irq(sysbus_from_qdev(nvic), 0, cpu_pic[ARM_PIC_CPU_IRQ]);
|
|
for (i = 0; i < 64; i++) {
|
|
pic[i] = qdev_get_gpio_in(nvic, i);
|
|
}
|
|
|
|
#ifdef TARGET_WORDS_BIGENDIAN
|
|
big_endian = 1;
|
|
#else
|
|
big_endian = 0;
|
|
#endif
|
|
|
|
image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL,
|
|
big_endian, ELF_MACHINE, 1);
|
|
if (image_size < 0) {
|
|
image_size = load_image_targphys(kernel_filename, 0, flash_size);
|
|
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] = ldl_phys(0);
|
|
pc = ldl_phys(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,
|
|
qemu_ram_alloc(0x1000) | IO_MEM_RAM);
|
|
|
|
return pic;
|
|
}
|
|
|
|
static SysBusDeviceInfo bitband_info = {
|
|
.init = bitband_init,
|
|
.qdev.name = "ARM,bitband-memory",
|
|
.qdev.size = sizeof(BitBandState),
|
|
.qdev.props = (Property[]) {
|
|
DEFINE_PROP_UINT32("base", BitBandState, base, 0),
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
}
|
|
};
|
|
|
|
static void armv7m_register_devices(void)
|
|
{
|
|
sysbus_register_withprop(&bitband_info);
|
|
}
|
|
|
|
device_init(armv7m_register_devices)
|