xref: /minix3/minix/kernel/arch/i386/mpx.S (revision c9278f91709f7b7161c6dc1671806d6d16771556)
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 * Furthermore it contains the assembler startup code for Minix and the 32-bit
4 * interrupt handlers.  It cooperates with the code in "start.c" to set up a
5 * good environment for main().
6 *
7 * Kernel is entered either because of kernel-calls, ipc-calls, interrupts or
8 * exceptions. TSS is set so that the kernel stack is loaded. The user context is
9 * saved to the proc table and the handler of the event is called. Once the
10 * handler is done, switch_to_user() function is called to pick a new process,
11 * finish what needs to be done for the next process to run, sets its context
12 * and switch to userspace.
13 *
14 * For communication with the boot monitor at startup time some constant
15 * data are compiled into the beginning of the text segment. This facilitates
16 * reading the data at the start of the boot process, since only the first
17 * sector of the file needs to be read.
18 *
19 * Some data storage is also allocated at the end of this file. This data
20 * will be at the start of the data segment of the kernel and will be read
21 * and modified by the boot monitor before the kernel starts.
22 */
23
24#include "kernel/kernel.h" /* configures the kernel */
25
26/* sections */
27
28#include <machine/vm.h>
29#include "kernel/kernel.h"
30#include <minix/config.h>
31#include <minix/const.h>
32#include <minix/ipcconst.h>
33#include <minix/com.h>
34#include <machine/asm.h>
35#include <machine/interrupt.h>
36#include "archconst.h"
37#include "kernel/const.h"
38#include "kernel/proc.h"
39#include "sconst.h"
40#include <machine/multiboot.h>
41
42#include "arch_proto.h" /* K_STACK_SIZE */
43
44#ifdef CONFIG_SMP
45#include "kernel/smp.h"
46#endif
47
48/* Selected 386 tss offsets. */
49#define TSS3_S_SP0	4
50
51IMPORT(usermapped_offset)
52IMPORT(copr_not_available_handler)
53IMPORT(params_size)
54IMPORT(params_offset)
55IMPORT(switch_to_user)
56IMPORT(multiboot_init)
57
58.text
59/*===========================================================================*/
60/*				interrupt handlers			     */
61/*		interrupt handlers for 386 32-bit protected mode	     */
62/*===========================================================================*/
63
64#define PIC_IRQ_HANDLER(irq)	\
65	push	$irq							    	;\
66	call	_C_LABEL(irq_handle)	/* intr_handle(irq_handlers[irq]) */	;\
67	add	$4, %esp						    	;
68
69/*===========================================================================*/
70/*				hwint00 - 07				     */
71/*===========================================================================*/
72/* Note this is a macro, it just looks like a subroutine. */
73
74#define hwint_master(irq) \
75	TEST_INT_IN_KERNEL(4, 0f)					;\
76									\
77	SAVE_PROCESS_CTX(0, KTS_INT_HARD)				;\
78	push	%ebp							;\
79	movl	$0, %ebp	/* for stack trace */			;\
80	call	_C_LABEL(context_stop)					;\
81	add	$4, %esp						;\
82	PIC_IRQ_HANDLER(irq)						;\
83	movb	$END_OF_INT, %al					;\
84	outb	$INT_CTL	/* reenable interrupts in master pic */	;\
85	jmp	_C_LABEL(switch_to_user)				;\
86									\
870:									\
88	pusha								;\
89	call	_C_LABEL(context_stop_idle)				;\
90	PIC_IRQ_HANDLER(irq)						;\
91	movb	$END_OF_INT, %al					;\
92	outb	$INT_CTL	/* reenable interrupts in master pic */	;\
93	CLEAR_IF(10*4(%esp))						;\
94	popa								;\
95	iret								;
96
97/* Each of these entry points is an expansion of the hwint_master macro */
98ENTRY(hwint00)
99/* Interrupt routine for irq 0 (the clock). */
100	hwint_master(0)
101
102ENTRY(hwint01)
103/* Interrupt routine for irq 1 (keyboard) */
104	hwint_master(1)
105
106ENTRY(hwint02)
107/* Interrupt routine for irq 2 (cascade!) */
108	hwint_master(2)
109
110ENTRY(hwint03)
111/* Interrupt routine for irq 3 (second serial) */
112	hwint_master(3)
113
114ENTRY(hwint04)
115/* Interrupt routine for irq 4 (first serial) */
116	hwint_master(4)
117
118ENTRY(hwint05)
119/* Interrupt routine for irq 5 (XT winchester) */
120	hwint_master(5)
121
122ENTRY(hwint06)
123/* Interrupt routine for irq 6 (floppy) */
124	hwint_master(6)
125
126ENTRY(hwint07)
127/* Interrupt routine for irq 7 (printer) */
128	hwint_master(7)
129
130/*===========================================================================*/
131/*				hwint08 - 15				     */
132/*===========================================================================*/
133/* Note this is a macro, it just looks like a subroutine. */
134#define hwint_slave(irq)	\
135	TEST_INT_IN_KERNEL(4, 0f)					;\
136									\
137	SAVE_PROCESS_CTX(0, KTS_INT_HARD)				;\
138	push	%ebp							;\
139	movl	$0, %ebp	/* for stack trace */			;\
140	call	_C_LABEL(context_stop)					;\
141	add	$4, %esp						;\
142	PIC_IRQ_HANDLER(irq)						;\
143	movb	$END_OF_INT, %al					;\
144	outb	$INT_CTL	/* reenable interrupts in master pic */	;\
145	outb	$INT2_CTL	/* reenable slave 8259		  */	;\
146	jmp	_C_LABEL(switch_to_user)				;\
147									\
1480:									\
149	pusha								;\
150	call	_C_LABEL(context_stop_idle)				;\
151	PIC_IRQ_HANDLER(irq)						;\
152	movb	$END_OF_INT, %al					;\
153	outb	$INT_CTL	/* reenable interrupts in master pic */	;\
154	outb	$INT2_CTL	/* reenable slave 8259		  */	;\
155	CLEAR_IF(10*4(%esp))						;\
156	popa								;\
157	iret								;
158
159/* Each of these entry points is an expansion of the hwint_slave macro */
160ENTRY(hwint08)
161/* Interrupt routine for irq 8 (realtime clock) */
162	hwint_slave(8)
163
164ENTRY(hwint09)
165/* Interrupt routine for irq 9 (irq 2 redirected) */
166	hwint_slave(9)
167
168ENTRY(hwint10)
169/* Interrupt routine for irq 10 */
170	hwint_slave(10)
171
172ENTRY(hwint11)
173/* Interrupt routine for irq 11 */
174	hwint_slave(11)
175
176ENTRY(hwint12)
177/* Interrupt routine for irq 12 */
178	hwint_slave(12)
179
180ENTRY(hwint13)
181/* Interrupt routine for irq 13 (FPU exception) */
182	hwint_slave(13)
183
184ENTRY(hwint14)
185/* Interrupt routine for irq 14 (AT winchester) */
186	hwint_slave(14)
187
188ENTRY(hwint15)
189/* Interrupt routine for irq 15 */
190	hwint_slave(15)
191
192/* differences with sysenter:
193 *   - we have to find our own per-cpu stack (i.e. post-SYSCALL
194 *     %esp is not configured)
195 *   - we have to save the post-SYSRET %eip, provided by the cpu
196 *     in %ecx
197 *   - the system call parameters are passed in %ecx, so we userland
198 *     code that executes SYSCALL copies %ecx to %edx. So the roles
199 *     of %ecx and %edx are reversed
200 *   - we can use %esi as a scratch register
201 */
202#define ipc_entry_syscall_percpu(cpu)			;\
203ENTRY(ipc_entry_syscall_cpu ## cpu)			;\
204	xchg	%ecx, %edx				;\
205	mov	k_percpu_stacks+4*cpu, %esi		;\
206	mov	(%esi), %ebp 				;\
207	movl	$KTS_SYSCALL, P_KERN_TRAP_STYLE(%ebp)	;\
208	xchg	%esp, %esi				;\
209	jmp	syscall_sysenter_common
210
211ipc_entry_syscall_percpu(0)
212ipc_entry_syscall_percpu(1)
213ipc_entry_syscall_percpu(2)
214ipc_entry_syscall_percpu(3)
215ipc_entry_syscall_percpu(4)
216ipc_entry_syscall_percpu(5)
217ipc_entry_syscall_percpu(6)
218ipc_entry_syscall_percpu(7)
219
220ENTRY(ipc_entry_sysenter)
221	/* SYSENTER simply sets kernel segments, EIP to here, and ESP
222	 * to tss->sp0 (through MSR). so no automatic context saving is done.
223	 * interrupts are disabled.
224	 *
225	 * register usage:
226	 * edi: call type (IPCVEC, KERVEC)
227	 * ebx, eax, ecx: syscall params, set by userland
228	 * esi, edx: esp, eip to restore, set by userland
229	 *
230	 * no state is automatically saved; userland does all of that.
231	 */
232	mov	(%esp), %ebp /* get proc saved by arch_finish_switch_to_user */
233
234	/* inform kernel we entered by sysenter and should
235	 * therefore exit through restore_user_context_sysenter
236	 */
237	movl	$KTS_SYSENTER, P_KERN_TRAP_STYLE(%ebp)
238	add	usermapped_offset, %edx	/* compensate for mapping difference */
239
240syscall_sysenter_common:
241	mov	%esi, SPREG(%ebp)	/* esi is return esp */
242	mov	%edx, PCREG(%ebp)	/* edx is return eip */
243
244	/* save PSW */
245	pushf
246	pop	%edx
247	mov	%edx, PSWREG(%ebp)
248
249	/* check for call type; do_ipc? */
250	cmp	$IPCVEC_UM, %edi
251	jz	ipc_entry_common
252
253	/* check for kernel trap */
254	cmp	$KERVEC_UM, %edi
255	jz	kernel_call_entry_common
256
257	/* unrecognized call number; restore user with error */
258	movl	$-1, AXREG(%ebp)
259	push	%ebp
260	call	restore_user_context	/* restore_user_context(%ebp); */
261
262/*
263 * IPC is only from a process to kernel
264 */
265ENTRY(ipc_entry_softint_orig)
266	SAVE_PROCESS_CTX(0, KTS_INT_ORIG)
267	jmp ipc_entry_common
268
269ENTRY(ipc_entry_softint_um)
270	SAVE_PROCESS_CTX(0, KTS_INT_UM)
271	jmp ipc_entry_common
272
273ENTRY(ipc_entry_common)
274	/* save the pointer to the current process */
275	push	%ebp
276
277	/*
278	 * pass the syscall arguments from userspace to the handler.
279	 * SAVE_PROCESS_CTX() does not clobber these registers, they are still
280	 * set as the userspace have set them
281	 */
282	push	%ebx
283	push	%eax
284	push	%ecx
285
286	/* stop user process cycles */
287	push	%ebp
288	/* for stack trace */
289	movl	$0, %ebp
290	call	_C_LABEL(context_stop)
291	add	$4, %esp
292
293	call	_C_LABEL(do_ipc)
294
295	/* restore the current process pointer and save the return value */
296	add	$3 * 4, %esp
297	pop	%esi
298	mov     %eax, AXREG(%esi)
299
300	jmp	_C_LABEL(switch_to_user)
301
302
303/*
304 * kernel call is only from a process to kernel
305 */
306ENTRY(kernel_call_entry_orig)
307	SAVE_PROCESS_CTX(0, KTS_INT_ORIG)
308	jmp	kernel_call_entry_common
309
310ENTRY(kernel_call_entry_um)
311	SAVE_PROCESS_CTX(0, KTS_INT_UM)
312	jmp	kernel_call_entry_common
313
314ENTRY(kernel_call_entry_common)
315	/* save the pointer to the current process */
316	push	%ebp
317
318	/*
319	 * pass the syscall arguments from userspace to the handler.
320	 * SAVE_PROCESS_CTX() does not clobber these registers, they are still
321	 * set as the userspace have set them
322	 */
323	push	%eax
324
325	/* stop user process cycles */
326	push	%ebp
327	/* for stack trace */
328	movl	$0, %ebp
329	call	_C_LABEL(context_stop)
330	add	$4, %esp
331
332	call	_C_LABEL(kernel_call)
333
334	/* restore the current process pointer and save the return value */
335	add	$8, %esp
336
337	jmp	_C_LABEL(switch_to_user)
338
339
340.balign 16
341/*
342 * called by the exception interrupt vectors. If the exception does not push
343 * errorcode, we assume that the vector handler pushed 0 instead. Next pushed
344 * thing is the vector number. From this point on we can continue as if every
345 * exception pushes an error code
346 */
347exception_entry:
348	/*
349	 * check if it is a nested trap by comparing the saved code segment
350	 * descriptor with the kernel CS first
351	 */
352	TEST_INT_IN_KERNEL(12, exception_entry_nested)
353
354exception_entry_from_user:
355	SAVE_PROCESS_CTX(8, KTS_INT_HARD)
356
357	/* stop user process cycles */
358	push	%ebp
359	/* for stack trace clear %ebp */
360	movl	$0, %ebp
361	call	_C_LABEL(context_stop)
362	add	$4, %esp
363
364	/*
365	 * push a pointer to the interrupt state pushed by the cpu and the
366	 * vector number pushed by the vector handler just before calling
367	 * exception_entry and call the exception handler.
368	 */
369	push	%esp
370	push	$0	/* it's not a nested exception */
371	call 	_C_LABEL(exception_handler)
372
373	jmp	_C_LABEL(switch_to_user)
374
375exception_entry_nested:
376
377	pusha
378	mov	%esp, %eax
379	add	$(8 * 4), %eax
380	push	%eax
381	pushl	$1			/* it's a nested exception */
382	call	_C_LABEL(exception_handler)
383	add	$8, %esp
384	popa
385
386	/* clear the error code and the exception number */
387	add	$8, %esp
388	/* resume execution at the point of exception */
389	iret
390
391ENTRY(restore_user_context_sysenter)
392	/* return to userspace using sysexit.
393	 * most of the context saving the userspace process is
394	 * responsible for, we just have to take care of the right EIP
395	 * and ESP restoring here to resume execution, and set EAX and
396	 * EBX to the saved status values.
397	 */
398	mov	4(%esp), %ebp		/* retrieve proc ptr arg */
399	movw	$USER_DS_SELECTOR, %ax
400	movw	%ax, %ds
401	mov	PCREG(%ebp), %edx	/* sysexit restores EIP using EDX */
402	mov	SPREG(%ebp), %ecx	/* sysexit restores ESP using ECX */
403	mov	AXREG(%ebp), %eax	/* trap return value */
404	mov	BXREG(%ebp), %ebx	/* secondary return value */
405
406        /* restore PSW */
407        movl    PSWREG(%ebp), %edi      /* load desired PSW to EDI */
408        push    %edi
409        popf
410
411	sti				/* enable interrupts */
412	sysexit				/* jump to EIP in user */
413
414ENTRY(restore_user_context_syscall)
415	/* return to userspace using sysret.
416	 * the procedure is very similar to sysexit; it requires
417	 * manual %esp restoring, new EIP in ECX, does not require
418	 * enabling interrupts, and of course sysret instead of sysexit.
419	 */
420	mov	4(%esp), %ebp		/* retrieve proc ptr arg */
421
422        /* restore PSW (before we switch to user stack!) */
423        movl    PSWREG(%ebp), %edi      /* load desired PSW to EDI */
424        push    %edi
425        popf
426
427	mov	PCREG(%ebp), %ecx	/* sysret restores EIP using ECX */
428	mov	SPREG(%ebp), %esp	/* restore ESP directly */
429	mov	AXREG(%ebp), %eax	/* trap return value */
430	mov	BXREG(%ebp), %ebx	/* secondary return value */
431
432	sysret				/* jump to EIP in user */
433
434ENTRY(restore_user_context_int)
435	mov	4(%esp), %ebp	/* will assume P_STACKBASE == 0 */
436
437	/* reconstruct the stack for iret */
438	push	$USER_DS_SELECTOR	/* ss */
439	movl	SPREG(%ebp), %eax
440	push	%eax
441	movl	PSWREG(%ebp), %eax
442	push	%eax
443	push	$USER_CS_SELECTOR	/* cs */
444	movl	PCREG(%ebp), %eax
445	push	%eax
446
447	/* Restore segments as the user should see them. */
448	movw	$USER_DS_SELECTOR, %si
449        movw    %si, %ds
450        movw    %si, %es
451        movw    %si, %fs
452        movw    %si, %gs
453
454	/* Same for general-purpose registers. */
455	RESTORE_GP_REGS(%ebp)
456
457	movl	BPREG(%ebp), %ebp
458
459	iret	/* continue process */
460
461/*===========================================================================*/
462/*				exception handlers			     */
463/*===========================================================================*/
464
465#define EXCEPTION_ERR_CODE(vector)	\
466	push	$vector			;\
467	jmp	exception_entry
468
469#define EXCEPTION_NO_ERR_CODE(vector)	\
470	pushl	$0		;\
471	EXCEPTION_ERR_CODE(vector)
472
473LABEL(divide_error)
474	EXCEPTION_NO_ERR_CODE(DIVIDE_VECTOR)
475
476LABEL(single_step_exception)
477	EXCEPTION_NO_ERR_CODE(DEBUG_VECTOR)
478
479LABEL(nmi)
480#ifndef USE_WATCHDOG
481	EXCEPTION_NO_ERR_CODE(NMI_VECTOR)
482#else
483	/*
484	 * We have to be very careful as this interrupt can occur anytime. On
485	 * the other hand, if it interrupts a user process, we will resume the
486	 * same process which makes things a little simpler. We know that we are
487	 * already on kernel stack whenever it happened and we can be
488	 * conservative and save everything as we don't need to be extremely
489	 * efficient as the interrupt is infrequent and some overhead is already
490	 * expected.
491	 */
492
493	/*
494	 * save the important registers. We don't save %cs and %ss and they are
495	 * saved and restored by CPU
496	 */
497	pushw	%ds
498	pushw	%es
499	pushw	%fs
500	pushw	%gs
501	pusha
502
503	/*
504	 * We cannot be sure about the state of the kernel segment register,
505	 * however, we always set %ds and %es to the same as %ss
506	 */
507	mov	%ss, %si
508	mov	%si, %ds
509	mov	%si, %es
510
511	push	%esp
512	call	_C_LABEL(nmi_watchdog_handler)
513	add	$4, %esp
514
515	/* restore all the important registers as they were before the trap */
516	popa
517	popw	%gs
518	popw	%fs
519	popw	%es
520	popw	%ds
521
522	iret
523#endif
524
525LABEL(breakpoint_exception)
526	EXCEPTION_NO_ERR_CODE(BREAKPOINT_VECTOR)
527
528LABEL(overflow)
529	EXCEPTION_NO_ERR_CODE(OVERFLOW_VECTOR)
530
531LABEL(bounds_check)
532	EXCEPTION_NO_ERR_CODE(BOUNDS_VECTOR)
533
534LABEL(inval_opcode)
535	EXCEPTION_NO_ERR_CODE(INVAL_OP_VECTOR)
536
537LABEL(copr_not_available)
538	TEST_INT_IN_KERNEL(4, copr_not_available_in_kernel)
539	cld			/* set direction flag to a known value */
540	SAVE_PROCESS_CTX(0, KTS_INT_HARD)
541	/* stop user process cycles */
542	push	%ebp
543	mov	$0, %ebp
544	call	_C_LABEL(context_stop)
545	call	_C_LABEL(copr_not_available_handler)
546	/* reached upon failure only */
547	jmp	_C_LABEL(switch_to_user)
548
549copr_not_available_in_kernel:
550	pushl	$0
551	pushl	$COPROC_NOT_VECTOR
552	jmp	exception_entry_nested
553
554LABEL(double_fault)
555	EXCEPTION_ERR_CODE(DOUBLE_FAULT_VECTOR)
556
557LABEL(copr_seg_overrun)
558	EXCEPTION_NO_ERR_CODE(COPROC_SEG_VECTOR)
559
560LABEL(inval_tss)
561	EXCEPTION_ERR_CODE(INVAL_TSS_VECTOR)
562
563LABEL(segment_not_present)
564	EXCEPTION_ERR_CODE(SEG_NOT_VECTOR)
565
566LABEL(stack_exception)
567	EXCEPTION_ERR_CODE(STACK_FAULT_VECTOR)
568
569LABEL(general_protection)
570	EXCEPTION_ERR_CODE(PROTECTION_VECTOR)
571
572LABEL(page_fault)
573	EXCEPTION_ERR_CODE(PAGE_FAULT_VECTOR)
574
575LABEL(copr_error)
576	EXCEPTION_NO_ERR_CODE(COPROC_ERR_VECTOR)
577
578LABEL(alignment_check)
579	EXCEPTION_NO_ERR_CODE(ALIGNMENT_CHECK_VECTOR)
580
581LABEL(machine_check)
582	EXCEPTION_NO_ERR_CODE(MACHINE_CHECK_VECTOR)
583
584LABEL(simd_exception)
585	EXCEPTION_NO_ERR_CODE(SIMD_EXCEPTION_VECTOR)
586
587/*===========================================================================*/
588/*				reload_cr3				     */
589/*===========================================================================*/
590/* PUBLIC void reload_cr3(void); */
591ENTRY(reload_cr3)
592	push    %ebp
593	mov     %esp, %ebp
594	mov	%cr3, %eax
595	mov	%eax, %cr3
596	pop     %ebp
597	ret
598
599#ifdef CONFIG_SMP
600ENTRY(startup_ap_32)
601	/*
602	 * we are in protected mode now, %cs is correct and we need to set the
603	 * data descriptors before we can touch anything
604	 *
605	 * first load the regular, highly mapped idt, gdt
606	 */
607
608	/*
609	 * use the boot stack for now. The running CPUs are already using their
610	 * own stack, the rest is still waiting to be booted
611	 */
612	movw	$KERN_DS_SELECTOR, %ax
613	mov	%ax, %ds
614	mov	%ax, %ss
615	mov	$_C_LABEL(k_boot_stktop) - 4, %esp
616
617	/* load the highly mapped idt, gdt, per-cpu tss */
618	call	_C_LABEL(prot_load_selectors)
619
620	jmp	_C_LABEL(smp_ap_boot)
621	hlt
622#endif
623
624/*===========================================================================*/
625/*				data					     */
626/*===========================================================================*/
627
628.data
629.short	0x526F	/* this must be the first data entry (magic #) */
630
631.bss
632k_initial_stack:
633.space	K_STACK_SIZE
634LABEL(__k_unpaged_k_initial_stktop)
635
636/*
637 * the kernel stack
638 */
639k_boot_stack:
640.space	K_STACK_SIZE	/* kernel stack */ /* FIXME use macro here */
641LABEL(k_boot_stktop)	/* top of kernel stack */
642
643.balign K_STACK_SIZE
644LABEL(k_stacks_start)
645
646/* two pages for each stack, one for data, other as a sandbox */
647.space	2 * (K_STACK_SIZE * CONFIG_MAX_CPUS)
648
649LABEL(k_stacks_end)
650
651/* top of kernel stack */
652