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