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