caa7efa117
. unnecessary to do it manually with ELF . also makes 1 extra alignment page unnecessary
528 lines
13 KiB
ArmAsm
528 lines
13 KiB
ArmAsm
/* 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.
|
|
* Furthermore it contains the assembler startup code for Minix and the 32-bit
|
|
* interrupt handlers. It cooperates with the code in "start.c" to set up a
|
|
* good environment for main().
|
|
*
|
|
* 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.
|
|
*
|
|
* For communication with the boot monitor at startup time some constant
|
|
* data are compiled into the beginning of the text segment. This facilitates
|
|
* reading the data at the start of the boot process, since only the first
|
|
* sector of the file needs to be read.
|
|
*
|
|
* Some data storage is also allocated at the end of this file. This data
|
|
* will be at the start of the data segment of the kernel and will be read
|
|
* and modified by the boot monitor before the kernel starts.
|
|
*/
|
|
|
|
#include "kernel/kernel.h" /* configures the kernel */
|
|
|
|
/* sections */
|
|
|
|
#include <machine/vm.h>
|
|
#include "../../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 "arch_proto.h" /* K_STACK_SIZE */
|
|
|
|
#ifdef CONFIG_SMP
|
|
#include "kernel/smp.h"
|
|
#endif
|
|
|
|
/* Selected 386 tss offsets. */
|
|
#define TSS3_S_SP0 4
|
|
|
|
IMPORT(copr_not_available_handler)
|
|
IMPORT(params_size)
|
|
IMPORT(params_offset)
|
|
IMPORT(switch_to_user)
|
|
IMPORT(multiboot_init)
|
|
|
|
.text
|
|
/*===========================================================================*/
|
|
/* interrupt handlers */
|
|
/* interrupt handlers for 386 32-bit protected mode */
|
|
/*===========================================================================*/
|
|
|
|
#define PIC_IRQ_HANDLER(irq) \
|
|
push $irq ;\
|
|
call _C_LABEL(irq_handle) /* intr_handle(irq_handlers[irq]) */ ;\
|
|
add $4, %esp ;
|
|
|
|
/*===========================================================================*/
|
|
/* hwint00 - 07 */
|
|
/*===========================================================================*/
|
|
/* Note this is a macro, it just looks like a subroutine. */
|
|
|
|
#define hwint_master(irq) \
|
|
TEST_INT_IN_KERNEL(4, 0f) ;\
|
|
\
|
|
SAVE_PROCESS_CTX(0) ;\
|
|
push %ebp ;\
|
|
movl $0, %ebp /* for stack trace */ ;\
|
|
call _C_LABEL(context_stop) ;\
|
|
add $4, %esp ;\
|
|
PIC_IRQ_HANDLER(irq) ;\
|
|
movb $END_OF_INT, %al ;\
|
|
outb $INT_CTL /* reenable interrupts in master pic */ ;\
|
|
jmp _C_LABEL(switch_to_user) ;\
|
|
\
|
|
0: \
|
|
pusha ;\
|
|
call _C_LABEL(context_stop_idle) ;\
|
|
PIC_IRQ_HANDLER(irq) ;\
|
|
movb $END_OF_INT, %al ;\
|
|
outb $INT_CTL /* reenable interrupts in master pic */ ;\
|
|
CLEAR_IF(10*4(%esp)) ;\
|
|
popa ;\
|
|
iret ;
|
|
|
|
/* Each of these entry points is an expansion of the hwint_master macro */
|
|
ENTRY(hwint00)
|
|
/* Interrupt routine for irq 0 (the clock). */
|
|
hwint_master(0)
|
|
|
|
ENTRY(hwint01)
|
|
/* Interrupt routine for irq 1 (keyboard) */
|
|
hwint_master(1)
|
|
|
|
ENTRY(hwint02)
|
|
/* Interrupt routine for irq 2 (cascade!) */
|
|
hwint_master(2)
|
|
|
|
ENTRY(hwint03)
|
|
/* Interrupt routine for irq 3 (second serial) */
|
|
hwint_master(3)
|
|
|
|
ENTRY(hwint04)
|
|
/* Interrupt routine for irq 4 (first serial) */
|
|
hwint_master(4)
|
|
|
|
ENTRY(hwint05)
|
|
/* Interrupt routine for irq 5 (XT winchester) */
|
|
hwint_master(5)
|
|
|
|
ENTRY(hwint06)
|
|
/* Interrupt routine for irq 6 (floppy) */
|
|
hwint_master(6)
|
|
|
|
ENTRY(hwint07)
|
|
/* Interrupt routine for irq 7 (printer) */
|
|
hwint_master(7)
|
|
|
|
/*===========================================================================*/
|
|
/* hwint08 - 15 */
|
|
/*===========================================================================*/
|
|
/* Note this is a macro, it just looks like a subroutine. */
|
|
#define hwint_slave(irq) \
|
|
TEST_INT_IN_KERNEL(4, 0f) ;\
|
|
\
|
|
SAVE_PROCESS_CTX(0) ;\
|
|
push %ebp ;\
|
|
movl $0, %ebp /* for stack trace */ ;\
|
|
call _C_LABEL(context_stop) ;\
|
|
add $4, %esp ;\
|
|
PIC_IRQ_HANDLER(irq) ;\
|
|
movb $END_OF_INT, %al ;\
|
|
outb $INT_CTL /* reenable interrupts in master pic */ ;\
|
|
outb $INT2_CTL /* reenable slave 8259 */ ;\
|
|
jmp _C_LABEL(switch_to_user) ;\
|
|
\
|
|
0: \
|
|
pusha ;\
|
|
call _C_LABEL(context_stop_idle) ;\
|
|
PIC_IRQ_HANDLER(irq) ;\
|
|
movb $END_OF_INT, %al ;\
|
|
outb $INT_CTL /* reenable interrupts in master pic */ ;\
|
|
outb $INT2_CTL /* reenable slave 8259 */ ;\
|
|
CLEAR_IF(10*4(%esp)) ;\
|
|
popa ;\
|
|
iret ;
|
|
|
|
/* Each of these entry points is an expansion of the hwint_slave macro */
|
|
ENTRY(hwint08)
|
|
/* Interrupt routine for irq 8 (realtime clock) */
|
|
hwint_slave(8)
|
|
|
|
ENTRY(hwint09)
|
|
/* Interrupt routine for irq 9 (irq 2 redirected) */
|
|
hwint_slave(9)
|
|
|
|
ENTRY(hwint10)
|
|
/* Interrupt routine for irq 10 */
|
|
hwint_slave(10)
|
|
|
|
ENTRY(hwint11)
|
|
/* Interrupt routine for irq 11 */
|
|
hwint_slave(11)
|
|
|
|
ENTRY(hwint12)
|
|
/* Interrupt routine for irq 12 */
|
|
hwint_slave(12)
|
|
|
|
ENTRY(hwint13)
|
|
/* Interrupt routine for irq 13 (FPU exception) */
|
|
hwint_slave(13)
|
|
|
|
ENTRY(hwint14)
|
|
/* Interrupt routine for irq 14 (AT winchester) */
|
|
hwint_slave(14)
|
|
|
|
ENTRY(hwint15)
|
|
/* Interrupt routine for irq 15 */
|
|
hwint_slave(15)
|
|
|
|
/*
|
|
* IPC is only from a process to kernel
|
|
*/
|
|
ENTRY(ipc_entry)
|
|
|
|
SAVE_PROCESS_CTX(0)
|
|
|
|
/* save the pointer to the current process */
|
|
push %ebp
|
|
|
|
/*
|
|
* 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 %ebx
|
|
push %eax
|
|
push %ecx
|
|
|
|
/* stop user process cycles */
|
|
push %ebp
|
|
/* for stack trace */
|
|
movl $0, %ebp
|
|
call _C_LABEL(context_stop)
|
|
add $4, %esp
|
|
|
|
call _C_LABEL(do_ipc)
|
|
|
|
/* restore the current process pointer and save the return value */
|
|
add $3 * 4, %esp
|
|
pop %esi
|
|
mov %eax, AXREG(%esi)
|
|
|
|
jmp _C_LABEL(switch_to_user)
|
|
|
|
|
|
/*
|
|
* kernel call is only from a process to kernel
|
|
*/
|
|
ENTRY(kernel_call_entry)
|
|
|
|
SAVE_PROCESS_CTX(0)
|
|
|
|
/* save the pointer to the current process */
|
|
push %ebp
|
|
|
|
/*
|
|
* 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 %eax
|
|
|
|
/* stop user process cycles */
|
|
push %ebp
|
|
/* for stack trace */
|
|
movl $0, %ebp
|
|
call _C_LABEL(context_stop)
|
|
add $4, %esp
|
|
|
|
call _C_LABEL(kernel_call)
|
|
|
|
/* restore the current process pointer and save the return value */
|
|
add $8, %esp
|
|
|
|
jmp _C_LABEL(switch_to_user)
|
|
|
|
|
|
.balign 16
|
|
/*
|
|
* called by the exception interrupt vectors. If the exception does not push
|
|
* errorcode, we assume that the vector handler pushed 0 instead. Next pushed
|
|
* thing is the vector number. From this point on we can continue as if every
|
|
* exception pushes an error code
|
|
*/
|
|
exception_entry:
|
|
/*
|
|
* check if it is a nested trap by comparing the saved code segment
|
|
* descriptor with the kernel CS first
|
|
*/
|
|
TEST_INT_IN_KERNEL(12, exception_entry_nested)
|
|
|
|
exception_entry_from_user:
|
|
SAVE_PROCESS_CTX(8)
|
|
|
|
/* stop user process cycles */
|
|
push %ebp
|
|
/* for stack trace clear %ebp */
|
|
movl $0, %ebp
|
|
call _C_LABEL(context_stop)
|
|
add $4, %esp
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
push %esp
|
|
push $0 /* it's not a nested exception */
|
|
call _C_LABEL(exception_handler)
|
|
|
|
jmp _C_LABEL(switch_to_user)
|
|
|
|
exception_entry_nested:
|
|
|
|
pusha
|
|
mov %esp, %eax
|
|
add $(8 * 4), %eax
|
|
push %eax
|
|
pushl $1 /* it's a nested exception */
|
|
call _C_LABEL(exception_handler)
|
|
add $8, %esp
|
|
popa
|
|
|
|
/* clear the error code and the exception number */
|
|
add $8, %esp
|
|
/* resume execution at the point of exception */
|
|
iret
|
|
|
|
/*===========================================================================*/
|
|
/* restart */
|
|
/*===========================================================================*/
|
|
ENTRY(restore_user_context)
|
|
mov 4(%esp), %ebp /* will assume P_STACKBASE == 0 */
|
|
|
|
/* reconstruct the stack for iret */
|
|
push $USER_DS_SELECTOR /* ss */
|
|
movl SPREG(%ebp), %eax
|
|
push %eax
|
|
movl PSWREG(%ebp), %eax
|
|
push %eax
|
|
push $USER_CS_SELECTOR /* cs */
|
|
movl PCREG(%ebp), %eax
|
|
push %eax
|
|
|
|
/* Restore segments as the user should see them. */
|
|
movw $USER_DS_SELECTOR, %si
|
|
movw %si, %ds
|
|
movw %si, %es
|
|
movw %si, %fs
|
|
movw %si, %gs
|
|
|
|
/* Same for general-purpose registers. */
|
|
RESTORE_GP_REGS(%ebp)
|
|
|
|
movl BPREG(%ebp), %ebp
|
|
|
|
iret /* continue process */
|
|
|
|
/*===========================================================================*/
|
|
/* exception handlers */
|
|
/*===========================================================================*/
|
|
|
|
#define EXCEPTION_ERR_CODE(vector) \
|
|
push $vector ;\
|
|
jmp exception_entry
|
|
|
|
#define EXCEPTION_NO_ERR_CODE(vector) \
|
|
pushl $0 ;\
|
|
EXCEPTION_ERR_CODE(vector)
|
|
|
|
LABEL(divide_error)
|
|
EXCEPTION_NO_ERR_CODE(DIVIDE_VECTOR)
|
|
|
|
LABEL(single_step_exception)
|
|
EXCEPTION_NO_ERR_CODE(DEBUG_VECTOR)
|
|
|
|
LABEL(nmi)
|
|
#ifndef USE_WATCHDOG
|
|
EXCEPTION_NO_ERR_CODE(NMI_VECTOR)
|
|
#else
|
|
/*
|
|
* We have to be very careful as this interrupt can occur anytime. On
|
|
* the other hand, if it interrupts a user process, we will resume the
|
|
* same process which makes things a little simpler. We know that we are
|
|
* already on kernel stack whenever it happened and we can be
|
|
* conservative and save everything as we don't need to be extremely
|
|
* efficient as the interrupt is infrequent and some overhead is already
|
|
* expected.
|
|
*/
|
|
|
|
/*
|
|
* save the important registers. We don't save %cs and %ss and they are
|
|
* saved and restored by CPU
|
|
*/
|
|
pushw %ds
|
|
pushw %es
|
|
pushw %fs
|
|
pushw %gs
|
|
pusha
|
|
|
|
/*
|
|
* We cannot be sure about the state of the kernel segment register,
|
|
* however, we always set %ds and %es to the same as %ss
|
|
*/
|
|
mov %ss, %si
|
|
mov %si, %ds
|
|
mov %si, %es
|
|
|
|
push %esp
|
|
call _C_LABEL(nmi_watchdog_handler)
|
|
add $4, %esp
|
|
|
|
/* restore all the important registers as they were before the trap */
|
|
popa
|
|
popw %gs
|
|
popw %fs
|
|
popw %es
|
|
popw %ds
|
|
|
|
iret
|
|
#endif
|
|
|
|
LABEL(breakpoint_exception)
|
|
EXCEPTION_NO_ERR_CODE(BREAKPOINT_VECTOR)
|
|
|
|
LABEL(overflow)
|
|
EXCEPTION_NO_ERR_CODE(OVERFLOW_VECTOR)
|
|
|
|
LABEL(bounds_check)
|
|
EXCEPTION_NO_ERR_CODE(BOUNDS_VECTOR)
|
|
|
|
LABEL(inval_opcode)
|
|
EXCEPTION_NO_ERR_CODE(INVAL_OP_VECTOR)
|
|
|
|
LABEL(copr_not_available)
|
|
TEST_INT_IN_KERNEL(4, copr_not_available_in_kernel)
|
|
cld /* set direction flag to a known value */
|
|
SAVE_PROCESS_CTX(0)
|
|
/* stop user process cycles */
|
|
push %ebp
|
|
mov $0, %ebp
|
|
call _C_LABEL(context_stop)
|
|
call _C_LABEL(copr_not_available_handler)
|
|
/* reached upon failure only */
|
|
jmp _C_LABEL(switch_to_user)
|
|
|
|
copr_not_available_in_kernel:
|
|
pushl $0
|
|
pushl $COPROC_NOT_VECTOR
|
|
jmp exception_entry_nested
|
|
|
|
LABEL(double_fault)
|
|
EXCEPTION_ERR_CODE(DOUBLE_FAULT_VECTOR)
|
|
|
|
LABEL(copr_seg_overrun)
|
|
EXCEPTION_NO_ERR_CODE(COPROC_SEG_VECTOR)
|
|
|
|
LABEL(inval_tss)
|
|
EXCEPTION_ERR_CODE(INVAL_TSS_VECTOR)
|
|
|
|
LABEL(segment_not_present)
|
|
EXCEPTION_ERR_CODE(SEG_NOT_VECTOR)
|
|
|
|
LABEL(stack_exception)
|
|
EXCEPTION_ERR_CODE(STACK_FAULT_VECTOR)
|
|
|
|
LABEL(general_protection)
|
|
EXCEPTION_ERR_CODE(PROTECTION_VECTOR)
|
|
|
|
LABEL(page_fault)
|
|
EXCEPTION_ERR_CODE(PAGE_FAULT_VECTOR)
|
|
|
|
LABEL(copr_error)
|
|
EXCEPTION_NO_ERR_CODE(COPROC_ERR_VECTOR)
|
|
|
|
LABEL(alignment_check)
|
|
EXCEPTION_NO_ERR_CODE(ALIGNMENT_CHECK_VECTOR)
|
|
|
|
LABEL(machine_check)
|
|
EXCEPTION_NO_ERR_CODE(MACHINE_CHECK_VECTOR)
|
|
|
|
LABEL(simd_exception)
|
|
EXCEPTION_NO_ERR_CODE(SIMD_EXCEPTION_VECTOR)
|
|
|
|
/*===========================================================================*/
|
|
/* reload_cr3 */
|
|
/*===========================================================================*/
|
|
/* PUBLIC void reload_cr3(void); */
|
|
ENTRY(reload_cr3)
|
|
push %ebp
|
|
mov %esp, %ebp
|
|
mov %cr3, %eax
|
|
mov %eax, %cr3
|
|
pop %ebp
|
|
ret
|
|
|
|
#ifdef CONFIG_SMP
|
|
ENTRY(startup_ap_32)
|
|
/*
|
|
* we are in protected mode now, %cs is correct and we need to set the
|
|
* data descriptors before we can touch anything
|
|
*
|
|
* first load the regular, highly mapped idt, gdt
|
|
*/
|
|
|
|
/*
|
|
* use the boot stack for now. The running CPUs are already using their
|
|
* own stack, the rest is still waiting to be booted
|
|
*/
|
|
movw $KERN_DS_SELECTOR, %ax
|
|
mov %ax, %ds
|
|
mov %ax, %ss
|
|
mov $_C_LABEL(k_boot_stktop) - 4, %esp
|
|
|
|
/* load the highly mapped idt, gdt, per-cpu tss */
|
|
call _C_LABEL(prot_load_selectors)
|
|
|
|
jmp _C_LABEL(smp_ap_boot)
|
|
hlt
|
|
#endif
|
|
|
|
/*===========================================================================*/
|
|
/* data */
|
|
/*===========================================================================*/
|
|
|
|
.data
|
|
.short 0x526F /* this must be the first data entry (magic #) */
|
|
.bss
|
|
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 */
|