qemu/hw/arm/armv7m.c
<<
>>
Prefs
   1/*
   2 * ARMV7M System emulation.
   3 *
   4 * Copyright (c) 2006-2007 CodeSourcery.
   5 * Written by Paul Brook
   6 *
   7 * This code is licensed under the GPL.
   8 */
   9
  10#include "qemu/osdep.h"
  11#include "qapi/error.h"
  12#include "qemu-common.h"
  13#include "cpu.h"
  14#include "hw/sysbus.h"
  15#include "hw/arm/arm.h"
  16#include "hw/loader.h"
  17#include "elf.h"
  18#include "sysemu/qtest.h"
  19#include "qemu/error-report.h"
  20
  21/* Bitbanded IO.  Each word corresponds to a single bit.  */
  22
  23/* Get the byte address of the real memory for a bitband access.  */
  24static inline uint32_t bitband_addr(void * opaque, uint32_t addr)
  25{
  26    uint32_t res;
  27
  28    res = *(uint32_t *)opaque;
  29    res |= (addr & 0x1ffffff) >> 5;
  30    return res;
  31
  32}
  33
  34static uint32_t bitband_readb(void *opaque, hwaddr offset)
  35{
  36    uint8_t v;
  37    cpu_physical_memory_read(bitband_addr(opaque, offset), &v, 1);
  38    return (v & (1 << ((offset >> 2) & 7))) != 0;
  39}
  40
  41static void bitband_writeb(void *opaque, hwaddr offset,
  42                           uint32_t value)
  43{
  44    uint32_t addr;
  45    uint8_t mask;
  46    uint8_t v;
  47    addr = bitband_addr(opaque, offset);
  48    mask = (1 << ((offset >> 2) & 7));
  49    cpu_physical_memory_read(addr, &v, 1);
  50    if (value & 1)
  51        v |= mask;
  52    else
  53        v &= ~mask;
  54    cpu_physical_memory_write(addr, &v, 1);
  55}
  56
  57static uint32_t bitband_readw(void *opaque, hwaddr offset)
  58{
  59    uint32_t addr;
  60    uint16_t mask;
  61    uint16_t v;
  62    addr = bitband_addr(opaque, offset) & ~1;
  63    mask = (1 << ((offset >> 2) & 15));
  64    mask = tswap16(mask);
  65    cpu_physical_memory_read(addr, &v, 2);
  66    return (v & mask) != 0;
  67}
  68
  69static void bitband_writew(void *opaque, hwaddr offset,
  70                           uint32_t value)
  71{
  72    uint32_t addr;
  73    uint16_t mask;
  74    uint16_t v;
  75    addr = bitband_addr(opaque, offset) & ~1;
  76    mask = (1 << ((offset >> 2) & 15));
  77    mask = tswap16(mask);
  78    cpu_physical_memory_read(addr, &v, 2);
  79    if (value & 1)
  80        v |= mask;
  81    else
  82        v &= ~mask;
  83    cpu_physical_memory_write(addr, &v, 2);
  84}
  85
  86static uint32_t bitband_readl(void *opaque, hwaddr offset)
  87{
  88    uint32_t addr;
  89    uint32_t mask;
  90    uint32_t v;
  91    addr = bitband_addr(opaque, offset) & ~3;
  92    mask = (1 << ((offset >> 2) & 31));
  93    mask = tswap32(mask);
  94    cpu_physical_memory_read(addr, &v, 4);
  95    return (v & mask) != 0;
  96}
  97
  98static void bitband_writel(void *opaque, hwaddr offset,
  99                           uint32_t value)
 100{
 101    uint32_t addr;
 102    uint32_t mask;
 103    uint32_t v;
 104    addr = bitband_addr(opaque, offset) & ~3;
 105    mask = (1 << ((offset >> 2) & 31));
 106    mask = tswap32(mask);
 107    cpu_physical_memory_read(addr, &v, 4);
 108    if (value & 1)
 109        v |= mask;
 110    else
 111        v &= ~mask;
 112    cpu_physical_memory_write(addr, &v, 4);
 113}
 114
 115static const MemoryRegionOps bitband_ops = {
 116    .old_mmio = {
 117        .read = { bitband_readb, bitband_readw, bitband_readl, },
 118        .write = { bitband_writeb, bitband_writew, bitband_writel, },
 119    },
 120    .endianness = DEVICE_NATIVE_ENDIAN,
 121};
 122
 123#define TYPE_BITBAND "ARM,bitband-memory"
 124#define BITBAND(obj) OBJECT_CHECK(BitBandState, (obj), TYPE_BITBAND)
 125
 126typedef struct {
 127    /*< private >*/
 128    SysBusDevice parent_obj;
 129    /*< public >*/
 130
 131    MemoryRegion iomem;
 132    uint32_t base;
 133} BitBandState;
 134
 135static int bitband_init(SysBusDevice *dev)
 136{
 137    BitBandState *s = BITBAND(dev);
 138
 139    memory_region_init_io(&s->iomem, OBJECT(s), &bitband_ops, &s->base,
 140                          "bitband", 0x02000000);
 141    sysbus_init_mmio(dev, &s->iomem);
 142    return 0;
 143}
 144
 145static void armv7m_bitband_init(void)
 146{
 147    DeviceState *dev;
 148
 149    dev = qdev_create(NULL, TYPE_BITBAND);
 150    qdev_prop_set_uint32(dev, "base", 0x20000000);
 151    qdev_init_nofail(dev);
 152    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x22000000);
 153
 154    dev = qdev_create(NULL, TYPE_BITBAND);
 155    qdev_prop_set_uint32(dev, "base", 0x40000000);
 156    qdev_init_nofail(dev);
 157    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x42000000);
 158}
 159
 160/* Board init.  */
 161
 162static void armv7m_reset(void *opaque)
 163{
 164    ARMCPU *cpu = opaque;
 165
 166    cpu_reset(CPU(cpu));
 167}
 168
 169/* Init CPU and memory for a v7-M based board.
 170   mem_size is in bytes.
 171   Returns the NVIC array.  */
 172
 173DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
 174                      const char *kernel_filename, const char *cpu_model)
 175{
 176    ARMCPU *cpu;
 177    CPUARMState *env;
 178    DeviceState *nvic;
 179    int image_size;
 180    uint64_t entry;
 181    uint64_t lowaddr;
 182    int big_endian;
 183    MemoryRegion *hack = g_new(MemoryRegion, 1);
 184
 185    if (cpu_model == NULL) {
 186        cpu_model = "cortex-m3";
 187    }
 188    cpu = cpu_arm_init(cpu_model);
 189    if (cpu == NULL) {
 190        fprintf(stderr, "Unable to find CPU definition\n");
 191        exit(1);
 192    }
 193    env = &cpu->env;
 194
 195    armv7m_bitband_init();
 196
 197    nvic = qdev_create(NULL, "armv7m_nvic");
 198    qdev_prop_set_uint32(nvic, "num-irq", num_irq);
 199    env->nvic = nvic;
 200    qdev_init_nofail(nvic);
 201    sysbus_connect_irq(SYS_BUS_DEVICE(nvic), 0,
 202                       qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ));
 203
 204#ifdef TARGET_WORDS_BIGENDIAN
 205    big_endian = 1;
 206#else
 207    big_endian = 0;
 208#endif
 209
 210    if (!kernel_filename && !qtest_enabled()) {
 211        fprintf(stderr, "Guest image must be specified (using -kernel)\n");
 212        exit(1);
 213    }
 214
 215    if (kernel_filename) {
 216        image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr,
 217                              NULL, big_endian, EM_ARM, 1, 0);
 218        if (image_size < 0) {
 219            image_size = load_image_targphys(kernel_filename, 0, mem_size);
 220            lowaddr = 0;
 221        }
 222        if (image_size < 0) {
 223            error_report("Could not load kernel '%s'", kernel_filename);
 224            exit(1);
 225        }
 226    }
 227
 228    /* Hack to map an additional page of ram at the top of the address
 229       space.  This stops qemu complaining about executing code outside RAM
 230       when returning from an exception.  */
 231    memory_region_init_ram(hack, NULL, "armv7m.hack", 0x1000, &error_fatal);
 232    vmstate_register_ram_global(hack);
 233    memory_region_add_subregion(system_memory, 0xfffff000, hack);
 234
 235    qemu_register_reset(armv7m_reset, cpu);
 236    return nvic;
 237}
 238
 239static Property bitband_properties[] = {
 240    DEFINE_PROP_UINT32("base", BitBandState, base, 0),
 241    DEFINE_PROP_END_OF_LIST(),
 242};
 243
 244static void bitband_class_init(ObjectClass *klass, void *data)
 245{
 246    DeviceClass *dc = DEVICE_CLASS(klass);
 247    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
 248
 249    k->init = bitband_init;
 250    dc->props = bitband_properties;
 251}
 252
 253static const TypeInfo bitband_info = {
 254    .name          = TYPE_BITBAND,
 255    .parent        = TYPE_SYS_BUS_DEVICE,
 256    .instance_size = sizeof(BitBandState),
 257    .class_init    = bitband_class_init,
 258};
 259
 260static void armv7m_register_types(void)
 261{
 262    type_register_static(&bitband_info);
 263}
 264
 265type_init(armv7m_register_types)
 266