]>
Commit | Line | Data |
---|---|---|
9ee6e8bb PB |
1 | /* |
2 | * ARMV7M System emulation. | |
3 | * | |
4 | * Copyright (c) 2006-2007 CodeSourcery. | |
5 | * Written by Paul Brook | |
6 | * | |
2167f7bc | 7 | * This code is licensed under the GPL. |
9ee6e8bb PB |
8 | */ |
9 | ||
12b16722 | 10 | #include "qemu/osdep.h" |
56b7c66f | 11 | #include "hw/arm/armv7m.h" |
da34e65c | 12 | #include "qapi/error.h" |
83c9f4ca | 13 | #include "hw/sysbus.h" |
12ec8bd5 | 14 | #include "hw/arm/boot.h" |
83c9f4ca | 15 | #include "hw/loader.h" |
a27bd6c7 | 16 | #include "hw/qdev-properties.h" |
ca20cf32 | 17 | #include "elf.h" |
71e8a915 | 18 | #include "sysemu/reset.h" |
5633b90a | 19 | #include "qemu/error-report.h" |
0b8fa32f | 20 | #include "qemu/module.h" |
618119c2 | 21 | #include "exec/address-spaces.h" |
c60c1b0d | 22 | #include "target/arm/idau.h" |
9ee6e8bb PB |
23 | |
24 | /* Bitbanded IO. Each word corresponds to a single bit. */ | |
25 | ||
2167f7bc | 26 | /* Get the byte address of the real memory for a bitband access. */ |
f68d881c | 27 | static inline hwaddr bitband_addr(BitBandState *s, hwaddr offset) |
9ee6e8bb | 28 | { |
f68d881c | 29 | return s->base | (offset & 0x1ffffff) >> 5; |
9ee6e8bb PB |
30 | } |
31 | ||
f68d881c PM |
32 | static MemTxResult bitband_read(void *opaque, hwaddr offset, |
33 | uint64_t *data, unsigned size, MemTxAttrs attrs) | |
9ee6e8bb | 34 | { |
f68d881c PM |
35 | BitBandState *s = opaque; |
36 | uint8_t buf[4]; | |
37 | MemTxResult res; | |
38 | int bitpos, bit; | |
39 | hwaddr addr; | |
40 | ||
41 | assert(size <= 4); | |
42 | ||
43 | /* Find address in underlying memory and round down to multiple of size */ | |
44 | addr = bitband_addr(s, offset) & (-size); | |
b516572f | 45 | res = address_space_read(&s->source_as, addr, attrs, buf, size); |
f68d881c PM |
46 | if (res) { |
47 | return res; | |
48 | } | |
49 | /* Bit position in the N bytes read... */ | |
50 | bitpos = (offset >> 2) & ((size * 8) - 1); | |
51 | /* ...converted to byte in buffer and bit in byte */ | |
52 | bit = (buf[bitpos >> 3] >> (bitpos & 7)) & 1; | |
53 | *data = bit; | |
54 | return MEMTX_OK; | |
9ee6e8bb PB |
55 | } |
56 | ||
f68d881c PM |
57 | static MemTxResult bitband_write(void *opaque, hwaddr offset, uint64_t value, |
58 | unsigned size, MemTxAttrs attrs) | |
9ee6e8bb | 59 | { |
f68d881c PM |
60 | BitBandState *s = opaque; |
61 | uint8_t buf[4]; | |
62 | MemTxResult res; | |
63 | int bitpos, bit; | |
64 | hwaddr addr; | |
65 | ||
66 | assert(size <= 4); | |
67 | ||
68 | /* Find address in underlying memory and round down to multiple of size */ | |
69 | addr = bitband_addr(s, offset) & (-size); | |
b516572f | 70 | res = address_space_read(&s->source_as, addr, attrs, buf, size); |
f68d881c PM |
71 | if (res) { |
72 | return res; | |
73 | } | |
74 | /* Bit position in the N bytes read... */ | |
75 | bitpos = (offset >> 2) & ((size * 8) - 1); | |
76 | /* ...converted to byte in buffer and bit in byte */ | |
77 | bit = 1 << (bitpos & 7); | |
78 | if (value & 1) { | |
79 | buf[bitpos >> 3] |= bit; | |
80 | } else { | |
81 | buf[bitpos >> 3] &= ~bit; | |
82 | } | |
b516572f | 83 | return address_space_write(&s->source_as, addr, attrs, buf, size); |
9ee6e8bb PB |
84 | } |
85 | ||
f69bf9d4 | 86 | static const MemoryRegionOps bitband_ops = { |
f68d881c PM |
87 | .read_with_attrs = bitband_read, |
88 | .write_with_attrs = bitband_write, | |
f69bf9d4 | 89 | .endianness = DEVICE_NATIVE_ENDIAN, |
f68d881c PM |
90 | .impl.min_access_size = 1, |
91 | .impl.max_access_size = 4, | |
92 | .valid.min_access_size = 1, | |
93 | .valid.max_access_size = 4, | |
9ee6e8bb PB |
94 | }; |
95 | ||
3f5ab254 | 96 | static void bitband_init(Object *obj) |
9ee6e8bb | 97 | { |
3f5ab254 XZ |
98 | BitBandState *s = BITBAND(obj); |
99 | SysBusDevice *dev = SYS_BUS_DEVICE(obj); | |
9ee6e8bb | 100 | |
f68d881c | 101 | memory_region_init_io(&s->iomem, obj, &bitband_ops, s, |
64bde0f3 | 102 | "bitband", 0x02000000); |
750ecd44 | 103 | sysbus_init_mmio(dev, &s->iomem); |
40905a6a PB |
104 | } |
105 | ||
f68d881c PM |
106 | static void bitband_realize(DeviceState *dev, Error **errp) |
107 | { | |
108 | BitBandState *s = BITBAND(dev); | |
109 | ||
110 | if (!s->source_memory) { | |
111 | error_setg(errp, "source-memory property not set"); | |
112 | return; | |
113 | } | |
114 | ||
b516572f | 115 | address_space_init(&s->source_as, s->source_memory, "bitband-source"); |
f68d881c PM |
116 | } |
117 | ||
9ee6e8bb | 118 | /* Board init. */ |
983fe826 | 119 | |
56b7c66f PM |
120 | static const hwaddr bitband_input_addr[ARMV7M_NUM_BITBANDS] = { |
121 | 0x20000000, 0x40000000 | |
122 | }; | |
123 | ||
124 | static const hwaddr bitband_output_addr[ARMV7M_NUM_BITBANDS] = { | |
125 | 0x22000000, 0x42000000 | |
126 | }; | |
127 | ||
128 | static void armv7m_instance_init(Object *obj) | |
129 | { | |
130 | ARMv7MState *s = ARMV7M(obj); | |
131 | int i; | |
132 | ||
133 | /* Can't init the cpu here, we don't yet know which model to use */ | |
134 | ||
618119c2 PM |
135 | memory_region_init(&s->container, obj, "armv7m-container", UINT64_MAX); |
136 | ||
71f916be | 137 | object_initialize_child(obj, "nvic", &s->nvic, TYPE_NVIC); |
56b7c66f | 138 | object_property_add_alias(obj, "num-irq", |
d2623129 | 139 | OBJECT(&s->nvic), "num-irq"); |
56b7c66f PM |
140 | |
141 | for (i = 0; i < ARRAY_SIZE(s->bitband); i++) { | |
5a147c8c MA |
142 | object_initialize_child(obj, "bitband[*]", &s->bitband[i], |
143 | TYPE_BITBAND); | |
56b7c66f PM |
144 | } |
145 | } | |
146 | ||
147 | static void armv7m_realize(DeviceState *dev, Error **errp) | |
148 | { | |
149 | ARMv7MState *s = ARMV7M(dev); | |
98957a94 | 150 | SysBusDevice *sbd; |
56b7c66f PM |
151 | Error *err = NULL; |
152 | int i; | |
56b7c66f | 153 | |
618119c2 PM |
154 | if (!s->board_memory) { |
155 | error_setg(errp, "memory property was not set"); | |
156 | return; | |
157 | } | |
158 | ||
159 | memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1); | |
160 | ||
e4c81e3a PM |
161 | s->cpu = ARM_CPU(object_new_with_props(s->cpu_type, OBJECT(s), "cpu", |
162 | &err, NULL)); | |
163 | if (err != NULL) { | |
164 | error_propagate(errp, err); | |
165 | return; | |
166 | } | |
56b7c66f | 167 | |
5325cc34 | 168 | object_property_set_link(OBJECT(s->cpu), "memory", OBJECT(&s->container), |
618119c2 | 169 | &error_abort); |
efba1595 | 170 | if (object_property_find(OBJECT(s->cpu), "idau")) { |
5325cc34 | 171 | object_property_set_link(OBJECT(s->cpu), "idau", s->idau, |
c24d9716 | 172 | &error_abort); |
c60c1b0d | 173 | } |
efba1595 | 174 | if (object_property_find(OBJECT(s->cpu), "init-svtor")) { |
778a2dc5 | 175 | if (!object_property_set_uint(OBJECT(s->cpu), "init-svtor", |
668f62ec | 176 | s->init_svtor, errp)) { |
60d75d81 PM |
177 | return; |
178 | } | |
179 | } | |
efba1595 | 180 | if (object_property_find(OBJECT(s->cpu), "start-powered-off")) { |
778a2dc5 | 181 | if (!object_property_set_bool(OBJECT(s->cpu), "start-powered-off", |
668f62ec | 182 | s->start_powered_off, errp)) { |
66647809 PM |
183 | return; |
184 | } | |
185 | } | |
efba1595 | 186 | if (object_property_find(OBJECT(s->cpu), "vfp")) { |
668f62ec | 187 | if (!object_property_set_bool(OBJECT(s->cpu), "vfp", s->vfp, errp)) { |
e0cf7b81 PM |
188 | return; |
189 | } | |
190 | } | |
efba1595 | 191 | if (object_property_find(OBJECT(s->cpu), "dsp")) { |
668f62ec | 192 | if (!object_property_set_bool(OBJECT(s->cpu), "dsp", s->dsp, errp)) { |
e0cf7b81 PM |
193 | return; |
194 | } | |
195 | } | |
95f87565 | 196 | |
3693f217 PM |
197 | /* |
198 | * Tell the CPU where the NVIC is; it will fail realize if it doesn't | |
199 | * have one. Similarly, tell the NVIC where its CPU is. | |
95f87565 PM |
200 | */ |
201 | s->cpu->env.nvic = &s->nvic; | |
3693f217 | 202 | s->nvic.cpu = s->cpu; |
95f87565 | 203 | |
668f62ec | 204 | if (!qdev_realize(DEVICE(s->cpu), NULL, errp)) { |
56b7c66f PM |
205 | return; |
206 | } | |
207 | ||
208 | /* Note that we must realize the NVIC after the CPU */ | |
668f62ec | 209 | if (!sysbus_realize(SYS_BUS_DEVICE(&s->nvic), errp)) { |
56b7c66f PM |
210 | return; |
211 | } | |
212 | ||
213 | /* Alias the NVIC's input and output GPIOs as our own so the board | |
214 | * code can wire them up. (We do this in realize because the | |
215 | * NVIC doesn't create the input GPIO array until realize.) | |
216 | */ | |
217 | qdev_pass_gpios(DEVICE(&s->nvic), dev, NULL); | |
218 | qdev_pass_gpios(DEVICE(&s->nvic), dev, "SYSRESETREQ"); | |
514b4f36 | 219 | qdev_pass_gpios(DEVICE(&s->nvic), dev, "NMI"); |
56b7c66f PM |
220 | |
221 | /* Wire the NVIC up to the CPU */ | |
98957a94 PM |
222 | sbd = SYS_BUS_DEVICE(&s->nvic); |
223 | sysbus_connect_irq(sbd, 0, | |
56b7c66f | 224 | qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ)); |
56b7c66f | 225 | |
a724377a | 226 | memory_region_add_subregion(&s->container, 0xe0000000, |
98957a94 PM |
227 | sysbus_mmio_get_region(sbd, 0)); |
228 | ||
210d1867 MA |
229 | for (i = 0; i < ARRAY_SIZE(s->bitband); i++) { |
230 | if (s->enable_bitband) { | |
a1c5a062 SH |
231 | Object *obj = OBJECT(&s->bitband[i]); |
232 | SysBusDevice *sbd = SYS_BUS_DEVICE(&s->bitband[i]); | |
233 | ||
778a2dc5 | 234 | if (!object_property_set_int(obj, "base", |
668f62ec | 235 | bitband_input_addr[i], errp)) { |
a1c5a062 SH |
236 | return; |
237 | } | |
5325cc34 MA |
238 | object_property_set_link(obj, "source-memory", |
239 | OBJECT(s->board_memory), &error_abort); | |
668f62ec | 240 | if (!sysbus_realize(SYS_BUS_DEVICE(obj), errp)) { |
a1c5a062 SH |
241 | return; |
242 | } | |
243 | ||
244 | memory_region_add_subregion(&s->container, bitband_output_addr[i], | |
245 | sysbus_mmio_get_region(sbd, 0)); | |
210d1867 MA |
246 | } else { |
247 | object_unparent(OBJECT(&s->bitband[i])); | |
56b7c66f | 248 | } |
56b7c66f PM |
249 | } |
250 | } | |
251 | ||
252 | static Property armv7m_properties[] = { | |
ba1ba5cc | 253 | DEFINE_PROP_STRING("cpu-type", ARMv7MState, cpu_type), |
e2ff1215 FZ |
254 | DEFINE_PROP_LINK("memory", ARMv7MState, board_memory, TYPE_MEMORY_REGION, |
255 | MemoryRegion *), | |
c60c1b0d | 256 | DEFINE_PROP_LINK("idau", ARMv7MState, idau, TYPE_IDAU_INTERFACE, Object *), |
60d75d81 | 257 | DEFINE_PROP_UINT32("init-svtor", ARMv7MState, init_svtor, 0), |
a1c5a062 | 258 | DEFINE_PROP_BOOL("enable-bitband", ARMv7MState, enable_bitband, false), |
66647809 PM |
259 | DEFINE_PROP_BOOL("start-powered-off", ARMv7MState, start_powered_off, |
260 | false), | |
e0cf7b81 PM |
261 | DEFINE_PROP_BOOL("vfp", ARMv7MState, vfp, true), |
262 | DEFINE_PROP_BOOL("dsp", ARMv7MState, dsp, true), | |
56b7c66f PM |
263 | DEFINE_PROP_END_OF_LIST(), |
264 | }; | |
265 | ||
266 | static void armv7m_class_init(ObjectClass *klass, void *data) | |
267 | { | |
268 | DeviceClass *dc = DEVICE_CLASS(klass); | |
269 | ||
270 | dc->realize = armv7m_realize; | |
4f67d30b | 271 | device_class_set_props(dc, armv7m_properties); |
56b7c66f PM |
272 | } |
273 | ||
274 | static const TypeInfo armv7m_info = { | |
275 | .name = TYPE_ARMV7M, | |
276 | .parent = TYPE_SYS_BUS_DEVICE, | |
277 | .instance_size = sizeof(ARMv7MState), | |
278 | .instance_init = armv7m_instance_init, | |
279 | .class_init = armv7m_class_init, | |
280 | }; | |
281 | ||
983fe826 PB |
282 | static void armv7m_reset(void *opaque) |
283 | { | |
31363f12 AF |
284 | ARMCPU *cpu = opaque; |
285 | ||
286 | cpu_reset(CPU(cpu)); | |
983fe826 PB |
287 | } |
288 | ||
3651c285 PM |
289 | void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size) |
290 | { | |
291 | int image_size; | |
292 | uint64_t entry; | |
3651c285 | 293 | int big_endian; |
891f3bc3 PM |
294 | AddressSpace *as; |
295 | int asidx; | |
296 | CPUState *cs = CPU(cpu); | |
9ee6e8bb | 297 | |
ca20cf32 BS |
298 | #ifdef TARGET_WORDS_BIGENDIAN |
299 | big_endian = 1; | |
300 | #else | |
301 | big_endian = 0; | |
302 | #endif | |
303 | ||
891f3bc3 PM |
304 | if (arm_feature(&cpu->env, ARM_FEATURE_EL3)) { |
305 | asidx = ARMASIdx_S; | |
306 | } else { | |
307 | asidx = ARMASIdx_NS; | |
308 | } | |
309 | as = cpu_get_address_space(cs, asidx); | |
310 | ||
5633b90a | 311 | if (kernel_filename) { |
4366e1db | 312 | image_size = load_elf_as(kernel_filename, NULL, NULL, NULL, |
617160c9 | 313 | &entry, NULL, NULL, |
891f3bc3 | 314 | NULL, big_endian, EM_ARM, 1, 0, as); |
5633b90a | 315 | if (image_size < 0) { |
891f3bc3 PM |
316 | image_size = load_image_targphys_as(kernel_filename, 0, |
317 | mem_size, as); | |
5633b90a AF |
318 | } |
319 | if (image_size < 0) { | |
320 | error_report("Could not load kernel '%s'", kernel_filename); | |
321 | exit(1); | |
322 | } | |
9ee6e8bb PB |
323 | } |
324 | ||
3651c285 PM |
325 | /* CPU objects (unlike devices) are not automatically reset on system |
326 | * reset, so we must always register a handler to do so. Unlike | |
327 | * A-profile CPUs, we don't need to do anything special in the | |
328 | * handler to arrange that it starts correctly. | |
329 | * This is arguably the wrong place to do this, but it matches the | |
330 | * way A-profile does it. Note that this means that every M profile | |
331 | * board must call this function! | |
332 | */ | |
31363f12 | 333 | qemu_register_reset(armv7m_reset, cpu); |
9ee6e8bb | 334 | } |
40905a6a | 335 | |
999e12bb AL |
336 | static Property bitband_properties[] = { |
337 | DEFINE_PROP_UINT32("base", BitBandState, base, 0), | |
5f486f97 FZ |
338 | DEFINE_PROP_LINK("source-memory", BitBandState, source_memory, |
339 | TYPE_MEMORY_REGION, MemoryRegion *), | |
999e12bb AL |
340 | DEFINE_PROP_END_OF_LIST(), |
341 | }; | |
342 | ||
343 | static void bitband_class_init(ObjectClass *klass, void *data) | |
344 | { | |
39bffca2 | 345 | DeviceClass *dc = DEVICE_CLASS(klass); |
999e12bb | 346 | |
f68d881c | 347 | dc->realize = bitband_realize; |
4f67d30b | 348 | device_class_set_props(dc, bitband_properties); |
999e12bb AL |
349 | } |
350 | ||
8c43a6f0 | 351 | static const TypeInfo bitband_info = { |
936230a7 | 352 | .name = TYPE_BITBAND, |
39bffca2 AL |
353 | .parent = TYPE_SYS_BUS_DEVICE, |
354 | .instance_size = sizeof(BitBandState), | |
3f5ab254 | 355 | .instance_init = bitband_init, |
39bffca2 | 356 | .class_init = bitband_class_init, |
ee6847d1 GH |
357 | }; |
358 | ||
83f7d43a | 359 | static void armv7m_register_types(void) |
40905a6a | 360 | { |
39bffca2 | 361 | type_register_static(&bitband_info); |
56b7c66f | 362 | type_register_static(&armv7m_info); |
40905a6a PB |
363 | } |
364 | ||
83f7d43a | 365 | type_init(armv7m_register_types) |