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