]>
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" |
4771d756 PB |
13 | #include "qemu-common.h" |
14 | #include "cpu.h" | |
83c9f4ca | 15 | #include "hw/sysbus.h" |
bd2be150 | 16 | #include "hw/arm/arm.h" |
83c9f4ca | 17 | #include "hw/loader.h" |
ca20cf32 | 18 | #include "elf.h" |
5633b90a AF |
19 | #include "sysemu/qtest.h" |
20 | #include "qemu/error-report.h" | |
618119c2 | 21 | #include "exec/address-spaces.h" |
9ee6e8bb PB |
22 | |
23 | /* Bitbanded IO. Each word corresponds to a single bit. */ | |
24 | ||
2167f7bc | 25 | /* Get the byte address of the real memory for a bitband access. */ |
f68d881c | 26 | static inline hwaddr bitband_addr(BitBandState *s, hwaddr offset) |
9ee6e8bb | 27 | { |
f68d881c | 28 | return s->base | (offset & 0x1ffffff) >> 5; |
9ee6e8bb PB |
29 | } |
30 | ||
f68d881c PM |
31 | static MemTxResult bitband_read(void *opaque, hwaddr offset, |
32 | uint64_t *data, unsigned size, MemTxAttrs attrs) | |
9ee6e8bb | 33 | { |
f68d881c PM |
34 | BitBandState *s = opaque; |
35 | uint8_t buf[4]; | |
36 | MemTxResult res; | |
37 | int bitpos, bit; | |
38 | hwaddr addr; | |
39 | ||
40 | assert(size <= 4); | |
41 | ||
42 | /* Find address in underlying memory and round down to multiple of size */ | |
43 | addr = bitband_addr(s, offset) & (-size); | |
44 | res = address_space_read(s->source_as, addr, attrs, buf, size); | |
45 | if (res) { | |
46 | return res; | |
47 | } | |
48 | /* Bit position in the N bytes read... */ | |
49 | bitpos = (offset >> 2) & ((size * 8) - 1); | |
50 | /* ...converted to byte in buffer and bit in byte */ | |
51 | bit = (buf[bitpos >> 3] >> (bitpos & 7)) & 1; | |
52 | *data = bit; | |
53 | return MEMTX_OK; | |
9ee6e8bb PB |
54 | } |
55 | ||
f68d881c PM |
56 | static MemTxResult bitband_write(void *opaque, hwaddr offset, uint64_t value, |
57 | unsigned size, MemTxAttrs attrs) | |
9ee6e8bb | 58 | { |
f68d881c PM |
59 | BitBandState *s = opaque; |
60 | uint8_t buf[4]; | |
61 | MemTxResult res; | |
62 | int bitpos, bit; | |
63 | hwaddr addr; | |
64 | ||
65 | assert(size <= 4); | |
66 | ||
67 | /* Find address in underlying memory and round down to multiple of size */ | |
68 | addr = bitband_addr(s, offset) & (-size); | |
69 | res = address_space_read(s->source_as, addr, attrs, buf, size); | |
70 | if (res) { | |
71 | return res; | |
72 | } | |
73 | /* Bit position in the N bytes read... */ | |
74 | bitpos = (offset >> 2) & ((size * 8) - 1); | |
75 | /* ...converted to byte in buffer and bit in byte */ | |
76 | bit = 1 << (bitpos & 7); | |
77 | if (value & 1) { | |
78 | buf[bitpos >> 3] |= bit; | |
79 | } else { | |
80 | buf[bitpos >> 3] &= ~bit; | |
81 | } | |
82 | return address_space_write(s->source_as, addr, attrs, buf, size); | |
9ee6e8bb PB |
83 | } |
84 | ||
f69bf9d4 | 85 | static const MemoryRegionOps bitband_ops = { |
f68d881c PM |
86 | .read_with_attrs = bitband_read, |
87 | .write_with_attrs = bitband_write, | |
f69bf9d4 | 88 | .endianness = DEVICE_NATIVE_ENDIAN, |
f68d881c PM |
89 | .impl.min_access_size = 1, |
90 | .impl.max_access_size = 4, | |
91 | .valid.min_access_size = 1, | |
92 | .valid.max_access_size = 4, | |
9ee6e8bb PB |
93 | }; |
94 | ||
3f5ab254 | 95 | static void bitband_init(Object *obj) |
9ee6e8bb | 96 | { |
3f5ab254 XZ |
97 | BitBandState *s = BITBAND(obj); |
98 | SysBusDevice *dev = SYS_BUS_DEVICE(obj); | |
9ee6e8bb | 99 | |
f68d881c PM |
100 | object_property_add_link(obj, "source-memory", |
101 | TYPE_MEMORY_REGION, | |
102 | (Object **)&s->source_memory, | |
103 | qdev_prop_allow_set_link_before_realize, | |
104 | OBJ_PROP_LINK_UNREF_ON_RELEASE, | |
105 | &error_abort); | |
106 | memory_region_init_io(&s->iomem, obj, &bitband_ops, s, | |
64bde0f3 | 107 | "bitband", 0x02000000); |
750ecd44 | 108 | sysbus_init_mmio(dev, &s->iomem); |
40905a6a PB |
109 | } |
110 | ||
f68d881c PM |
111 | static void bitband_realize(DeviceState *dev, Error **errp) |
112 | { | |
113 | BitBandState *s = BITBAND(dev); | |
114 | ||
115 | if (!s->source_memory) { | |
116 | error_setg(errp, "source-memory property not set"); | |
117 | return; | |
118 | } | |
119 | ||
120 | s->source_as = address_space_init_shareable(s->source_memory, | |
121 | "bitband-source"); | |
122 | } | |
123 | ||
9ee6e8bb | 124 | /* Board init. */ |
983fe826 | 125 | |
56b7c66f PM |
126 | static const hwaddr bitband_input_addr[ARMV7M_NUM_BITBANDS] = { |
127 | 0x20000000, 0x40000000 | |
128 | }; | |
129 | ||
130 | static const hwaddr bitband_output_addr[ARMV7M_NUM_BITBANDS] = { | |
131 | 0x22000000, 0x42000000 | |
132 | }; | |
133 | ||
134 | static void armv7m_instance_init(Object *obj) | |
135 | { | |
136 | ARMv7MState *s = ARMV7M(obj); | |
137 | int i; | |
138 | ||
139 | /* Can't init the cpu here, we don't yet know which model to use */ | |
140 | ||
618119c2 PM |
141 | object_property_add_link(obj, "memory", |
142 | TYPE_MEMORY_REGION, | |
143 | (Object **)&s->board_memory, | |
144 | qdev_prop_allow_set_link_before_realize, | |
145 | OBJ_PROP_LINK_UNREF_ON_RELEASE, | |
146 | &error_abort); | |
147 | memory_region_init(&s->container, obj, "armv7m-container", UINT64_MAX); | |
148 | ||
56b7c66f PM |
149 | object_initialize(&s->nvic, sizeof(s->nvic), "armv7m_nvic"); |
150 | qdev_set_parent_bus(DEVICE(&s->nvic), sysbus_get_default()); | |
151 | object_property_add_alias(obj, "num-irq", | |
152 | OBJECT(&s->nvic), "num-irq", &error_abort); | |
153 | ||
154 | for (i = 0; i < ARRAY_SIZE(s->bitband); i++) { | |
155 | object_initialize(&s->bitband[i], sizeof(s->bitband[i]), TYPE_BITBAND); | |
156 | qdev_set_parent_bus(DEVICE(&s->bitband[i]), sysbus_get_default()); | |
157 | } | |
158 | } | |
159 | ||
160 | static void armv7m_realize(DeviceState *dev, Error **errp) | |
161 | { | |
162 | ARMv7MState *s = ARMV7M(dev); | |
98957a94 | 163 | SysBusDevice *sbd; |
56b7c66f PM |
164 | Error *err = NULL; |
165 | int i; | |
166 | char **cpustr; | |
167 | ObjectClass *oc; | |
168 | const char *typename; | |
169 | CPUClass *cc; | |
170 | ||
618119c2 PM |
171 | if (!s->board_memory) { |
172 | error_setg(errp, "memory property was not set"); | |
173 | return; | |
174 | } | |
175 | ||
176 | memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1); | |
177 | ||
56b7c66f PM |
178 | cpustr = g_strsplit(s->cpu_model, ",", 2); |
179 | ||
180 | oc = cpu_class_by_name(TYPE_ARM_CPU, cpustr[0]); | |
181 | if (!oc) { | |
182 | error_setg(errp, "Unknown CPU model %s", cpustr[0]); | |
183 | g_strfreev(cpustr); | |
184 | return; | |
185 | } | |
186 | ||
187 | cc = CPU_CLASS(oc); | |
188 | typename = object_class_get_name(oc); | |
189 | cc->parse_features(typename, cpustr[1], &err); | |
190 | g_strfreev(cpustr); | |
191 | if (err) { | |
192 | error_propagate(errp, err); | |
193 | return; | |
194 | } | |
195 | ||
196 | s->cpu = ARM_CPU(object_new(typename)); | |
197 | if (!s->cpu) { | |
198 | error_setg(errp, "Unknown CPU model %s", s->cpu_model); | |
199 | return; | |
200 | } | |
201 | ||
618119c2 PM |
202 | object_property_set_link(OBJECT(s->cpu), OBJECT(&s->container), "memory", |
203 | &error_abort); | |
56b7c66f PM |
204 | object_property_set_bool(OBJECT(s->cpu), true, "realized", &err); |
205 | if (err != NULL) { | |
206 | error_propagate(errp, err); | |
207 | return; | |
208 | } | |
209 | ||
210 | /* Note that we must realize the NVIC after the CPU */ | |
211 | object_property_set_bool(OBJECT(&s->nvic), true, "realized", &err); | |
212 | if (err != NULL) { | |
213 | error_propagate(errp, err); | |
214 | return; | |
215 | } | |
216 | ||
217 | /* Alias the NVIC's input and output GPIOs as our own so the board | |
218 | * code can wire them up. (We do this in realize because the | |
219 | * NVIC doesn't create the input GPIO array until realize.) | |
220 | */ | |
221 | qdev_pass_gpios(DEVICE(&s->nvic), dev, NULL); | |
222 | qdev_pass_gpios(DEVICE(&s->nvic), dev, "SYSRESETREQ"); | |
223 | ||
224 | /* Wire the NVIC up to the CPU */ | |
98957a94 PM |
225 | sbd = SYS_BUS_DEVICE(&s->nvic); |
226 | sysbus_connect_irq(sbd, 0, | |
56b7c66f PM |
227 | qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ)); |
228 | s->cpu->env.nvic = &s->nvic; | |
229 | ||
98957a94 PM |
230 | memory_region_add_subregion(&s->container, 0xe000e000, |
231 | sysbus_mmio_get_region(sbd, 0)); | |
232 | ||
56b7c66f PM |
233 | for (i = 0; i < ARRAY_SIZE(s->bitband); i++) { |
234 | Object *obj = OBJECT(&s->bitband[i]); | |
235 | SysBusDevice *sbd = SYS_BUS_DEVICE(&s->bitband[i]); | |
236 | ||
237 | object_property_set_int(obj, bitband_input_addr[i], "base", &err); | |
238 | if (err != NULL) { | |
239 | error_propagate(errp, err); | |
240 | return; | |
241 | } | |
f68d881c PM |
242 | object_property_set_link(obj, OBJECT(s->board_memory), |
243 | "source-memory", &error_abort); | |
56b7c66f PM |
244 | object_property_set_bool(obj, true, "realized", &err); |
245 | if (err != NULL) { | |
246 | error_propagate(errp, err); | |
247 | return; | |
248 | } | |
249 | ||
618119c2 PM |
250 | memory_region_add_subregion(&s->container, bitband_output_addr[i], |
251 | sysbus_mmio_get_region(sbd, 0)); | |
56b7c66f PM |
252 | } |
253 | } | |
254 | ||
255 | static Property armv7m_properties[] = { | |
256 | DEFINE_PROP_STRING("cpu-model", ARMv7MState, cpu_model), | |
257 | DEFINE_PROP_END_OF_LIST(), | |
258 | }; | |
259 | ||
260 | static void armv7m_class_init(ObjectClass *klass, void *data) | |
261 | { | |
262 | DeviceClass *dc = DEVICE_CLASS(klass); | |
263 | ||
264 | dc->realize = armv7m_realize; | |
265 | dc->props = armv7m_properties; | |
266 | } | |
267 | ||
268 | static const TypeInfo armv7m_info = { | |
269 | .name = TYPE_ARMV7M, | |
270 | .parent = TYPE_SYS_BUS_DEVICE, | |
271 | .instance_size = sizeof(ARMv7MState), | |
272 | .instance_init = armv7m_instance_init, | |
273 | .class_init = armv7m_class_init, | |
274 | }; | |
275 | ||
983fe826 PB |
276 | static void armv7m_reset(void *opaque) |
277 | { | |
31363f12 AF |
278 | ARMCPU *cpu = opaque; |
279 | ||
280 | cpu_reset(CPU(cpu)); | |
983fe826 PB |
281 | } |
282 | ||
9ee6e8bb | 283 | /* Init CPU and memory for a v7-M based board. |
fe6ac447 | 284 | mem_size is in bytes. |
21e0c38f | 285 | Returns the ARMv7M device. */ |
9ee6e8bb | 286 | |
20c59c38 | 287 | DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, |
9ee6e8bb PB |
288 | const char *kernel_filename, const char *cpu_model) |
289 | { | |
21e0c38f | 290 | DeviceState *armv7m; |
9ee6e8bb | 291 | |
0f37c99b | 292 | if (cpu_model == NULL) { |
21e0c38f | 293 | cpu_model = "cortex-m3"; |
0f37c99b | 294 | } |
21e0c38f PM |
295 | |
296 | armv7m = qdev_create(NULL, "armv7m"); | |
297 | qdev_prop_set_uint32(armv7m, "num-irq", num_irq); | |
298 | qdev_prop_set_string(armv7m, "cpu-model", cpu_model); | |
618119c2 PM |
299 | object_property_set_link(OBJECT(armv7m), OBJECT(get_system_memory()), |
300 | "memory", &error_abort); | |
21e0c38f PM |
301 | /* This will exit with an error if the user passed us a bad cpu_model */ |
302 | qdev_init_nofail(armv7m); | |
303 | ||
304 | armv7m_load_kernel(ARM_CPU(first_cpu), kernel_filename, mem_size); | |
305 | return armv7m; | |
3651c285 PM |
306 | } |
307 | ||
308 | void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size) | |
309 | { | |
310 | int image_size; | |
311 | uint64_t entry; | |
312 | uint64_t lowaddr; | |
313 | int big_endian; | |
9ee6e8bb | 314 | |
ca20cf32 BS |
315 | #ifdef TARGET_WORDS_BIGENDIAN |
316 | big_endian = 1; | |
317 | #else | |
318 | big_endian = 0; | |
319 | #endif | |
320 | ||
5633b90a | 321 | if (!kernel_filename && !qtest_enabled()) { |
01fd41ab PC |
322 | fprintf(stderr, "Guest image must be specified (using -kernel)\n"); |
323 | exit(1); | |
324 | } | |
325 | ||
5633b90a AF |
326 | if (kernel_filename) { |
327 | image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr, | |
7ef295ea | 328 | NULL, big_endian, EM_ARM, 1, 0); |
5633b90a | 329 | if (image_size < 0) { |
fe6ac447 | 330 | image_size = load_image_targphys(kernel_filename, 0, mem_size); |
5633b90a AF |
331 | lowaddr = 0; |
332 | } | |
333 | if (image_size < 0) { | |
334 | error_report("Could not load kernel '%s'", kernel_filename); | |
335 | exit(1); | |
336 | } | |
9ee6e8bb PB |
337 | } |
338 | ||
3651c285 PM |
339 | /* CPU objects (unlike devices) are not automatically reset on system |
340 | * reset, so we must always register a handler to do so. Unlike | |
341 | * A-profile CPUs, we don't need to do anything special in the | |
342 | * handler to arrange that it starts correctly. | |
343 | * This is arguably the wrong place to do this, but it matches the | |
344 | * way A-profile does it. Note that this means that every M profile | |
345 | * board must call this function! | |
346 | */ | |
31363f12 | 347 | qemu_register_reset(armv7m_reset, cpu); |
9ee6e8bb | 348 | } |
40905a6a | 349 | |
999e12bb AL |
350 | static Property bitband_properties[] = { |
351 | DEFINE_PROP_UINT32("base", BitBandState, base, 0), | |
352 | DEFINE_PROP_END_OF_LIST(), | |
353 | }; | |
354 | ||
355 | static void bitband_class_init(ObjectClass *klass, void *data) | |
356 | { | |
39bffca2 | 357 | DeviceClass *dc = DEVICE_CLASS(klass); |
999e12bb | 358 | |
f68d881c | 359 | dc->realize = bitband_realize; |
39bffca2 | 360 | dc->props = bitband_properties; |
999e12bb AL |
361 | } |
362 | ||
8c43a6f0 | 363 | static const TypeInfo bitband_info = { |
936230a7 | 364 | .name = TYPE_BITBAND, |
39bffca2 AL |
365 | .parent = TYPE_SYS_BUS_DEVICE, |
366 | .instance_size = sizeof(BitBandState), | |
3f5ab254 | 367 | .instance_init = bitband_init, |
39bffca2 | 368 | .class_init = bitband_class_init, |
ee6847d1 GH |
369 | }; |
370 | ||
83f7d43a | 371 | static void armv7m_register_types(void) |
40905a6a | 372 | { |
39bffca2 | 373 | type_register_static(&bitband_info); |
56b7c66f | 374 | type_register_static(&armv7m_info); |
40905a6a PB |
375 | } |
376 | ||
83f7d43a | 377 | type_init(armv7m_register_types) |