root / hw / arm / armv7m.c @ 0d09e41a
History | View | Annotate | Download (7.6 kB)
1 | 9ee6e8bb | pbrook | /*
|
---|---|---|---|
2 | 9ee6e8bb | pbrook | * ARMV7M System emulation.
|
3 | 9ee6e8bb | pbrook | *
|
4 | 9ee6e8bb | pbrook | * Copyright (c) 2006-2007 CodeSourcery.
|
5 | 9ee6e8bb | pbrook | * Written by Paul Brook
|
6 | 9ee6e8bb | pbrook | *
|
7 | 2167f7bc | Matthew Fernandez | * This code is licensed under the GPL.
|
8 | 9ee6e8bb | pbrook | */
|
9 | 9ee6e8bb | pbrook | |
10 | 83c9f4ca | Paolo Bonzini | #include "hw/sysbus.h" |
11 | 0d09e41a | Paolo Bonzini | #include "hw/arm.h" |
12 | 83c9f4ca | Paolo Bonzini | #include "hw/loader.h" |
13 | ca20cf32 | Blue Swirl | #include "elf.h" |
14 | 9ee6e8bb | pbrook | |
15 | 9ee6e8bb | pbrook | /* Bitbanded IO. Each word corresponds to a single bit. */
|
16 | 9ee6e8bb | pbrook | |
17 | 2167f7bc | Matthew Fernandez | /* Get the byte address of the real memory for a bitband access. */
|
18 | 8da3ff18 | pbrook | static inline uint32_t bitband_addr(void * opaque, uint32_t addr) |
19 | 9ee6e8bb | pbrook | { |
20 | 9ee6e8bb | pbrook | uint32_t res; |
21 | 9ee6e8bb | pbrook | |
22 | 8da3ff18 | pbrook | res = *(uint32_t *)opaque; |
23 | 9ee6e8bb | pbrook | res |= (addr & 0x1ffffff) >> 5; |
24 | 9ee6e8bb | pbrook | return res;
|
25 | 9ee6e8bb | pbrook | |
26 | 9ee6e8bb | pbrook | } |
27 | 9ee6e8bb | pbrook | |
28 | a8170e5e | Avi Kivity | static uint32_t bitband_readb(void *opaque, hwaddr offset) |
29 | 9ee6e8bb | pbrook | { |
30 | 9ee6e8bb | pbrook | uint8_t v; |
31 | 8da3ff18 | pbrook | cpu_physical_memory_read(bitband_addr(opaque, offset), &v, 1);
|
32 | 9ee6e8bb | pbrook | return (v & (1 << ((offset >> 2) & 7))) != 0; |
33 | 9ee6e8bb | pbrook | } |
34 | 9ee6e8bb | pbrook | |
35 | a8170e5e | Avi Kivity | static void bitband_writeb(void *opaque, hwaddr offset, |
36 | 9ee6e8bb | pbrook | uint32_t value) |
37 | 9ee6e8bb | pbrook | { |
38 | 9ee6e8bb | pbrook | uint32_t addr; |
39 | 9ee6e8bb | pbrook | uint8_t mask; |
40 | 9ee6e8bb | pbrook | uint8_t v; |
41 | 8da3ff18 | pbrook | addr = bitband_addr(opaque, offset); |
42 | 9ee6e8bb | pbrook | mask = (1 << ((offset >> 2) & 7)); |
43 | 9ee6e8bb | pbrook | cpu_physical_memory_read(addr, &v, 1);
|
44 | 9ee6e8bb | pbrook | if (value & 1) |
45 | 9ee6e8bb | pbrook | v |= mask; |
46 | 9ee6e8bb | pbrook | else
|
47 | 9ee6e8bb | pbrook | v &= ~mask; |
48 | 9ee6e8bb | pbrook | cpu_physical_memory_write(addr, &v, 1);
|
49 | 9ee6e8bb | pbrook | } |
50 | 9ee6e8bb | pbrook | |
51 | a8170e5e | Avi Kivity | static uint32_t bitband_readw(void *opaque, hwaddr offset) |
52 | 9ee6e8bb | pbrook | { |
53 | 9ee6e8bb | pbrook | uint32_t addr; |
54 | 9ee6e8bb | pbrook | uint16_t mask; |
55 | 9ee6e8bb | pbrook | uint16_t v; |
56 | 8da3ff18 | pbrook | addr = bitband_addr(opaque, offset) & ~1;
|
57 | 9ee6e8bb | pbrook | mask = (1 << ((offset >> 2) & 15)); |
58 | 9ee6e8bb | pbrook | mask = tswap16(mask); |
59 | 9ee6e8bb | pbrook | cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
|
60 | 9ee6e8bb | pbrook | return (v & mask) != 0; |
61 | 9ee6e8bb | pbrook | } |
62 | 9ee6e8bb | pbrook | |
63 | a8170e5e | Avi Kivity | static void bitband_writew(void *opaque, hwaddr offset, |
64 | 9ee6e8bb | pbrook | uint32_t value) |
65 | 9ee6e8bb | pbrook | { |
66 | 9ee6e8bb | pbrook | uint32_t addr; |
67 | 9ee6e8bb | pbrook | uint16_t mask; |
68 | 9ee6e8bb | pbrook | uint16_t v; |
69 | 8da3ff18 | pbrook | addr = bitband_addr(opaque, offset) & ~1;
|
70 | 9ee6e8bb | pbrook | mask = (1 << ((offset >> 2) & 15)); |
71 | 9ee6e8bb | pbrook | mask = tswap16(mask); |
72 | 9ee6e8bb | pbrook | cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
|
73 | 9ee6e8bb | pbrook | if (value & 1) |
74 | 9ee6e8bb | pbrook | v |= mask; |
75 | 9ee6e8bb | pbrook | else
|
76 | 9ee6e8bb | pbrook | v &= ~mask; |
77 | 9ee6e8bb | pbrook | cpu_physical_memory_write(addr, (uint8_t *)&v, 2);
|
78 | 9ee6e8bb | pbrook | } |
79 | 9ee6e8bb | pbrook | |
80 | a8170e5e | Avi Kivity | static uint32_t bitband_readl(void *opaque, hwaddr offset) |
81 | 9ee6e8bb | pbrook | { |
82 | 9ee6e8bb | pbrook | uint32_t addr; |
83 | 9ee6e8bb | pbrook | uint32_t mask; |
84 | 9ee6e8bb | pbrook | uint32_t v; |
85 | 8da3ff18 | pbrook | addr = bitband_addr(opaque, offset) & ~3;
|
86 | 9ee6e8bb | pbrook | mask = (1 << ((offset >> 2) & 31)); |
87 | 9ee6e8bb | pbrook | mask = tswap32(mask); |
88 | 9ee6e8bb | pbrook | cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
|
89 | 9ee6e8bb | pbrook | return (v & mask) != 0; |
90 | 9ee6e8bb | pbrook | } |
91 | 9ee6e8bb | pbrook | |
92 | a8170e5e | Avi Kivity | static void bitband_writel(void *opaque, hwaddr offset, |
93 | 9ee6e8bb | pbrook | uint32_t value) |
94 | 9ee6e8bb | pbrook | { |
95 | 9ee6e8bb | pbrook | uint32_t addr; |
96 | 9ee6e8bb | pbrook | uint32_t mask; |
97 | 9ee6e8bb | pbrook | uint32_t v; |
98 | 8da3ff18 | pbrook | addr = bitband_addr(opaque, offset) & ~3;
|
99 | 9ee6e8bb | pbrook | mask = (1 << ((offset >> 2) & 31)); |
100 | 9ee6e8bb | pbrook | mask = tswap32(mask); |
101 | 9ee6e8bb | pbrook | cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
|
102 | 9ee6e8bb | pbrook | if (value & 1) |
103 | 9ee6e8bb | pbrook | v |= mask; |
104 | 9ee6e8bb | pbrook | else
|
105 | 9ee6e8bb | pbrook | v &= ~mask; |
106 | 9ee6e8bb | pbrook | cpu_physical_memory_write(addr, (uint8_t *)&v, 4);
|
107 | 9ee6e8bb | pbrook | } |
108 | 9ee6e8bb | pbrook | |
109 | f69bf9d4 | Avi Kivity | static const MemoryRegionOps bitband_ops = { |
110 | f69bf9d4 | Avi Kivity | .old_mmio = { |
111 | f69bf9d4 | Avi Kivity | .read = { bitband_readb, bitband_readw, bitband_readl, }, |
112 | f69bf9d4 | Avi Kivity | .write = { bitband_writeb, bitband_writew, bitband_writel, }, |
113 | f69bf9d4 | Avi Kivity | }, |
114 | f69bf9d4 | Avi Kivity | .endianness = DEVICE_NATIVE_ENDIAN, |
115 | 9ee6e8bb | pbrook | }; |
116 | 9ee6e8bb | pbrook | |
117 | 40905a6a | Paul Brook | typedef struct { |
118 | 40905a6a | Paul Brook | SysBusDevice busdev; |
119 | f69bf9d4 | Avi Kivity | MemoryRegion iomem; |
120 | 40905a6a | Paul Brook | uint32_t base; |
121 | 40905a6a | Paul Brook | } BitBandState; |
122 | 40905a6a | Paul Brook | |
123 | 81a322d4 | Gerd Hoffmann | static int bitband_init(SysBusDevice *dev) |
124 | 9ee6e8bb | pbrook | { |
125 | 40905a6a | Paul Brook | BitBandState *s = FROM_SYSBUS(BitBandState, dev); |
126 | 9ee6e8bb | pbrook | |
127 | f69bf9d4 | Avi Kivity | memory_region_init_io(&s->iomem, &bitband_ops, &s->base, "bitband",
|
128 | f69bf9d4 | Avi Kivity | 0x02000000);
|
129 | 750ecd44 | Avi Kivity | sysbus_init_mmio(dev, &s->iomem); |
130 | 81a322d4 | Gerd Hoffmann | return 0; |
131 | 40905a6a | Paul Brook | } |
132 | 40905a6a | Paul Brook | |
133 | 40905a6a | Paul Brook | static void armv7m_bitband_init(void) |
134 | 40905a6a | Paul Brook | { |
135 | 40905a6a | Paul Brook | DeviceState *dev; |
136 | 40905a6a | Paul Brook | |
137 | 40905a6a | Paul Brook | dev = qdev_create(NULL, "ARM,bitband-memory"); |
138 | ee6847d1 | Gerd Hoffmann | qdev_prop_set_uint32(dev, "base", 0x20000000); |
139 | e23a1b33 | Markus Armbruster | qdev_init_nofail(dev); |
140 | 1356b98d | Andreas Färber | sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x22000000); |
141 | 40905a6a | Paul Brook | |
142 | 40905a6a | Paul Brook | dev = qdev_create(NULL, "ARM,bitband-memory"); |
143 | ee6847d1 | Gerd Hoffmann | qdev_prop_set_uint32(dev, "base", 0x40000000); |
144 | e23a1b33 | Markus Armbruster | qdev_init_nofail(dev); |
145 | 1356b98d | Andreas Färber | sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x42000000); |
146 | 9ee6e8bb | pbrook | } |
147 | 9ee6e8bb | pbrook | |
148 | 9ee6e8bb | pbrook | /* Board init. */
|
149 | 983fe826 | Paul Brook | |
150 | 983fe826 | Paul Brook | static void armv7m_reset(void *opaque) |
151 | 983fe826 | Paul Brook | { |
152 | 31363f12 | Andreas Färber | ARMCPU *cpu = opaque; |
153 | 31363f12 | Andreas Färber | |
154 | 31363f12 | Andreas Färber | cpu_reset(CPU(cpu)); |
155 | 983fe826 | Paul Brook | } |
156 | 983fe826 | Paul Brook | |
157 | 9ee6e8bb | pbrook | /* Init CPU and memory for a v7-M based board.
|
158 | 9ee6e8bb | pbrook | flash_size and sram_size are in kb.
|
159 | 9ee6e8bb | pbrook | Returns the NVIC array. */
|
160 | 9ee6e8bb | pbrook | |
161 | 7d6f78cf | Avi Kivity | qemu_irq *armv7m_init(MemoryRegion *address_space_mem, |
162 | 7d6f78cf | Avi Kivity | int flash_size, int sram_size, |
163 | 9ee6e8bb | pbrook | const char *kernel_filename, const char *cpu_model) |
164 | 9ee6e8bb | pbrook | { |
165 | 0f37c99b | Andreas Färber | ARMCPU *cpu; |
166 | 5ae93306 | Andreas Färber | CPUARMState *env; |
167 | fe7e8758 | Paul Brook | DeviceState *nvic; |
168 | fe7e8758 | Paul Brook | /* FIXME: make this local state. */
|
169 | fe7e8758 | Paul Brook | static qemu_irq pic[64]; |
170 | fe7e8758 | Paul Brook | qemu_irq *cpu_pic; |
171 | 9ee6e8bb | pbrook | int image_size;
|
172 | 9ee6e8bb | pbrook | uint64_t entry; |
173 | 9ee6e8bb | pbrook | uint64_t lowaddr; |
174 | fe7e8758 | Paul Brook | int i;
|
175 | ca20cf32 | Blue Swirl | int big_endian;
|
176 | 7d6f78cf | Avi Kivity | MemoryRegion *sram = g_new(MemoryRegion, 1);
|
177 | 7d6f78cf | Avi Kivity | MemoryRegion *flash = g_new(MemoryRegion, 1);
|
178 | 7d6f78cf | Avi Kivity | MemoryRegion *hack = g_new(MemoryRegion, 1);
|
179 | 9ee6e8bb | pbrook | |
180 | 9ee6e8bb | pbrook | flash_size *= 1024;
|
181 | 9ee6e8bb | pbrook | sram_size *= 1024;
|
182 | 9ee6e8bb | pbrook | |
183 | 0f37c99b | Andreas Färber | if (cpu_model == NULL) { |
184 | 9ee6e8bb | pbrook | cpu_model = "cortex-m3";
|
185 | 0f37c99b | Andreas Färber | } |
186 | 0f37c99b | Andreas Färber | cpu = cpu_arm_init(cpu_model); |
187 | 0f37c99b | Andreas Färber | if (cpu == NULL) { |
188 | 9ee6e8bb | pbrook | fprintf(stderr, "Unable to find CPU definition\n");
|
189 | 9ee6e8bb | pbrook | exit(1);
|
190 | 9ee6e8bb | pbrook | } |
191 | 0f37c99b | Andreas Färber | env = &cpu->env; |
192 | 9ee6e8bb | pbrook | |
193 | 9ee6e8bb | pbrook | #if 0
|
194 | 9ee6e8bb | pbrook | /* > 32Mb SRAM gets complicated because it overlaps the bitband area.
|
195 | 9ee6e8bb | pbrook | We don't have proper commandline options, so allocate half of memory
|
196 | 9ee6e8bb | pbrook | as SRAM, up to a maximum of 32Mb, and the rest as code. */
|
197 | 9ee6e8bb | pbrook | if (ram_size > (512 + 32) * 1024 * 1024)
|
198 | 9ee6e8bb | pbrook | ram_size = (512 + 32) * 1024 * 1024;
|
199 | 9ee6e8bb | pbrook | sram_size = (ram_size / 2) & TARGET_PAGE_MASK;
|
200 | 9ee6e8bb | pbrook | if (sram_size > 32 * 1024 * 1024)
|
201 | 9ee6e8bb | pbrook | sram_size = 32 * 1024 * 1024;
|
202 | 9ee6e8bb | pbrook | code_size = ram_size - sram_size;
|
203 | 9ee6e8bb | pbrook | #endif
|
204 | 9ee6e8bb | pbrook | |
205 | 9ee6e8bb | pbrook | /* Flash programming is done via the SCU, so pretend it is ROM. */
|
206 | c5705a77 | Avi Kivity | memory_region_init_ram(flash, "armv7m.flash", flash_size);
|
207 | c5705a77 | Avi Kivity | vmstate_register_ram_global(flash); |
208 | 7d6f78cf | Avi Kivity | memory_region_set_readonly(flash, true);
|
209 | 7d6f78cf | Avi Kivity | memory_region_add_subregion(address_space_mem, 0, flash);
|
210 | c5705a77 | Avi Kivity | memory_region_init_ram(sram, "armv7m.sram", sram_size);
|
211 | c5705a77 | Avi Kivity | vmstate_register_ram_global(sram); |
212 | 7d6f78cf | Avi Kivity | memory_region_add_subregion(address_space_mem, 0x20000000, sram);
|
213 | 9ee6e8bb | pbrook | armv7m_bitband_init(); |
214 | 9ee6e8bb | pbrook | |
215 | fe7e8758 | Paul Brook | nvic = qdev_create(NULL, "armv7m_nvic"); |
216 | 983fe826 | Paul Brook | env->nvic = nvic; |
217 | e23a1b33 | Markus Armbruster | qdev_init_nofail(nvic); |
218 | 4bd74661 | Andreas Färber | cpu_pic = arm_pic_init_cpu(cpu); |
219 | 1356b98d | Andreas Färber | sysbus_connect_irq(SYS_BUS_DEVICE(nvic), 0, cpu_pic[ARM_PIC_CPU_IRQ]);
|
220 | fe7e8758 | Paul Brook | for (i = 0; i < 64; i++) { |
221 | 067a3ddc | Paul Brook | pic[i] = qdev_get_gpio_in(nvic, i); |
222 | fe7e8758 | Paul Brook | } |
223 | 9ee6e8bb | pbrook | |
224 | ca20cf32 | Blue Swirl | #ifdef TARGET_WORDS_BIGENDIAN
|
225 | ca20cf32 | Blue Swirl | big_endian = 1;
|
226 | ca20cf32 | Blue Swirl | #else
|
227 | ca20cf32 | Blue Swirl | big_endian = 0;
|
228 | ca20cf32 | Blue Swirl | #endif
|
229 | ca20cf32 | Blue Swirl | |
230 | 01fd41ab | Peter A. G. Crosthwaite | if (!kernel_filename) {
|
231 | 01fd41ab | Peter A. G. Crosthwaite | fprintf(stderr, "Guest image must be specified (using -kernel)\n");
|
232 | 01fd41ab | Peter A. G. Crosthwaite | exit(1);
|
233 | 01fd41ab | Peter A. G. Crosthwaite | } |
234 | 01fd41ab | Peter A. G. Crosthwaite | |
235 | 409dbce5 | Aurelien Jarno | image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr, |
236 | 409dbce5 | Aurelien Jarno | NULL, big_endian, ELF_MACHINE, 1); |
237 | 9ee6e8bb | pbrook | if (image_size < 0) { |
238 | dcac9679 | pbrook | image_size = load_image_targphys(kernel_filename, 0, flash_size);
|
239 | 9ee6e8bb | pbrook | lowaddr = 0;
|
240 | 9ee6e8bb | pbrook | } |
241 | 9ee6e8bb | pbrook | if (image_size < 0) { |
242 | 9ee6e8bb | pbrook | fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
243 | 9ee6e8bb | pbrook | kernel_filename); |
244 | 9ee6e8bb | pbrook | exit(1);
|
245 | 9ee6e8bb | pbrook | } |
246 | 9ee6e8bb | pbrook | |
247 | 9ee6e8bb | pbrook | /* Hack to map an additional page of ram at the top of the address
|
248 | 9ee6e8bb | pbrook | space. This stops qemu complaining about executing code outside RAM
|
249 | 9ee6e8bb | pbrook | when returning from an exception. */
|
250 | c5705a77 | Avi Kivity | memory_region_init_ram(hack, "armv7m.hack", 0x1000); |
251 | c5705a77 | Avi Kivity | vmstate_register_ram_global(hack); |
252 | 7d6f78cf | Avi Kivity | memory_region_add_subregion(address_space_mem, 0xfffff000, hack);
|
253 | 9ee6e8bb | pbrook | |
254 | 31363f12 | Andreas Färber | qemu_register_reset(armv7m_reset, cpu); |
255 | 9ee6e8bb | pbrook | return pic;
|
256 | 9ee6e8bb | pbrook | } |
257 | 40905a6a | Paul Brook | |
258 | 999e12bb | Anthony Liguori | static Property bitband_properties[] = {
|
259 | 999e12bb | Anthony Liguori | DEFINE_PROP_UINT32("base", BitBandState, base, 0), |
260 | 999e12bb | Anthony Liguori | DEFINE_PROP_END_OF_LIST(), |
261 | 999e12bb | Anthony Liguori | }; |
262 | 999e12bb | Anthony Liguori | |
263 | 999e12bb | Anthony Liguori | static void bitband_class_init(ObjectClass *klass, void *data) |
264 | 999e12bb | Anthony Liguori | { |
265 | 39bffca2 | Anthony Liguori | DeviceClass *dc = DEVICE_CLASS(klass); |
266 | 999e12bb | Anthony Liguori | SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); |
267 | 999e12bb | Anthony Liguori | |
268 | 999e12bb | Anthony Liguori | k->init = bitband_init; |
269 | 39bffca2 | Anthony Liguori | dc->props = bitband_properties; |
270 | 999e12bb | Anthony Liguori | } |
271 | 999e12bb | Anthony Liguori | |
272 | 8c43a6f0 | Andreas Färber | static const TypeInfo bitband_info = { |
273 | 39bffca2 | Anthony Liguori | .name = "ARM,bitband-memory",
|
274 | 39bffca2 | Anthony Liguori | .parent = TYPE_SYS_BUS_DEVICE, |
275 | 39bffca2 | Anthony Liguori | .instance_size = sizeof(BitBandState),
|
276 | 39bffca2 | Anthony Liguori | .class_init = bitband_class_init, |
277 | ee6847d1 | Gerd Hoffmann | }; |
278 | ee6847d1 | Gerd Hoffmann | |
279 | 83f7d43a | Andreas Färber | static void armv7m_register_types(void) |
280 | 40905a6a | Paul Brook | { |
281 | 39bffca2 | Anthony Liguori | type_register_static(&bitband_info); |
282 | 40905a6a | Paul Brook | } |
283 | 40905a6a | Paul Brook | |
284 | 83f7d43a | Andreas Färber | type_init(armv7m_register_types) |