armv7m: Make NVIC expose a memory region rather than mapping itself
[qemu/ar7.git] / hw / arm / armv7m.c
blobde9746665dc018f1e2bb106a172b83abf4e78026
1 /*
2 * ARMV7M System emulation.
4 * Copyright (c) 2006-2007 CodeSourcery.
5 * Written by Paul Brook
7 * This code is licensed under the GPL.
8 */
10 #include "qemu/osdep.h"
11 #include "hw/arm/armv7m.h"
12 #include "qapi/error.h"
13 #include "qemu-common.h"
14 #include "cpu.h"
15 #include "hw/sysbus.h"
16 #include "hw/arm/arm.h"
17 #include "hw/loader.h"
18 #include "elf.h"
19 #include "sysemu/qtest.h"
20 #include "qemu/error-report.h"
21 #include "exec/address-spaces.h"
23 /* Bitbanded IO. Each word corresponds to a single bit. */
25 /* Get the byte address of the real memory for a bitband access. */
26 static inline uint32_t bitband_addr(void * opaque, uint32_t addr)
28 uint32_t res;
30 res = *(uint32_t *)opaque;
31 res |= (addr & 0x1ffffff) >> 5;
32 return res;
36 static uint32_t bitband_readb(void *opaque, hwaddr offset)
38 uint8_t v;
39 cpu_physical_memory_read(bitband_addr(opaque, offset), &v, 1);
40 return (v & (1 << ((offset >> 2) & 7))) != 0;
43 static void bitband_writeb(void *opaque, hwaddr offset,
44 uint32_t value)
46 uint32_t addr;
47 uint8_t mask;
48 uint8_t v;
49 addr = bitband_addr(opaque, offset);
50 mask = (1 << ((offset >> 2) & 7));
51 cpu_physical_memory_read(addr, &v, 1);
52 if (value & 1)
53 v |= mask;
54 else
55 v &= ~mask;
56 cpu_physical_memory_write(addr, &v, 1);
59 static uint32_t bitband_readw(void *opaque, hwaddr offset)
61 uint32_t addr;
62 uint16_t mask;
63 uint16_t v;
64 addr = bitband_addr(opaque, offset) & ~1;
65 mask = (1 << ((offset >> 2) & 15));
66 mask = tswap16(mask);
67 cpu_physical_memory_read(addr, &v, 2);
68 return (v & mask) != 0;
71 static void bitband_writew(void *opaque, hwaddr offset,
72 uint32_t value)
74 uint32_t addr;
75 uint16_t mask;
76 uint16_t v;
77 addr = bitband_addr(opaque, offset) & ~1;
78 mask = (1 << ((offset >> 2) & 15));
79 mask = tswap16(mask);
80 cpu_physical_memory_read(addr, &v, 2);
81 if (value & 1)
82 v |= mask;
83 else
84 v &= ~mask;
85 cpu_physical_memory_write(addr, &v, 2);
88 static uint32_t bitband_readl(void *opaque, hwaddr offset)
90 uint32_t addr;
91 uint32_t mask;
92 uint32_t v;
93 addr = bitband_addr(opaque, offset) & ~3;
94 mask = (1 << ((offset >> 2) & 31));
95 mask = tswap32(mask);
96 cpu_physical_memory_read(addr, &v, 4);
97 return (v & mask) != 0;
100 static void bitband_writel(void *opaque, hwaddr offset,
101 uint32_t value)
103 uint32_t addr;
104 uint32_t mask;
105 uint32_t v;
106 addr = bitband_addr(opaque, offset) & ~3;
107 mask = (1 << ((offset >> 2) & 31));
108 mask = tswap32(mask);
109 cpu_physical_memory_read(addr, &v, 4);
110 if (value & 1)
111 v |= mask;
112 else
113 v &= ~mask;
114 cpu_physical_memory_write(addr, &v, 4);
117 static const MemoryRegionOps bitband_ops = {
118 .old_mmio = {
119 .read = { bitband_readb, bitband_readw, bitband_readl, },
120 .write = { bitband_writeb, bitband_writew, bitband_writel, },
122 .endianness = DEVICE_NATIVE_ENDIAN,
125 static void bitband_init(Object *obj)
127 BitBandState *s = BITBAND(obj);
128 SysBusDevice *dev = SYS_BUS_DEVICE(obj);
130 memory_region_init_io(&s->iomem, obj, &bitband_ops, &s->base,
131 "bitband", 0x02000000);
132 sysbus_init_mmio(dev, &s->iomem);
135 /* Board init. */
137 static const hwaddr bitband_input_addr[ARMV7M_NUM_BITBANDS] = {
138 0x20000000, 0x40000000
141 static const hwaddr bitband_output_addr[ARMV7M_NUM_BITBANDS] = {
142 0x22000000, 0x42000000
145 static void armv7m_instance_init(Object *obj)
147 ARMv7MState *s = ARMV7M(obj);
148 int i;
150 /* Can't init the cpu here, we don't yet know which model to use */
152 object_property_add_link(obj, "memory",
153 TYPE_MEMORY_REGION,
154 (Object **)&s->board_memory,
155 qdev_prop_allow_set_link_before_realize,
156 OBJ_PROP_LINK_UNREF_ON_RELEASE,
157 &error_abort);
158 memory_region_init(&s->container, obj, "armv7m-container", UINT64_MAX);
160 object_initialize(&s->nvic, sizeof(s->nvic), "armv7m_nvic");
161 qdev_set_parent_bus(DEVICE(&s->nvic), sysbus_get_default());
162 object_property_add_alias(obj, "num-irq",
163 OBJECT(&s->nvic), "num-irq", &error_abort);
165 for (i = 0; i < ARRAY_SIZE(s->bitband); i++) {
166 object_initialize(&s->bitband[i], sizeof(s->bitband[i]), TYPE_BITBAND);
167 qdev_set_parent_bus(DEVICE(&s->bitband[i]), sysbus_get_default());
171 static void armv7m_realize(DeviceState *dev, Error **errp)
173 ARMv7MState *s = ARMV7M(dev);
174 SysBusDevice *sbd;
175 Error *err = NULL;
176 int i;
177 char **cpustr;
178 ObjectClass *oc;
179 const char *typename;
180 CPUClass *cc;
182 if (!s->board_memory) {
183 error_setg(errp, "memory property was not set");
184 return;
187 memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1);
189 cpustr = g_strsplit(s->cpu_model, ",", 2);
191 oc = cpu_class_by_name(TYPE_ARM_CPU, cpustr[0]);
192 if (!oc) {
193 error_setg(errp, "Unknown CPU model %s", cpustr[0]);
194 g_strfreev(cpustr);
195 return;
198 cc = CPU_CLASS(oc);
199 typename = object_class_get_name(oc);
200 cc->parse_features(typename, cpustr[1], &err);
201 g_strfreev(cpustr);
202 if (err) {
203 error_propagate(errp, err);
204 return;
207 s->cpu = ARM_CPU(object_new(typename));
208 if (!s->cpu) {
209 error_setg(errp, "Unknown CPU model %s", s->cpu_model);
210 return;
213 object_property_set_link(OBJECT(s->cpu), OBJECT(&s->container), "memory",
214 &error_abort);
215 object_property_set_bool(OBJECT(s->cpu), true, "realized", &err);
216 if (err != NULL) {
217 error_propagate(errp, err);
218 return;
221 /* Note that we must realize the NVIC after the CPU */
222 object_property_set_bool(OBJECT(&s->nvic), true, "realized", &err);
223 if (err != NULL) {
224 error_propagate(errp, err);
225 return;
228 /* Alias the NVIC's input and output GPIOs as our own so the board
229 * code can wire them up. (We do this in realize because the
230 * NVIC doesn't create the input GPIO array until realize.)
232 qdev_pass_gpios(DEVICE(&s->nvic), dev, NULL);
233 qdev_pass_gpios(DEVICE(&s->nvic), dev, "SYSRESETREQ");
235 /* Wire the NVIC up to the CPU */
236 sbd = SYS_BUS_DEVICE(&s->nvic);
237 sysbus_connect_irq(sbd, 0,
238 qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ));
239 s->cpu->env.nvic = &s->nvic;
241 memory_region_add_subregion(&s->container, 0xe000e000,
242 sysbus_mmio_get_region(sbd, 0));
244 for (i = 0; i < ARRAY_SIZE(s->bitband); i++) {
245 Object *obj = OBJECT(&s->bitband[i]);
246 SysBusDevice *sbd = SYS_BUS_DEVICE(&s->bitband[i]);
248 object_property_set_int(obj, bitband_input_addr[i], "base", &err);
249 if (err != NULL) {
250 error_propagate(errp, err);
251 return;
253 object_property_set_bool(obj, true, "realized", &err);
254 if (err != NULL) {
255 error_propagate(errp, err);
256 return;
259 memory_region_add_subregion(&s->container, bitband_output_addr[i],
260 sysbus_mmio_get_region(sbd, 0));
264 static Property armv7m_properties[] = {
265 DEFINE_PROP_STRING("cpu-model", ARMv7MState, cpu_model),
266 DEFINE_PROP_END_OF_LIST(),
269 static void armv7m_class_init(ObjectClass *klass, void *data)
271 DeviceClass *dc = DEVICE_CLASS(klass);
273 dc->realize = armv7m_realize;
274 dc->props = armv7m_properties;
277 static const TypeInfo armv7m_info = {
278 .name = TYPE_ARMV7M,
279 .parent = TYPE_SYS_BUS_DEVICE,
280 .instance_size = sizeof(ARMv7MState),
281 .instance_init = armv7m_instance_init,
282 .class_init = armv7m_class_init,
285 static void armv7m_reset(void *opaque)
287 ARMCPU *cpu = opaque;
289 cpu_reset(CPU(cpu));
292 /* Init CPU and memory for a v7-M based board.
293 mem_size is in bytes.
294 Returns the ARMv7M device. */
296 DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
297 const char *kernel_filename, const char *cpu_model)
299 DeviceState *armv7m;
301 if (cpu_model == NULL) {
302 cpu_model = "cortex-m3";
305 armv7m = qdev_create(NULL, "armv7m");
306 qdev_prop_set_uint32(armv7m, "num-irq", num_irq);
307 qdev_prop_set_string(armv7m, "cpu-model", cpu_model);
308 object_property_set_link(OBJECT(armv7m), OBJECT(get_system_memory()),
309 "memory", &error_abort);
310 /* This will exit with an error if the user passed us a bad cpu_model */
311 qdev_init_nofail(armv7m);
313 armv7m_load_kernel(ARM_CPU(first_cpu), kernel_filename, mem_size);
314 return armv7m;
317 void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size)
319 int image_size;
320 uint64_t entry;
321 uint64_t lowaddr;
322 int big_endian;
324 #ifdef TARGET_WORDS_BIGENDIAN
325 big_endian = 1;
326 #else
327 big_endian = 0;
328 #endif
330 if (!kernel_filename && !qtest_enabled()) {
331 fprintf(stderr, "Guest image must be specified (using -kernel)\n");
332 exit(1);
335 if (kernel_filename) {
336 image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr,
337 NULL, big_endian, EM_ARM, 1, 0);
338 if (image_size < 0) {
339 image_size = load_image_targphys(kernel_filename, 0, mem_size);
340 lowaddr = 0;
342 if (image_size < 0) {
343 error_report("Could not load kernel '%s'", kernel_filename);
344 exit(1);
348 /* CPU objects (unlike devices) are not automatically reset on system
349 * reset, so we must always register a handler to do so. Unlike
350 * A-profile CPUs, we don't need to do anything special in the
351 * handler to arrange that it starts correctly.
352 * This is arguably the wrong place to do this, but it matches the
353 * way A-profile does it. Note that this means that every M profile
354 * board must call this function!
356 qemu_register_reset(armv7m_reset, cpu);
359 static Property bitband_properties[] = {
360 DEFINE_PROP_UINT32("base", BitBandState, base, 0),
361 DEFINE_PROP_END_OF_LIST(),
364 static void bitband_class_init(ObjectClass *klass, void *data)
366 DeviceClass *dc = DEVICE_CLASS(klass);
368 dc->props = bitband_properties;
371 static const TypeInfo bitband_info = {
372 .name = TYPE_BITBAND,
373 .parent = TYPE_SYS_BUS_DEVICE,
374 .instance_size = sizeof(BitBandState),
375 .instance_init = bitband_init,
376 .class_init = bitband_class_init,
379 static void armv7m_register_types(void)
381 type_register_static(&bitband_info);
382 type_register_static(&armv7m_info);
385 type_init(armv7m_register_types)