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