304cd34bc2a007f74d82130531e722d300531667
[sdk/emulator/qemu.git] / hw / armv7m.c
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
10 #include "sysbus.h"
11 #include "arm-misc.h"
12 #include "sysemu.h"
13 #include "loader.h"
14 #include "elf.h"
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.  */
19 static inline uint32_t bitband_addr(void * opaque, uint32_t addr)
20 {
21     uint32_t res;
22
23     res = *(uint32_t *)opaque;
24     res |= (addr & 0x1ffffff) >> 5;
25     return res;
26
27 }
28
29 static uint32_t bitband_readb(void *opaque, target_phys_addr_t offset)
30 {
31     uint8_t v;
32     cpu_physical_memory_read(bitband_addr(opaque, offset), &v, 1);
33     return (v & (1 << ((offset >> 2) & 7))) != 0;
34 }
35
36 static void bitband_writeb(void *opaque, target_phys_addr_t offset,
37                            uint32_t value)
38 {
39     uint32_t addr;
40     uint8_t mask;
41     uint8_t v;
42     addr = bitband_addr(opaque, offset);
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
52 static uint32_t bitband_readw(void *opaque, target_phys_addr_t offset)
53 {
54     uint32_t addr;
55     uint16_t mask;
56     uint16_t v;
57     addr = bitband_addr(opaque, offset) & ~1;
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
64 static void bitband_writew(void *opaque, target_phys_addr_t offset,
65                            uint32_t value)
66 {
67     uint32_t addr;
68     uint16_t mask;
69     uint16_t v;
70     addr = bitband_addr(opaque, offset) & ~1;
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
81 static uint32_t bitband_readl(void *opaque, target_phys_addr_t offset)
82 {
83     uint32_t addr;
84     uint32_t mask;
85     uint32_t v;
86     addr = bitband_addr(opaque, offset) & ~3;
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
93 static void bitband_writel(void *opaque, target_phys_addr_t offset,
94                            uint32_t value)
95 {
96     uint32_t addr;
97     uint32_t mask;
98     uint32_t v;
99     addr = bitband_addr(opaque, offset) & ~3;
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
110 static CPUReadMemoryFunc * const bitband_readfn[] = {
111    bitband_readb,
112    bitband_readw,
113    bitband_readl
114 };
115
116 static CPUWriteMemoryFunc * const bitband_writefn[] = {
117    bitband_writeb,
118    bitband_writew,
119    bitband_writel
120 };
121
122 typedef struct {
123     SysBusDevice busdev;
124     uint32_t base;
125 } BitBandState;
126
127 static int bitband_init(SysBusDevice *dev)
128 {
129     BitBandState *s = FROM_SYSBUS(BitBandState, dev);
130     int iomemtype;
131
132     iomemtype = cpu_register_io_memory(bitband_readfn, bitband_writefn,
133                                        &s->base, DEVICE_NATIVE_ENDIAN);
134     sysbus_init_mmio(dev, 0x02000000, iomemtype);
135     return 0;
136 }
137
138 static void armv7m_bitband_init(void)
139 {
140     DeviceState *dev;
141
142     dev = qdev_create(NULL, "ARM,bitband-memory");
143     qdev_prop_set_uint32(dev, "base", 0x20000000);
144     qdev_init_nofail(dev);
145     sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0x22000000);
146
147     dev = qdev_create(NULL, "ARM,bitband-memory");
148     qdev_prop_set_uint32(dev, "base", 0x40000000);
149     qdev_init_nofail(dev);
150     sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0x42000000);
151 }
152
153 /* Board init.  */
154
155 static void armv7m_reset(void *opaque)
156 {
157     cpu_reset((CPUState *)opaque);
158 }
159
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;
168     DeviceState *nvic;
169     /* FIXME: make this local state.  */
170     static qemu_irq pic[64];
171     qemu_irq *cpu_pic;
172     int image_size;
173     uint64_t entry;
174     uint64_t lowaddr;
175     int i;
176     int big_endian;
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.  */
202     cpu_register_physical_memory(0, flash_size,
203                                  qemu_ram_alloc(NULL, "armv7m.flash",
204                                                 flash_size) | IO_MEM_ROM);
205     cpu_register_physical_memory(0x20000000, sram_size,
206                                  qemu_ram_alloc(NULL, "armv7m.sram",
207                                                 sram_size) | IO_MEM_RAM);
208     armv7m_bitband_init();
209
210     nvic = qdev_create(NULL, "armv7m_nvic");
211     env->nvic = nvic;
212     qdev_init_nofail(nvic);
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++) {
216         pic[i] = qdev_get_gpio_in(nvic, i);
217     }
218
219 #ifdef TARGET_WORDS_BIGENDIAN
220     big_endian = 1;
221 #else
222     big_endian = 0;
223 #endif
224
225     image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr,
226                           NULL, big_endian, ELF_MACHINE, 1);
227     if (image_size < 0) {
228         image_size = load_image_targphys(kernel_filename, 0, flash_size);
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
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.  */
240     cpu_register_physical_memory(0xfffff000, 0x1000,
241                                  qemu_ram_alloc(NULL, "armv7m.hack", 
242                                                 0x1000) | IO_MEM_RAM);
243
244     qemu_register_reset(armv7m_reset, env);
245     return pic;
246 }
247
248 static SysBusDeviceInfo bitband_info = {
249     .init = bitband_init,
250     .qdev.name  = "ARM,bitband-memory",
251     .qdev.size  = sizeof(BitBandState),
252     .qdev.props = (Property[]) {
253         DEFINE_PROP_UINT32("base", BitBandState, base, 0),
254         DEFINE_PROP_END_OF_LIST(),
255     }
256 };
257
258 static void armv7m_register_devices(void)
259 {
260     sysbus_register_withprop(&bitband_info);
261 }
262
263 device_init(armv7m_register_devices)