]>
Commit | Line | Data |
---|---|---|
de343bb6 PM |
1 | /* |
2 | * Arm IoT Kit security controller | |
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" | |
0b8fa32f | 14 | #include "qemu/module.h" |
de343bb6 PM |
15 | #include "qapi/error.h" |
16 | #include "trace.h" | |
17 | #include "hw/sysbus.h" | |
18 | #include "hw/registerfields.h" | |
64552b6b | 19 | #include "hw/irq.h" |
de343bb6 PM |
20 | #include "hw/misc/iotkit-secctl.h" |
21 | ||
22 | /* Registers in the secure privilege control block */ | |
23 | REG32(SECRESPCFG, 0x10) | |
24 | REG32(NSCCFG, 0x14) | |
25 | REG32(SECMPCINTSTATUS, 0x1c) | |
26 | REG32(SECPPCINTSTAT, 0x20) | |
27 | REG32(SECPPCINTCLR, 0x24) | |
28 | REG32(SECPPCINTEN, 0x28) | |
29 | REG32(SECMSCINTSTAT, 0x30) | |
30 | REG32(SECMSCINTCLR, 0x34) | |
31 | REG32(SECMSCINTEN, 0x38) | |
32 | REG32(BRGINTSTAT, 0x40) | |
33 | REG32(BRGINTCLR, 0x44) | |
34 | REG32(BRGINTEN, 0x48) | |
35 | REG32(AHBNSPPC0, 0x50) | |
36 | REG32(AHBNSPPCEXP0, 0x60) | |
37 | REG32(AHBNSPPCEXP1, 0x64) | |
38 | REG32(AHBNSPPCEXP2, 0x68) | |
39 | REG32(AHBNSPPCEXP3, 0x6c) | |
40 | REG32(APBNSPPC0, 0x70) | |
41 | REG32(APBNSPPC1, 0x74) | |
42 | REG32(APBNSPPCEXP0, 0x80) | |
43 | REG32(APBNSPPCEXP1, 0x84) | |
44 | REG32(APBNSPPCEXP2, 0x88) | |
45 | REG32(APBNSPPCEXP3, 0x8c) | |
46 | REG32(AHBSPPPC0, 0x90) | |
47 | REG32(AHBSPPPCEXP0, 0xa0) | |
48 | REG32(AHBSPPPCEXP1, 0xa4) | |
49 | REG32(AHBSPPPCEXP2, 0xa8) | |
50 | REG32(AHBSPPPCEXP3, 0xac) | |
51 | REG32(APBSPPPC0, 0xb0) | |
52 | REG32(APBSPPPC1, 0xb4) | |
53 | REG32(APBSPPPCEXP0, 0xc0) | |
54 | REG32(APBSPPPCEXP1, 0xc4) | |
55 | REG32(APBSPPPCEXP2, 0xc8) | |
56 | REG32(APBSPPPCEXP3, 0xcc) | |
57 | REG32(NSMSCEXP, 0xd0) | |
58 | REG32(PID4, 0xfd0) | |
59 | REG32(PID5, 0xfd4) | |
60 | REG32(PID6, 0xfd8) | |
61 | REG32(PID7, 0xfdc) | |
62 | REG32(PID0, 0xfe0) | |
63 | REG32(PID1, 0xfe4) | |
64 | REG32(PID2, 0xfe8) | |
65 | REG32(PID3, 0xfec) | |
66 | REG32(CID0, 0xff0) | |
67 | REG32(CID1, 0xff4) | |
68 | REG32(CID2, 0xff8) | |
69 | REG32(CID3, 0xffc) | |
70 | ||
71 | /* Registers in the non-secure privilege control block */ | |
72 | REG32(AHBNSPPPC0, 0x90) | |
73 | REG32(AHBNSPPPCEXP0, 0xa0) | |
74 | REG32(AHBNSPPPCEXP1, 0xa4) | |
75 | REG32(AHBNSPPPCEXP2, 0xa8) | |
76 | REG32(AHBNSPPPCEXP3, 0xac) | |
77 | REG32(APBNSPPPC0, 0xb0) | |
78 | REG32(APBNSPPPC1, 0xb4) | |
79 | REG32(APBNSPPPCEXP0, 0xc0) | |
80 | REG32(APBNSPPPCEXP1, 0xc4) | |
81 | REG32(APBNSPPPCEXP2, 0xc8) | |
82 | REG32(APBNSPPPCEXP3, 0xcc) | |
83 | /* PID and CID registers are also present in the NS block */ | |
84 | ||
85 | static const uint8_t iotkit_secctl_s_idregs[] = { | |
86 | 0x04, 0x00, 0x00, 0x00, | |
87 | 0x52, 0xb8, 0x0b, 0x00, | |
88 | 0x0d, 0xf0, 0x05, 0xb1, | |
89 | }; | |
90 | ||
91 | static const uint8_t iotkit_secctl_ns_idregs[] = { | |
92 | 0x04, 0x00, 0x00, 0x00, | |
93 | 0x53, 0xb8, 0x0b, 0x00, | |
94 | 0x0d, 0xf0, 0x05, 0xb1, | |
95 | }; | |
96 | ||
b3717c23 PM |
97 | /* The register sets for the various PPCs (AHB internal, APB internal, |
98 | * AHB expansion, APB expansion) are all set up so that they are | |
99 | * in 16-aligned blocks so offsets 0xN0, 0xN4, 0xN8, 0xNC are PPCs | |
100 | * 0, 1, 2, 3 of that type, so we can convert a register address offset | |
101 | * into an an index into a PPC array easily. | |
102 | */ | |
103 | static inline int offset_to_ppc_idx(uint32_t offset) | |
104 | { | |
105 | return extract32(offset, 2, 2); | |
106 | } | |
107 | ||
108 | typedef void PerPPCFunction(IoTKitSecCtlPPC *ppc); | |
109 | ||
110 | static void foreach_ppc(IoTKitSecCtl *s, PerPPCFunction *fn) | |
111 | { | |
112 | int i; | |
113 | ||
114 | for (i = 0; i < IOTS_NUM_APB_PPC; i++) { | |
115 | fn(&s->apb[i]); | |
116 | } | |
117 | for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) { | |
118 | fn(&s->apbexp[i]); | |
119 | } | |
120 | for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) { | |
121 | fn(&s->ahbexp[i]); | |
122 | } | |
123 | } | |
124 | ||
de343bb6 PM |
125 | static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr, |
126 | uint64_t *pdata, | |
127 | unsigned size, MemTxAttrs attrs) | |
128 | { | |
129 | uint64_t r; | |
130 | uint32_t offset = addr & ~0x3; | |
b3717c23 | 131 | IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); |
de343bb6 PM |
132 | |
133 | switch (offset) { | |
134 | case A_AHBNSPPC0: | |
135 | case A_AHBSPPPC0: | |
136 | r = 0; | |
137 | break; | |
138 | case A_SECRESPCFG: | |
b3717c23 PM |
139 | r = s->secrespcfg; |
140 | break; | |
b1ce38e1 PM |
141 | case A_NSCCFG: |
142 | r = s->nsccfg; | |
143 | break; | |
3fd3cb2f PM |
144 | case A_SECMPCINTSTATUS: |
145 | r = s->mpcintstatus; | |
146 | break; | |
de343bb6 | 147 | case A_SECPPCINTSTAT: |
b3717c23 PM |
148 | r = s->secppcintstat; |
149 | break; | |
de343bb6 | 150 | case A_SECPPCINTEN: |
b3717c23 PM |
151 | r = s->secppcinten; |
152 | break; | |
b1ce38e1 PM |
153 | case A_BRGINTSTAT: |
154 | /* QEMU's bus fabric can never report errors as it doesn't buffer | |
155 | * writes, so we never report bridge interrupts. | |
156 | */ | |
157 | r = 0; | |
158 | break; | |
159 | case A_BRGINTEN: | |
160 | r = s->brginten; | |
161 | break; | |
de343bb6 PM |
162 | case A_AHBNSPPCEXP0: |
163 | case A_AHBNSPPCEXP1: | |
164 | case A_AHBNSPPCEXP2: | |
165 | case A_AHBNSPPCEXP3: | |
b3717c23 PM |
166 | r = s->ahbexp[offset_to_ppc_idx(offset)].ns; |
167 | break; | |
de343bb6 PM |
168 | case A_APBNSPPC0: |
169 | case A_APBNSPPC1: | |
b3717c23 PM |
170 | r = s->apb[offset_to_ppc_idx(offset)].ns; |
171 | break; | |
de343bb6 PM |
172 | case A_APBNSPPCEXP0: |
173 | case A_APBNSPPCEXP1: | |
174 | case A_APBNSPPCEXP2: | |
175 | case A_APBNSPPCEXP3: | |
b3717c23 PM |
176 | r = s->apbexp[offset_to_ppc_idx(offset)].ns; |
177 | break; | |
de343bb6 PM |
178 | case A_AHBSPPPCEXP0: |
179 | case A_AHBSPPPCEXP1: | |
180 | case A_AHBSPPPCEXP2: | |
181 | case A_AHBSPPPCEXP3: | |
b3717c23 PM |
182 | r = s->apbexp[offset_to_ppc_idx(offset)].sp; |
183 | break; | |
de343bb6 PM |
184 | case A_APBSPPPC0: |
185 | case A_APBSPPPC1: | |
b3717c23 PM |
186 | r = s->apb[offset_to_ppc_idx(offset)].sp; |
187 | break; | |
de343bb6 PM |
188 | case A_APBSPPPCEXP0: |
189 | case A_APBSPPPCEXP1: | |
190 | case A_APBSPPPCEXP2: | |
191 | case A_APBSPPPCEXP3: | |
b3717c23 PM |
192 | r = s->apbexp[offset_to_ppc_idx(offset)].sp; |
193 | break; | |
b3717c23 | 194 | case A_SECMSCINTSTAT: |
81a75deb PM |
195 | r = s->secmscintstat; |
196 | break; | |
b3717c23 | 197 | case A_SECMSCINTEN: |
81a75deb PM |
198 | r = s->secmscinten; |
199 | break; | |
de343bb6 | 200 | case A_NSMSCEXP: |
81a75deb | 201 | r = s->nsmscexp; |
de343bb6 PM |
202 | break; |
203 | case A_PID4: | |
204 | case A_PID5: | |
205 | case A_PID6: | |
206 | case A_PID7: | |
207 | case A_PID0: | |
208 | case A_PID1: | |
209 | case A_PID2: | |
210 | case A_PID3: | |
211 | case A_CID0: | |
212 | case A_CID1: | |
213 | case A_CID2: | |
214 | case A_CID3: | |
215 | r = iotkit_secctl_s_idregs[(offset - A_PID4) / 4]; | |
216 | break; | |
217 | case A_SECPPCINTCLR: | |
218 | case A_SECMSCINTCLR: | |
219 | case A_BRGINTCLR: | |
220 | qemu_log_mask(LOG_GUEST_ERROR, | |
221 | "IotKit SecCtl S block read: write-only offset 0x%x\n", | |
222 | offset); | |
223 | r = 0; | |
224 | break; | |
225 | default: | |
226 | qemu_log_mask(LOG_GUEST_ERROR, | |
227 | "IotKit SecCtl S block read: bad offset 0x%x\n", offset); | |
228 | r = 0; | |
229 | break; | |
230 | } | |
231 | ||
232 | if (size != 4) { | |
233 | /* None of our registers are access-sensitive, so just pull the right | |
234 | * byte out of the word read result. | |
235 | */ | |
236 | r = extract32(r, (addr & 3) * 8, size * 8); | |
237 | } | |
238 | ||
239 | trace_iotkit_secctl_s_read(offset, r, size); | |
240 | *pdata = r; | |
241 | return MEMTX_OK; | |
242 | } | |
243 | ||
b3717c23 PM |
244 | static void iotkit_secctl_update_ppc_ap(IoTKitSecCtlPPC *ppc) |
245 | { | |
246 | int i; | |
247 | ||
248 | for (i = 0; i < ppc->numports; i++) { | |
249 | bool v; | |
250 | ||
251 | if (extract32(ppc->ns, i, 1)) { | |
252 | v = extract32(ppc->nsp, i, 1); | |
253 | } else { | |
254 | v = extract32(ppc->sp, i, 1); | |
255 | } | |
256 | qemu_set_irq(ppc->ap[i], v); | |
257 | } | |
258 | } | |
259 | ||
260 | static void iotkit_secctl_ppc_ns_write(IoTKitSecCtlPPC *ppc, uint32_t value) | |
261 | { | |
262 | int i; | |
263 | ||
264 | ppc->ns = value & MAKE_64BIT_MASK(0, ppc->numports); | |
265 | for (i = 0; i < ppc->numports; i++) { | |
266 | qemu_set_irq(ppc->nonsec[i], extract32(ppc->ns, i, 1)); | |
267 | } | |
268 | iotkit_secctl_update_ppc_ap(ppc); | |
269 | } | |
270 | ||
271 | static void iotkit_secctl_ppc_sp_write(IoTKitSecCtlPPC *ppc, uint32_t value) | |
272 | { | |
273 | ppc->sp = value & MAKE_64BIT_MASK(0, ppc->numports); | |
274 | iotkit_secctl_update_ppc_ap(ppc); | |
275 | } | |
276 | ||
277 | static void iotkit_secctl_ppc_nsp_write(IoTKitSecCtlPPC *ppc, uint32_t value) | |
278 | { | |
279 | ppc->nsp = value & MAKE_64BIT_MASK(0, ppc->numports); | |
280 | iotkit_secctl_update_ppc_ap(ppc); | |
281 | } | |
282 | ||
283 | static void iotkit_secctl_ppc_update_irq_clear(IoTKitSecCtlPPC *ppc) | |
284 | { | |
285 | uint32_t value = ppc->parent->secppcintstat; | |
286 | ||
287 | qemu_set_irq(ppc->irq_clear, extract32(value, ppc->irq_bit_offset, 1)); | |
288 | } | |
289 | ||
290 | static void iotkit_secctl_ppc_update_irq_enable(IoTKitSecCtlPPC *ppc) | |
291 | { | |
292 | uint32_t value = ppc->parent->secppcinten; | |
293 | ||
294 | qemu_set_irq(ppc->irq_enable, extract32(value, ppc->irq_bit_offset, 1)); | |
295 | } | |
296 | ||
81a75deb PM |
297 | static void iotkit_secctl_update_mscexp_irqs(qemu_irq *msc_irqs, uint32_t value) |
298 | { | |
299 | int i; | |
300 | ||
301 | for (i = 0; i < IOTS_NUM_EXP_MSC; i++) { | |
302 | qemu_set_irq(msc_irqs[i], extract32(value, i + 16, 1)); | |
303 | } | |
304 | } | |
305 | ||
306 | static void iotkit_secctl_update_msc_irq(IoTKitSecCtl *s) | |
307 | { | |
308 | /* Update the combined MSC IRQ, based on S_MSCEXP_STATUS and S_MSCEXP_EN */ | |
309 | bool level = s->secmscintstat & s->secmscinten; | |
310 | ||
311 | qemu_set_irq(s->msc_irq, level); | |
312 | } | |
313 | ||
de343bb6 PM |
314 | static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr, |
315 | uint64_t value, | |
316 | unsigned size, MemTxAttrs attrs) | |
317 | { | |
b3717c23 | 318 | IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); |
de343bb6 | 319 | uint32_t offset = addr; |
b3717c23 | 320 | IoTKitSecCtlPPC *ppc; |
de343bb6 PM |
321 | |
322 | trace_iotkit_secctl_s_write(offset, value, size); | |
323 | ||
324 | if (size != 4) { | |
325 | /* Byte and halfword writes are ignored */ | |
326 | qemu_log_mask(LOG_GUEST_ERROR, | |
327 | "IotKit SecCtl S block write: bad size, ignored\n"); | |
328 | return MEMTX_OK; | |
329 | } | |
330 | ||
331 | switch (offset) { | |
b1ce38e1 PM |
332 | case A_NSCCFG: |
333 | s->nsccfg = value & 3; | |
334 | qemu_set_irq(s->nsc_cfg_irq, s->nsccfg); | |
335 | break; | |
de343bb6 | 336 | case A_SECRESPCFG: |
b3717c23 PM |
337 | value &= 1; |
338 | s->secrespcfg = value; | |
339 | qemu_set_irq(s->sec_resp_cfg, s->secrespcfg); | |
340 | break; | |
de343bb6 | 341 | case A_SECPPCINTCLR: |
b3717c23 PM |
342 | value &= 0x00f000f3; |
343 | foreach_ppc(s, iotkit_secctl_ppc_update_irq_clear); | |
344 | break; | |
de343bb6 | 345 | case A_SECPPCINTEN: |
b3717c23 PM |
346 | s->secppcinten = value & 0x00f000f3; |
347 | foreach_ppc(s, iotkit_secctl_ppc_update_irq_enable); | |
348 | break; | |
b1ce38e1 PM |
349 | case A_BRGINTCLR: |
350 | break; | |
351 | case A_BRGINTEN: | |
352 | s->brginten = value & 0xffff0000; | |
353 | break; | |
de343bb6 PM |
354 | case A_AHBNSPPCEXP0: |
355 | case A_AHBNSPPCEXP1: | |
356 | case A_AHBNSPPCEXP2: | |
357 | case A_AHBNSPPCEXP3: | |
b3717c23 PM |
358 | ppc = &s->ahbexp[offset_to_ppc_idx(offset)]; |
359 | iotkit_secctl_ppc_ns_write(ppc, value); | |
360 | break; | |
de343bb6 PM |
361 | case A_APBNSPPC0: |
362 | case A_APBNSPPC1: | |
b3717c23 PM |
363 | ppc = &s->apb[offset_to_ppc_idx(offset)]; |
364 | iotkit_secctl_ppc_ns_write(ppc, value); | |
365 | break; | |
de343bb6 PM |
366 | case A_APBNSPPCEXP0: |
367 | case A_APBNSPPCEXP1: | |
368 | case A_APBNSPPCEXP2: | |
369 | case A_APBNSPPCEXP3: | |
b3717c23 PM |
370 | ppc = &s->apbexp[offset_to_ppc_idx(offset)]; |
371 | iotkit_secctl_ppc_ns_write(ppc, value); | |
372 | break; | |
de343bb6 PM |
373 | case A_AHBSPPPCEXP0: |
374 | case A_AHBSPPPCEXP1: | |
375 | case A_AHBSPPPCEXP2: | |
376 | case A_AHBSPPPCEXP3: | |
b3717c23 PM |
377 | ppc = &s->ahbexp[offset_to_ppc_idx(offset)]; |
378 | iotkit_secctl_ppc_sp_write(ppc, value); | |
379 | break; | |
de343bb6 PM |
380 | case A_APBSPPPC0: |
381 | case A_APBSPPPC1: | |
b3717c23 PM |
382 | ppc = &s->apb[offset_to_ppc_idx(offset)]; |
383 | iotkit_secctl_ppc_sp_write(ppc, value); | |
384 | break; | |
de343bb6 PM |
385 | case A_APBSPPPCEXP0: |
386 | case A_APBSPPPCEXP1: | |
387 | case A_APBSPPPCEXP2: | |
388 | case A_APBSPPPCEXP3: | |
b3717c23 PM |
389 | ppc = &s->apbexp[offset_to_ppc_idx(offset)]; |
390 | iotkit_secctl_ppc_sp_write(ppc, value); | |
391 | break; | |
b3717c23 | 392 | case A_SECMSCINTCLR: |
81a75deb PM |
393 | iotkit_secctl_update_mscexp_irqs(s->mscexp_clear, value); |
394 | break; | |
b3717c23 | 395 | case A_SECMSCINTEN: |
81a75deb PM |
396 | s->secmscinten = value; |
397 | iotkit_secctl_update_msc_irq(s); | |
398 | break; | |
399 | case A_NSMSCEXP: | |
400 | s->nsmscexp = value; | |
401 | iotkit_secctl_update_mscexp_irqs(s->mscexp_ns, value); | |
de343bb6 PM |
402 | break; |
403 | case A_SECMPCINTSTATUS: | |
404 | case A_SECPPCINTSTAT: | |
405 | case A_SECMSCINTSTAT: | |
406 | case A_BRGINTSTAT: | |
407 | case A_AHBNSPPC0: | |
408 | case A_AHBSPPPC0: | |
de343bb6 PM |
409 | case A_PID4: |
410 | case A_PID5: | |
411 | case A_PID6: | |
412 | case A_PID7: | |
413 | case A_PID0: | |
414 | case A_PID1: | |
415 | case A_PID2: | |
416 | case A_PID3: | |
417 | case A_CID0: | |
418 | case A_CID1: | |
419 | case A_CID2: | |
420 | case A_CID3: | |
421 | qemu_log_mask(LOG_GUEST_ERROR, | |
422 | "IoTKit SecCtl S block write: " | |
423 | "read-only offset 0x%x\n", offset); | |
424 | break; | |
425 | default: | |
426 | qemu_log_mask(LOG_GUEST_ERROR, | |
427 | "IotKit SecCtl S block write: bad offset 0x%x\n", | |
428 | offset); | |
429 | break; | |
430 | } | |
431 | ||
432 | return MEMTX_OK; | |
433 | } | |
434 | ||
435 | static MemTxResult iotkit_secctl_ns_read(void *opaque, hwaddr addr, | |
436 | uint64_t *pdata, | |
437 | unsigned size, MemTxAttrs attrs) | |
438 | { | |
b3717c23 | 439 | IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); |
de343bb6 PM |
440 | uint64_t r; |
441 | uint32_t offset = addr & ~0x3; | |
442 | ||
443 | switch (offset) { | |
444 | case A_AHBNSPPPC0: | |
445 | r = 0; | |
446 | break; | |
447 | case A_AHBNSPPPCEXP0: | |
448 | case A_AHBNSPPPCEXP1: | |
449 | case A_AHBNSPPPCEXP2: | |
450 | case A_AHBNSPPPCEXP3: | |
b3717c23 PM |
451 | r = s->ahbexp[offset_to_ppc_idx(offset)].nsp; |
452 | break; | |
de343bb6 PM |
453 | case A_APBNSPPPC0: |
454 | case A_APBNSPPPC1: | |
b3717c23 PM |
455 | r = s->apb[offset_to_ppc_idx(offset)].nsp; |
456 | break; | |
de343bb6 PM |
457 | case A_APBNSPPPCEXP0: |
458 | case A_APBNSPPPCEXP1: | |
459 | case A_APBNSPPPCEXP2: | |
460 | case A_APBNSPPPCEXP3: | |
b3717c23 | 461 | r = s->apbexp[offset_to_ppc_idx(offset)].nsp; |
de343bb6 PM |
462 | break; |
463 | case A_PID4: | |
464 | case A_PID5: | |
465 | case A_PID6: | |
466 | case A_PID7: | |
467 | case A_PID0: | |
468 | case A_PID1: | |
469 | case A_PID2: | |
470 | case A_PID3: | |
471 | case A_CID0: | |
472 | case A_CID1: | |
473 | case A_CID2: | |
474 | case A_CID3: | |
475 | r = iotkit_secctl_ns_idregs[(offset - A_PID4) / 4]; | |
476 | break; | |
477 | default: | |
478 | qemu_log_mask(LOG_GUEST_ERROR, | |
479 | "IotKit SecCtl NS block write: bad offset 0x%x\n", | |
480 | offset); | |
481 | r = 0; | |
482 | break; | |
483 | } | |
484 | ||
485 | if (size != 4) { | |
486 | /* None of our registers are access-sensitive, so just pull the right | |
487 | * byte out of the word read result. | |
488 | */ | |
489 | r = extract32(r, (addr & 3) * 8, size * 8); | |
490 | } | |
491 | ||
492 | trace_iotkit_secctl_ns_read(offset, r, size); | |
493 | *pdata = r; | |
494 | return MEMTX_OK; | |
495 | } | |
496 | ||
497 | static MemTxResult iotkit_secctl_ns_write(void *opaque, hwaddr addr, | |
498 | uint64_t value, | |
499 | unsigned size, MemTxAttrs attrs) | |
500 | { | |
b3717c23 | 501 | IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); |
de343bb6 | 502 | uint32_t offset = addr; |
b3717c23 | 503 | IoTKitSecCtlPPC *ppc; |
de343bb6 PM |
504 | |
505 | trace_iotkit_secctl_ns_write(offset, value, size); | |
506 | ||
507 | if (size != 4) { | |
508 | /* Byte and halfword writes are ignored */ | |
509 | qemu_log_mask(LOG_GUEST_ERROR, | |
510 | "IotKit SecCtl NS block write: bad size, ignored\n"); | |
511 | return MEMTX_OK; | |
512 | } | |
513 | ||
514 | switch (offset) { | |
515 | case A_AHBNSPPPCEXP0: | |
516 | case A_AHBNSPPPCEXP1: | |
517 | case A_AHBNSPPPCEXP2: | |
518 | case A_AHBNSPPPCEXP3: | |
b3717c23 PM |
519 | ppc = &s->ahbexp[offset_to_ppc_idx(offset)]; |
520 | iotkit_secctl_ppc_nsp_write(ppc, value); | |
521 | break; | |
de343bb6 PM |
522 | case A_APBNSPPPC0: |
523 | case A_APBNSPPPC1: | |
b3717c23 PM |
524 | ppc = &s->apb[offset_to_ppc_idx(offset)]; |
525 | iotkit_secctl_ppc_nsp_write(ppc, value); | |
526 | break; | |
de343bb6 PM |
527 | case A_APBNSPPPCEXP0: |
528 | case A_APBNSPPPCEXP1: | |
529 | case A_APBNSPPPCEXP2: | |
530 | case A_APBNSPPPCEXP3: | |
b3717c23 PM |
531 | ppc = &s->apbexp[offset_to_ppc_idx(offset)]; |
532 | iotkit_secctl_ppc_nsp_write(ppc, value); | |
de343bb6 PM |
533 | break; |
534 | case A_AHBNSPPPC0: | |
535 | case A_PID4: | |
536 | case A_PID5: | |
537 | case A_PID6: | |
538 | case A_PID7: | |
539 | case A_PID0: | |
540 | case A_PID1: | |
541 | case A_PID2: | |
542 | case A_PID3: | |
543 | case A_CID0: | |
544 | case A_CID1: | |
545 | case A_CID2: | |
546 | case A_CID3: | |
547 | qemu_log_mask(LOG_GUEST_ERROR, | |
548 | "IoTKit SecCtl NS block write: " | |
549 | "read-only offset 0x%x\n", offset); | |
550 | break; | |
551 | default: | |
552 | qemu_log_mask(LOG_GUEST_ERROR, | |
553 | "IotKit SecCtl NS block write: bad offset 0x%x\n", | |
554 | offset); | |
555 | break; | |
556 | } | |
557 | ||
558 | return MEMTX_OK; | |
559 | } | |
560 | ||
561 | static const MemoryRegionOps iotkit_secctl_s_ops = { | |
562 | .read_with_attrs = iotkit_secctl_s_read, | |
563 | .write_with_attrs = iotkit_secctl_s_write, | |
564 | .endianness = DEVICE_LITTLE_ENDIAN, | |
565 | .valid.min_access_size = 1, | |
566 | .valid.max_access_size = 4, | |
567 | .impl.min_access_size = 1, | |
568 | .impl.max_access_size = 4, | |
569 | }; | |
570 | ||
571 | static const MemoryRegionOps iotkit_secctl_ns_ops = { | |
572 | .read_with_attrs = iotkit_secctl_ns_read, | |
573 | .write_with_attrs = iotkit_secctl_ns_write, | |
574 | .endianness = DEVICE_LITTLE_ENDIAN, | |
575 | .valid.min_access_size = 1, | |
576 | .valid.max_access_size = 4, | |
577 | .impl.min_access_size = 1, | |
578 | .impl.max_access_size = 4, | |
579 | }; | |
580 | ||
b3717c23 PM |
581 | static void iotkit_secctl_reset_ppc(IoTKitSecCtlPPC *ppc) |
582 | { | |
583 | ppc->ns = 0; | |
584 | ppc->sp = 0; | |
585 | ppc->nsp = 0; | |
586 | } | |
587 | ||
de343bb6 PM |
588 | static void iotkit_secctl_reset(DeviceState *dev) |
589 | { | |
b3717c23 PM |
590 | IoTKitSecCtl *s = IOTKIT_SECCTL(dev); |
591 | ||
592 | s->secppcintstat = 0; | |
593 | s->secppcinten = 0; | |
594 | s->secrespcfg = 0; | |
b1ce38e1 PM |
595 | s->nsccfg = 0; |
596 | s->brginten = 0; | |
b3717c23 PM |
597 | |
598 | foreach_ppc(s, iotkit_secctl_reset_ppc); | |
599 | } | |
600 | ||
3fd3cb2f PM |
601 | static void iotkit_secctl_mpc_status(void *opaque, int n, int level) |
602 | { | |
603 | IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); | |
604 | ||
0a78d7eb | 605 | s->mpcintstatus = deposit32(s->mpcintstatus, n, 1, !!level); |
3fd3cb2f PM |
606 | } |
607 | ||
608 | static void iotkit_secctl_mpcexp_status(void *opaque, int n, int level) | |
609 | { | |
610 | IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); | |
611 | ||
612 | s->mpcintstatus = deposit32(s->mpcintstatus, n + 16, 1, !!level); | |
613 | } | |
614 | ||
81a75deb PM |
615 | static void iotkit_secctl_mscexp_status(void *opaque, int n, int level) |
616 | { | |
617 | IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); | |
618 | ||
619 | s->secmscintstat = deposit32(s->secmscintstat, n + 16, 1, !!level); | |
620 | iotkit_secctl_update_msc_irq(s); | |
621 | } | |
622 | ||
b3717c23 PM |
623 | static void iotkit_secctl_ppc_irqstatus(void *opaque, int n, int level) |
624 | { | |
625 | IoTKitSecCtlPPC *ppc = opaque; | |
626 | IoTKitSecCtl *s = IOTKIT_SECCTL(ppc->parent); | |
627 | int irqbit = ppc->irq_bit_offset + n; | |
de343bb6 | 628 | |
b3717c23 PM |
629 | s->secppcintstat = deposit32(s->secppcintstat, irqbit, 1, level); |
630 | } | |
631 | ||
632 | static void iotkit_secctl_init_ppc(IoTKitSecCtl *s, | |
633 | IoTKitSecCtlPPC *ppc, | |
634 | const char *name, | |
635 | int numports, | |
636 | int irq_bit_offset) | |
637 | { | |
638 | char *gpioname; | |
639 | DeviceState *dev = DEVICE(s); | |
640 | ||
641 | ppc->numports = numports; | |
642 | ppc->irq_bit_offset = irq_bit_offset; | |
643 | ppc->parent = s; | |
644 | ||
645 | gpioname = g_strdup_printf("%s_nonsec", name); | |
646 | qdev_init_gpio_out_named(dev, ppc->nonsec, gpioname, numports); | |
647 | g_free(gpioname); | |
648 | gpioname = g_strdup_printf("%s_ap", name); | |
649 | qdev_init_gpio_out_named(dev, ppc->ap, gpioname, numports); | |
650 | g_free(gpioname); | |
651 | gpioname = g_strdup_printf("%s_irq_enable", name); | |
652 | qdev_init_gpio_out_named(dev, &ppc->irq_enable, gpioname, 1); | |
653 | g_free(gpioname); | |
654 | gpioname = g_strdup_printf("%s_irq_clear", name); | |
655 | qdev_init_gpio_out_named(dev, &ppc->irq_clear, gpioname, 1); | |
656 | g_free(gpioname); | |
657 | gpioname = g_strdup_printf("%s_irq_status", name); | |
658 | qdev_init_gpio_in_named_with_opaque(dev, iotkit_secctl_ppc_irqstatus, | |
659 | ppc, gpioname, 1); | |
660 | g_free(gpioname); | |
de343bb6 PM |
661 | } |
662 | ||
663 | static void iotkit_secctl_init(Object *obj) | |
664 | { | |
665 | IoTKitSecCtl *s = IOTKIT_SECCTL(obj); | |
666 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); | |
b3717c23 PM |
667 | DeviceState *dev = DEVICE(obj); |
668 | int i; | |
669 | ||
670 | iotkit_secctl_init_ppc(s, &s->apb[0], "apb_ppc0", | |
671 | IOTS_APB_PPC0_NUM_PORTS, 0); | |
672 | iotkit_secctl_init_ppc(s, &s->apb[1], "apb_ppc1", | |
673 | IOTS_APB_PPC1_NUM_PORTS, 1); | |
674 | ||
675 | for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) { | |
676 | IoTKitSecCtlPPC *ppc = &s->apbexp[i]; | |
677 | char *ppcname = g_strdup_printf("apb_ppcexp%d", i); | |
678 | iotkit_secctl_init_ppc(s, ppc, ppcname, IOTS_PPC_NUM_PORTS, 4 + i); | |
679 | g_free(ppcname); | |
680 | } | |
681 | for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) { | |
682 | IoTKitSecCtlPPC *ppc = &s->ahbexp[i]; | |
683 | char *ppcname = g_strdup_printf("ahb_ppcexp%d", i); | |
684 | iotkit_secctl_init_ppc(s, ppc, ppcname, IOTS_PPC_NUM_PORTS, 20 + i); | |
685 | g_free(ppcname); | |
686 | } | |
687 | ||
688 | qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1); | |
b1ce38e1 | 689 | qdev_init_gpio_out_named(dev, &s->nsc_cfg_irq, "nsc_cfg", 1); |
de343bb6 | 690 | |
0a78d7eb PM |
691 | qdev_init_gpio_in_named(dev, iotkit_secctl_mpc_status, "mpc_status", |
692 | IOTS_NUM_MPC); | |
3fd3cb2f PM |
693 | qdev_init_gpio_in_named(dev, iotkit_secctl_mpcexp_status, |
694 | "mpcexp_status", IOTS_NUM_EXP_MPC); | |
695 | ||
81a75deb PM |
696 | qdev_init_gpio_in_named(dev, iotkit_secctl_mscexp_status, |
697 | "mscexp_status", IOTS_NUM_EXP_MSC); | |
698 | qdev_init_gpio_out_named(dev, s->mscexp_clear, "mscexp_clear", | |
699 | IOTS_NUM_EXP_MSC); | |
700 | qdev_init_gpio_out_named(dev, s->mscexp_ns, "mscexp_ns", | |
701 | IOTS_NUM_EXP_MSC); | |
702 | qdev_init_gpio_out_named(dev, &s->msc_irq, "msc_irq", 1); | |
703 | ||
de343bb6 PM |
704 | memory_region_init_io(&s->s_regs, obj, &iotkit_secctl_s_ops, |
705 | s, "iotkit-secctl-s-regs", 0x1000); | |
706 | memory_region_init_io(&s->ns_regs, obj, &iotkit_secctl_ns_ops, | |
707 | s, "iotkit-secctl-ns-regs", 0x1000); | |
708 | sysbus_init_mmio(sbd, &s->s_regs); | |
709 | sysbus_init_mmio(sbd, &s->ns_regs); | |
710 | } | |
711 | ||
b3717c23 PM |
712 | static const VMStateDescription iotkit_secctl_ppc_vmstate = { |
713 | .name = "iotkit-secctl-ppc", | |
714 | .version_id = 1, | |
715 | .minimum_version_id = 1, | |
716 | .fields = (VMStateField[]) { | |
717 | VMSTATE_UINT32(ns, IoTKitSecCtlPPC), | |
718 | VMSTATE_UINT32(sp, IoTKitSecCtlPPC), | |
719 | VMSTATE_UINT32(nsp, IoTKitSecCtlPPC), | |
720 | VMSTATE_END_OF_LIST() | |
721 | } | |
722 | }; | |
723 | ||
3fd3cb2f PM |
724 | static const VMStateDescription iotkit_secctl_mpcintstatus_vmstate = { |
725 | .name = "iotkit-secctl-mpcintstatus", | |
726 | .version_id = 1, | |
727 | .minimum_version_id = 1, | |
728 | .fields = (VMStateField[]) { | |
729 | VMSTATE_UINT32(mpcintstatus, IoTKitSecCtl), | |
730 | VMSTATE_END_OF_LIST() | |
731 | } | |
732 | }; | |
733 | ||
81a75deb PM |
734 | static bool needed_always(void *opaque) |
735 | { | |
736 | return true; | |
737 | } | |
738 | ||
739 | static const VMStateDescription iotkit_secctl_msc_vmstate = { | |
740 | .name = "iotkit-secctl/msc", | |
741 | .version_id = 1, | |
742 | .minimum_version_id = 1, | |
743 | .needed = needed_always, | |
744 | .fields = (VMStateField[]) { | |
745 | VMSTATE_UINT32(secmscintstat, IoTKitSecCtl), | |
746 | VMSTATE_UINT32(secmscinten, IoTKitSecCtl), | |
747 | VMSTATE_UINT32(nsmscexp, IoTKitSecCtl), | |
748 | VMSTATE_END_OF_LIST() | |
749 | } | |
750 | }; | |
751 | ||
de343bb6 PM |
752 | static const VMStateDescription iotkit_secctl_vmstate = { |
753 | .name = "iotkit-secctl", | |
754 | .version_id = 1, | |
755 | .minimum_version_id = 1, | |
756 | .fields = (VMStateField[]) { | |
b3717c23 PM |
757 | VMSTATE_UINT32(secppcintstat, IoTKitSecCtl), |
758 | VMSTATE_UINT32(secppcinten, IoTKitSecCtl), | |
759 | VMSTATE_UINT32(secrespcfg, IoTKitSecCtl), | |
b1ce38e1 PM |
760 | VMSTATE_UINT32(nsccfg, IoTKitSecCtl), |
761 | VMSTATE_UINT32(brginten, IoTKitSecCtl), | |
b3717c23 PM |
762 | VMSTATE_STRUCT_ARRAY(apb, IoTKitSecCtl, IOTS_NUM_APB_PPC, 1, |
763 | iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC), | |
764 | VMSTATE_STRUCT_ARRAY(apbexp, IoTKitSecCtl, IOTS_NUM_APB_EXP_PPC, 1, | |
765 | iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC), | |
766 | VMSTATE_STRUCT_ARRAY(ahbexp, IoTKitSecCtl, IOTS_NUM_AHB_EXP_PPC, 1, | |
767 | iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC), | |
de343bb6 | 768 | VMSTATE_END_OF_LIST() |
3fd3cb2f PM |
769 | }, |
770 | .subsections = (const VMStateDescription*[]) { | |
771 | &iotkit_secctl_mpcintstatus_vmstate, | |
81a75deb | 772 | &iotkit_secctl_msc_vmstate, |
3fd3cb2f PM |
773 | NULL |
774 | }, | |
de343bb6 PM |
775 | }; |
776 | ||
777 | static void iotkit_secctl_class_init(ObjectClass *klass, void *data) | |
778 | { | |
779 | DeviceClass *dc = DEVICE_CLASS(klass); | |
780 | ||
781 | dc->vmsd = &iotkit_secctl_vmstate; | |
782 | dc->reset = iotkit_secctl_reset; | |
783 | } | |
784 | ||
785 | static const TypeInfo iotkit_secctl_info = { | |
786 | .name = TYPE_IOTKIT_SECCTL, | |
787 | .parent = TYPE_SYS_BUS_DEVICE, | |
788 | .instance_size = sizeof(IoTKitSecCtl), | |
789 | .instance_init = iotkit_secctl_init, | |
790 | .class_init = iotkit_secctl_class_init, | |
791 | }; | |
792 | ||
793 | static void iotkit_secctl_register_types(void) | |
794 | { | |
795 | type_register_static(&iotkit_secctl_info); | |
796 | } | |
797 | ||
798 | type_init(iotkit_secctl_register_types); |