xref: /minix3/minix/kernel/arch/earm/mpx.S (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1*433d6423SLionel Sambuc/* This file is part of the lowest layer of the MINIX kernel.  (The other part
2*433d6423SLionel Sambuc * is "proc.c".)  The lowest layer does process switching and message handling.
3*433d6423SLionel Sambuc *
4*433d6423SLionel Sambuc * Kernel is entered either because of kernel-calls, ipc-calls, interrupts or
5*433d6423SLionel Sambuc * exceptions. TSS is set so that the kernel stack is loaded. The user context is
6*433d6423SLionel Sambuc * saved to the proc table and the handler of the event is called. Once the
7*433d6423SLionel Sambuc * handler is done, switch_to_user() function is called to pick a new process,
8*433d6423SLionel Sambuc * finish what needs to be done for the next process to run, sets its context
9*433d6423SLionel Sambuc * and switch to userspace.
10*433d6423SLionel Sambuc */
11*433d6423SLionel Sambuc
12*433d6423SLionel Sambuc#include "kernel/kernel.h" /* configures the kernel */
13*433d6423SLionel Sambuc
14*433d6423SLionel Sambuc/* sections */
15*433d6423SLionel Sambuc
16*433d6423SLionel Sambuc#include <machine/vm.h>
17*433d6423SLionel Sambuc#include "kernel/kernel.h"
18*433d6423SLionel Sambuc#include <minix/config.h>
19*433d6423SLionel Sambuc#include <minix/const.h>
20*433d6423SLionel Sambuc#include <minix/com.h>
21*433d6423SLionel Sambuc#include <machine/asm.h>
22*433d6423SLionel Sambuc#include <machine/interrupt.h>
23*433d6423SLionel Sambuc#include "archconst.h"
24*433d6423SLionel Sambuc#include "kernel/const.h"
25*433d6423SLionel Sambuc#include "kernel/proc.h"
26*433d6423SLionel Sambuc#include "sconst.h"
27*433d6423SLionel Sambuc#include <machine/multiboot.h>
28*433d6423SLionel Sambuc#include <machine/ipcconst.h>
29*433d6423SLionel Sambuc#include <machine/cpu.h>
30*433d6423SLionel Sambuc#include <arm/armreg.h>
31*433d6423SLionel Sambuc#include "bsp_intr.h"
32*433d6423SLionel Sambuc
33*433d6423SLionel Sambuc#include "arch_proto.h" /* K_STACK_SIZE */
34*433d6423SLionel Sambuc
35*433d6423SLionel SambucIMPORT(svc_stack)
36*433d6423SLionel Sambuc
37*433d6423SLionel Sambuc/*
38*433d6423SLionel Sambuc * Adjust lr, push pc/psr when exception triggered and switch to SVC mode
39*433d6423SLionel Sambuc * The 'lr_offset' argument holds the adjustment.
40*433d6423SLionel Sambuc *
41*433d6423SLionel Sambuc * When an instruction causes the ARM core to enter the exception handler
42*433d6423SLionel Sambuc * the value of pc is stored in the link register (lr). By default on ARM
43*433d6423SLionel Sambuc * the program counter is 3 instruction a head of the current instruction
44*433d6423SLionel Sambuc * being executed (because of the 3 stage pipeline). Depending on where in
45*433d6423SLionel Sambuc * the pipeline the exception happens lr will need to de adjusted to find
46*433d6423SLionel Sambuc * the proper return address.
47*433d6423SLionel Sambuc */
48*433d6423SLionel Sambuc.macro switch_to_svc lr_offset
49*433d6423SLionel Sambuc	sub	lr, lr, #\lr_offset	/* do the adjustment */
50*433d6423SLionel Sambuc	srsdb	sp!, #PSR_SVC32_MODE	/* store the saved the return */
51*433d6423SLionel Sambuc					/* address and program status */
52*433d6423SLionel Sambuc					/* register onto the kernel stack */
53*433d6423SLionel Sambuc					/* Also modify the stack pointer. */
54*433d6423SLionel Sambuc	cps	#PSR_SVC32_MODE		/* do the switch to SVC. */
55*433d6423SLionel Sambuc.endm
56*433d6423SLionel Sambuc
57*433d6423SLionel Sambuc/*
58*433d6423SLionel Sambuc * Test if the exception/interrupt occurred in the kernel.
59*433d6423SLionel Sambuc * Jump to 'label' argument if it occurred in the kernel.
60*433d6423SLionel Sambuc *
61*433d6423SLionel Sambuc * NOTE: switch_to_svc must be called first */
62*433d6423SLionel Sambuc.macro test_int_in_kernel, label
63*433d6423SLionel Sambuc	push	{r3}
64*433d6423SLionel Sambuc	ldr	r3, [sp, #8] 			/* get spsr. */
65*433d6423SLionel Sambuc	orr	r3, r3, #(PSR_F | PSR_I)	/* mask interrupts on return. */
66*433d6423SLionel Sambuc	str	r3, [sp, #8]			/* store spsr. */
67*433d6423SLionel Sambuc	and	r3, r3, #PSR_MODE		/* mask the ARM mode. */
68*433d6423SLionel Sambuc	cmp	r3, #PSR_USR32_MODE		/* compare it to user mode. */
69*433d6423SLionel Sambuc	pop	{r3}
70*433d6423SLionel Sambuc	bne	\label				/* In-kernel handling. */
71*433d6423SLionel Sambuc.endm
72*433d6423SLionel Sambuc
73*433d6423SLionel Sambuc/* Save the register context to the proc structure */
74*433d6423SLionel Sambuc.macro save_process_ctx
75*433d6423SLionel Sambuc	add	sp, sp, #8		/* We expect srsdb pushed cpsr and lr on */
76*433d6423SLionel Sambuc					/* the stack. */
77*433d6423SLionel Sambuc	ldr	lr, [sp]		/* lr = proc_ptr. */
78*433d6423SLionel Sambuc	stm	lr, {r0-r14}^		/* store the user mode registers */
79*433d6423SLionel Sambuc					/* proc_ptr->p_reg.r0-r14 = r0-r14. */
80*433d6423SLionel Sambuc	ldr	r12, [sp, #-8]		/* r12 = pc stored on the stack. */
81*433d6423SLionel Sambuc	str	r12, [lr, #PCREG]	/* proc_ptr->p_reg.pc = r12. */
82*433d6423SLionel Sambuc	ldr	r12, [sp, #-4]		/* r12 = cpsr stored on the stack. */
83*433d6423SLionel Sambuc	str	r12, [lr, #PSREG]	/* proc_ptr->p_reg.psr = r12. */
84*433d6423SLionel Sambuc.endm
85*433d6423SLionel Sambuc
86*433d6423SLionel Sambuc.macro exception_handler exc_name, exc_num, lr_offset
87*433d6423SLionel SambucENTRY(\exc_name\()_entry)
88*433d6423SLionel Sambuc	switch_to_svc \lr_offset
89*433d6423SLionel Sambuc	test_int_in_kernel \exc_name\()_entry_nested
90*433d6423SLionel Sambuc
91*433d6423SLionel Sambuc\exc_name\()entry_from_user:
92*433d6423SLionel Sambuc	save_process_ctx
93*433d6423SLionel Sambuc
94*433d6423SLionel Sambuc	ldr	fp, [sp]	/* save the pointer to the current process. */
95*433d6423SLionel Sambuc	add	r4, fp, #PCREG	/* save the exception pc (saved lr_user) */
96*433d6423SLionel Sambuc				/* r4-r9 are callee save. */
97*433d6423SLionel Sambuc
98*433d6423SLionel Sambuc	/* stop user process cycles */
99*433d6423SLionel Sambuc	mov	r0, fp		/* first param: caller proc ptr. */
100*433d6423SLionel Sambuc	mov	fp, #0		/* for stack trace. */
101*433d6423SLionel Sambuc	bl	_C_LABEL(context_stop)
102*433d6423SLionel Sambuc
103*433d6423SLionel Sambuc	/*
104*433d6423SLionel Sambuc	 * push a pointer to the interrupt state pushed by the cpu and the
105*433d6423SLionel Sambuc	 * vector number pushed by the vector handler just before calling
106*433d6423SLionel Sambuc	 * exception_entry and call the exception handler.
107*433d6423SLionel Sambuc	 */
108*433d6423SLionel Sambuc	mov	r0, #0		/* it is not a nested exception. */
109*433d6423SLionel Sambuc	mov	r1, r4		/* saved lr. */
110*433d6423SLionel Sambuc	mov 	r2, #\exc_num	/* vector number */
111*433d6423SLionel Sambuc	bl 	_C_LABEL(exception_handler)
112*433d6423SLionel Sambuc	b	_C_LABEL(switch_to_user)
113*433d6423SLionel Sambuc
114*433d6423SLionel Sambuc\exc_name\()_entry_nested:
115*433d6423SLionel Sambuc	push	{r0-r12, lr}
116*433d6423SLionel Sambuc	mov	r0, #1		/* it is a nested exception. */
117*433d6423SLionel Sambuc	add	r1, sp, #56	/* saved lr */
118*433d6423SLionel Sambuc	mov	r2, #\exc_num	/* vector number */
119*433d6423SLionel Sambuc	bl	_C_LABEL(exception_handler)
120*433d6423SLionel Sambuc	pop	{r0-r12, lr}
121*433d6423SLionel Sambuc	rfeia	sp!
122*433d6423SLionel Sambuc.endm
123*433d6423SLionel Sambuc
124*433d6423SLionel Sambuc
125*433d6423SLionel Sambuc/* Exception handlers */
126*433d6423SLionel Sambucexception_handler data_abort DATA_ABORT_VECTOR 8
127*433d6423SLionel Sambucexception_handler prefetch_abort PREFETCH_ABORT_VECTOR 4
128*433d6423SLionel Sambucexception_handler undefined_inst UNDEFINED_INST_VECTOR 4
129*433d6423SLionel Sambuc
130*433d6423SLionel Sambuc
131*433d6423SLionel SambucENTRY(irq_entry)
132*433d6423SLionel Sambuc	switch_to_svc 4
133*433d6423SLionel Sambuc	test_int_in_kernel irq_entry_from_kernel
134*433d6423SLionel Sambuc
135*433d6423SLionel Sambucirq_entry_from_user:
136*433d6423SLionel Sambuc	save_process_ctx
137*433d6423SLionel Sambuc
138*433d6423SLionel Sambuc	/* save the pointer to the current process */
139*433d6423SLionel Sambuc	ldr	fp, [sp]
140*433d6423SLionel Sambuc
141*433d6423SLionel Sambuc	push	{fp}				/* save caller proc ptr. */
142*433d6423SLionel Sambuc	sub	sp, sp, #4			/* maintain stack alignment. */
143*433d6423SLionel Sambuc
144*433d6423SLionel Sambuc	/* stop user process cycles */
145*433d6423SLionel Sambuc	mov	r0, fp  			/* first param: caller proc ptr. */
146*433d6423SLionel Sambuc	mov	fp, #0				/* for stack trace. */
147*433d6423SLionel Sambuc	bl	_C_LABEL(context_stop)
148*433d6423SLionel Sambuc
149*433d6423SLionel Sambuc	/* call handler */
150*433d6423SLionel Sambuc	bl	_C_LABEL(bsp_irq_handle)	/* bsp_irq_handle(void) */
151*433d6423SLionel Sambuc
152*433d6423SLionel Sambuc	add	sp, sp, #4
153*433d6423SLionel Sambuc	pop	{fp}				/* caller proc ptr. */
154*433d6423SLionel Sambuc	dsb					/* data synchronization barrier. */
155*433d6423SLionel Sambuc
156*433d6423SLionel Sambuc	b	_C_LABEL(switch_to_user)
157*433d6423SLionel Sambuc
158*433d6423SLionel Sambucirq_entry_from_kernel:
159*433d6423SLionel Sambuc	push	{r0-r12, lr}
160*433d6423SLionel Sambuc	bl	_C_LABEL(context_stop_idle)
161*433d6423SLionel Sambuc
162*433d6423SLionel Sambuc	/* call handler */
163*433d6423SLionel Sambuc	bl	_C_LABEL(bsp_irq_handle)	/* bsp_irq_handle(void). */
164*433d6423SLionel Sambuc
165*433d6423SLionel Sambuc	/* data synchronization barrier */ dsb
166*433d6423SLionel Sambuc	pop	{r0-r12, lr}
167*433d6423SLionel Sambuc	rfeia	sp!
168*433d6423SLionel Sambuc
169*433d6423SLionel Sambuc
170*433d6423SLionel Sambuc/*
171*433d6423SLionel Sambuc * supervisor call (SVC) kernel entry point
172*433d6423SLionel Sambuc */
173*433d6423SLionel SambucENTRY(svc_entry)
174*433d6423SLionel Sambuc	/*  Store the LR and the SPSR of the current mode onto the SVC stack */
175*433d6423SLionel Sambuc	srsdb	sp!, #PSR_SVC32_MODE
176*433d6423SLionel Sambuc	save_process_ctx
177*433d6423SLionel Sambuc
178*433d6423SLionel Sambuc	/* save the pointer to the current process */
179*433d6423SLionel Sambuc	ldr	fp, [sp]
180*433d6423SLionel Sambuc
181*433d6423SLionel Sambuc	cmp	r3, #KERVEC_INTR
182*433d6423SLionel Sambuc	beq	kernel_call_entry
183*433d6423SLionel Sambuc	cmp	r3, #IPCVEC_INTR
184*433d6423SLionel Sambuc	beq	ipc_entry
185*433d6423SLionel Sambuc
186*433d6423SLionel Sambuc	/* return -1 to the current process as an invalid SWI was called .*/
187*433d6423SLionel Sambuc	mov	r0, #-1
188*433d6423SLionel Sambuc	str	r0, [fp, #REG0]
189*433d6423SLionel Sambuc	b	_C_LABEL(switch_to_user)
190*433d6423SLionel Sambuc
191*433d6423SLionel Sambuc/*
192*433d6423SLionel Sambuc * kernel call is only from a process to kernel
193*433d6423SLionel Sambuc */
194*433d6423SLionel SambucENTRY(kernel_call_entry)
195*433d6423SLionel Sambuc	/*
196*433d6423SLionel Sambuc	 * pass the syscall arguments from userspace to the handler.
197*433d6423SLionel Sambuc	 * save_process_ctx() does not clobber these registers, they are still
198*433d6423SLionel Sambuc	 * set as the userspace has set them.
199*433d6423SLionel Sambuc	 */
200*433d6423SLionel Sambuc	push	{fp}			/* save caller proc ptr. */
201*433d6423SLionel Sambuc	push	{r0}			/* save msg ptr so it's not clobbered. */
202*433d6423SLionel Sambuc
203*433d6423SLionel Sambuc	/* stop user process cycles */
204*433d6423SLionel Sambuc	mov	r0, fp			/* first param: caller proc ptr */
205*433d6423SLionel Sambuc	mov	fp, #0			/* for stack trace */
206*433d6423SLionel Sambuc	bl	_C_LABEL(context_stop)
207*433d6423SLionel Sambuc
208*433d6423SLionel Sambuc	pop	{r0} 			/* first param: msg ptr. */
209*433d6423SLionel Sambuc	pop	{r1} 			/* second param: caller proc ptr. */
210*433d6423SLionel Sambuc	bl	_C_LABEL(kernel_call)
211*433d6423SLionel Sambuc
212*433d6423SLionel Sambuc	b	_C_LABEL(switch_to_user)
213*433d6423SLionel Sambuc
214*433d6423SLionel Sambuc/*
215*433d6423SLionel Sambuc * IPC is only from a process to kernel
216*433d6423SLionel Sambuc */
217*433d6423SLionel SambucENTRY(ipc_entry)
218*433d6423SLionel Sambuc	/*
219*433d6423SLionel Sambuc	 * pass the syscall arguments from userspace to the handler.
220*433d6423SLionel Sambuc	 * save_process_ctx() does not clobber these registers, they are still
221*433d6423SLionel Sambuc	 * set as the userspace have set them
222*433d6423SLionel Sambuc	 */
223*433d6423SLionel Sambuc	push	{fp}			/* save caller proc ptr. */
224*433d6423SLionel Sambuc	push	{r0-r2}			/* save regs so they're not clobbered. */
225*433d6423SLionel Sambuc
226*433d6423SLionel Sambuc	/* stop user process cycles */
227*433d6423SLionel Sambuc	mov	r0, fp  		/* first param: caller proc ptr. */
228*433d6423SLionel Sambuc	mov	fp, #0  		/* for stack trace. */
229*433d6423SLionel Sambuc	bl	_C_LABEL(context_stop)
230*433d6423SLionel Sambuc
231*433d6423SLionel Sambuc	pop	{r0-r2} /* restore regs */
232*433d6423SLionel Sambuc	bl	_C_LABEL(do_ipc)
233*433d6423SLionel Sambuc
234*433d6423SLionel Sambuc	/* restore the current process pointer and save the return value */
235*433d6423SLionel Sambuc	pop	{fp}			/* caller proc ptr. */
236*433d6423SLionel Sambuc	str	r0, [fp, #REG0]
237*433d6423SLionel Sambuc
238*433d6423SLionel Sambuc	b	_C_LABEL(switch_to_user)
239*433d6423SLionel Sambuc
240*433d6423SLionel SambucENTRY(invalid_svc)
241*433d6423SLionel Sambuc	b	.
242*433d6423SLionel Sambuc
243*433d6423SLionel SambucENTRY(restore_user_context)
244*433d6423SLionel Sambuc	/* sp holds the proc ptr */
245*433d6423SLionel Sambuc	mov sp, r0
246*433d6423SLionel Sambuc
247*433d6423SLionel Sambuc	/* Set SPSR and LR for return */
248*433d6423SLionel Sambuc	ldr r0, [sp, #PSREG]
249*433d6423SLionel Sambuc	msr spsr_fsxc, r0  		/* flags , status, extension control. */
250*433d6423SLionel Sambuc	ldr lr, [sp, #PCREG]
251*433d6423SLionel Sambuc
252*433d6423SLionel Sambuc	/* Restore user-mode registers from proc struct */
253*433d6423SLionel Sambuc	ldm sp, {r0-r14}^
254*433d6423SLionel Sambuc
255*433d6423SLionel Sambuc	ldr sp, =_C_LABEL(svc_stack)
256*433d6423SLionel Sambuc	ldr sp, [sp]
257*433d6423SLionel Sambuc
258*433d6423SLionel Sambuc	/* To user mode! */
259*433d6423SLionel Sambuc	movs pc, lr		/* preferred way of returning from svc */
260*433d6423SLionel Sambuc
261*433d6423SLionel Sambuc/*===========================================================================*/
262*433d6423SLionel Sambuc/*				data					     */
263*433d6423SLionel Sambuc/*===========================================================================*/
264*433d6423SLionel Sambuc
265*433d6423SLionel Sambuc.data
266*433d6423SLionel Sambuc.short	0x526F	/* this must be the first data entry (magic #) */
267*433d6423SLionel Sambuc.bss
268*433d6423SLionel Sambuc.data
269*433d6423SLionel Sambuc.balign 4
270*433d6423SLionel Sambuck_initial_stack:
271*433d6423SLionel Sambuc.space	K_STACK_SIZE
272*433d6423SLionel SambucLABEL(__k_unpaged_k_initial_stktop)
273*433d6423SLionel Sambuc
274*433d6423SLionel Sambuc/*
275*433d6423SLionel Sambuc * the kernel stack
276*433d6423SLionel Sambuc */
277*433d6423SLionel Sambuck_boot_stack:
278*433d6423SLionel Sambuc.space	K_STACK_SIZE	/* kernel stack */ /* FIXME use macro here */
279*433d6423SLionel SambucLABEL(k_boot_stktop)	/* top of kernel stack */
280*433d6423SLionel Sambuc
281*433d6423SLionel Sambuc.balign K_STACK_SIZE
282*433d6423SLionel SambucLABEL(k_stacks_start)
283*433d6423SLionel Sambuc
284*433d6423SLionel Sambuc/* two pages for each stack, one for data, other as a sandbox */
285*433d6423SLionel Sambuc.space	2 * (K_STACK_SIZE * CONFIG_MAX_CPUS)
286*433d6423SLionel Sambuc
287*433d6423SLionel SambucLABEL(k_stacks_end)
288*433d6423SLionel Sambuc
289*433d6423SLionel Sambuc/* top of kernel stack */
290