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