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