]>
Commit | Line | Data |
---|---|---|
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 "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" | |
22 | #include "target/arm/idau.h" | |
23 | ||
24 | /* Bitbanded IO. Each word corresponds to a single bit. */ | |
25 | ||
26 | /* Get the byte address of the real memory for a bitband access. */ | |
27 | static inline hwaddr bitband_addr(BitBandState *s, hwaddr offset) | |
28 | { | |
29 | return s->base | (offset & 0x1ffffff) >> 5; | |
30 | } | |
31 | ||
32 | static MemTxResult bitband_read(void *opaque, hwaddr offset, | |
33 | uint64_t *data, unsigned size, MemTxAttrs attrs) | |
34 | { | |
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); | |
45 | res = address_space_read(&s->source_as, addr, attrs, buf, size); | |
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; | |
55 | } | |
56 | ||
57 | static MemTxResult bitband_write(void *opaque, hwaddr offset, uint64_t value, | |
58 | unsigned size, MemTxAttrs attrs) | |
59 | { | |
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); | |
70 | res = address_space_read(&s->source_as, addr, attrs, buf, size); | |
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 | } | |
83 | return address_space_write(&s->source_as, addr, attrs, buf, size); | |
84 | } | |
85 | ||
86 | static const MemoryRegionOps bitband_ops = { | |
87 | .read_with_attrs = bitband_read, | |
88 | .write_with_attrs = bitband_write, | |
89 | .endianness = DEVICE_NATIVE_ENDIAN, | |
90 | .impl.min_access_size = 1, | |
91 | .impl.max_access_size = 4, | |
92 | .valid.min_access_size = 1, | |
93 | .valid.max_access_size = 4, | |
94 | }; | |
95 | ||
96 | static void bitband_init(Object *obj) | |
97 | { | |
98 | BitBandState *s = BITBAND(obj); | |
99 | SysBusDevice *dev = SYS_BUS_DEVICE(obj); | |
100 | ||
101 | memory_region_init_io(&s->iomem, obj, &bitband_ops, s, | |
102 | "bitband", 0x02000000); | |
103 | sysbus_init_mmio(dev, &s->iomem); | |
104 | } | |
105 | ||
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 | ||
115 | address_space_init(&s->source_as, s->source_memory, "bitband-source"); | |
116 | } | |
117 | ||
118 | /* Board init. */ | |
119 | ||
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 | ||
135 | memory_region_init(&s->container, obj, "armv7m-container", UINT64_MAX); | |
136 | ||
137 | object_initialize(&s->nvic, sizeof(s->nvic), TYPE_NVIC); | |
138 | qdev_set_parent_bus(DEVICE(&s->nvic), sysbus_get_default()); | |
139 | object_property_add_alias(obj, "num-irq", | |
140 | OBJECT(&s->nvic), "num-irq", &error_abort); | |
141 | ||
142 | for (i = 0; i < ARRAY_SIZE(s->bitband); i++) { | |
143 | object_initialize(&s->bitband[i], sizeof(s->bitband[i]), TYPE_BITBAND); | |
144 | qdev_set_parent_bus(DEVICE(&s->bitband[i]), sysbus_get_default()); | |
145 | } | |
146 | } | |
147 | ||
148 | static void armv7m_realize(DeviceState *dev, Error **errp) | |
149 | { | |
150 | ARMv7MState *s = ARMV7M(dev); | |
151 | SysBusDevice *sbd; | |
152 | Error *err = NULL; | |
153 | int i; | |
154 | ||
155 | if (!s->board_memory) { | |
156 | error_setg(errp, "memory property was not set"); | |
157 | return; | |
158 | } | |
159 | ||
160 | memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1); | |
161 | ||
162 | s->cpu = ARM_CPU(object_new(s->cpu_type)); | |
163 | ||
164 | object_property_set_link(OBJECT(s->cpu), OBJECT(&s->container), "memory", | |
165 | &error_abort); | |
166 | if (object_property_find(OBJECT(s->cpu), "idau", NULL)) { | |
167 | object_property_set_link(OBJECT(s->cpu), s->idau, "idau", &err); | |
168 | if (err != NULL) { | |
169 | error_propagate(errp, err); | |
170 | return; | |
171 | } | |
172 | } | |
173 | if (object_property_find(OBJECT(s->cpu), "init-svtor", NULL)) { | |
174 | object_property_set_uint(OBJECT(s->cpu), s->init_svtor, | |
175 | "init-svtor", &err); | |
176 | if (err != NULL) { | |
177 | error_propagate(errp, err); | |
178 | return; | |
179 | } | |
180 | } | |
181 | object_property_set_bool(OBJECT(s->cpu), true, "realized", &err); | |
182 | if (err != NULL) { | |
183 | error_propagate(errp, err); | |
184 | return; | |
185 | } | |
186 | ||
187 | /* Note that we must realize the NVIC after the CPU */ | |
188 | object_property_set_bool(OBJECT(&s->nvic), true, "realized", &err); | |
189 | if (err != NULL) { | |
190 | error_propagate(errp, err); | |
191 | return; | |
192 | } | |
193 | ||
194 | /* Alias the NVIC's input and output GPIOs as our own so the board | |
195 | * code can wire them up. (We do this in realize because the | |
196 | * NVIC doesn't create the input GPIO array until realize.) | |
197 | */ | |
198 | qdev_pass_gpios(DEVICE(&s->nvic), dev, NULL); | |
199 | qdev_pass_gpios(DEVICE(&s->nvic), dev, "SYSRESETREQ"); | |
200 | ||
201 | /* Wire the NVIC up to the CPU */ | |
202 | sbd = SYS_BUS_DEVICE(&s->nvic); | |
203 | sysbus_connect_irq(sbd, 0, | |
204 | qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ)); | |
205 | s->cpu->env.nvic = &s->nvic; | |
206 | ||
207 | memory_region_add_subregion(&s->container, 0xe000e000, | |
208 | sysbus_mmio_get_region(sbd, 0)); | |
209 | ||
210 | for (i = 0; i < ARRAY_SIZE(s->bitband); i++) { | |
211 | Object *obj = OBJECT(&s->bitband[i]); | |
212 | SysBusDevice *sbd = SYS_BUS_DEVICE(&s->bitband[i]); | |
213 | ||
214 | object_property_set_int(obj, bitband_input_addr[i], "base", &err); | |
215 | if (err != NULL) { | |
216 | error_propagate(errp, err); | |
217 | return; | |
218 | } | |
219 | object_property_set_link(obj, OBJECT(s->board_memory), | |
220 | "source-memory", &error_abort); | |
221 | object_property_set_bool(obj, true, "realized", &err); | |
222 | if (err != NULL) { | |
223 | error_propagate(errp, err); | |
224 | return; | |
225 | } | |
226 | ||
227 | memory_region_add_subregion(&s->container, bitband_output_addr[i], | |
228 | sysbus_mmio_get_region(sbd, 0)); | |
229 | } | |
230 | } | |
231 | ||
232 | static Property armv7m_properties[] = { | |
233 | DEFINE_PROP_STRING("cpu-type", ARMv7MState, cpu_type), | |
234 | DEFINE_PROP_LINK("memory", ARMv7MState, board_memory, TYPE_MEMORY_REGION, | |
235 | MemoryRegion *), | |
236 | DEFINE_PROP_LINK("idau", ARMv7MState, idau, TYPE_IDAU_INTERFACE, Object *), | |
237 | DEFINE_PROP_UINT32("init-svtor", ARMv7MState, init_svtor, 0), | |
238 | DEFINE_PROP_END_OF_LIST(), | |
239 | }; | |
240 | ||
241 | static void armv7m_class_init(ObjectClass *klass, void *data) | |
242 | { | |
243 | DeviceClass *dc = DEVICE_CLASS(klass); | |
244 | ||
245 | dc->realize = armv7m_realize; | |
246 | dc->props = armv7m_properties; | |
247 | } | |
248 | ||
249 | static const TypeInfo armv7m_info = { | |
250 | .name = TYPE_ARMV7M, | |
251 | .parent = TYPE_SYS_BUS_DEVICE, | |
252 | .instance_size = sizeof(ARMv7MState), | |
253 | .instance_init = armv7m_instance_init, | |
254 | .class_init = armv7m_class_init, | |
255 | }; | |
256 | ||
257 | static void armv7m_reset(void *opaque) | |
258 | { | |
259 | ARMCPU *cpu = opaque; | |
260 | ||
261 | cpu_reset(CPU(cpu)); | |
262 | } | |
263 | ||
264 | void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size) | |
265 | { | |
266 | int image_size; | |
267 | uint64_t entry; | |
268 | uint64_t lowaddr; | |
269 | int big_endian; | |
270 | AddressSpace *as; | |
271 | int asidx; | |
272 | CPUState *cs = CPU(cpu); | |
273 | ||
274 | #ifdef TARGET_WORDS_BIGENDIAN | |
275 | big_endian = 1; | |
276 | #else | |
277 | big_endian = 0; | |
278 | #endif | |
279 | ||
280 | if (!kernel_filename && !qtest_enabled()) { | |
281 | error_report("Guest image must be specified (using -kernel)"); | |
282 | exit(1); | |
283 | } | |
284 | ||
285 | if (arm_feature(&cpu->env, ARM_FEATURE_EL3)) { | |
286 | asidx = ARMASIdx_S; | |
287 | } else { | |
288 | asidx = ARMASIdx_NS; | |
289 | } | |
290 | as = cpu_get_address_space(cs, asidx); | |
291 | ||
292 | if (kernel_filename) { | |
293 | image_size = load_elf_as(kernel_filename, NULL, NULL, &entry, &lowaddr, | |
294 | NULL, big_endian, EM_ARM, 1, 0, as); | |
295 | if (image_size < 0) { | |
296 | image_size = load_image_targphys_as(kernel_filename, 0, | |
297 | mem_size, as); | |
298 | lowaddr = 0; | |
299 | } | |
300 | if (image_size < 0) { | |
301 | error_report("Could not load kernel '%s'", kernel_filename); | |
302 | exit(1); | |
303 | } | |
304 | } | |
305 | ||
306 | /* CPU objects (unlike devices) are not automatically reset on system | |
307 | * reset, so we must always register a handler to do so. Unlike | |
308 | * A-profile CPUs, we don't need to do anything special in the | |
309 | * handler to arrange that it starts correctly. | |
310 | * This is arguably the wrong place to do this, but it matches the | |
311 | * way A-profile does it. Note that this means that every M profile | |
312 | * board must call this function! | |
313 | */ | |
314 | qemu_register_reset(armv7m_reset, cpu); | |
315 | } | |
316 | ||
317 | static Property bitband_properties[] = { | |
318 | DEFINE_PROP_UINT32("base", BitBandState, base, 0), | |
319 | DEFINE_PROP_LINK("source-memory", BitBandState, source_memory, | |
320 | TYPE_MEMORY_REGION, MemoryRegion *), | |
321 | DEFINE_PROP_END_OF_LIST(), | |
322 | }; | |
323 | ||
324 | static void bitband_class_init(ObjectClass *klass, void *data) | |
325 | { | |
326 | DeviceClass *dc = DEVICE_CLASS(klass); | |
327 | ||
328 | dc->realize = bitband_realize; | |
329 | dc->props = bitband_properties; | |
330 | } | |
331 | ||
332 | static const TypeInfo bitband_info = { | |
333 | .name = TYPE_BITBAND, | |
334 | .parent = TYPE_SYS_BUS_DEVICE, | |
335 | .instance_size = sizeof(BitBandState), | |
336 | .instance_init = bitband_init, | |
337 | .class_init = bitband_class_init, | |
338 | }; | |
339 | ||
340 | static void armv7m_register_types(void) | |
341 | { | |
342 | type_register_static(&bitband_info); | |
343 | type_register_static(&armv7m_info); | |
344 | } | |
345 | ||
346 | type_init(armv7m_register_types) |