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