pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 1 | /* |
| 2 | * ARMV7M System emulation. |
| 3 | * |
| 4 | * Copyright (c) 2006-2007 CodeSourcery. |
| 5 | * Written by Paul Brook |
| 6 | * |
| 7 | * This code is licenced under the GPL. |
| 8 | */ |
| 9 | |
Paul Brook | fe7e875 | 2009-05-14 22:35:08 +0100 | [diff] [blame] | 10 | #include "sysbus.h" |
pbrook | 87ecb68 | 2007-11-17 17:14:51 +0000 | [diff] [blame] | 11 | #include "arm-misc.h" |
| 12 | #include "sysemu.h" |
Blue Swirl | ca20cf3 | 2009-09-20 14:58:02 +0000 | [diff] [blame] | 13 | #include "loader.h" |
| 14 | #include "elf.h" |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 15 | |
| 16 | /* Bitbanded IO. Each word corresponds to a single bit. */ |
| 17 | |
| 18 | /* Get the byte address of the real memory for a bitband acess. */ |
pbrook | 8da3ff1 | 2008-12-01 18:59:50 +0000 | [diff] [blame] | 19 | static inline uint32_t bitband_addr(void * opaque, uint32_t addr) |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 20 | { |
| 21 | uint32_t res; |
| 22 | |
pbrook | 8da3ff1 | 2008-12-01 18:59:50 +0000 | [diff] [blame] | 23 | res = *(uint32_t *)opaque; |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 24 | res |= (addr & 0x1ffffff) >> 5; |
| 25 | return res; |
| 26 | |
| 27 | } |
| 28 | |
Anthony Liguori | c227f09 | 2009-10-01 16:12:16 -0500 | [diff] [blame] | 29 | static uint32_t bitband_readb(void *opaque, target_phys_addr_t offset) |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 30 | { |
| 31 | uint8_t v; |
pbrook | 8da3ff1 | 2008-12-01 18:59:50 +0000 | [diff] [blame] | 32 | cpu_physical_memory_read(bitband_addr(opaque, offset), &v, 1); |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 33 | return (v & (1 << ((offset >> 2) & 7))) != 0; |
| 34 | } |
| 35 | |
Anthony Liguori | c227f09 | 2009-10-01 16:12:16 -0500 | [diff] [blame] | 36 | static void bitband_writeb(void *opaque, target_phys_addr_t offset, |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 37 | uint32_t value) |
| 38 | { |
| 39 | uint32_t addr; |
| 40 | uint8_t mask; |
| 41 | uint8_t v; |
pbrook | 8da3ff1 | 2008-12-01 18:59:50 +0000 | [diff] [blame] | 42 | addr = bitband_addr(opaque, offset); |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 43 | mask = (1 << ((offset >> 2) & 7)); |
| 44 | cpu_physical_memory_read(addr, &v, 1); |
| 45 | if (value & 1) |
| 46 | v |= mask; |
| 47 | else |
| 48 | v &= ~mask; |
| 49 | cpu_physical_memory_write(addr, &v, 1); |
| 50 | } |
| 51 | |
Anthony Liguori | c227f09 | 2009-10-01 16:12:16 -0500 | [diff] [blame] | 52 | static uint32_t bitband_readw(void *opaque, target_phys_addr_t offset) |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 53 | { |
| 54 | uint32_t addr; |
| 55 | uint16_t mask; |
| 56 | uint16_t v; |
pbrook | 8da3ff1 | 2008-12-01 18:59:50 +0000 | [diff] [blame] | 57 | addr = bitband_addr(opaque, offset) & ~1; |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 58 | mask = (1 << ((offset >> 2) & 15)); |
| 59 | mask = tswap16(mask); |
| 60 | cpu_physical_memory_read(addr, (uint8_t *)&v, 2); |
| 61 | return (v & mask) != 0; |
| 62 | } |
| 63 | |
Anthony Liguori | c227f09 | 2009-10-01 16:12:16 -0500 | [diff] [blame] | 64 | static void bitband_writew(void *opaque, target_phys_addr_t offset, |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 65 | uint32_t value) |
| 66 | { |
| 67 | uint32_t addr; |
| 68 | uint16_t mask; |
| 69 | uint16_t v; |
pbrook | 8da3ff1 | 2008-12-01 18:59:50 +0000 | [diff] [blame] | 70 | addr = bitband_addr(opaque, offset) & ~1; |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 71 | mask = (1 << ((offset >> 2) & 15)); |
| 72 | mask = tswap16(mask); |
| 73 | cpu_physical_memory_read(addr, (uint8_t *)&v, 2); |
| 74 | if (value & 1) |
| 75 | v |= mask; |
| 76 | else |
| 77 | v &= ~mask; |
| 78 | cpu_physical_memory_write(addr, (uint8_t *)&v, 2); |
| 79 | } |
| 80 | |
Anthony Liguori | c227f09 | 2009-10-01 16:12:16 -0500 | [diff] [blame] | 81 | static uint32_t bitband_readl(void *opaque, target_phys_addr_t offset) |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 82 | { |
| 83 | uint32_t addr; |
| 84 | uint32_t mask; |
| 85 | uint32_t v; |
pbrook | 8da3ff1 | 2008-12-01 18:59:50 +0000 | [diff] [blame] | 86 | addr = bitband_addr(opaque, offset) & ~3; |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 87 | mask = (1 << ((offset >> 2) & 31)); |
| 88 | mask = tswap32(mask); |
| 89 | cpu_physical_memory_read(addr, (uint8_t *)&v, 4); |
| 90 | return (v & mask) != 0; |
| 91 | } |
| 92 | |
Anthony Liguori | c227f09 | 2009-10-01 16:12:16 -0500 | [diff] [blame] | 93 | static void bitband_writel(void *opaque, target_phys_addr_t offset, |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 94 | uint32_t value) |
| 95 | { |
| 96 | uint32_t addr; |
| 97 | uint32_t mask; |
| 98 | uint32_t v; |
pbrook | 8da3ff1 | 2008-12-01 18:59:50 +0000 | [diff] [blame] | 99 | addr = bitband_addr(opaque, offset) & ~3; |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 100 | mask = (1 << ((offset >> 2) & 31)); |
| 101 | mask = tswap32(mask); |
| 102 | cpu_physical_memory_read(addr, (uint8_t *)&v, 4); |
| 103 | if (value & 1) |
| 104 | v |= mask; |
| 105 | else |
| 106 | v &= ~mask; |
| 107 | cpu_physical_memory_write(addr, (uint8_t *)&v, 4); |
| 108 | } |
| 109 | |
Blue Swirl | d60efc6 | 2009-08-25 18:29:31 +0000 | [diff] [blame] | 110 | static CPUReadMemoryFunc * const bitband_readfn[] = { |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 111 | bitband_readb, |
| 112 | bitband_readw, |
| 113 | bitband_readl |
| 114 | }; |
| 115 | |
Blue Swirl | d60efc6 | 2009-08-25 18:29:31 +0000 | [diff] [blame] | 116 | static CPUWriteMemoryFunc * const bitband_writefn[] = { |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 117 | bitband_writeb, |
| 118 | bitband_writew, |
| 119 | bitband_writel |
| 120 | }; |
| 121 | |
Paul Brook | 40905a6 | 2009-06-03 15:16:49 +0100 | [diff] [blame] | 122 | typedef struct { |
| 123 | SysBusDevice busdev; |
| 124 | uint32_t base; |
| 125 | } BitBandState; |
| 126 | |
Gerd Hoffmann | 81a322d | 2009-08-14 10:36:05 +0200 | [diff] [blame] | 127 | static int bitband_init(SysBusDevice *dev) |
Paul Brook | 40905a6 | 2009-06-03 15:16:49 +0100 | [diff] [blame] | 128 | { |
| 129 | BitBandState *s = FROM_SYSBUS(BitBandState, dev); |
| 130 | int iomemtype; |
| 131 | |
Avi Kivity | 1eed09c | 2009-06-14 11:38:51 +0300 | [diff] [blame] | 132 | iomemtype = cpu_register_io_memory(bitband_readfn, bitband_writefn, |
Alexander Graf | 2507c12 | 2010-12-08 12:05:37 +0100 | [diff] [blame] | 133 | &s->base, DEVICE_NATIVE_ENDIAN); |
Paul Brook | 40905a6 | 2009-06-03 15:16:49 +0100 | [diff] [blame] | 134 | sysbus_init_mmio(dev, 0x02000000, iomemtype); |
Gerd Hoffmann | 81a322d | 2009-08-14 10:36:05 +0200 | [diff] [blame] | 135 | return 0; |
Paul Brook | 40905a6 | 2009-06-03 15:16:49 +0100 | [diff] [blame] | 136 | } |
| 137 | |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 138 | static void armv7m_bitband_init(void) |
| 139 | { |
Paul Brook | 40905a6 | 2009-06-03 15:16:49 +0100 | [diff] [blame] | 140 | DeviceState *dev; |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 141 | |
Paul Brook | 40905a6 | 2009-06-03 15:16:49 +0100 | [diff] [blame] | 142 | dev = qdev_create(NULL, "ARM,bitband-memory"); |
Gerd Hoffmann | ee6847d | 2009-07-15 13:43:31 +0200 | [diff] [blame] | 143 | qdev_prop_set_uint32(dev, "base", 0x20000000); |
Markus Armbruster | e23a1b3 | 2009-10-07 01:15:58 +0200 | [diff] [blame] | 144 | qdev_init_nofail(dev); |
Paul Brook | 40905a6 | 2009-06-03 15:16:49 +0100 | [diff] [blame] | 145 | sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0x22000000); |
| 146 | |
| 147 | dev = qdev_create(NULL, "ARM,bitband-memory"); |
Gerd Hoffmann | ee6847d | 2009-07-15 13:43:31 +0200 | [diff] [blame] | 148 | qdev_prop_set_uint32(dev, "base", 0x40000000); |
Markus Armbruster | e23a1b3 | 2009-10-07 01:15:58 +0200 | [diff] [blame] | 149 | qdev_init_nofail(dev); |
Paul Brook | 40905a6 | 2009-06-03 15:16:49 +0100 | [diff] [blame] | 150 | sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0x42000000); |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 151 | } |
| 152 | |
| 153 | /* Board init. */ |
Paul Brook | 983fe82 | 2010-04-05 19:34:51 +0100 | [diff] [blame] | 154 | |
| 155 | static void armv7m_reset(void *opaque) |
| 156 | { |
| 157 | cpu_reset((CPUState *)opaque); |
| 158 | } |
| 159 | |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 160 | /* Init CPU and memory for a v7-M based board. |
| 161 | flash_size and sram_size are in kb. |
| 162 | Returns the NVIC array. */ |
| 163 | |
| 164 | qemu_irq *armv7m_init(int flash_size, int sram_size, |
| 165 | const char *kernel_filename, const char *cpu_model) |
| 166 | { |
| 167 | CPUState *env; |
Paul Brook | fe7e875 | 2009-05-14 22:35:08 +0100 | [diff] [blame] | 168 | DeviceState *nvic; |
| 169 | /* FIXME: make this local state. */ |
| 170 | static qemu_irq pic[64]; |
| 171 | qemu_irq *cpu_pic; |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 172 | int image_size; |
| 173 | uint64_t entry; |
| 174 | uint64_t lowaddr; |
Paul Brook | fe7e875 | 2009-05-14 22:35:08 +0100 | [diff] [blame] | 175 | int i; |
Blue Swirl | ca20cf3 | 2009-09-20 14:58:02 +0000 | [diff] [blame] | 176 | int big_endian; |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 177 | |
| 178 | flash_size *= 1024; |
| 179 | sram_size *= 1024; |
| 180 | |
| 181 | if (!cpu_model) |
| 182 | cpu_model = "cortex-m3"; |
| 183 | env = cpu_init(cpu_model); |
| 184 | if (!env) { |
| 185 | fprintf(stderr, "Unable to find CPU definition\n"); |
| 186 | exit(1); |
| 187 | } |
| 188 | |
| 189 | #if 0 |
| 190 | /* > 32Mb SRAM gets complicated because it overlaps the bitband area. |
| 191 | We don't have proper commandline options, so allocate half of memory |
| 192 | as SRAM, up to a maximum of 32Mb, and the rest as code. */ |
| 193 | if (ram_size > (512 + 32) * 1024 * 1024) |
| 194 | ram_size = (512 + 32) * 1024 * 1024; |
| 195 | sram_size = (ram_size / 2) & TARGET_PAGE_MASK; |
| 196 | if (sram_size > 32 * 1024 * 1024) |
| 197 | sram_size = 32 * 1024 * 1024; |
| 198 | code_size = ram_size - sram_size; |
| 199 | #endif |
| 200 | |
| 201 | /* Flash programming is done via the SCU, so pretend it is ROM. */ |
pbrook | dcac967 | 2009-04-09 20:05:49 +0000 | [diff] [blame] | 202 | cpu_register_physical_memory(0, flash_size, |
Alex Williamson | 1724f04 | 2010-06-25 11:09:35 -0600 | [diff] [blame] | 203 | qemu_ram_alloc(NULL, "armv7m.flash", |
| 204 | flash_size) | IO_MEM_ROM); |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 205 | cpu_register_physical_memory(0x20000000, sram_size, |
Alex Williamson | 1724f04 | 2010-06-25 11:09:35 -0600 | [diff] [blame] | 206 | qemu_ram_alloc(NULL, "armv7m.sram", |
| 207 | sram_size) | IO_MEM_RAM); |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 208 | armv7m_bitband_init(); |
| 209 | |
Paul Brook | fe7e875 | 2009-05-14 22:35:08 +0100 | [diff] [blame] | 210 | nvic = qdev_create(NULL, "armv7m_nvic"); |
Paul Brook | 983fe82 | 2010-04-05 19:34:51 +0100 | [diff] [blame] | 211 | env->nvic = nvic; |
Markus Armbruster | e23a1b3 | 2009-10-07 01:15:58 +0200 | [diff] [blame] | 212 | qdev_init_nofail(nvic); |
Paul Brook | fe7e875 | 2009-05-14 22:35:08 +0100 | [diff] [blame] | 213 | cpu_pic = arm_pic_init_cpu(env); |
| 214 | sysbus_connect_irq(sysbus_from_qdev(nvic), 0, cpu_pic[ARM_PIC_CPU_IRQ]); |
| 215 | for (i = 0; i < 64; i++) { |
Paul Brook | 067a3dd | 2009-05-26 14:56:11 +0100 | [diff] [blame] | 216 | pic[i] = qdev_get_gpio_in(nvic, i); |
Paul Brook | fe7e875 | 2009-05-14 22:35:08 +0100 | [diff] [blame] | 217 | } |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 218 | |
Blue Swirl | ca20cf3 | 2009-09-20 14:58:02 +0000 | [diff] [blame] | 219 | #ifdef TARGET_WORDS_BIGENDIAN |
| 220 | big_endian = 1; |
| 221 | #else |
| 222 | big_endian = 0; |
| 223 | #endif |
| 224 | |
Aurelien Jarno | 409dbce | 2010-03-14 21:20:59 +0100 | [diff] [blame] | 225 | image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr, |
| 226 | NULL, big_endian, ELF_MACHINE, 1); |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 227 | if (image_size < 0) { |
pbrook | dcac967 | 2009-04-09 20:05:49 +0000 | [diff] [blame] | 228 | image_size = load_image_targphys(kernel_filename, 0, flash_size); |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 229 | lowaddr = 0; |
| 230 | } |
| 231 | if (image_size < 0) { |
| 232 | fprintf(stderr, "qemu: could not load kernel '%s'\n", |
| 233 | kernel_filename); |
| 234 | exit(1); |
| 235 | } |
| 236 | |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 237 | /* Hack to map an additional page of ram at the top of the address |
| 238 | space. This stops qemu complaining about executing code outside RAM |
| 239 | when returning from an exception. */ |
pbrook | dcac967 | 2009-04-09 20:05:49 +0000 | [diff] [blame] | 240 | cpu_register_physical_memory(0xfffff000, 0x1000, |
Alex Williamson | 1724f04 | 2010-06-25 11:09:35 -0600 | [diff] [blame] | 241 | qemu_ram_alloc(NULL, "armv7m.hack", |
| 242 | 0x1000) | IO_MEM_RAM); |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 243 | |
Paul Brook | 983fe82 | 2010-04-05 19:34:51 +0100 | [diff] [blame] | 244 | qemu_register_reset(armv7m_reset, env); |
pbrook | 9ee6e8b | 2007-11-11 00:04:49 +0000 | [diff] [blame] | 245 | return pic; |
| 246 | } |
Paul Brook | 40905a6 | 2009-06-03 15:16:49 +0100 | [diff] [blame] | 247 | |
Gerd Hoffmann | ee6847d | 2009-07-15 13:43:31 +0200 | [diff] [blame] | 248 | static SysBusDeviceInfo bitband_info = { |
| 249 | .init = bitband_init, |
| 250 | .qdev.name = "ARM,bitband-memory", |
| 251 | .qdev.size = sizeof(BitBandState), |
| 252 | .qdev.props = (Property[]) { |
Gerd Hoffmann | 1832efa | 2009-08-03 17:35:21 +0200 | [diff] [blame] | 253 | DEFINE_PROP_UINT32("base", BitBandState, base, 0), |
| 254 | DEFINE_PROP_END_OF_LIST(), |
Gerd Hoffmann | ee6847d | 2009-07-15 13:43:31 +0200 | [diff] [blame] | 255 | } |
| 256 | }; |
| 257 | |
Paul Brook | 40905a6 | 2009-06-03 15:16:49 +0100 | [diff] [blame] | 258 | static void armv7m_register_devices(void) |
| 259 | { |
Gerd Hoffmann | ee6847d | 2009-07-15 13:43:31 +0200 | [diff] [blame] | 260 | sysbus_register_withprop(&bitband_info); |
Paul Brook | 40905a6 | 2009-06-03 15:16:49 +0100 | [diff] [blame] | 261 | } |
| 262 | |
| 263 | device_init(armv7m_register_devices) |