Time to write a code to process interrupts. All interrupts that happens in system use the vector of interrupts (8 of them) which is located at 0xffff0000 (high memory case). This vector contains addresses that CPU should pass execution to.
1. | +00 | Reset |
2. | +04 | Undefined |
3. | +08 | SWI |
4. | +0C | Prefetch abort |
5. | +10 | Data abort |
6. | +14 | Reserved |
7. | +18 | IRQ |
8. | +1C | FIQ |
In main() we have to call function called trapinit() to do all initializations of interrupts handling.
Example: when interrupt happens – say IRQ (number 7), then cpu will pass execution to 0xffff0000+18. Our task is to initialize that place with codes that will pass execution to specific calls in kernel.
It is done in next way: in assembler file intr.s we create two functions:
1TEXT vectors(SB), $-4
2 MOVW 0x18(PC), PC /* reset */
3 MOVW 0x18(PC), PC /* undefined */
4 MOVW 0x18(PC), PC /* SWI */
5 MOVW 0x18(PC), PC /* prefetch abort */
6 MOVW 0x18(PC), PC /* data abort */
7 MOVW 0x18(PC), PC /* reserved */
8 MOVW 0x18(PC), PC /* IRQ */
9 MOVW 0x18(PC), PC /* FIQ */
10
11TEXT vtable(SB), $-4
12 WORD $_vsvc(SB) /* reset, in svc mode already */
13 WORD $_vund(SB) /* undefined, switch to svc mode */
14 WORD $_vsvc(SB) /* swi, in svc mode already */
15 WORD $_vpab(SB) /* prefetch abort, switch to svc mode */
16 WORD $_vdab(SB) /* data abort, switch to svc mode */
17 WORD $_vsvc(SB) /* reserved */
18 WORD $_virq(SB) /* IRQ, switch to svc mode */
19 WORD $_vfiq(SB) /* FIQ, switch to svc mode */
20
21TEXT _vund(SB), $-4
22 ...
Then in trapinit() we copy bytes of these functions to 0xffff0000:
1enum { Nvec = 8 }; /* # of vectors */
2typedef struct Vpage0 {
3 void (*vectors[Nvec])(void);
4 u32int vtable[Nvec];
5} Vpage0;
6
7void trapinit(void) {
8 Vpage0 *vpage0;
9 /* set up the exception vectors */
10 vpage0 = (Vpage0*)HVECTORS;
11 memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors));
12 memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable));
13 ...
You may see that when execution passed to 0xffff0000+18 (HVECTORS+IRQ), then cpu does MOVW 0x18(PC), PC, which means to jump to address which is located in memory just +0x18 above – where the vtable with addresses of our kernel routines _virq()
Then our 8 assembler routines to do initial logic of handling interrupts:
1TEXT _vund(SB), $-4
2 MOVM.DB [R0-R3], (SP)
3 MOVW $PsrMund, R0
4 B _vswitch
5
6TEXT _vsvc(SB), $-4
7 MOVW.W R14, -4(SP)
8 MOVW CPSR, R14
9 MOVW.W R14, -4(SP)
10 BIC $PsrMask, R14
11 ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14
12 MOVW R14, CPSR
13 MOVW $PsrMsvc, R14
14 MOVW.W R14, -4(SP)
15 B _vsaveu
16
17TEXT _vpab(SB), $-4
18 MOVM.DB [R0-R3], (R13)
19 MOVW $PsrMabt, R0
20 B _vswitch
21
22TEXT _vdab(SB), $-4
23 MOVM.DB [R0-R3], (R13)
24 MOVW $(PsrMabt+1), R0
25 B _vswitch
26
27TEXT _vfiq(SB), $-4 /* FIQ */
28 MOVM.DB [R0-R3], (R13)
29 MOVW $PsrMfiq, R0
30 B _vswitch
31
32TEXT _virq(SB), $-4 /* IRQ */
33 MOVM.DB [R0-R3], (R13)
34 MOVW $PsrMirq, R0
35
36_vswitch: /* switch to svc mode */
You see that they are using 4 words of stack [R0-R3]. That I will show later in trapinit(), but idea that we can set different stack addresses for each interrupt types – that are those arrays we added to Mach: ulong fiqstack[4]; ulong irqstack[4]; ulong abtstack[4]; ulong undstack[4];
Later it switches to svc mode and cpu gets stack with address which is set for svc mode.
1_vswitch: /* switch to svc mode */
2 MOVW SPSR, R1 /* state of cpu, cpsr */
3 MOVW R14, R2 /* return code */
4 MOVW SP, R3 /* stack */
5
6 MOVW CPSR, R14
7 BIC $PsrMask, R14
8 ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14
9 MOVW R14, CPSR /* switch! */
10
11 MOVW R0, R0 /* gratuitous noop */
12
13 MOVM.DB.W [R0-R2], (SP) /* set ureg->{type, psr, pc}; */
14 /* SP points to ureg->type */
15 MOVW R3, -12(SP)
16 MOVM.IA (R3), [R0-R3] /* restore [R0-R3] from previous mode */
17
18_vsaveu:
19 MOVW.W R11, -4(SP) /* save link */
20
21 SUB $8, SP
22 MOVM.DB.W [R0-R12], (SP)
23
24 MOVW $setR12(SB), R12/* Make sure we've got the kernel's SB */
25 MOVW SP, R0 /* first arg is pointer to ureg */
26 SUB $(4*2), SP /* space for argument+link (for debugger) */
27 MOVW $0xdeaddead, R11/* marker */
28 BL trap(SB)
29
30_vrfe: /* Restore Regs */
31 MOVW CPSR, R0 /* splhi on return */
32 ORR $(PsrDirq|PsrDfiq), R0, R1
33 MOVW R1, CPSR
34 ADD $(8+4*15), SP /* [r0-R14]+argument+link */
35 MOVW (SP), R14 /* restore link */
36 MOVW 8(SP), R0
37 MOVW R0, SPSR
38 MOVM.DB.S (SP), [R0-R14] /* restore user registers */
39 MOVW R0, R0 /* gratuitous nop */
40 ADD $12, SP /* skip saved link+type+SPSR*/
41 RFE /* MOVM.IA.S.W (SP), [PC] */
In codes above we can use R0-R3 as we saved then in Mach object. With 4 registers we can perform disabling interrupts (svc), next CPU state saving into Ureg object, adjusting SB and finally pass the control to trap() C-routine of kernel to process the exception.
On return from trap() – _vrfe, it does opposite, restore registres from Ureg object, enables interrupts and pass control back to the point where execution was interrupted.
FILES: