]>
Commit | Line | Data |
---|---|---|
9ee6e8bb PB |
1 | /* |
2 | * ARM Nested Vectored Interrupt Controller | |
3 | * | |
4 | * Copyright (c) 2006-2007 CodeSourcery. | |
5 | * Written by Paul Brook | |
6 | * | |
8e31bf38 | 7 | * This code is licensed under the GPL. |
9ee6e8bb PB |
8 | * |
9 | * The ARMv7M System controller is fairly tightly tied in with the | |
10 | * NVIC. Much of that is also implemented here. | |
11 | */ | |
12 | ||
8ef94f0b | 13 | #include "qemu/osdep.h" |
da34e65c | 14 | #include "qapi/error.h" |
4771d756 | 15 | #include "qemu-common.h" |
33c11879 | 16 | #include "cpu.h" |
83c9f4ca | 17 | #include "hw/sysbus.h" |
1de7afc9 | 18 | #include "qemu/timer.h" |
bd2be150 | 19 | #include "hw/arm/arm.h" |
6bf436cf | 20 | #include "hw/arm/armv7m_nvic.h" |
da6d674e | 21 | #include "target/arm/cpu.h" |
03dd024f | 22 | #include "qemu/log.h" |
da6d674e MD |
23 | #include "trace.h" |
24 | ||
25 | /* IRQ number counting: | |
26 | * | |
27 | * the num-irq property counts the number of external IRQ lines | |
28 | * | |
29 | * NVICState::num_irq counts the total number of exceptions | |
30 | * (external IRQs, the 15 internal exceptions including reset, | |
31 | * and one for the unused exception number 0). | |
32 | * | |
33 | * NVIC_MAX_IRQ is the highest permitted number of external IRQ lines. | |
34 | * | |
35 | * NVIC_MAX_VECTORS is the highest permitted number of exceptions. | |
36 | * | |
37 | * Iterating through all exceptions should typically be done with | |
38 | * for (i = 1; i < s->num_irq; i++) to avoid the unused slot 0. | |
39 | * | |
40 | * The external qemu_irq lines are the NVIC's external IRQ lines, | |
41 | * so line 0 is exception 16. | |
42 | * | |
43 | * In the terminology of the architecture manual, "interrupts" are | |
44 | * a subcategory of exception referring to the external interrupts | |
45 | * (which are exception numbers NVIC_FIRST_IRQ and upward). | |
46 | * For historical reasons QEMU tends to use "interrupt" and | |
47 | * "exception" more or less interchangeably. | |
48 | */ | |
49 | #define NVIC_FIRST_IRQ 16 | |
da6d674e MD |
50 | #define NVIC_MAX_IRQ (NVIC_MAX_VECTORS - NVIC_FIRST_IRQ) |
51 | ||
52 | /* Effective running priority of the CPU when no exception is active | |
53 | * (higher than the highest possible priority value) | |
54 | */ | |
55 | #define NVIC_NOEXC_PRIO 0x100 | |
56 | ||
2a29ddee PM |
57 | static const uint8_t nvic_id[] = { |
58 | 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 | |
59 | }; | |
60 | ||
9ee6e8bb PB |
61 | /* qemu timers run at 1GHz. We want something closer to 1MHz. */ |
62 | #define SYSTICK_SCALE 1000ULL | |
63 | ||
64 | #define SYSTICK_ENABLE (1 << 0) | |
65 | #define SYSTICK_TICKINT (1 << 1) | |
66 | #define SYSTICK_CLKSOURCE (1 << 2) | |
67 | #define SYSTICK_COUNTFLAG (1 << 16) | |
68 | ||
7ee930d0 BS |
69 | int system_clock_scale; |
70 | ||
e57ec016 | 71 | /* Conversion factor from qemu timer to SysTick frequencies. */ |
f797c075 | 72 | static inline int64_t systick_scale(NVICState *s) |
9ee6e8bb PB |
73 | { |
74 | if (s->systick.control & SYSTICK_CLKSOURCE) | |
e57ec016 | 75 | return system_clock_scale; |
9ee6e8bb PB |
76 | else |
77 | return 1000; | |
78 | } | |
79 | ||
f797c075 | 80 | static void systick_reload(NVICState *s, int reset) |
9ee6e8bb | 81 | { |
165cdaf8 AH |
82 | /* The Cortex-M3 Devices Generic User Guide says that "When the |
83 | * ENABLE bit is set to 1, the counter loads the RELOAD value from the | |
84 | * SYST RVR register and then counts down". So, we need to check the | |
85 | * ENABLE bit before reloading the value. | |
86 | */ | |
87 | if ((s->systick.control & SYSTICK_ENABLE) == 0) { | |
88 | return; | |
89 | } | |
90 | ||
9ee6e8bb | 91 | if (reset) |
bc72ad67 | 92 | s->systick.tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |
9ee6e8bb | 93 | s->systick.tick += (s->systick.reload + 1) * systick_scale(s); |
bc72ad67 | 94 | timer_mod(s->systick.timer, s->systick.tick); |
9ee6e8bb PB |
95 | } |
96 | ||
97 | static void systick_timer_tick(void * opaque) | |
98 | { | |
f797c075 | 99 | NVICState *s = (NVICState *)opaque; |
9ee6e8bb PB |
100 | s->systick.control |= SYSTICK_COUNTFLAG; |
101 | if (s->systick.control & SYSTICK_TICKINT) { | |
102 | /* Trigger the interrupt. */ | |
103 | armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK); | |
104 | } | |
105 | if (s->systick.reload == 0) { | |
106 | s->systick.control &= ~SYSTICK_ENABLE; | |
107 | } else { | |
108 | systick_reload(s, 0); | |
109 | } | |
110 | } | |
111 | ||
f797c075 | 112 | static void systick_reset(NVICState *s) |
aecff692 PM |
113 | { |
114 | s->systick.control = 0; | |
115 | s->systick.reload = 0; | |
116 | s->systick.tick = 0; | |
bc72ad67 | 117 | timer_del(s->systick.timer); |
aecff692 PM |
118 | } |
119 | ||
da6d674e MD |
120 | static int nvic_pending_prio(NVICState *s) |
121 | { | |
122 | /* return the priority of the current pending interrupt, | |
123 | * or NVIC_NOEXC_PRIO if no interrupt is pending | |
124 | */ | |
125 | return s->vectpending ? s->vectors[s->vectpending].prio : NVIC_NOEXC_PRIO; | |
126 | } | |
127 | ||
128 | /* Return the value of the ISCR RETTOBASE bit: | |
129 | * 1 if there is exactly one active exception | |
130 | * 0 if there is more than one active exception | |
131 | * UNKNOWN if there are no active exceptions (we choose 1, | |
132 | * which matches the choice Cortex-M3 is documented as making). | |
133 | * | |
134 | * NB: some versions of the documentation talk about this | |
135 | * counting "active exceptions other than the one shown by IPSR"; | |
136 | * this is only different in the obscure corner case where guest | |
137 | * code has manually deactivated an exception and is about | |
138 | * to fail an exception-return integrity check. The definition | |
139 | * above is the one from the v8M ARM ARM and is also in line | |
140 | * with the behaviour documented for the Cortex-M3. | |
141 | */ | |
142 | static bool nvic_rettobase(NVICState *s) | |
143 | { | |
144 | int irq, nhand = 0; | |
145 | ||
146 | for (irq = ARMV7M_EXCP_RESET; irq < s->num_irq; irq++) { | |
147 | if (s->vectors[irq].active) { | |
148 | nhand++; | |
149 | if (nhand == 2) { | |
150 | return 0; | |
151 | } | |
152 | } | |
153 | } | |
154 | ||
155 | return 1; | |
156 | } | |
157 | ||
158 | /* Return the value of the ISCR ISRPENDING bit: | |
159 | * 1 if an external interrupt is pending | |
160 | * 0 if no external interrupt is pending | |
161 | */ | |
162 | static bool nvic_isrpending(NVICState *s) | |
163 | { | |
164 | int irq; | |
165 | ||
166 | /* We can shortcut if the highest priority pending interrupt | |
167 | * happens to be external or if there is nothing pending. | |
168 | */ | |
169 | if (s->vectpending > NVIC_FIRST_IRQ) { | |
170 | return true; | |
171 | } | |
172 | if (s->vectpending == 0) { | |
173 | return false; | |
174 | } | |
175 | ||
176 | for (irq = NVIC_FIRST_IRQ; irq < s->num_irq; irq++) { | |
177 | if (s->vectors[irq].pending) { | |
178 | return true; | |
179 | } | |
180 | } | |
181 | return false; | |
182 | } | |
183 | ||
184 | /* Return a mask word which clears the subpriority bits from | |
185 | * a priority value for an M-profile exception, leaving only | |
186 | * the group priority. | |
187 | */ | |
188 | static inline uint32_t nvic_gprio_mask(NVICState *s) | |
189 | { | |
190 | return ~0U << (s->prigroup + 1); | |
191 | } | |
192 | ||
193 | /* Recompute vectpending and exception_prio */ | |
194 | static void nvic_recompute_state(NVICState *s) | |
195 | { | |
196 | int i; | |
197 | int pend_prio = NVIC_NOEXC_PRIO; | |
198 | int active_prio = NVIC_NOEXC_PRIO; | |
199 | int pend_irq = 0; | |
200 | ||
201 | for (i = 1; i < s->num_irq; i++) { | |
202 | VecInfo *vec = &s->vectors[i]; | |
203 | ||
204 | if (vec->enabled && vec->pending && vec->prio < pend_prio) { | |
205 | pend_prio = vec->prio; | |
206 | pend_irq = i; | |
207 | } | |
208 | if (vec->active && vec->prio < active_prio) { | |
209 | active_prio = vec->prio; | |
210 | } | |
211 | } | |
212 | ||
213 | s->vectpending = pend_irq; | |
214 | s->exception_prio = active_prio & nvic_gprio_mask(s); | |
215 | ||
216 | trace_nvic_recompute_state(s->vectpending, s->exception_prio); | |
217 | } | |
218 | ||
219 | /* Return the current execution priority of the CPU | |
220 | * (equivalent to the pseudocode ExecutionPriority function). | |
221 | * This is a value between -2 (NMI priority) and NVIC_NOEXC_PRIO. | |
222 | */ | |
223 | static inline int nvic_exec_prio(NVICState *s) | |
224 | { | |
225 | CPUARMState *env = &s->cpu->env; | |
226 | int running; | |
227 | ||
228 | if (env->daif & PSTATE_F) { /* FAULTMASK */ | |
229 | running = -1; | |
230 | } else if (env->daif & PSTATE_I) { /* PRIMASK */ | |
231 | running = 0; | |
232 | } else if (env->v7m.basepri > 0) { | |
233 | running = env->v7m.basepri & nvic_gprio_mask(s); | |
234 | } else { | |
235 | running = NVIC_NOEXC_PRIO; /* lower than any possible priority */ | |
236 | } | |
237 | /* consider priority of active handler */ | |
238 | return MIN(running, s->exception_prio); | |
239 | } | |
240 | ||
7ecdaa4a PM |
241 | bool armv7m_nvic_can_take_pending_exception(void *opaque) |
242 | { | |
243 | NVICState *s = opaque; | |
244 | ||
245 | return nvic_exec_prio(s) > nvic_pending_prio(s); | |
246 | } | |
247 | ||
da6d674e MD |
248 | /* caller must call nvic_irq_update() after this */ |
249 | static void set_prio(NVICState *s, unsigned irq, uint8_t prio) | |
250 | { | |
251 | assert(irq > ARMV7M_EXCP_NMI); /* only use for configurable prios */ | |
252 | assert(irq < s->num_irq); | |
253 | ||
254 | s->vectors[irq].prio = prio; | |
255 | ||
256 | trace_nvic_set_prio(irq, prio); | |
257 | } | |
258 | ||
259 | /* Recompute state and assert irq line accordingly. | |
260 | * Must be called after changes to: | |
261 | * vec->active, vec->enabled, vec->pending or vec->prio for any vector | |
262 | * prigroup | |
263 | */ | |
264 | static void nvic_irq_update(NVICState *s) | |
265 | { | |
266 | int lvl; | |
267 | int pend_prio; | |
268 | ||
269 | nvic_recompute_state(s); | |
270 | pend_prio = nvic_pending_prio(s); | |
271 | ||
272 | /* Raise NVIC output if this IRQ would be taken, except that we | |
273 | * ignore the effects of the BASEPRI, FAULTMASK and PRIMASK (which | |
274 | * will be checked for in arm_v7m_cpu_exec_interrupt()); changes | |
275 | * to those CPU registers don't cause us to recalculate the NVIC | |
276 | * pending info. | |
277 | */ | |
278 | lvl = (pend_prio < s->exception_prio); | |
279 | trace_nvic_irq_update(s->vectpending, pend_prio, s->exception_prio, lvl); | |
280 | qemu_set_irq(s->excpout, lvl); | |
281 | } | |
282 | ||
283 | static void armv7m_nvic_clear_pending(void *opaque, int irq) | |
284 | { | |
285 | NVICState *s = (NVICState *)opaque; | |
286 | VecInfo *vec; | |
287 | ||
288 | assert(irq > ARMV7M_EXCP_RESET && irq < s->num_irq); | |
289 | ||
290 | vec = &s->vectors[irq]; | |
291 | trace_nvic_clear_pending(irq, vec->enabled, vec->prio); | |
292 | if (vec->pending) { | |
293 | vec->pending = 0; | |
294 | nvic_irq_update(s); | |
295 | } | |
296 | } | |
297 | ||
9ee6e8bb PB |
298 | void armv7m_nvic_set_pending(void *opaque, int irq) |
299 | { | |
f797c075 | 300 | NVICState *s = (NVICState *)opaque; |
da6d674e MD |
301 | VecInfo *vec; |
302 | ||
303 | assert(irq > ARMV7M_EXCP_RESET && irq < s->num_irq); | |
304 | ||
305 | vec = &s->vectors[irq]; | |
306 | trace_nvic_set_pending(irq, vec->enabled, vec->prio); | |
a73c98e1 MD |
307 | |
308 | ||
309 | if (irq >= ARMV7M_EXCP_HARD && irq < ARMV7M_EXCP_PENDSV) { | |
310 | /* If a synchronous exception is pending then it may be | |
311 | * escalated to HardFault if: | |
312 | * * it is equal or lower priority to current execution | |
313 | * * it is disabled | |
314 | * (ie we need to take it immediately but we can't do so). | |
315 | * Asynchronous exceptions (and interrupts) simply remain pending. | |
316 | * | |
317 | * For QEMU, we don't have any imprecise (asynchronous) faults, | |
318 | * so we can assume that PREFETCH_ABORT and DATA_ABORT are always | |
319 | * synchronous. | |
320 | * Debug exceptions are awkward because only Debug exceptions | |
321 | * resulting from the BKPT instruction should be escalated, | |
322 | * but we don't currently implement any Debug exceptions other | |
323 | * than those that result from BKPT, so we treat all debug exceptions | |
324 | * as needing escalation. | |
325 | * | |
326 | * This all means we can identify whether to escalate based only on | |
327 | * the exception number and don't (yet) need the caller to explicitly | |
328 | * tell us whether this exception is synchronous or not. | |
329 | */ | |
330 | int running = nvic_exec_prio(s); | |
331 | bool escalate = false; | |
332 | ||
333 | if (vec->prio >= running) { | |
334 | trace_nvic_escalate_prio(irq, vec->prio, running); | |
335 | escalate = true; | |
336 | } else if (!vec->enabled) { | |
337 | trace_nvic_escalate_disabled(irq); | |
338 | escalate = true; | |
339 | } | |
340 | ||
341 | if (escalate) { | |
342 | if (running < 0) { | |
343 | /* We want to escalate to HardFault but we can't take a | |
344 | * synchronous HardFault at this point either. This is a | |
345 | * Lockup condition due to a guest bug. We don't model | |
346 | * Lockup, so report via cpu_abort() instead. | |
347 | */ | |
348 | cpu_abort(&s->cpu->parent_obj, | |
349 | "Lockup: can't escalate %d to HardFault " | |
350 | "(current priority %d)\n", irq, running); | |
351 | } | |
352 | ||
353 | /* We can do the escalation, so we take HardFault instead */ | |
354 | irq = ARMV7M_EXCP_HARD; | |
355 | vec = &s->vectors[irq]; | |
356 | s->cpu->env.v7m.hfsr |= R_V7M_HFSR_FORCED_MASK; | |
357 | } | |
358 | } | |
359 | ||
da6d674e MD |
360 | if (!vec->pending) { |
361 | vec->pending = 1; | |
362 | nvic_irq_update(s); | |
363 | } | |
9ee6e8bb PB |
364 | } |
365 | ||
366 | /* Make pending IRQ active. */ | |
a5d82355 | 367 | void armv7m_nvic_acknowledge_irq(void *opaque) |
9ee6e8bb | 368 | { |
f797c075 | 369 | NVICState *s = (NVICState *)opaque; |
da6d674e MD |
370 | CPUARMState *env = &s->cpu->env; |
371 | const int pending = s->vectpending; | |
372 | const int running = nvic_exec_prio(s); | |
373 | int pendgroupprio; | |
374 | VecInfo *vec; | |
375 | ||
376 | assert(pending > ARMV7M_EXCP_RESET && pending < s->num_irq); | |
377 | ||
378 | vec = &s->vectors[pending]; | |
379 | ||
380 | assert(vec->enabled); | |
381 | assert(vec->pending); | |
382 | ||
383 | pendgroupprio = vec->prio & nvic_gprio_mask(s); | |
384 | assert(pendgroupprio < running); | |
385 | ||
386 | trace_nvic_acknowledge_irq(pending, vec->prio); | |
387 | ||
388 | vec->active = 1; | |
389 | vec->pending = 0; | |
390 | ||
391 | env->v7m.exception = s->vectpending; | |
392 | ||
393 | nvic_irq_update(s); | |
9ee6e8bb PB |
394 | } |
395 | ||
aa488fe3 | 396 | int armv7m_nvic_complete_irq(void *opaque, int irq) |
9ee6e8bb | 397 | { |
f797c075 | 398 | NVICState *s = (NVICState *)opaque; |
da6d674e | 399 | VecInfo *vec; |
aa488fe3 | 400 | int ret; |
da6d674e MD |
401 | |
402 | assert(irq > ARMV7M_EXCP_RESET && irq < s->num_irq); | |
403 | ||
404 | vec = &s->vectors[irq]; | |
405 | ||
406 | trace_nvic_complete_irq(irq); | |
407 | ||
aa488fe3 PM |
408 | if (!vec->active) { |
409 | /* Tell the caller this was an illegal exception return */ | |
410 | return -1; | |
411 | } | |
412 | ||
413 | ret = nvic_rettobase(s); | |
414 | ||
da6d674e MD |
415 | vec->active = 0; |
416 | if (vec->level) { | |
417 | /* Re-pend the exception if it's still held high; only | |
418 | * happens for extenal IRQs | |
419 | */ | |
420 | assert(irq >= NVIC_FIRST_IRQ); | |
421 | vec->pending = 1; | |
422 | } | |
423 | ||
424 | nvic_irq_update(s); | |
aa488fe3 PM |
425 | |
426 | return ret; | |
da6d674e MD |
427 | } |
428 | ||
429 | /* callback when external interrupt line is changed */ | |
430 | static void set_irq_level(void *opaque, int n, int level) | |
431 | { | |
432 | NVICState *s = opaque; | |
433 | VecInfo *vec; | |
434 | ||
435 | n += NVIC_FIRST_IRQ; | |
436 | ||
437 | assert(n >= NVIC_FIRST_IRQ && n < s->num_irq); | |
438 | ||
439 | trace_nvic_set_irq_level(n, level); | |
440 | ||
441 | /* The pending status of an external interrupt is | |
442 | * latched on rising edge and exception handler return. | |
443 | * | |
444 | * Pulsing the IRQ will always run the handler | |
445 | * once, and the handler will re-run until the | |
446 | * level is low when the handler completes. | |
447 | */ | |
448 | vec = &s->vectors[n]; | |
449 | if (level != vec->level) { | |
450 | vec->level = level; | |
451 | if (level) { | |
452 | armv7m_nvic_set_pending(s, n); | |
453 | } | |
454 | } | |
9ee6e8bb PB |
455 | } |
456 | ||
f797c075 | 457 | static uint32_t nvic_readl(NVICState *s, uint32_t offset) |
9ee6e8bb | 458 | { |
d713ea6c | 459 | ARMCPU *cpu = s->cpu; |
9ee6e8bb | 460 | uint32_t val; |
9ee6e8bb PB |
461 | |
462 | switch (offset) { | |
463 | case 4: /* Interrupt Control Type. */ | |
da6d674e | 464 | return ((s->num_irq - NVIC_FIRST_IRQ) / 32) - 1; |
9ee6e8bb PB |
465 | case 0x10: /* SysTick Control and Status. */ |
466 | val = s->systick.control; | |
467 | s->systick.control &= ~SYSTICK_COUNTFLAG; | |
468 | return val; | |
469 | case 0x14: /* SysTick Reload Value. */ | |
470 | return s->systick.reload; | |
471 | case 0x18: /* SysTick Current Value. */ | |
472 | { | |
473 | int64_t t; | |
474 | if ((s->systick.control & SYSTICK_ENABLE) == 0) | |
475 | return 0; | |
bc72ad67 | 476 | t = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |
9ee6e8bb PB |
477 | if (t >= s->systick.tick) |
478 | return 0; | |
479 | val = ((s->systick.tick - (t + 1)) / systick_scale(s)) + 1; | |
480 | /* The interrupt in triggered when the timer reaches zero. | |
481 | However the counter is not reloaded until the next clock | |
482 | tick. This is a hack to return zero during the first tick. */ | |
483 | if (val > s->systick.reload) | |
484 | val = 0; | |
485 | return val; | |
486 | } | |
487 | case 0x1c: /* SysTick Calibration Value. */ | |
488 | return 10000; | |
489 | case 0xd00: /* CPUID Base. */ | |
e3da9921 | 490 | return cpu->midr; |
e03ba136 | 491 | case 0xd04: /* Interrupt Control State. */ |
9ee6e8bb | 492 | /* VECTACTIVE */ |
b06c262b | 493 | val = cpu->env.v7m.exception; |
9ee6e8bb | 494 | /* VECTPENDING */ |
da6d674e MD |
495 | val |= (s->vectpending & 0xff) << 12; |
496 | /* ISRPENDING - set if any external IRQ is pending */ | |
497 | if (nvic_isrpending(s)) { | |
498 | val |= (1 << 22); | |
499 | } | |
500 | /* RETTOBASE - set if only one handler is active */ | |
501 | if (nvic_rettobase(s)) { | |
502 | val |= (1 << 11); | |
9ee6e8bb PB |
503 | } |
504 | /* PENDSTSET */ | |
da6d674e | 505 | if (s->vectors[ARMV7M_EXCP_SYSTICK].pending) { |
9ee6e8bb | 506 | val |= (1 << 26); |
da6d674e | 507 | } |
9ee6e8bb | 508 | /* PENDSVSET */ |
da6d674e | 509 | if (s->vectors[ARMV7M_EXCP_PENDSV].pending) { |
9ee6e8bb | 510 | val |= (1 << 28); |
da6d674e | 511 | } |
9ee6e8bb | 512 | /* NMIPENDSET */ |
da6d674e | 513 | if (s->vectors[ARMV7M_EXCP_NMI].pending) { |
9ee6e8bb | 514 | val |= (1 << 31); |
da6d674e MD |
515 | } |
516 | /* ISRPREEMPT not implemented */ | |
9ee6e8bb PB |
517 | return val; |
518 | case 0xd08: /* Vector Table Offset. */ | |
4917cf44 | 519 | return cpu->env.v7m.vecbase; |
9ee6e8bb | 520 | case 0xd0c: /* Application Interrupt/Reset Control. */ |
1004102a | 521 | return 0xfa050000 | (s->prigroup << 8); |
9ee6e8bb PB |
522 | case 0xd10: /* System Control. */ |
523 | /* TODO: Implement SLEEPONEXIT. */ | |
524 | return 0; | |
525 | case 0xd14: /* Configuration Control. */ | |
e6b33209 | 526 | return cpu->env.v7m.ccr; |
9ee6e8bb PB |
527 | case 0xd24: /* System Handler Status. */ |
528 | val = 0; | |
da6d674e MD |
529 | if (s->vectors[ARMV7M_EXCP_MEM].active) { |
530 | val |= (1 << 0); | |
531 | } | |
532 | if (s->vectors[ARMV7M_EXCP_BUS].active) { | |
533 | val |= (1 << 1); | |
534 | } | |
535 | if (s->vectors[ARMV7M_EXCP_USAGE].active) { | |
536 | val |= (1 << 3); | |
537 | } | |
538 | if (s->vectors[ARMV7M_EXCP_SVC].active) { | |
539 | val |= (1 << 7); | |
540 | } | |
541 | if (s->vectors[ARMV7M_EXCP_DEBUG].active) { | |
542 | val |= (1 << 8); | |
543 | } | |
544 | if (s->vectors[ARMV7M_EXCP_PENDSV].active) { | |
545 | val |= (1 << 10); | |
546 | } | |
547 | if (s->vectors[ARMV7M_EXCP_SYSTICK].active) { | |
548 | val |= (1 << 11); | |
549 | } | |
550 | if (s->vectors[ARMV7M_EXCP_USAGE].pending) { | |
551 | val |= (1 << 12); | |
552 | } | |
553 | if (s->vectors[ARMV7M_EXCP_MEM].pending) { | |
554 | val |= (1 << 13); | |
555 | } | |
556 | if (s->vectors[ARMV7M_EXCP_BUS].pending) { | |
557 | val |= (1 << 14); | |
558 | } | |
559 | if (s->vectors[ARMV7M_EXCP_SVC].pending) { | |
560 | val |= (1 << 15); | |
561 | } | |
562 | if (s->vectors[ARMV7M_EXCP_MEM].enabled) { | |
563 | val |= (1 << 16); | |
564 | } | |
565 | if (s->vectors[ARMV7M_EXCP_BUS].enabled) { | |
566 | val |= (1 << 17); | |
567 | } | |
568 | if (s->vectors[ARMV7M_EXCP_USAGE].enabled) { | |
569 | val |= (1 << 18); | |
570 | } | |
9ee6e8bb PB |
571 | return val; |
572 | case 0xd28: /* Configurable Fault Status. */ | |
e6b33209 | 573 | return cpu->env.v7m.cfsr; |
9ee6e8bb | 574 | case 0xd2c: /* Hard Fault Status. */ |
e6b33209 | 575 | return cpu->env.v7m.hfsr; |
9ee6e8bb | 576 | case 0xd30: /* Debug Fault Status. */ |
e6b33209 MD |
577 | return cpu->env.v7m.dfsr; |
578 | case 0xd34: /* MMFAR MemManage Fault Address */ | |
579 | return cpu->env.v7m.mmfar; | |
9ee6e8bb | 580 | case 0xd38: /* Bus Fault Address. */ |
e6b33209 | 581 | return cpu->env.v7m.bfar; |
9ee6e8bb PB |
582 | case 0xd3c: /* Aux Fault Status. */ |
583 | /* TODO: Implement fault status registers. */ | |
e6b33209 MD |
584 | qemu_log_mask(LOG_UNIMP, |
585 | "Aux Fault status registers unimplemented\n"); | |
e72e3ffc | 586 | return 0; |
9ee6e8bb PB |
587 | case 0xd40: /* PFR0. */ |
588 | return 0x00000030; | |
589 | case 0xd44: /* PRF1. */ | |
590 | return 0x00000200; | |
591 | case 0xd48: /* DFR0. */ | |
592 | return 0x00100000; | |
593 | case 0xd4c: /* AFR0. */ | |
594 | return 0x00000000; | |
595 | case 0xd50: /* MMFR0. */ | |
596 | return 0x00000030; | |
597 | case 0xd54: /* MMFR1. */ | |
598 | return 0x00000000; | |
599 | case 0xd58: /* MMFR2. */ | |
600 | return 0x00000000; | |
601 | case 0xd5c: /* MMFR3. */ | |
602 | return 0x00000000; | |
603 | case 0xd60: /* ISAR0. */ | |
604 | return 0x01141110; | |
605 | case 0xd64: /* ISAR1. */ | |
606 | return 0x02111000; | |
607 | case 0xd68: /* ISAR2. */ | |
608 | return 0x21112231; | |
609 | case 0xd6c: /* ISAR3. */ | |
610 | return 0x01111110; | |
611 | case 0xd70: /* ISAR4. */ | |
612 | return 0x01310102; | |
613 | /* TODO: Implement debug registers. */ | |
614 | default: | |
e72e3ffc PM |
615 | qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad read offset 0x%x\n", offset); |
616 | return 0; | |
9ee6e8bb PB |
617 | } |
618 | } | |
619 | ||
f797c075 | 620 | static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value) |
9ee6e8bb | 621 | { |
d713ea6c | 622 | ARMCPU *cpu = s->cpu; |
9ee6e8bb PB |
623 | uint32_t oldval; |
624 | switch (offset) { | |
625 | case 0x10: /* SysTick Control and Status. */ | |
626 | oldval = s->systick.control; | |
627 | s->systick.control &= 0xfffffff8; | |
628 | s->systick.control |= value & 7; | |
629 | if ((oldval ^ value) & SYSTICK_ENABLE) { | |
bc72ad67 | 630 | int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |
9ee6e8bb PB |
631 | if (value & SYSTICK_ENABLE) { |
632 | if (s->systick.tick) { | |
633 | s->systick.tick += now; | |
bc72ad67 | 634 | timer_mod(s->systick.timer, s->systick.tick); |
9ee6e8bb PB |
635 | } else { |
636 | systick_reload(s, 1); | |
637 | } | |
638 | } else { | |
bc72ad67 | 639 | timer_del(s->systick.timer); |
9ee6e8bb PB |
640 | s->systick.tick -= now; |
641 | if (s->systick.tick < 0) | |
642 | s->systick.tick = 0; | |
643 | } | |
644 | } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) { | |
645 | /* This is a hack. Force the timer to be reloaded | |
646 | when the reference clock is changed. */ | |
647 | systick_reload(s, 1); | |
648 | } | |
649 | break; | |
650 | case 0x14: /* SysTick Reload Value. */ | |
651 | s->systick.reload = value; | |
652 | break; | |
653 | case 0x18: /* SysTick Current Value. Writes reload the timer. */ | |
654 | systick_reload(s, 1); | |
655 | s->systick.control &= ~SYSTICK_COUNTFLAG; | |
656 | break; | |
657 | case 0xd04: /* Interrupt Control State. */ | |
658 | if (value & (1 << 31)) { | |
659 | armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI); | |
660 | } | |
661 | if (value & (1 << 28)) { | |
662 | armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV); | |
663 | } else if (value & (1 << 27)) { | |
da6d674e | 664 | armv7m_nvic_clear_pending(s, ARMV7M_EXCP_PENDSV); |
9ee6e8bb PB |
665 | } |
666 | if (value & (1 << 26)) { | |
667 | armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK); | |
668 | } else if (value & (1 << 25)) { | |
da6d674e | 669 | armv7m_nvic_clear_pending(s, ARMV7M_EXCP_SYSTICK); |
9ee6e8bb PB |
670 | } |
671 | break; | |
672 | case 0xd08: /* Vector Table Offset. */ | |
4917cf44 | 673 | cpu->env.v7m.vecbase = value & 0xffffff80; |
9ee6e8bb PB |
674 | break; |
675 | case 0xd0c: /* Application Interrupt/Reset Control. */ | |
676 | if ((value >> 16) == 0x05fa) { | |
e192becd MD |
677 | if (value & 4) { |
678 | qemu_irq_pulse(s->sysresetreq); | |
679 | } | |
9ee6e8bb | 680 | if (value & 2) { |
14790f73 MD |
681 | qemu_log_mask(LOG_GUEST_ERROR, |
682 | "Setting VECTCLRACTIVE when not in DEBUG mode " | |
683 | "is UNPREDICTABLE\n"); | |
9ee6e8bb | 684 | } |
e192becd | 685 | if (value & 1) { |
14790f73 MD |
686 | qemu_log_mask(LOG_GUEST_ERROR, |
687 | "Setting VECTRESET when not in DEBUG mode " | |
688 | "is UNPREDICTABLE\n"); | |
9ee6e8bb | 689 | } |
1004102a | 690 | s->prigroup = extract32(value, 8, 3); |
da6d674e | 691 | nvic_irq_update(s); |
9ee6e8bb PB |
692 | } |
693 | break; | |
694 | case 0xd10: /* System Control. */ | |
9ee6e8bb | 695 | /* TODO: Implement control registers. */ |
e6b33209 MD |
696 | qemu_log_mask(LOG_UNIMP, "NVIC: SCR unimplemented\n"); |
697 | break; | |
698 | case 0xd14: /* Configuration Control. */ | |
699 | /* Enforce RAZ/WI on reserved and must-RAZ/WI bits */ | |
700 | value &= (R_V7M_CCR_STKALIGN_MASK | | |
701 | R_V7M_CCR_BFHFNMIGN_MASK | | |
702 | R_V7M_CCR_DIV_0_TRP_MASK | | |
703 | R_V7M_CCR_UNALIGN_TRP_MASK | | |
704 | R_V7M_CCR_USERSETMPEND_MASK | | |
705 | R_V7M_CCR_NONBASETHRDENA_MASK); | |
706 | ||
707 | cpu->env.v7m.ccr = value; | |
e72e3ffc | 708 | break; |
9ee6e8bb | 709 | case 0xd24: /* System Handler Control. */ |
5db53e35 PM |
710 | s->vectors[ARMV7M_EXCP_MEM].active = (value & (1 << 0)) != 0; |
711 | s->vectors[ARMV7M_EXCP_BUS].active = (value & (1 << 1)) != 0; | |
712 | s->vectors[ARMV7M_EXCP_USAGE].active = (value & (1 << 3)) != 0; | |
713 | s->vectors[ARMV7M_EXCP_SVC].active = (value & (1 << 7)) != 0; | |
714 | s->vectors[ARMV7M_EXCP_DEBUG].active = (value & (1 << 8)) != 0; | |
715 | s->vectors[ARMV7M_EXCP_PENDSV].active = (value & (1 << 10)) != 0; | |
716 | s->vectors[ARMV7M_EXCP_SYSTICK].active = (value & (1 << 11)) != 0; | |
717 | s->vectors[ARMV7M_EXCP_USAGE].pending = (value & (1 << 12)) != 0; | |
718 | s->vectors[ARMV7M_EXCP_MEM].pending = (value & (1 << 13)) != 0; | |
719 | s->vectors[ARMV7M_EXCP_BUS].pending = (value & (1 << 14)) != 0; | |
720 | s->vectors[ARMV7M_EXCP_SVC].pending = (value & (1 << 15)) != 0; | |
da6d674e MD |
721 | s->vectors[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0; |
722 | s->vectors[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0; | |
723 | s->vectors[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0; | |
724 | nvic_irq_update(s); | |
9ee6e8bb PB |
725 | break; |
726 | case 0xd28: /* Configurable Fault Status. */ | |
e6b33209 MD |
727 | cpu->env.v7m.cfsr &= ~value; /* W1C */ |
728 | break; | |
9ee6e8bb | 729 | case 0xd2c: /* Hard Fault Status. */ |
e6b33209 MD |
730 | cpu->env.v7m.hfsr &= ~value; /* W1C */ |
731 | break; | |
9ee6e8bb | 732 | case 0xd30: /* Debug Fault Status. */ |
e6b33209 MD |
733 | cpu->env.v7m.dfsr &= ~value; /* W1C */ |
734 | break; | |
9ee6e8bb | 735 | case 0xd34: /* Mem Manage Address. */ |
e6b33209 MD |
736 | cpu->env.v7m.mmfar = value; |
737 | return; | |
9ee6e8bb | 738 | case 0xd38: /* Bus Fault Address. */ |
e6b33209 MD |
739 | cpu->env.v7m.bfar = value; |
740 | return; | |
9ee6e8bb | 741 | case 0xd3c: /* Aux Fault Status. */ |
e72e3ffc | 742 | qemu_log_mask(LOG_UNIMP, |
e6b33209 | 743 | "NVIC: Aux fault status registers unimplemented\n"); |
e72e3ffc | 744 | break; |
2a29ddee | 745 | case 0xf00: /* Software Triggered Interrupt Register */ |
da6d674e | 746 | { |
bdd04fc7 | 747 | /* user mode can only write to STIR if CCR.USERSETMPEND permits it */ |
da6d674e MD |
748 | int excnum = (value & 0x1ff) + NVIC_FIRST_IRQ; |
749 | if (excnum < s->num_irq && | |
bdd04fc7 MD |
750 | (arm_current_el(&cpu->env) || |
751 | (cpu->env.v7m.ccr & R_V7M_CCR_USERSETMPEND_MASK))) { | |
da6d674e | 752 | armv7m_nvic_set_pending(s, excnum); |
2a29ddee PM |
753 | } |
754 | break; | |
da6d674e | 755 | } |
9ee6e8bb | 756 | default: |
e72e3ffc PM |
757 | qemu_log_mask(LOG_GUEST_ERROR, |
758 | "NVIC: Bad write offset 0x%x\n", offset); | |
9ee6e8bb PB |
759 | } |
760 | } | |
761 | ||
a8170e5e | 762 | static uint64_t nvic_sysreg_read(void *opaque, hwaddr addr, |
2a29ddee PM |
763 | unsigned size) |
764 | { | |
f797c075 | 765 | NVICState *s = (NVICState *)opaque; |
2a29ddee | 766 | uint32_t offset = addr; |
da6d674e | 767 | unsigned i, startvec, end; |
0e8153dd AB |
768 | uint32_t val; |
769 | ||
770 | switch (offset) { | |
da6d674e MD |
771 | /* reads of set and clear both return the status */ |
772 | case 0x100 ... 0x13f: /* NVIC Set enable */ | |
773 | offset += 0x80; | |
774 | /* fall through */ | |
775 | case 0x180 ... 0x1bf: /* NVIC Clear enable */ | |
776 | val = 0; | |
777 | startvec = offset - 0x180 + NVIC_FIRST_IRQ; /* vector # */ | |
778 | ||
779 | for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) { | |
780 | if (s->vectors[startvec + i].enabled) { | |
781 | val |= (1 << i); | |
782 | } | |
783 | } | |
784 | break; | |
785 | case 0x200 ... 0x23f: /* NVIC Set pend */ | |
786 | offset += 0x80; | |
787 | /* fall through */ | |
788 | case 0x280 ... 0x2bf: /* NVIC Clear pend */ | |
789 | val = 0; | |
790 | startvec = offset - 0x280 + NVIC_FIRST_IRQ; /* vector # */ | |
791 | for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) { | |
792 | if (s->vectors[startvec + i].pending) { | |
793 | val |= (1 << i); | |
794 | } | |
795 | } | |
796 | break; | |
797 | case 0x300 ... 0x33f: /* NVIC Active */ | |
798 | val = 0; | |
799 | startvec = offset - 0x300 + NVIC_FIRST_IRQ; /* vector # */ | |
800 | ||
801 | for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) { | |
802 | if (s->vectors[startvec + i].active) { | |
803 | val |= (1 << i); | |
804 | } | |
805 | } | |
806 | break; | |
807 | case 0x400 ... 0x5ef: /* NVIC Priority */ | |
808 | val = 0; | |
809 | startvec = offset - 0x400 + NVIC_FIRST_IRQ; /* vector # */ | |
810 | ||
811 | for (i = 0; i < size && startvec + i < s->num_irq; i++) { | |
812 | val |= s->vectors[startvec + i].prio << (8 * i); | |
813 | } | |
814 | break; | |
0e8153dd AB |
815 | case 0xd18 ... 0xd23: /* System Handler Priority. */ |
816 | val = 0; | |
817 | for (i = 0; i < size; i++) { | |
da6d674e | 818 | val |= s->vectors[(offset - 0xd14) + i].prio << (i * 8); |
0e8153dd | 819 | } |
da6d674e | 820 | break; |
0e8153dd | 821 | case 0xfe0 ... 0xfff: /* ID. */ |
2a29ddee | 822 | if (offset & 3) { |
da6d674e MD |
823 | val = 0; |
824 | } else { | |
825 | val = nvic_id[(offset - 0xfe0) >> 2]; | |
826 | } | |
827 | break; | |
828 | default: | |
829 | if (size == 4) { | |
830 | val = nvic_readl(s, offset); | |
831 | } else { | |
832 | qemu_log_mask(LOG_GUEST_ERROR, | |
833 | "NVIC: Bad read of size %d at offset 0x%x\n", | |
834 | size, offset); | |
835 | val = 0; | |
2a29ddee | 836 | } |
2a29ddee | 837 | } |
da6d674e MD |
838 | |
839 | trace_nvic_sysreg_read(addr, val, size); | |
840 | return val; | |
2a29ddee PM |
841 | } |
842 | ||
a8170e5e | 843 | static void nvic_sysreg_write(void *opaque, hwaddr addr, |
2a29ddee PM |
844 | uint64_t value, unsigned size) |
845 | { | |
f797c075 | 846 | NVICState *s = (NVICState *)opaque; |
2a29ddee | 847 | uint32_t offset = addr; |
da6d674e MD |
848 | unsigned i, startvec, end; |
849 | unsigned setval = 0; | |
850 | ||
851 | trace_nvic_sysreg_write(addr, value, size); | |
0e8153dd AB |
852 | |
853 | switch (offset) { | |
da6d674e MD |
854 | case 0x100 ... 0x13f: /* NVIC Set enable */ |
855 | offset += 0x80; | |
856 | setval = 1; | |
857 | /* fall through */ | |
858 | case 0x180 ... 0x1bf: /* NVIC Clear enable */ | |
859 | startvec = 8 * (offset - 0x180) + NVIC_FIRST_IRQ; | |
860 | ||
861 | for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) { | |
862 | if (value & (1 << i)) { | |
863 | s->vectors[startvec + i].enabled = setval; | |
864 | } | |
865 | } | |
866 | nvic_irq_update(s); | |
867 | return; | |
868 | case 0x200 ... 0x23f: /* NVIC Set pend */ | |
869 | /* the special logic in armv7m_nvic_set_pending() | |
870 | * is not needed since IRQs are never escalated | |
871 | */ | |
872 | offset += 0x80; | |
873 | setval = 1; | |
874 | /* fall through */ | |
875 | case 0x280 ... 0x2bf: /* NVIC Clear pend */ | |
876 | startvec = 8 * (offset - 0x280) + NVIC_FIRST_IRQ; /* vector # */ | |
877 | ||
878 | for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) { | |
879 | if (value & (1 << i)) { | |
880 | s->vectors[startvec + i].pending = setval; | |
881 | } | |
882 | } | |
883 | nvic_irq_update(s); | |
884 | return; | |
885 | case 0x300 ... 0x33f: /* NVIC Active */ | |
886 | return; /* R/O */ | |
887 | case 0x400 ... 0x5ef: /* NVIC Priority */ | |
888 | startvec = 8 * (offset - 0x400) + NVIC_FIRST_IRQ; /* vector # */ | |
889 | ||
890 | for (i = 0; i < size && startvec + i < s->num_irq; i++) { | |
891 | set_prio(s, startvec + i, (value >> (i * 8)) & 0xff); | |
892 | } | |
893 | nvic_irq_update(s); | |
894 | return; | |
0e8153dd AB |
895 | case 0xd18 ... 0xd23: /* System Handler Priority. */ |
896 | for (i = 0; i < size; i++) { | |
da6d674e MD |
897 | unsigned hdlidx = (offset - 0xd14) + i; |
898 | set_prio(s, hdlidx, (value >> (i * 8)) & 0xff); | |
0e8153dd | 899 | } |
da6d674e | 900 | nvic_irq_update(s); |
0e8153dd AB |
901 | return; |
902 | } | |
2a29ddee | 903 | if (size == 4) { |
0e8153dd | 904 | nvic_writel(s, offset, value); |
2a29ddee PM |
905 | return; |
906 | } | |
e72e3ffc PM |
907 | qemu_log_mask(LOG_GUEST_ERROR, |
908 | "NVIC: Bad write of size %d at offset 0x%x\n", size, offset); | |
2a29ddee PM |
909 | } |
910 | ||
911 | static const MemoryRegionOps nvic_sysreg_ops = { | |
912 | .read = nvic_sysreg_read, | |
913 | .write = nvic_sysreg_write, | |
914 | .endianness = DEVICE_NATIVE_ENDIAN, | |
915 | }; | |
916 | ||
da6d674e MD |
917 | static int nvic_post_load(void *opaque, int version_id) |
918 | { | |
919 | NVICState *s = opaque; | |
920 | unsigned i; | |
921 | ||
922 | /* Check for out of range priority settings */ | |
923 | if (s->vectors[ARMV7M_EXCP_RESET].prio != -3 || | |
924 | s->vectors[ARMV7M_EXCP_NMI].prio != -2 || | |
925 | s->vectors[ARMV7M_EXCP_HARD].prio != -1) { | |
926 | return 1; | |
927 | } | |
928 | for (i = ARMV7M_EXCP_MEM; i < s->num_irq; i++) { | |
929 | if (s->vectors[i].prio & ~0xff) { | |
930 | return 1; | |
931 | } | |
932 | } | |
933 | ||
934 | nvic_recompute_state(s); | |
935 | ||
936 | return 0; | |
937 | } | |
938 | ||
939 | static const VMStateDescription vmstate_VecInfo = { | |
940 | .name = "armv7m_nvic_info", | |
941 | .version_id = 1, | |
942 | .minimum_version_id = 1, | |
943 | .fields = (VMStateField[]) { | |
944 | VMSTATE_INT16(prio, VecInfo), | |
945 | VMSTATE_UINT8(enabled, VecInfo), | |
946 | VMSTATE_UINT8(pending, VecInfo), | |
947 | VMSTATE_UINT8(active, VecInfo), | |
948 | VMSTATE_UINT8(level, VecInfo), | |
949 | VMSTATE_END_OF_LIST() | |
950 | } | |
951 | }; | |
952 | ||
0797226c JQ |
953 | static const VMStateDescription vmstate_nvic = { |
954 | .name = "armv7m_nvic", | |
da6d674e MD |
955 | .version_id = 3, |
956 | .minimum_version_id = 3, | |
957 | .post_load = &nvic_post_load, | |
8f1e884b | 958 | .fields = (VMStateField[]) { |
da6d674e MD |
959 | VMSTATE_STRUCT_ARRAY(vectors, NVICState, NVIC_MAX_VECTORS, 1, |
960 | vmstate_VecInfo, VecInfo), | |
f797c075 PM |
961 | VMSTATE_UINT32(systick.control, NVICState), |
962 | VMSTATE_UINT32(systick.reload, NVICState), | |
963 | VMSTATE_INT64(systick.tick, NVICState), | |
964 | VMSTATE_TIMER_PTR(systick.timer, NVICState), | |
1004102a | 965 | VMSTATE_UINT32(prigroup, NVICState), |
0797226c JQ |
966 | VMSTATE_END_OF_LIST() |
967 | } | |
968 | }; | |
23e39294 | 969 | |
da6d674e MD |
970 | static Property props_nvic[] = { |
971 | /* Number of external IRQ lines (so excluding the 16 internal exceptions) */ | |
972 | DEFINE_PROP_UINT32("num-irq", NVICState, num_irq, 64), | |
973 | DEFINE_PROP_END_OF_LIST() | |
974 | }; | |
975 | ||
aecff692 PM |
976 | static void armv7m_nvic_reset(DeviceState *dev) |
977 | { | |
f797c075 | 978 | NVICState *s = NVIC(dev); |
da6d674e MD |
979 | |
980 | s->vectors[ARMV7M_EXCP_NMI].enabled = 1; | |
981 | s->vectors[ARMV7M_EXCP_HARD].enabled = 1; | |
982 | /* MEM, BUS, and USAGE are enabled through | |
983 | * the System Handler Control register | |
b3387ede | 984 | */ |
da6d674e MD |
985 | s->vectors[ARMV7M_EXCP_SVC].enabled = 1; |
986 | s->vectors[ARMV7M_EXCP_DEBUG].enabled = 1; | |
987 | s->vectors[ARMV7M_EXCP_PENDSV].enabled = 1; | |
988 | s->vectors[ARMV7M_EXCP_SYSTICK].enabled = 1; | |
989 | ||
990 | s->vectors[ARMV7M_EXCP_RESET].prio = -3; | |
991 | s->vectors[ARMV7M_EXCP_NMI].prio = -2; | |
992 | s->vectors[ARMV7M_EXCP_HARD].prio = -1; | |
993 | ||
994 | /* Strictly speaking the reset handler should be enabled. | |
995 | * However, we don't simulate soft resets through the NVIC, | |
996 | * and the reset vector should never be pended. | |
997 | * So we leave it disabled to catch logic errors. | |
998 | */ | |
999 | ||
1000 | s->exception_prio = NVIC_NOEXC_PRIO; | |
1001 | s->vectpending = 0; | |
1002 | ||
aecff692 PM |
1003 | systick_reset(s); |
1004 | } | |
1005 | ||
53111180 | 1006 | static void armv7m_nvic_realize(DeviceState *dev, Error **errp) |
9ee6e8bb | 1007 | { |
f797c075 | 1008 | NVICState *s = NVIC(dev); |
9ee6e8bb | 1009 | |
d713ea6c MD |
1010 | s->cpu = ARM_CPU(qemu_get_cpu(0)); |
1011 | assert(s->cpu); | |
da6d674e MD |
1012 | |
1013 | if (s->num_irq > NVIC_MAX_IRQ) { | |
1014 | error_setg(errp, "num-irq %d exceeds NVIC maximum", s->num_irq); | |
53111180 PM |
1015 | return; |
1016 | } | |
da6d674e MD |
1017 | |
1018 | qdev_init_gpio_in(dev, set_irq_level, s->num_irq); | |
1019 | ||
1020 | /* include space for internal exception vectors */ | |
1021 | s->num_irq += NVIC_FIRST_IRQ; | |
1022 | ||
1023 | /* The NVIC and System Control Space (SCS) starts at 0xe000e000 | |
1024 | * and looks like this: | |
1025 | * 0x004 - ICTR | |
1026 | * 0x010 - 0x1c - systick | |
1027 | * 0x100..0x7ec - NVIC | |
1028 | * 0x7f0..0xcff - Reserved | |
1029 | * 0xd00..0xd3c - SCS registers | |
1030 | * 0xd40..0xeff - Reserved or Not implemented | |
1031 | * 0xf00 - STIR | |
1032 | * | |
1033 | * At the moment there is only one thing in the container region, | |
1034 | * but we leave it in place to allow us to pull systick out into | |
1035 | * its own device object later. | |
2a29ddee | 1036 | */ |
1437c94b | 1037 | memory_region_init(&s->container, OBJECT(s), "nvic", 0x1000); |
2a29ddee PM |
1038 | /* The system register region goes at the bottom of the priority |
1039 | * stack as it covers the whole page. | |
1040 | */ | |
1437c94b | 1041 | memory_region_init_io(&s->sysregmem, OBJECT(s), &nvic_sysreg_ops, s, |
2a29ddee PM |
1042 | "nvic_sysregs", 0x1000); |
1043 | memory_region_add_subregion(&s->container, 0, &s->sysregmem); | |
da6d674e | 1044 | |
98957a94 PM |
1045 | sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->container); |
1046 | ||
bc72ad67 | 1047 | s->systick.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, systick_timer_tick, s); |
9ee6e8bb | 1048 | } |
fe7e8758 | 1049 | |
55e00a19 PM |
1050 | static void armv7m_nvic_instance_init(Object *obj) |
1051 | { | |
1052 | /* We have a different default value for the num-irq property | |
1053 | * than our superclass. This function runs after qdev init | |
1054 | * has set the defaults from the Property array and before | |
1055 | * any user-specified property setting, so just modify the | |
fae15286 | 1056 | * value in the GICState struct. |
55e00a19 | 1057 | */ |
e192becd | 1058 | DeviceState *dev = DEVICE(obj); |
f797c075 | 1059 | NVICState *nvic = NVIC(obj); |
da6d674e MD |
1060 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); |
1061 | ||
1062 | sysbus_init_irq(sbd, &nvic->excpout); | |
e192becd | 1063 | qdev_init_gpio_out_named(dev, &nvic->sysresetreq, "SYSRESETREQ", 1); |
55e00a19 | 1064 | } |
39bffca2 | 1065 | |
999e12bb AL |
1066 | static void armv7m_nvic_class_init(ObjectClass *klass, void *data) |
1067 | { | |
39bffca2 | 1068 | DeviceClass *dc = DEVICE_CLASS(klass); |
999e12bb | 1069 | |
39bffca2 | 1070 | dc->vmsd = &vmstate_nvic; |
da6d674e | 1071 | dc->props = props_nvic; |
aecff692 | 1072 | dc->reset = armv7m_nvic_reset; |
53111180 | 1073 | dc->realize = armv7m_nvic_realize; |
999e12bb AL |
1074 | } |
1075 | ||
8c43a6f0 | 1076 | static const TypeInfo armv7m_nvic_info = { |
1e8cae4d | 1077 | .name = TYPE_NVIC, |
da6d674e | 1078 | .parent = TYPE_SYS_BUS_DEVICE, |
55e00a19 | 1079 | .instance_init = armv7m_nvic_instance_init, |
f797c075 | 1080 | .instance_size = sizeof(NVICState), |
39bffca2 | 1081 | .class_init = armv7m_nvic_class_init, |
da6d674e | 1082 | .class_size = sizeof(SysBusDeviceClass), |
a32134aa ML |
1083 | }; |
1084 | ||
83f7d43a | 1085 | static void armv7m_nvic_register_types(void) |
fe7e8758 | 1086 | { |
39bffca2 | 1087 | type_register_static(&armv7m_nvic_info); |
fe7e8758 PB |
1088 | } |
1089 | ||
83f7d43a | 1090 | type_init(armv7m_nvic_register_types) |