]>
Commit | Line | Data |
---|---|---|
9e5e54d1 | 1 | /* |
93dbd103 | 2 | * Arm SSE (Subsystems for Embedded): IoTKit |
9e5e54d1 PM |
3 | * |
4 | * Copyright (c) 2018 Linaro Limited | |
5 | * Written by Peter Maydell | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 or | |
9 | * (at your option) any later version. | |
10 | */ | |
11 | ||
12 | #include "qemu/osdep.h" | |
13 | #include "qemu/log.h" | |
14 | #include "qapi/error.h" | |
15 | #include "trace.h" | |
16 | #include "hw/sysbus.h" | |
17 | #include "hw/registerfields.h" | |
6eee5d24 | 18 | #include "hw/arm/armsse.h" |
9e5e54d1 PM |
19 | #include "hw/arm/arm.h" |
20 | ||
4c3690b5 PM |
21 | struct ARMSSEInfo { |
22 | const char *name; | |
f0cab7fe | 23 | int sram_banks; |
4c3690b5 PM |
24 | }; |
25 | ||
26 | static const ARMSSEInfo armsse_variants[] = { | |
27 | { | |
28 | .name = TYPE_IOTKIT, | |
f0cab7fe | 29 | .sram_banks = 1, |
4c3690b5 PM |
30 | }, |
31 | }; | |
32 | ||
d61e4e1f PM |
33 | /* Clock frequency in HZ of the 32KHz "slow clock" */ |
34 | #define S32KCLK (32 * 1000) | |
35 | ||
9e5e54d1 PM |
36 | /* Create an alias region of @size bytes starting at @base |
37 | * which mirrors the memory starting at @orig. | |
38 | */ | |
93dbd103 | 39 | static void make_alias(ARMSSE *s, MemoryRegion *mr, const char *name, |
9e5e54d1 PM |
40 | hwaddr base, hwaddr size, hwaddr orig) |
41 | { | |
42 | memory_region_init_alias(mr, NULL, name, &s->container, orig, size); | |
43 | /* The alias is even lower priority than unimplemented_device regions */ | |
44 | memory_region_add_subregion_overlap(&s->container, base, mr, -1500); | |
45 | } | |
46 | ||
9e5e54d1 PM |
47 | static void irq_status_forwarder(void *opaque, int n, int level) |
48 | { | |
49 | qemu_irq destirq = opaque; | |
50 | ||
51 | qemu_set_irq(destirq, level); | |
52 | } | |
53 | ||
54 | static void nsccfg_handler(void *opaque, int n, int level) | |
55 | { | |
93dbd103 | 56 | ARMSSE *s = ARMSSE(opaque); |
9e5e54d1 PM |
57 | |
58 | s->nsccfg = level; | |
59 | } | |
60 | ||
13628891 | 61 | static void armsse_forward_ppc(ARMSSE *s, const char *ppcname, int ppcnum) |
9e5e54d1 PM |
62 | { |
63 | /* Each of the 4 AHB and 4 APB PPCs that might be present in a | |
93dbd103 | 64 | * system using the ARMSSE has a collection of control lines which |
9e5e54d1 | 65 | * are provided by the security controller and which we want to |
93dbd103 PM |
66 | * expose as control lines on the ARMSSE device itself, so the |
67 | * code using the ARMSSE can wire them up to the PPCs. | |
9e5e54d1 PM |
68 | */ |
69 | SplitIRQ *splitter = &s->ppc_irq_splitter[ppcnum]; | |
13628891 | 70 | DeviceState *armssedev = DEVICE(s); |
9e5e54d1 PM |
71 | DeviceState *dev_secctl = DEVICE(&s->secctl); |
72 | DeviceState *dev_splitter = DEVICE(splitter); | |
73 | char *name; | |
74 | ||
75 | name = g_strdup_printf("%s_nonsec", ppcname); | |
13628891 | 76 | qdev_pass_gpios(dev_secctl, armssedev, name); |
9e5e54d1 PM |
77 | g_free(name); |
78 | name = g_strdup_printf("%s_ap", ppcname); | |
13628891 | 79 | qdev_pass_gpios(dev_secctl, armssedev, name); |
9e5e54d1 PM |
80 | g_free(name); |
81 | name = g_strdup_printf("%s_irq_enable", ppcname); | |
13628891 | 82 | qdev_pass_gpios(dev_secctl, armssedev, name); |
9e5e54d1 PM |
83 | g_free(name); |
84 | name = g_strdup_printf("%s_irq_clear", ppcname); | |
13628891 | 85 | qdev_pass_gpios(dev_secctl, armssedev, name); |
9e5e54d1 PM |
86 | g_free(name); |
87 | ||
88 | /* irq_status is a little more tricky, because we need to | |
89 | * split it so we can send it both to the security controller | |
90 | * and to our OR gate for the NVIC interrupt line. | |
91 | * Connect up the splitter's outputs, and create a GPIO input | |
92 | * which will pass the line state to the input splitter. | |
93 | */ | |
94 | name = g_strdup_printf("%s_irq_status", ppcname); | |
95 | qdev_connect_gpio_out(dev_splitter, 0, | |
96 | qdev_get_gpio_in_named(dev_secctl, | |
97 | name, 0)); | |
98 | qdev_connect_gpio_out(dev_splitter, 1, | |
99 | qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), ppcnum)); | |
100 | s->irq_status_in[ppcnum] = qdev_get_gpio_in(dev_splitter, 0); | |
13628891 | 101 | qdev_init_gpio_in_named_with_opaque(armssedev, irq_status_forwarder, |
9e5e54d1 PM |
102 | s->irq_status_in[ppcnum], name, 1); |
103 | g_free(name); | |
104 | } | |
105 | ||
13628891 | 106 | static void armsse_forward_sec_resp_cfg(ARMSSE *s) |
9e5e54d1 PM |
107 | { |
108 | /* Forward the 3rd output from the splitter device as a | |
13628891 | 109 | * named GPIO output of the armsse object. |
9e5e54d1 PM |
110 | */ |
111 | DeviceState *dev = DEVICE(s); | |
112 | DeviceState *dev_splitter = DEVICE(&s->sec_resp_splitter); | |
113 | ||
114 | qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1); | |
115 | s->sec_resp_cfg_in = qemu_allocate_irq(irq_status_forwarder, | |
116 | s->sec_resp_cfg, 1); | |
117 | qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in); | |
118 | } | |
119 | ||
13628891 | 120 | static void armsse_init(Object *obj) |
9e5e54d1 | 121 | { |
93dbd103 | 122 | ARMSSE *s = ARMSSE(obj); |
f0cab7fe PM |
123 | ARMSSEClass *asc = ARMSSE_GET_CLASS(obj); |
124 | const ARMSSEInfo *info = asc->info; | |
9e5e54d1 PM |
125 | int i; |
126 | ||
f0cab7fe PM |
127 | assert(info->sram_banks <= MAX_SRAM_BANKS); |
128 | ||
13628891 | 129 | memory_region_init(&s->container, obj, "armsse-container", UINT64_MAX); |
9e5e54d1 | 130 | |
955cbc6b TH |
131 | sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), |
132 | TYPE_ARMV7M); | |
9e5e54d1 PM |
133 | qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type", |
134 | ARM_CPU_TYPE_NAME("cortex-m33")); | |
135 | ||
955cbc6b TH |
136 | sysbus_init_child_obj(obj, "secctl", &s->secctl, sizeof(s->secctl), |
137 | TYPE_IOTKIT_SECCTL); | |
138 | sysbus_init_child_obj(obj, "apb-ppc0", &s->apb_ppc0, sizeof(s->apb_ppc0), | |
139 | TYPE_TZ_PPC); | |
140 | sysbus_init_child_obj(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1), | |
141 | TYPE_TZ_PPC); | |
f0cab7fe PM |
142 | for (i = 0; i < info->sram_banks; i++) { |
143 | char *name = g_strdup_printf("mpc%d", i); | |
144 | sysbus_init_child_obj(obj, name, &s->mpc[i], | |
145 | sizeof(s->mpc[i]), TYPE_TZ_MPC); | |
146 | g_free(name); | |
147 | } | |
955cbc6b TH |
148 | object_initialize_child(obj, "mpc-irq-orgate", &s->mpc_irq_orgate, |
149 | sizeof(s->mpc_irq_orgate), TYPE_OR_IRQ, | |
150 | &error_abort, NULL); | |
151 | ||
f0cab7fe | 152 | for (i = 0; i < IOTS_NUM_EXP_MPC + info->sram_banks; i++) { |
bb75e16d PM |
153 | char *name = g_strdup_printf("mpc-irq-splitter-%d", i); |
154 | SplitIRQ *splitter = &s->mpc_irq_splitter[i]; | |
155 | ||
955cbc6b TH |
156 | object_initialize_child(obj, name, splitter, sizeof(*splitter), |
157 | TYPE_SPLIT_IRQ, &error_abort, NULL); | |
bb75e16d PM |
158 | g_free(name); |
159 | } | |
955cbc6b TH |
160 | sysbus_init_child_obj(obj, "timer0", &s->timer0, sizeof(s->timer0), |
161 | TYPE_CMSDK_APB_TIMER); | |
162 | sysbus_init_child_obj(obj, "timer1", &s->timer1, sizeof(s->timer1), | |
163 | TYPE_CMSDK_APB_TIMER); | |
e2d203ba PM |
164 | sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer), |
165 | TYPE_CMSDK_APB_TIMER); | |
955cbc6b | 166 | sysbus_init_child_obj(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer), |
017d069d | 167 | TYPE_CMSDK_APB_DUALTIMER); |
d61e4e1f PM |
168 | sysbus_init_child_obj(obj, "s32kwatchdog", &s->s32kwatchdog, |
169 | sizeof(s->s32kwatchdog), TYPE_CMSDK_APB_WATCHDOG); | |
170 | sysbus_init_child_obj(obj, "nswatchdog", &s->nswatchdog, | |
171 | sizeof(s->nswatchdog), TYPE_CMSDK_APB_WATCHDOG); | |
172 | sysbus_init_child_obj(obj, "swatchdog", &s->swatchdog, | |
173 | sizeof(s->swatchdog), TYPE_CMSDK_APB_WATCHDOG); | |
13628891 | 174 | sysbus_init_child_obj(obj, "armsse-sysctl", &s->sysctl, |
06e65af3 | 175 | sizeof(s->sysctl), TYPE_IOTKIT_SYSCTL); |
13628891 | 176 | sysbus_init_child_obj(obj, "armsse-sysinfo", &s->sysinfo, |
06e65af3 | 177 | sizeof(s->sysinfo), TYPE_IOTKIT_SYSINFO); |
d61e4e1f PM |
178 | object_initialize_child(obj, "nmi-orgate", &s->nmi_orgate, |
179 | sizeof(s->nmi_orgate), TYPE_OR_IRQ, | |
180 | &error_abort, NULL); | |
955cbc6b TH |
181 | object_initialize_child(obj, "ppc-irq-orgate", &s->ppc_irq_orgate, |
182 | sizeof(s->ppc_irq_orgate), TYPE_OR_IRQ, | |
183 | &error_abort, NULL); | |
184 | object_initialize_child(obj, "sec-resp-splitter", &s->sec_resp_splitter, | |
185 | sizeof(s->sec_resp_splitter), TYPE_SPLIT_IRQ, | |
186 | &error_abort, NULL); | |
9e5e54d1 PM |
187 | for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { |
188 | char *name = g_strdup_printf("ppc-irq-splitter-%d", i); | |
189 | SplitIRQ *splitter = &s->ppc_irq_splitter[i]; | |
190 | ||
955cbc6b TH |
191 | object_initialize_child(obj, name, splitter, sizeof(*splitter), |
192 | TYPE_SPLIT_IRQ, &error_abort, NULL); | |
193 | g_free(name); | |
9e5e54d1 | 194 | } |
9e5e54d1 PM |
195 | } |
196 | ||
13628891 | 197 | static void armsse_exp_irq(void *opaque, int n, int level) |
9e5e54d1 | 198 | { |
93dbd103 | 199 | ARMSSE *s = ARMSSE(opaque); |
9e5e54d1 PM |
200 | |
201 | qemu_set_irq(s->exp_irqs[n], level); | |
202 | } | |
203 | ||
13628891 | 204 | static void armsse_mpcexp_status(void *opaque, int n, int level) |
bb75e16d | 205 | { |
93dbd103 | 206 | ARMSSE *s = ARMSSE(opaque); |
bb75e16d PM |
207 | qemu_set_irq(s->mpcexp_status_in[n], level); |
208 | } | |
209 | ||
13628891 | 210 | static void armsse_realize(DeviceState *dev, Error **errp) |
9e5e54d1 | 211 | { |
93dbd103 | 212 | ARMSSE *s = ARMSSE(dev); |
f0cab7fe PM |
213 | ARMSSEClass *asc = ARMSSE_GET_CLASS(dev); |
214 | const ARMSSEInfo *info = asc->info; | |
9e5e54d1 PM |
215 | int i; |
216 | MemoryRegion *mr; | |
217 | Error *err = NULL; | |
218 | SysBusDevice *sbd_apb_ppc0; | |
219 | SysBusDevice *sbd_secctl; | |
220 | DeviceState *dev_apb_ppc0; | |
221 | DeviceState *dev_apb_ppc1; | |
222 | DeviceState *dev_secctl; | |
223 | DeviceState *dev_splitter; | |
224 | ||
225 | if (!s->board_memory) { | |
226 | error_setg(errp, "memory property was not set"); | |
227 | return; | |
228 | } | |
229 | ||
230 | if (!s->mainclk_frq) { | |
231 | error_setg(errp, "MAINCLK property was not set"); | |
232 | return; | |
233 | } | |
234 | ||
235 | /* Handling of which devices should be available only to secure | |
236 | * code is usually done differently for M profile than for A profile. | |
237 | * Instead of putting some devices only into the secure address space, | |
238 | * devices exist in both address spaces but with hard-wired security | |
239 | * permissions that will cause the CPU to fault for non-secure accesses. | |
240 | * | |
93dbd103 | 241 | * The ARMSSE has an IDAU (Implementation Defined Access Unit), |
9e5e54d1 | 242 | * which specifies hard-wired security permissions for different |
93dbd103 | 243 | * areas of the physical address space. For the ARMSSE IDAU, the |
9e5e54d1 PM |
244 | * top 4 bits of the physical address are the IDAU region ID, and |
245 | * if bit 28 (ie the lowest bit of the ID) is 0 then this is an NS | |
246 | * region, otherwise it is an S region. | |
247 | * | |
248 | * The various devices and RAMs are generally all mapped twice, | |
249 | * once into a region that the IDAU defines as secure and once | |
250 | * into a non-secure region. They sit behind either a Memory | |
251 | * Protection Controller (for RAM) or a Peripheral Protection | |
252 | * Controller (for devices), which allow a more fine grained | |
253 | * configuration of whether non-secure accesses are permitted. | |
254 | * | |
255 | * (The other place that guest software can configure security | |
256 | * permissions is in the architected SAU (Security Attribution | |
257 | * Unit), which is entirely inside the CPU. The IDAU can upgrade | |
258 | * the security attributes for a region to more restrictive than | |
259 | * the SAU specifies, but cannot downgrade them.) | |
260 | * | |
261 | * 0x10000000..0x1fffffff alias of 0x00000000..0x0fffffff | |
262 | * 0x20000000..0x2007ffff 32KB FPGA block RAM | |
263 | * 0x30000000..0x3fffffff alias of 0x20000000..0x2fffffff | |
264 | * 0x40000000..0x4000ffff base peripheral region 1 | |
93dbd103 | 265 | * 0x40010000..0x4001ffff CPU peripherals (none for ARMSSE) |
9e5e54d1 PM |
266 | * 0x40020000..0x4002ffff system control element peripherals |
267 | * 0x40080000..0x400fffff base peripheral region 2 | |
268 | * 0x50000000..0x5fffffff alias of 0x40000000..0x4fffffff | |
269 | */ | |
270 | ||
271 | memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1); | |
272 | ||
273 | qdev_prop_set_uint32(DEVICE(&s->armv7m), "num-irq", s->exp_numirq + 32); | |
274 | /* In real hardware the initial Secure VTOR is set from the INITSVTOR0 | |
275 | * register in the IoT Kit System Control Register block, and the | |
276 | * initial value of that is in turn specifiable by the FPGA that | |
277 | * instantiates the IoT Kit. In QEMU we don't implement this wrinkle, | |
278 | * and simply set the CPU's init-svtor to the IoT Kit default value. | |
279 | */ | |
280 | qdev_prop_set_uint32(DEVICE(&s->armv7m), "init-svtor", 0x10000000); | |
281 | object_property_set_link(OBJECT(&s->armv7m), OBJECT(&s->container), | |
282 | "memory", &err); | |
283 | if (err) { | |
284 | error_propagate(errp, err); | |
285 | return; | |
286 | } | |
287 | object_property_set_link(OBJECT(&s->armv7m), OBJECT(s), "idau", &err); | |
288 | if (err) { | |
289 | error_propagate(errp, err); | |
290 | return; | |
291 | } | |
292 | object_property_set_bool(OBJECT(&s->armv7m), true, "realized", &err); | |
293 | if (err) { | |
294 | error_propagate(errp, err); | |
295 | return; | |
296 | } | |
297 | ||
298 | /* Connect our EXP_IRQ GPIOs to the NVIC's lines 32 and up. */ | |
299 | s->exp_irqs = g_new(qemu_irq, s->exp_numirq); | |
300 | for (i = 0; i < s->exp_numirq; i++) { | |
301 | s->exp_irqs[i] = qdev_get_gpio_in(DEVICE(&s->armv7m), i + 32); | |
302 | } | |
13628891 | 303 | qdev_init_gpio_in_named(dev, armsse_exp_irq, "EXP_IRQ", s->exp_numirq); |
9e5e54d1 PM |
304 | |
305 | /* Set up the big aliases first */ | |
306 | make_alias(s, &s->alias1, "alias 1", 0x10000000, 0x10000000, 0x00000000); | |
307 | make_alias(s, &s->alias2, "alias 2", 0x30000000, 0x10000000, 0x20000000); | |
308 | /* The 0x50000000..0x5fffffff region is not a pure alias: it has | |
309 | * a few extra devices that only appear there (generally the | |
310 | * control interfaces for the protection controllers). | |
311 | * We implement this by mapping those devices over the top of this | |
312 | * alias MR at a higher priority. | |
313 | */ | |
314 | make_alias(s, &s->alias3, "alias 3", 0x50000000, 0x10000000, 0x40000000); | |
315 | ||
9e5e54d1 PM |
316 | |
317 | /* Security controller */ | |
318 | object_property_set_bool(OBJECT(&s->secctl), true, "realized", &err); | |
319 | if (err) { | |
320 | error_propagate(errp, err); | |
321 | return; | |
322 | } | |
323 | sbd_secctl = SYS_BUS_DEVICE(&s->secctl); | |
324 | dev_secctl = DEVICE(&s->secctl); | |
325 | sysbus_mmio_map(sbd_secctl, 0, 0x50080000); | |
326 | sysbus_mmio_map(sbd_secctl, 1, 0x40080000); | |
327 | ||
328 | s->nsc_cfg_in = qemu_allocate_irq(nsccfg_handler, s, 1); | |
329 | qdev_connect_gpio_out_named(dev_secctl, "nsc_cfg", 0, s->nsc_cfg_in); | |
330 | ||
331 | /* The sec_resp_cfg output from the security controller must be split into | |
93dbd103 PM |
332 | * multiple lines, one for each of the PPCs within the ARMSSE and one |
333 | * that will be an output from the ARMSSE to the system. | |
9e5e54d1 PM |
334 | */ |
335 | object_property_set_int(OBJECT(&s->sec_resp_splitter), 3, | |
336 | "num-lines", &err); | |
337 | if (err) { | |
338 | error_propagate(errp, err); | |
339 | return; | |
340 | } | |
341 | object_property_set_bool(OBJECT(&s->sec_resp_splitter), true, | |
342 | "realized", &err); | |
343 | if (err) { | |
344 | error_propagate(errp, err); | |
345 | return; | |
346 | } | |
347 | dev_splitter = DEVICE(&s->sec_resp_splitter); | |
348 | qdev_connect_gpio_out_named(dev_secctl, "sec_resp_cfg", 0, | |
349 | qdev_get_gpio_in(dev_splitter, 0)); | |
350 | ||
f0cab7fe PM |
351 | /* Each SRAM bank lives behind its own Memory Protection Controller */ |
352 | for (i = 0; i < info->sram_banks; i++) { | |
353 | char *ramname = g_strdup_printf("armsse.sram%d", i); | |
354 | SysBusDevice *sbd_mpc; | |
355 | ||
356 | memory_region_init_ram(&s->sram[i], NULL, ramname, 0x00008000, &err); | |
357 | g_free(ramname); | |
358 | if (err) { | |
359 | error_propagate(errp, err); | |
360 | return; | |
361 | } | |
362 | object_property_set_link(OBJECT(&s->mpc[i]), OBJECT(&s->sram[i]), | |
363 | "downstream", &err); | |
364 | if (err) { | |
365 | error_propagate(errp, err); | |
366 | return; | |
367 | } | |
368 | object_property_set_bool(OBJECT(&s->mpc[i]), true, "realized", &err); | |
369 | if (err) { | |
370 | error_propagate(errp, err); | |
371 | return; | |
372 | } | |
373 | /* Map the upstream end of the MPC into the right place... */ | |
374 | sbd_mpc = SYS_BUS_DEVICE(&s->mpc[i]); | |
375 | memory_region_add_subregion(&s->container, 0x20000000 + i * 0x8000, | |
376 | sysbus_mmio_get_region(sbd_mpc, 1)); | |
377 | /* ...and its register interface */ | |
378 | memory_region_add_subregion(&s->container, 0x50083000 + i * 0x1000, | |
379 | sysbus_mmio_get_region(sbd_mpc, 0)); | |
af60b291 | 380 | } |
af60b291 | 381 | |
bb75e16d PM |
382 | /* We must OR together lines from the MPC splitters to go to the NVIC */ |
383 | object_property_set_int(OBJECT(&s->mpc_irq_orgate), | |
f0cab7fe PM |
384 | IOTS_NUM_EXP_MPC + info->sram_banks, |
385 | "num-lines", &err); | |
bb75e16d PM |
386 | if (err) { |
387 | error_propagate(errp, err); | |
388 | return; | |
389 | } | |
390 | object_property_set_bool(OBJECT(&s->mpc_irq_orgate), true, | |
391 | "realized", &err); | |
392 | if (err) { | |
393 | error_propagate(errp, err); | |
394 | return; | |
395 | } | |
396 | qdev_connect_gpio_out(DEVICE(&s->mpc_irq_orgate), 0, | |
397 | qdev_get_gpio_in(DEVICE(&s->armv7m), 9)); | |
398 | ||
9e5e54d1 PM |
399 | /* Devices behind APB PPC0: |
400 | * 0x40000000: timer0 | |
401 | * 0x40001000: timer1 | |
402 | * 0x40002000: dual timer | |
403 | * We must configure and realize each downstream device and connect | |
404 | * it to the appropriate PPC port; then we can realize the PPC and | |
405 | * map its upstream ends to the right place in the container. | |
406 | */ | |
407 | qdev_prop_set_uint32(DEVICE(&s->timer0), "pclk-frq", s->mainclk_frq); | |
408 | object_property_set_bool(OBJECT(&s->timer0), true, "realized", &err); | |
409 | if (err) { | |
410 | error_propagate(errp, err); | |
411 | return; | |
412 | } | |
413 | sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer0), 0, | |
414 | qdev_get_gpio_in(DEVICE(&s->armv7m), 3)); | |
415 | mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer0), 0); | |
416 | object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[0]", &err); | |
417 | if (err) { | |
418 | error_propagate(errp, err); | |
419 | return; | |
420 | } | |
421 | ||
422 | qdev_prop_set_uint32(DEVICE(&s->timer1), "pclk-frq", s->mainclk_frq); | |
423 | object_property_set_bool(OBJECT(&s->timer1), true, "realized", &err); | |
424 | if (err) { | |
425 | error_propagate(errp, err); | |
426 | return; | |
427 | } | |
428 | sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer1), 0, | |
984b0c10 | 429 | qdev_get_gpio_in(DEVICE(&s->armv7m), 4)); |
9e5e54d1 PM |
430 | mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer1), 0); |
431 | object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[1]", &err); | |
432 | if (err) { | |
433 | error_propagate(errp, err); | |
434 | return; | |
435 | } | |
436 | ||
017d069d PM |
437 | |
438 | qdev_prop_set_uint32(DEVICE(&s->dualtimer), "pclk-frq", s->mainclk_frq); | |
9e5e54d1 PM |
439 | object_property_set_bool(OBJECT(&s->dualtimer), true, "realized", &err); |
440 | if (err) { | |
441 | error_propagate(errp, err); | |
442 | return; | |
443 | } | |
017d069d PM |
444 | sysbus_connect_irq(SYS_BUS_DEVICE(&s->dualtimer), 0, |
445 | qdev_get_gpio_in(DEVICE(&s->armv7m), 5)); | |
9e5e54d1 PM |
446 | mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dualtimer), 0); |
447 | object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[2]", &err); | |
448 | if (err) { | |
449 | error_propagate(errp, err); | |
450 | return; | |
451 | } | |
452 | ||
453 | object_property_set_bool(OBJECT(&s->apb_ppc0), true, "realized", &err); | |
454 | if (err) { | |
455 | error_propagate(errp, err); | |
456 | return; | |
457 | } | |
458 | ||
459 | sbd_apb_ppc0 = SYS_BUS_DEVICE(&s->apb_ppc0); | |
460 | dev_apb_ppc0 = DEVICE(&s->apb_ppc0); | |
461 | ||
462 | mr = sysbus_mmio_get_region(sbd_apb_ppc0, 0); | |
463 | memory_region_add_subregion(&s->container, 0x40000000, mr); | |
464 | mr = sysbus_mmio_get_region(sbd_apb_ppc0, 1); | |
465 | memory_region_add_subregion(&s->container, 0x40001000, mr); | |
466 | mr = sysbus_mmio_get_region(sbd_apb_ppc0, 2); | |
467 | memory_region_add_subregion(&s->container, 0x40002000, mr); | |
468 | for (i = 0; i < IOTS_APB_PPC0_NUM_PORTS; i++) { | |
469 | qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_nonsec", i, | |
470 | qdev_get_gpio_in_named(dev_apb_ppc0, | |
471 | "cfg_nonsec", i)); | |
472 | qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_ap", i, | |
473 | qdev_get_gpio_in_named(dev_apb_ppc0, | |
474 | "cfg_ap", i)); | |
475 | } | |
476 | qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_enable", 0, | |
477 | qdev_get_gpio_in_named(dev_apb_ppc0, | |
478 | "irq_enable", 0)); | |
479 | qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_clear", 0, | |
480 | qdev_get_gpio_in_named(dev_apb_ppc0, | |
481 | "irq_clear", 0)); | |
482 | qdev_connect_gpio_out(dev_splitter, 0, | |
483 | qdev_get_gpio_in_named(dev_apb_ppc0, | |
484 | "cfg_sec_resp", 0)); | |
485 | ||
486 | /* All the PPC irq lines (from the 2 internal PPCs and the 8 external | |
487 | * ones) are sent individually to the security controller, and also | |
488 | * ORed together to give a single combined PPC interrupt to the NVIC. | |
489 | */ | |
490 | object_property_set_int(OBJECT(&s->ppc_irq_orgate), | |
491 | NUM_PPCS, "num-lines", &err); | |
492 | if (err) { | |
493 | error_propagate(errp, err); | |
494 | return; | |
495 | } | |
496 | object_property_set_bool(OBJECT(&s->ppc_irq_orgate), true, | |
497 | "realized", &err); | |
498 | if (err) { | |
499 | error_propagate(errp, err); | |
500 | return; | |
501 | } | |
502 | qdev_connect_gpio_out(DEVICE(&s->ppc_irq_orgate), 0, | |
503 | qdev_get_gpio_in(DEVICE(&s->armv7m), 10)); | |
504 | ||
505 | /* 0x40010000 .. 0x4001ffff: private CPU region: unused in IoTKit */ | |
506 | ||
93dbd103 | 507 | /* 0x40020000 .. 0x4002ffff : ARMSSE system control peripheral region */ |
9e5e54d1 PM |
508 | /* Devices behind APB PPC1: |
509 | * 0x4002f000: S32K timer | |
510 | */ | |
e2d203ba | 511 | qdev_prop_set_uint32(DEVICE(&s->s32ktimer), "pclk-frq", S32KCLK); |
9e5e54d1 PM |
512 | object_property_set_bool(OBJECT(&s->s32ktimer), true, "realized", &err); |
513 | if (err) { | |
514 | error_propagate(errp, err); | |
515 | return; | |
516 | } | |
e2d203ba PM |
517 | sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32ktimer), 0, |
518 | qdev_get_gpio_in(DEVICE(&s->armv7m), 2)); | |
9e5e54d1 PM |
519 | mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->s32ktimer), 0); |
520 | object_property_set_link(OBJECT(&s->apb_ppc1), OBJECT(mr), "port[0]", &err); | |
521 | if (err) { | |
522 | error_propagate(errp, err); | |
523 | return; | |
524 | } | |
525 | ||
526 | object_property_set_bool(OBJECT(&s->apb_ppc1), true, "realized", &err); | |
527 | if (err) { | |
528 | error_propagate(errp, err); | |
529 | return; | |
530 | } | |
531 | mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->apb_ppc1), 0); | |
532 | memory_region_add_subregion(&s->container, 0x4002f000, mr); | |
533 | ||
534 | dev_apb_ppc1 = DEVICE(&s->apb_ppc1); | |
535 | qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_nonsec", 0, | |
536 | qdev_get_gpio_in_named(dev_apb_ppc1, | |
537 | "cfg_nonsec", 0)); | |
538 | qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_ap", 0, | |
539 | qdev_get_gpio_in_named(dev_apb_ppc1, | |
540 | "cfg_ap", 0)); | |
541 | qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_enable", 0, | |
542 | qdev_get_gpio_in_named(dev_apb_ppc1, | |
543 | "irq_enable", 0)); | |
544 | qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_clear", 0, | |
545 | qdev_get_gpio_in_named(dev_apb_ppc1, | |
546 | "irq_clear", 0)); | |
547 | qdev_connect_gpio_out(dev_splitter, 1, | |
548 | qdev_get_gpio_in_named(dev_apb_ppc1, | |
549 | "cfg_sec_resp", 0)); | |
550 | ||
06e65af3 PM |
551 | object_property_set_bool(OBJECT(&s->sysinfo), true, "realized", &err); |
552 | if (err) { | |
553 | error_propagate(errp, err); | |
554 | return; | |
555 | } | |
556 | /* System information registers */ | |
557 | sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysinfo), 0, 0x40020000); | |
558 | /* System control registers */ | |
559 | object_property_set_bool(OBJECT(&s->sysctl), true, "realized", &err); | |
560 | if (err) { | |
561 | error_propagate(errp, err); | |
562 | return; | |
563 | } | |
564 | sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysctl), 0, 0x50021000); | |
d61e4e1f PM |
565 | |
566 | /* This OR gate wires together outputs from the secure watchdogs to NMI */ | |
567 | object_property_set_int(OBJECT(&s->nmi_orgate), 2, "num-lines", &err); | |
568 | if (err) { | |
569 | error_propagate(errp, err); | |
570 | return; | |
571 | } | |
572 | object_property_set_bool(OBJECT(&s->nmi_orgate), true, "realized", &err); | |
573 | if (err) { | |
574 | error_propagate(errp, err); | |
575 | return; | |
576 | } | |
577 | qdev_connect_gpio_out(DEVICE(&s->nmi_orgate), 0, | |
578 | qdev_get_gpio_in_named(DEVICE(&s->armv7m), "NMI", 0)); | |
579 | ||
580 | qdev_prop_set_uint32(DEVICE(&s->s32kwatchdog), "wdogclk-frq", S32KCLK); | |
581 | object_property_set_bool(OBJECT(&s->s32kwatchdog), true, "realized", &err); | |
582 | if (err) { | |
583 | error_propagate(errp, err); | |
584 | return; | |
585 | } | |
586 | sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, | |
587 | qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 0)); | |
588 | sysbus_mmio_map(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, 0x5002e000); | |
9e5e54d1 | 589 | |
93dbd103 | 590 | /* 0x40080000 .. 0x4008ffff : ARMSSE second Base peripheral region */ |
9e5e54d1 | 591 | |
d61e4e1f PM |
592 | qdev_prop_set_uint32(DEVICE(&s->nswatchdog), "wdogclk-frq", s->mainclk_frq); |
593 | object_property_set_bool(OBJECT(&s->nswatchdog), true, "realized", &err); | |
594 | if (err) { | |
595 | error_propagate(errp, err); | |
596 | return; | |
597 | } | |
598 | sysbus_connect_irq(SYS_BUS_DEVICE(&s->nswatchdog), 0, | |
599 | qdev_get_gpio_in(DEVICE(&s->armv7m), 1)); | |
600 | sysbus_mmio_map(SYS_BUS_DEVICE(&s->nswatchdog), 0, 0x40081000); | |
601 | ||
602 | qdev_prop_set_uint32(DEVICE(&s->swatchdog), "wdogclk-frq", s->mainclk_frq); | |
603 | object_property_set_bool(OBJECT(&s->swatchdog), true, "realized", &err); | |
604 | if (err) { | |
605 | error_propagate(errp, err); | |
606 | return; | |
607 | } | |
608 | sysbus_connect_irq(SYS_BUS_DEVICE(&s->swatchdog), 0, | |
609 | qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 1)); | |
610 | sysbus_mmio_map(SYS_BUS_DEVICE(&s->swatchdog), 0, 0x50081000); | |
9e5e54d1 | 611 | |
9e5e54d1 PM |
612 | for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { |
613 | Object *splitter = OBJECT(&s->ppc_irq_splitter[i]); | |
614 | ||
615 | object_property_set_int(splitter, 2, "num-lines", &err); | |
616 | if (err) { | |
617 | error_propagate(errp, err); | |
618 | return; | |
619 | } | |
620 | object_property_set_bool(splitter, true, "realized", &err); | |
621 | if (err) { | |
622 | error_propagate(errp, err); | |
623 | return; | |
624 | } | |
625 | } | |
626 | ||
627 | for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) { | |
628 | char *ppcname = g_strdup_printf("ahb_ppcexp%d", i); | |
629 | ||
13628891 | 630 | armsse_forward_ppc(s, ppcname, i); |
9e5e54d1 PM |
631 | g_free(ppcname); |
632 | } | |
633 | ||
634 | for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) { | |
635 | char *ppcname = g_strdup_printf("apb_ppcexp%d", i); | |
636 | ||
13628891 | 637 | armsse_forward_ppc(s, ppcname, i + IOTS_NUM_AHB_EXP_PPC); |
9e5e54d1 PM |
638 | g_free(ppcname); |
639 | } | |
640 | ||
641 | for (i = NUM_EXTERNAL_PPCS; i < NUM_PPCS; i++) { | |
642 | /* Wire up IRQ splitter for internal PPCs */ | |
643 | DeviceState *devs = DEVICE(&s->ppc_irq_splitter[i]); | |
644 | char *gpioname = g_strdup_printf("apb_ppc%d_irq_status", | |
645 | i - NUM_EXTERNAL_PPCS); | |
646 | TZPPC *ppc = (i == NUM_EXTERNAL_PPCS) ? &s->apb_ppc0 : &s->apb_ppc1; | |
647 | ||
648 | qdev_connect_gpio_out(devs, 0, | |
649 | qdev_get_gpio_in_named(dev_secctl, gpioname, 0)); | |
650 | qdev_connect_gpio_out(devs, 1, | |
651 | qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), i)); | |
652 | qdev_connect_gpio_out_named(DEVICE(ppc), "irq", 0, | |
653 | qdev_get_gpio_in(devs, 0)); | |
7a35383a | 654 | g_free(gpioname); |
9e5e54d1 PM |
655 | } |
656 | ||
bb75e16d | 657 | /* Wire up the splitters for the MPC IRQs */ |
f0cab7fe | 658 | for (i = 0; i < IOTS_NUM_EXP_MPC + info->sram_banks; i++) { |
bb75e16d PM |
659 | SplitIRQ *splitter = &s->mpc_irq_splitter[i]; |
660 | DeviceState *dev_splitter = DEVICE(splitter); | |
661 | ||
662 | object_property_set_int(OBJECT(splitter), 2, "num-lines", &err); | |
663 | if (err) { | |
664 | error_propagate(errp, err); | |
665 | return; | |
666 | } | |
667 | object_property_set_bool(OBJECT(splitter), true, "realized", &err); | |
668 | if (err) { | |
669 | error_propagate(errp, err); | |
670 | return; | |
671 | } | |
672 | ||
673 | if (i < IOTS_NUM_EXP_MPC) { | |
674 | /* Splitter input is from GPIO input line */ | |
675 | s->mpcexp_status_in[i] = qdev_get_gpio_in(dev_splitter, 0); | |
676 | qdev_connect_gpio_out(dev_splitter, 0, | |
677 | qdev_get_gpio_in_named(dev_secctl, | |
678 | "mpcexp_status", i)); | |
679 | } else { | |
680 | /* Splitter input is from our own MPC */ | |
f0cab7fe PM |
681 | qdev_connect_gpio_out_named(DEVICE(&s->mpc[i - IOTS_NUM_EXP_MPC]), |
682 | "irq", 0, | |
bb75e16d PM |
683 | qdev_get_gpio_in(dev_splitter, 0)); |
684 | qdev_connect_gpio_out(dev_splitter, 0, | |
685 | qdev_get_gpio_in_named(dev_secctl, | |
686 | "mpc_status", 0)); | |
687 | } | |
688 | ||
689 | qdev_connect_gpio_out(dev_splitter, 1, | |
690 | qdev_get_gpio_in(DEVICE(&s->mpc_irq_orgate), i)); | |
691 | } | |
692 | /* Create GPIO inputs which will pass the line state for our | |
693 | * mpcexp_irq inputs to the correct splitter devices. | |
694 | */ | |
13628891 | 695 | qdev_init_gpio_in_named(dev, armsse_mpcexp_status, "mpcexp_status", |
bb75e16d PM |
696 | IOTS_NUM_EXP_MPC); |
697 | ||
13628891 | 698 | armsse_forward_sec_resp_cfg(s); |
9e5e54d1 | 699 | |
132b475a PM |
700 | /* Forward the MSC related signals */ |
701 | qdev_pass_gpios(dev_secctl, dev, "mscexp_status"); | |
702 | qdev_pass_gpios(dev_secctl, dev, "mscexp_clear"); | |
703 | qdev_pass_gpios(dev_secctl, dev, "mscexp_ns"); | |
704 | qdev_connect_gpio_out_named(dev_secctl, "msc_irq", 0, | |
705 | qdev_get_gpio_in(DEVICE(&s->armv7m), 11)); | |
706 | ||
707 | /* | |
708 | * Expose our container region to the board model; this corresponds | |
709 | * to the AHB Slave Expansion ports which allow bus master devices | |
710 | * (eg DMA controllers) in the board model to make transactions into | |
93dbd103 | 711 | * devices in the ARMSSE. |
132b475a PM |
712 | */ |
713 | sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container); | |
714 | ||
9e5e54d1 PM |
715 | system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq; |
716 | } | |
717 | ||
13628891 | 718 | static void armsse_idau_check(IDAUInterface *ii, uint32_t address, |
9e5e54d1 PM |
719 | int *iregion, bool *exempt, bool *ns, bool *nsc) |
720 | { | |
93dbd103 PM |
721 | /* |
722 | * For ARMSSE systems the IDAU responses are simple logical functions | |
9e5e54d1 PM |
723 | * of the address bits. The NSC attribute is guest-adjustable via the |
724 | * NSCCFG register in the security controller. | |
725 | */ | |
93dbd103 | 726 | ARMSSE *s = ARMSSE(ii); |
9e5e54d1 PM |
727 | int region = extract32(address, 28, 4); |
728 | ||
729 | *ns = !(region & 1); | |
730 | *nsc = (region == 1 && (s->nsccfg & 1)) || (region == 3 && (s->nsccfg & 2)); | |
731 | /* 0xe0000000..0xe00fffff and 0xf0000000..0xf00fffff are exempt */ | |
732 | *exempt = (address & 0xeff00000) == 0xe0000000; | |
733 | *iregion = region; | |
734 | } | |
735 | ||
13628891 | 736 | static const VMStateDescription armsse_vmstate = { |
9e5e54d1 PM |
737 | .name = "iotkit", |
738 | .version_id = 1, | |
739 | .minimum_version_id = 1, | |
740 | .fields = (VMStateField[]) { | |
93dbd103 | 741 | VMSTATE_UINT32(nsccfg, ARMSSE), |
9e5e54d1 PM |
742 | VMSTATE_END_OF_LIST() |
743 | } | |
744 | }; | |
745 | ||
13628891 | 746 | static Property armsse_properties[] = { |
93dbd103 | 747 | DEFINE_PROP_LINK("memory", ARMSSE, board_memory, TYPE_MEMORY_REGION, |
9e5e54d1 | 748 | MemoryRegion *), |
93dbd103 PM |
749 | DEFINE_PROP_UINT32("EXP_NUMIRQ", ARMSSE, exp_numirq, 64), |
750 | DEFINE_PROP_UINT32("MAINCLK", ARMSSE, mainclk_frq, 0), | |
9e5e54d1 PM |
751 | DEFINE_PROP_END_OF_LIST() |
752 | }; | |
753 | ||
13628891 | 754 | static void armsse_reset(DeviceState *dev) |
9e5e54d1 | 755 | { |
93dbd103 | 756 | ARMSSE *s = ARMSSE(dev); |
9e5e54d1 PM |
757 | |
758 | s->nsccfg = 0; | |
759 | } | |
760 | ||
13628891 | 761 | static void armsse_class_init(ObjectClass *klass, void *data) |
9e5e54d1 PM |
762 | { |
763 | DeviceClass *dc = DEVICE_CLASS(klass); | |
764 | IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(klass); | |
4c3690b5 | 765 | ARMSSEClass *asc = ARMSSE_CLASS(klass); |
9e5e54d1 | 766 | |
13628891 PM |
767 | dc->realize = armsse_realize; |
768 | dc->vmsd = &armsse_vmstate; | |
769 | dc->props = armsse_properties; | |
770 | dc->reset = armsse_reset; | |
771 | iic->check = armsse_idau_check; | |
4c3690b5 | 772 | asc->info = data; |
9e5e54d1 PM |
773 | } |
774 | ||
4c3690b5 | 775 | static const TypeInfo armsse_info = { |
93dbd103 | 776 | .name = TYPE_ARMSSE, |
9e5e54d1 | 777 | .parent = TYPE_SYS_BUS_DEVICE, |
93dbd103 | 778 | .instance_size = sizeof(ARMSSE), |
13628891 | 779 | .instance_init = armsse_init, |
4c3690b5 | 780 | .abstract = true, |
9e5e54d1 PM |
781 | .interfaces = (InterfaceInfo[]) { |
782 | { TYPE_IDAU_INTERFACE }, | |
783 | { } | |
784 | } | |
785 | }; | |
786 | ||
4c3690b5 | 787 | static void armsse_register_types(void) |
9e5e54d1 | 788 | { |
4c3690b5 PM |
789 | int i; |
790 | ||
791 | type_register_static(&armsse_info); | |
792 | ||
793 | for (i = 0; i < ARRAY_SIZE(armsse_variants); i++) { | |
794 | TypeInfo ti = { | |
795 | .name = armsse_variants[i].name, | |
796 | .parent = TYPE_ARMSSE, | |
13628891 | 797 | .class_init = armsse_class_init, |
4c3690b5 PM |
798 | .class_data = (void *)&armsse_variants[i], |
799 | }; | |
800 | type_register(&ti); | |
801 | } | |
9e5e54d1 PM |
802 | } |
803 | ||
4c3690b5 | 804 | type_init(armsse_register_types); |