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