xref: /qemu/hw/arm/armv7m.c (revision 7a4e543d)
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/sysbus.h"
12 #include "hw/arm/arm.h"
13 #include "hw/loader.h"
14 #include "elf.h"
15 #include "sysemu/qtest.h"
16 #include "qemu/error-report.h"
17 
18 /* Bitbanded IO.  Each word corresponds to a single bit.  */
19 
20 /* Get the byte address of the real memory for a bitband access.  */
21 static inline uint32_t bitband_addr(void * opaque, uint32_t addr)
22 {
23     uint32_t res;
24 
25     res = *(uint32_t *)opaque;
26     res |= (addr & 0x1ffffff) >> 5;
27     return res;
28 
29 }
30 
31 static uint32_t bitband_readb(void *opaque, hwaddr offset)
32 {
33     uint8_t v;
34     cpu_physical_memory_read(bitband_addr(opaque, offset), &v, 1);
35     return (v & (1 << ((offset >> 2) & 7))) != 0;
36 }
37 
38 static void bitband_writeb(void *opaque, hwaddr offset,
39                            uint32_t value)
40 {
41     uint32_t addr;
42     uint8_t mask;
43     uint8_t v;
44     addr = bitband_addr(opaque, offset);
45     mask = (1 << ((offset >> 2) & 7));
46     cpu_physical_memory_read(addr, &v, 1);
47     if (value & 1)
48         v |= mask;
49     else
50         v &= ~mask;
51     cpu_physical_memory_write(addr, &v, 1);
52 }
53 
54 static uint32_t bitband_readw(void *opaque, hwaddr offset)
55 {
56     uint32_t addr;
57     uint16_t mask;
58     uint16_t v;
59     addr = bitband_addr(opaque, offset) & ~1;
60     mask = (1 << ((offset >> 2) & 15));
61     mask = tswap16(mask);
62     cpu_physical_memory_read(addr, &v, 2);
63     return (v & mask) != 0;
64 }
65 
66 static void bitband_writew(void *opaque, hwaddr offset,
67                            uint32_t value)
68 {
69     uint32_t addr;
70     uint16_t mask;
71     uint16_t v;
72     addr = bitband_addr(opaque, offset) & ~1;
73     mask = (1 << ((offset >> 2) & 15));
74     mask = tswap16(mask);
75     cpu_physical_memory_read(addr, &v, 2);
76     if (value & 1)
77         v |= mask;
78     else
79         v &= ~mask;
80     cpu_physical_memory_write(addr, &v, 2);
81 }
82 
83 static uint32_t bitband_readl(void *opaque, hwaddr offset)
84 {
85     uint32_t addr;
86     uint32_t mask;
87     uint32_t v;
88     addr = bitband_addr(opaque, offset) & ~3;
89     mask = (1 << ((offset >> 2) & 31));
90     mask = tswap32(mask);
91     cpu_physical_memory_read(addr, &v, 4);
92     return (v & mask) != 0;
93 }
94 
95 static void bitband_writel(void *opaque, hwaddr offset,
96                            uint32_t value)
97 {
98     uint32_t addr;
99     uint32_t mask;
100     uint32_t v;
101     addr = bitband_addr(opaque, offset) & ~3;
102     mask = (1 << ((offset >> 2) & 31));
103     mask = tswap32(mask);
104     cpu_physical_memory_read(addr, &v, 4);
105     if (value & 1)
106         v |= mask;
107     else
108         v &= ~mask;
109     cpu_physical_memory_write(addr, &v, 4);
110 }
111 
112 static const MemoryRegionOps bitband_ops = {
113     .old_mmio = {
114         .read = { bitband_readb, bitband_readw, bitband_readl, },
115         .write = { bitband_writeb, bitband_writew, bitband_writel, },
116     },
117     .endianness = DEVICE_NATIVE_ENDIAN,
118 };
119 
120 #define TYPE_BITBAND "ARM,bitband-memory"
121 #define BITBAND(obj) OBJECT_CHECK(BitBandState, (obj), TYPE_BITBAND)
122 
123 typedef struct {
124     /*< private >*/
125     SysBusDevice parent_obj;
126     /*< public >*/
127 
128     MemoryRegion iomem;
129     uint32_t base;
130 } BitBandState;
131 
132 static int bitband_init(SysBusDevice *dev)
133 {
134     BitBandState *s = BITBAND(dev);
135 
136     memory_region_init_io(&s->iomem, OBJECT(s), &bitband_ops, &s->base,
137                           "bitband", 0x02000000);
138     sysbus_init_mmio(dev, &s->iomem);
139     return 0;
140 }
141 
142 static void armv7m_bitband_init(void)
143 {
144     DeviceState *dev;
145 
146     dev = qdev_create(NULL, TYPE_BITBAND);
147     qdev_prop_set_uint32(dev, "base", 0x20000000);
148     qdev_init_nofail(dev);
149     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x22000000);
150 
151     dev = qdev_create(NULL, TYPE_BITBAND);
152     qdev_prop_set_uint32(dev, "base", 0x40000000);
153     qdev_init_nofail(dev);
154     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x42000000);
155 }
156 
157 /* Board init.  */
158 
159 static void armv7m_reset(void *opaque)
160 {
161     ARMCPU *cpu = opaque;
162 
163     cpu_reset(CPU(cpu));
164 }
165 
166 /* Init CPU and memory for a v7-M based board.
167    mem_size is in bytes.
168    Returns the NVIC array.  */
169 
170 DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
171                       const char *kernel_filename, const char *cpu_model)
172 {
173     ARMCPU *cpu;
174     CPUARMState *env;
175     DeviceState *nvic;
176     int image_size;
177     uint64_t entry;
178     uint64_t lowaddr;
179     int big_endian;
180     MemoryRegion *hack = g_new(MemoryRegion, 1);
181 
182     if (cpu_model == NULL) {
183 	cpu_model = "cortex-m3";
184     }
185     cpu = cpu_arm_init(cpu_model);
186     if (cpu == NULL) {
187         fprintf(stderr, "Unable to find CPU definition\n");
188         exit(1);
189     }
190     env = &cpu->env;
191 
192     armv7m_bitband_init();
193 
194     nvic = qdev_create(NULL, "armv7m_nvic");
195     qdev_prop_set_uint32(nvic, "num-irq", num_irq);
196     env->nvic = nvic;
197     qdev_init_nofail(nvic);
198     sysbus_connect_irq(SYS_BUS_DEVICE(nvic), 0,
199                        qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ));
200 
201 #ifdef TARGET_WORDS_BIGENDIAN
202     big_endian = 1;
203 #else
204     big_endian = 0;
205 #endif
206 
207     if (!kernel_filename && !qtest_enabled()) {
208         fprintf(stderr, "Guest image must be specified (using -kernel)\n");
209         exit(1);
210     }
211 
212     if (kernel_filename) {
213         image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr,
214                               NULL, big_endian, EM_ARM, 1);
215         if (image_size < 0) {
216             image_size = load_image_targphys(kernel_filename, 0, mem_size);
217             lowaddr = 0;
218         }
219         if (image_size < 0) {
220             error_report("Could not load kernel '%s'", kernel_filename);
221             exit(1);
222         }
223     }
224 
225     /* Hack to map an additional page of ram at the top of the address
226        space.  This stops qemu complaining about executing code outside RAM
227        when returning from an exception.  */
228     memory_region_init_ram(hack, NULL, "armv7m.hack", 0x1000, &error_fatal);
229     vmstate_register_ram_global(hack);
230     memory_region_add_subregion(system_memory, 0xfffff000, hack);
231 
232     qemu_register_reset(armv7m_reset, cpu);
233     return nvic;
234 }
235 
236 static Property bitband_properties[] = {
237     DEFINE_PROP_UINT32("base", BitBandState, base, 0),
238     DEFINE_PROP_END_OF_LIST(),
239 };
240 
241 static void bitband_class_init(ObjectClass *klass, void *data)
242 {
243     DeviceClass *dc = DEVICE_CLASS(klass);
244     SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
245 
246     k->init = bitband_init;
247     dc->props = bitband_properties;
248 }
249 
250 static const TypeInfo bitband_info = {
251     .name          = TYPE_BITBAND,
252     .parent        = TYPE_SYS_BUS_DEVICE,
253     .instance_size = sizeof(BitBandState),
254     .class_init    = bitband_class_init,
255 };
256 
257 static void armv7m_register_types(void)
258 {
259     type_register_static(&bitband_info);
260 }
261 
262 type_init(armv7m_register_types)
263