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