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: