The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [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 | |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 10 | #include "sysbus.h" |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 11 | #include "arm-misc.h" |
| 12 | #include "sysemu.h" |
| 13 | |
| 14 | /* Bitbanded IO. Each word corresponds to a single bit. */ |
| 15 | |
| 16 | /* Get the byte address of the real memory for a bitband acess. */ |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 17 | static inline uint32_t bitband_addr(void * opaque, uint32_t addr) |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 18 | { |
| 19 | uint32_t res; |
| 20 | |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 21 | res = *(uint32_t *)opaque; |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 22 | res |= (addr & 0x1ffffff) >> 5; |
| 23 | return res; |
| 24 | |
| 25 | } |
| 26 | |
| 27 | static uint32_t bitband_readb(void *opaque, target_phys_addr_t offset) |
| 28 | { |
| 29 | uint8_t v; |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 30 | cpu_physical_memory_read(bitband_addr(opaque, offset), &v, 1); |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 31 | return (v & (1 << ((offset >> 2) & 7))) != 0; |
| 32 | } |
| 33 | |
| 34 | static void bitband_writeb(void *opaque, target_phys_addr_t offset, |
| 35 | uint32_t value) |
| 36 | { |
| 37 | uint32_t addr; |
| 38 | uint8_t mask; |
| 39 | uint8_t v; |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 40 | addr = bitband_addr(opaque, offset); |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 41 | mask = (1 << ((offset >> 2) & 7)); |
| 42 | cpu_physical_memory_read(addr, &v, 1); |
| 43 | if (value & 1) |
| 44 | v |= mask; |
| 45 | else |
| 46 | v &= ~mask; |
| 47 | cpu_physical_memory_write(addr, &v, 1); |
| 48 | } |
| 49 | |
| 50 | static uint32_t bitband_readw(void *opaque, target_phys_addr_t offset) |
| 51 | { |
| 52 | uint32_t addr; |
| 53 | uint16_t mask; |
| 54 | uint16_t v; |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 55 | addr = bitband_addr(opaque, offset) & ~1; |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 56 | mask = (1 << ((offset >> 2) & 15)); |
| 57 | mask = tswap16(mask); |
| 58 | cpu_physical_memory_read(addr, (uint8_t *)&v, 2); |
| 59 | return (v & mask) != 0; |
| 60 | } |
| 61 | |
| 62 | static void bitband_writew(void *opaque, target_phys_addr_t offset, |
| 63 | uint32_t value) |
| 64 | { |
| 65 | uint32_t addr; |
| 66 | uint16_t mask; |
| 67 | uint16_t v; |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 68 | addr = bitband_addr(opaque, offset) & ~1; |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 69 | mask = (1 << ((offset >> 2) & 15)); |
| 70 | mask = tswap16(mask); |
| 71 | cpu_physical_memory_read(addr, (uint8_t *)&v, 2); |
| 72 | if (value & 1) |
| 73 | v |= mask; |
| 74 | else |
| 75 | v &= ~mask; |
| 76 | cpu_physical_memory_write(addr, (uint8_t *)&v, 2); |
| 77 | } |
| 78 | |
| 79 | static uint32_t bitband_readl(void *opaque, target_phys_addr_t offset) |
| 80 | { |
| 81 | uint32_t addr; |
| 82 | uint32_t mask; |
| 83 | uint32_t v; |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 84 | addr = bitband_addr(opaque, offset) & ~3; |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 85 | mask = (1 << ((offset >> 2) & 31)); |
| 86 | mask = tswap32(mask); |
| 87 | cpu_physical_memory_read(addr, (uint8_t *)&v, 4); |
| 88 | return (v & mask) != 0; |
| 89 | } |
| 90 | |
| 91 | static void bitband_writel(void *opaque, target_phys_addr_t offset, |
| 92 | uint32_t value) |
| 93 | { |
| 94 | uint32_t addr; |
| 95 | uint32_t mask; |
| 96 | uint32_t v; |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 97 | addr = bitband_addr(opaque, offset) & ~3; |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 98 | mask = (1 << ((offset >> 2) & 31)); |
| 99 | mask = tswap32(mask); |
| 100 | cpu_physical_memory_read(addr, (uint8_t *)&v, 4); |
| 101 | if (value & 1) |
| 102 | v |= mask; |
| 103 | else |
| 104 | v &= ~mask; |
| 105 | cpu_physical_memory_write(addr, (uint8_t *)&v, 4); |
| 106 | } |
| 107 | |
| 108 | static CPUReadMemoryFunc *bitband_readfn[] = { |
| 109 | bitband_readb, |
| 110 | bitband_readw, |
| 111 | bitband_readl |
| 112 | }; |
| 113 | |
| 114 | static CPUWriteMemoryFunc *bitband_writefn[] = { |
| 115 | bitband_writeb, |
| 116 | bitband_writew, |
| 117 | bitband_writel |
| 118 | }; |
| 119 | |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 120 | typedef struct { |
| 121 | SysBusDevice busdev; |
| 122 | uint32_t base; |
| 123 | } BitBandState; |
| 124 | |
| 125 | static void bitband_init(SysBusDevice *dev) |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 126 | { |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 127 | BitBandState *s = FROM_SYSBUS(BitBandState, dev); |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 128 | int iomemtype; |
| 129 | |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 130 | s->base = qdev_get_prop_int(&dev->qdev, "base", 0); |
| 131 | iomemtype = cpu_register_io_memory(bitband_readfn, bitband_writefn, |
| 132 | &s->base); |
| 133 | sysbus_init_mmio(dev, 0x02000000, iomemtype); |
| 134 | } |
| 135 | |
| 136 | static void armv7m_bitband_init(void) |
| 137 | { |
| 138 | DeviceState *dev; |
| 139 | |
| 140 | dev = qdev_create(NULL, "ARM,bitband-memory"); |
| 141 | qdev_set_prop_int(dev, "base", 0x20000000); |
| 142 | qdev_init(dev); |
| 143 | sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0x22000000); |
| 144 | |
| 145 | dev = qdev_create(NULL, "ARM,bitband-memory"); |
| 146 | qdev_set_prop_int(dev, "base", 0x40000000); |
| 147 | qdev_init(dev); |
| 148 | sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0x42000000); |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 149 | } |
| 150 | |
| 151 | /* Board init. */ |
| 152 | /* Init CPU and memory for a v7-M based board. |
| 153 | flash_size and sram_size are in kb. |
| 154 | Returns the NVIC array. */ |
| 155 | |
| 156 | qemu_irq *armv7m_init(int flash_size, int sram_size, |
| 157 | const char *kernel_filename, const char *cpu_model) |
| 158 | { |
| 159 | CPUState *env; |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 160 | DeviceState *nvic; |
| 161 | /* FIXME: make this local state. */ |
| 162 | static qemu_irq pic[64]; |
| 163 | qemu_irq *cpu_pic; |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 164 | uint32_t pc; |
| 165 | int image_size; |
| 166 | uint64_t entry; |
| 167 | uint64_t lowaddr; |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 168 | int i; |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 169 | |
| 170 | flash_size *= 1024; |
| 171 | sram_size *= 1024; |
| 172 | |
| 173 | if (!cpu_model) |
| 174 | cpu_model = "cortex-m3"; |
| 175 | env = cpu_init(cpu_model); |
| 176 | if (!env) { |
| 177 | fprintf(stderr, "Unable to find CPU definition\n"); |
| 178 | exit(1); |
| 179 | } |
| 180 | |
| 181 | #if 0 |
| 182 | /* > 32Mb SRAM gets complicated because it overlaps the bitband area. |
| 183 | We don't have proper commandline options, so allocate half of memory |
| 184 | as SRAM, up to a maximum of 32Mb, and the rest as code. */ |
| 185 | if (ram_size > (512 + 32) * 1024 * 1024) |
| 186 | ram_size = (512 + 32) * 1024 * 1024; |
| 187 | sram_size = (ram_size / 2) & TARGET_PAGE_MASK; |
| 188 | if (sram_size > 32 * 1024 * 1024) |
| 189 | sram_size = 32 * 1024 * 1024; |
| 190 | code_size = ram_size - sram_size; |
| 191 | #endif |
| 192 | |
| 193 | /* Flash programming is done via the SCU, so pretend it is ROM. */ |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 194 | cpu_register_physical_memory(0, flash_size, |
| 195 | qemu_ram_alloc(flash_size) | IO_MEM_ROM); |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 196 | cpu_register_physical_memory(0x20000000, sram_size, |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 197 | qemu_ram_alloc(sram_size) | IO_MEM_RAM); |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 198 | armv7m_bitband_init(); |
| 199 | |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 200 | nvic = qdev_create(NULL, "armv7m_nvic"); |
| 201 | env->v7m.nvic = nvic; |
| 202 | qdev_init(nvic); |
| 203 | cpu_pic = arm_pic_init_cpu(env); |
| 204 | sysbus_connect_irq(sysbus_from_qdev(nvic), 0, cpu_pic[ARM_PIC_CPU_IRQ]); |
| 205 | for (i = 0; i < 64; i++) { |
| 206 | pic[i] = qdev_get_gpio_in(nvic, i); |
| 207 | } |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 208 | |
| 209 | image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL); |
| 210 | if (image_size < 0) { |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 211 | image_size = load_image_targphys(kernel_filename, 0, flash_size); |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 212 | lowaddr = 0; |
| 213 | } |
| 214 | if (image_size < 0) { |
| 215 | fprintf(stderr, "qemu: could not load kernel '%s'\n", |
| 216 | kernel_filename); |
| 217 | exit(1); |
| 218 | } |
| 219 | |
| 220 | /* If the image was loaded at address zero then assume it is a |
| 221 | regular ROM image and perform the normal CPU reset sequence. |
| 222 | Otherwise jump directly to the entry point. */ |
| 223 | if (lowaddr == 0) { |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 224 | env->regs[13] = ldl_phys(0); |
| 225 | pc = ldl_phys(4); |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 226 | } else { |
| 227 | pc = entry; |
| 228 | } |
| 229 | env->thumb = pc & 1; |
| 230 | env->regs[15] = pc & ~1; |
| 231 | |
| 232 | /* Hack to map an additional page of ram at the top of the address |
| 233 | space. This stops qemu complaining about executing code outside RAM |
| 234 | when returning from an exception. */ |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 235 | cpu_register_physical_memory(0xfffff000, 0x1000, |
| 236 | qemu_ram_alloc(0x1000) | IO_MEM_RAM); |
The Android Open Source Project | 8b23a6c | 2009-03-03 19:30:32 -0800 | [diff] [blame] | 237 | |
| 238 | return pic; |
| 239 | } |
| 240 | |
David 'Digit' Turner | 5d8f37a | 2009-09-14 14:32:27 -0700 | [diff] [blame] | 241 | static void armv7m_register_devices(void) |
| 242 | { |
| 243 | sysbus_register_dev("ARM,bitband-memory", sizeof(BitBandState), |
| 244 | bitband_init); |
| 245 | } |
| 246 | |
| 247 | device_init(armv7m_register_devices) |