Minix1.5/kernel/mpx88.x
| 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.
| Every transition to the kernel goes through this file. Transitions are
| caused by sending/receiving messages and by most interrupts. (RS232
| interrupts may be handled in the file "rs2.x" and then they rarely enter
| the kernel.)
| Transitions to the kernel may be nested. The initial entry may be with a
| system call, exception or hardware interrupt; reentries may only be made
| by hardware interrupts. The count of reentries is kept in 'k_reenter'.
| It is important for deciding whether to switch to the kernel stack and
| for protecting the message passing code in "proc.c".
| For the message passing trap, most of the machine state is saved in the
| proc table. (Some of the registers need not be saved.) Then the stack is
| switched to 'k_stack', and interrupts are reenabled. Finally, the system
| call handler (in C) is called. When it returns, interrupts are disabled
| again and the code falls into the restart routine, to finish off held-up
| interrupts and run the process or task whose pointer is in 'proc_ptr'.
| Hardware interrupt handlers do the same, except (1) The entire state must
| be saved. (2) There are too many handlers to do this inline, so the save
| routine is called. A few cycles are saved by pushing the address of the
| appropiate restart routine for a return later. (3) A stack switch is
| avoided when the stack is already switched. (4) The (master) 8259 interrupt
| controller is reenabled centrally in save(). (5) Each interrupt handler
| masks its interrupt line using the 8259 before enabling (other unmasked)
| interrupts, and unmasks it after servicing the interrupt. This limits the
| nest level to the number of lines and protects the handler from itself.
| The external entry points into this file are:
.define _clock_int | clock interrupt routine (HZ times per second)
.define _disk_int | disk interrupt routine
.define _eth_int | ethernet interrupt routine
.define _idle_task | executed when there is no work
.define _int00 | handlers for unused interrupt vectors < 17
.define _int01
.define _int02
.define _int03
.define _int04
.define _int05
.define _int06
.define _int07
.define _int08
.define _int09
.define _int10
.define _int11
.define _int12
.define _int13
.define _int14
.define _int15
.define _int16
.define _lpr_int | interrupt routine for each line printer interrupt
.define _mpx_1hook | init from real mode for real or protected mode
.define _mpx_2hook | init from protected mode for real or protected mode
.define _restart | start running a task or process
.define save | save the machine state in the proc table
.define _s_call | process or task wants to send or receive a message
.define _trp | all traps with vector >= 17 are vectored here
.define _tty_int | interrupt routine for each key depression and release
.define _wini_int | winchester interrupt routine
#include <minix/config.h>
#include <minix/const.h>
#include <minix/com.h>
#include "const.h"
#include "sconst.h"
#if INTEL_32BITS
#error /* this is not the 32-bit version */
#endif
| When using the more portable but slower C-code RS232 interrupt handlers,
| there are a few more entry points:
#if C_RS232_INT_HANDLERS
.define _prs232_int | same as rs232_int, different label for prot mode
.define _psecondary_int | same as secondary_int, different label for prot mode
.define _rs232_int | interrupt routine for each rs232 interrupt on port 1
.define _secondary_int | interrupt routine for each rs232 interrupt on port 2
#endif
| imported functions
.extern _clock_handler
.extern _dp8390_int
.extern _exception
.extern _interrupt
.extern kernel_ds
.extern _keyboard
.extern _pr_char
.extern _sys_call
.extern _unhold
#if C_RS232_INT_HANDLERS
.extern _rs232_1handler
.extern _rs232_2handler
#endif
| imported variables
.bss
.extern _break_vector
.extern _db_enabled
.extern _held_head
.extern _k_reenter
.extern k_stktop
.extern _pc_at
.extern _proc_ptr
.extern _protected_mode
.extern _ps
.extern _ps_mca
.extern _sstep_vector
.text
|*===========================================================================*
|* tty_int *
|*===========================================================================*
_tty_int: | Interrupt routine for terminal input.
call save | save the machine state
in INT_CTLMASK | mask out further keyboard interrupts
orb al,#KEYBOARD_MASK
out INT_CTLMASK
sti | allow unmasked, non-keyboard, interrupts
call _keyboard | process a keyboard interrupt
cli
in INT_CTLMASK | unmask keyboard interrupt
andb al,#notop(KEYBOARD_MASK)
out INT_CTLMASK
ret | return to appropiate restart built by save()
#if C_RS232_INT_HANDLERS
|*===========================================================================*
|* rs232_int *
|*===========================================================================*
_rs232_int: | Interrupt routine for rs232 I/O.
_prs232_int:
call save
in INT_CTLMASK
orb al,#RS232_MASK
out INT_CTLMASK
| Don't enable interrupts, the handlers are not designed for it.
| The mask was set as usual so the handler can reenable interrupts as desired.
call _rs232_1handler | process a serial line 1 interrupt
in INT_CTLMASK
andb al,#notop(RS232_MASK)
out INT_CTLMASK
ret
|*===========================================================================*
|* secondary_int *
|*===========================================================================*
_secondary_int: | Interrupt routine for rs232 port 2
_psecondary_int:
call save
in INT_CTLMASK
orb al,#SECONDARY_MASK
out INT_CTLMASK
call _rs232_2handler | process a serial line 2 interrupt
in INT_CTLMASK
andb al,#notop(SECONDARY_MASK)
out INT_CTLMASK
ret
#endif /* C_RS232_INT_HANDLERS */
|*===========================================================================*
|* lpr_int *
|*===========================================================================*
_lpr_int: | Interrupt routine for printer output.
call save
in INT_CTLMASK
orb al,#PRINTER_MASK
out INT_CTLMASK
sti
call _pr_char | process a line printer interrupt
cli
in INT_CTLMASK
#if ASLD
andb al,#notop(PRINTER_MASK)
#else
andb al,#notop(PRINTER_MASK) & 0xFF
#endif
out INT_CTLMASK
ret
|*===========================================================================*
|* disk_int *
|*===========================================================================*
_disk_int: | Interrupt routine for the floppy disk.
call save
cmp _ps,#0 | check for 2nd int controller on ps (?)
je over_ps_disk
movb al,#ENABLE
out 0x3C
over_ps_disk:
| The last doesn't make sense as an 8259 command, since the floppy vector
| seems to be the same on PS's so the usual 8259 must be controlling it.
| This used to be done at the start of the interrupt handler, in proc.c.
| Find out where it really goes, and if there are any mask bits in port
| 0x3D which have to be fiddled (here and in fdc_results()).
in INT_CTLMASK
orb al,#FLOPPY_MASK
out INT_CTLMASK
sti
mov ax,#FLOPPY
push ax
call _interrupt | interrupt(FLOPPY)
add sp,#2
cli
ret
|*===========================================================================*
|* wini_int *
|*===========================================================================*
_wini_int: | Interrupt routine for the winchester disk.
call save
cmp _pc_at,#0 | check for 2nd int controller on AT
jnz at_wini_int
xt_wini_int:
in INT_CTLMASK
orb al,#XT_WINI_MASK
out INT_CTLMASK
sti
mov ax,#WINCHESTER
push ax
call _interrupt | interrupt(WINCHESTER)
add sp,#2
cli
ret
at_wini_int:
in INT2_MASK
orb al,#AT_WINI_MASK
out INT2_MASK
sti
movb al,#ENABLE | reenable slave 8259
out INT2_CTL | the master one was done in save() as usual
mov ax,#WINCHESTER
push ax
call _interrupt | interrupt(WINCHESTER)
add sp,#2
cli
ret
|*===========================================================================*
|* clock_int *
|*===========================================================================*
_clock_int: | Interrupt routine for the clock.
call save
cmp _ps_mca, #0 | if not a PS/2, don't twiddle bits
je skip_mca_clock
in CLOCK_ACK_PORT | PS/2 clock needs to be told to stop
orb al,#CLOCK_ACK_BIT | interrupting CPU, or we'll hang
out CLOCK_ACK_PORT
skip_mca_clock:
in INT_CTLMASK
orb al,#CLOCK_MASK
out INT_CTLMASK
sti
call _clock_handler | process a clock interrupt
| This calls interrupt() only if necessary.
| Very rarely.
cli
in INT_CTLMASK
andb al,#notop(CLOCK_MASK)
out INT_CTLMASK
ret
|*===========================================================================*
|* eth_int *
|*===========================================================================*
_eth_int: | Interrupt routine for ethernet input
call save
in INT_CTLMASK
orb al,#ETHER_MASK
out INT_CTLMASK
sti
call _dp8390_int | call the handler
cli
ret
|*===========================================================================*
|* int00-16 *
|*===========================================================================*
_int00: | interrupt through vector 0
push ax
movb al,#0
j exception
_int01: | interrupt through vector 1, etc
push ds
seg cs
mov ds,kernel_ds
cmpb _db_enabled,#0
pop ds
jz over_sstep
seg cs
jmpi @cs_sstep_vector
over_sstep:
push ax
movb al,#1
j exception
_int02:
push ax
movb al,#2
j exception
_int03:
push ds
seg cs
mov ds,kernel_ds
cmpb _db_enabled,#0
pop ds
jz over_breakpoint
seg cs
jmpi @cs_break_vector
over_breakpoint:
push ax
movb al,#3
j exception
_int04:
push ax
movb al,#4
j exception
_int05:
push ax
movb al,#5
j exception
_int06:
push ax
movb al,#6
j exception
_int07:
push ax
movb al,#7
j exception
_int08:
push ax
movb al,#8
j exception
_int09:
push ax
movb al,#9
j exception
_int10:
push ax
movb al,#10
j exception
_int11:
push ax
movb al,#11
j exception
_int12:
push ax
movb al,#12
j exception
_int13:
push ax
movb al,#13
j exception
_int14:
push ax
movb al,#14
j exception
_int15:
push ax
movb al,#15
j exception
_int16:
push ax
movb al,#16
j exception
_trp:
push ax
movb al,#17 | any vector above 17 becomes 17
exception:
seg cs
movb ex_number,al | it's cumbersome to get this into dseg
pop ax
call save
seg cs
push ex_number | high byte is constant 0
call _exception | do whatever's necessary (sti only if safe)
add sp,#2
cli
ret
|*===========================================================================*
|* save *
|*===========================================================================*
save: | save the machine state in the proc table.
cld | set direction flag to a known value
push ds
push si
seg cs
mov ds,kernel_ds
incb _k_reenter | from -1 if not reentering
jnz push_current_stack | stack is already kernel's
mov si,_proc_ptr
mov AXREG(si),ax
mov BXREG(si),bx
mov CXREG(si),cx
mov DXREG(si),dx
pop SIREG(si)
mov DIREG(si),di
mov BPREG(si),bp
mov ESREG(si),es
pop DSREG(si)
pop bx | return adr
pop PCREG(si)
pop CSREG(si)
pop PSWREG(si)
mov SPREG(si),sp
mov SSREG(si),ss
mov dx,ds
mov ss,dx
mov sp,#k_stktop
#if SPLIMITS
mov splimit,#k_stack+8
#endif
mov ax,#_restart | build return address for interrupt handler
push ax
stack_switched:
mov es,dx
movb al,#ENABLE | reenable int controller for everyone (early)
out INT_CTL
jmp (bx)
push_current_stack:
push es
push bp
push di
push dx
push cx
push bx
push ax
mov bp,sp
mov bx,18(bp) | get the return adr; it becomes junk on stack
mov ax,#restart1
push ax
mov dx,ss
mov ds,dx
j stack_switched
|*===========================================================================*
|* s_call *
|*===========================================================================*
_s_call: | System calls are vectored here.
| Do save routine inline for speed,
| but don't save ax, bx, cx, dx,
| since C doesn't require preservation,
| and ax returns the result code anyway.
| Regs bp, si, di get saved by sys_call as
| well, but it is impractical not to preserve
| them here, in case context gets switched.
| Some special-case code in pick_proc()
| could avoid this.
cld | set direction flag to a known value
push ds
push si
seg cs
mov ds,kernel_ds
incb _k_reenter
mov si,_proc_ptr
pop SIREG(si)
mov DIREG(si),di
mov BPREG(si),bp
mov ESREG(si),es
pop DSREG(si)
pop PCREG(si)
pop CSREG(si)
pop PSWREG(si)
mov SPREG(si),sp
mov SSREG(si),ss
mov dx,ds
mov es,dx
mov ss,dx | interrupt handlers may not make system calls
mov sp,#k_stktop | so stack is not already switched
#if SPLIMITS
mov splimit,#k_stack+8
#endif
| end of inline save
| now set up parameters for C routine sys_call
push bx | pointer to user message
push ax | src/dest
push cx | SEND/RECEIVE/BOTH
sti | allow SWITCHER to be interrupted
call _sys_call | sys_call(function, src_dest, m_ptr)
| caller is now explicitly in proc_ptr
mov AXREG(si),ax | sys_call MUST PRESERVE si
cli
| Fall into code to restart proc/task running.
|*===========================================================================*
|* restart *
|*===========================================================================*
_restart:
| Flush any held-up interrupts.
| This reenables interrupts, so the current interrupt handler may reenter.
| This doesn't matter, because the current handler is about to exit and no
| other handlers can reenter since flushing is only done when k_reenter == 0.
cmp _held_head,#0 | do fast test to usually avoid function call
jz over_call_unhold
call _unhold | this is rare so overhead is acceptable
over_call_unhold:
mov si,_proc_ptr
#if SPLIMITS
mov ax,P_SPLIMIT(si) | splimit = p_splimit
mov splimit,ax
#endif
decb _k_reenter
mov ax,AXREG(si) | start restoring registers from proc table
| could make AXREG == 0 to use lodw here
mov bx,BXREG(si)
mov cx,CXREG(si)
mov dx,DXREG(si)
mov di,DIREG(si)
mov bp,BPREG(si)
mov es,ESREG(si)
mov ss,SSREG(si)
mov sp,SPREG(si)
push PSWREG(si) | fake interrupt stack frame
push CSREG(si)
push PCREG(si)
| could put si:ds together to use
| lds si,SIREG(si)
push DSREG(si)
mov si,SIREG(si)
pop ds
nop | helps debugger emulate iret - else pop skips
iret | return to user or task
restart1:
decb _k_reenter
pop ax
pop bx
pop cx
pop dx
pop di
pop bp
pop es
pop si
pop ds
add sp,#2 | skip return adr
iret
|*===========================================================================*
|* idle *
|*===========================================================================*
_idle_task: | executed when there is no work
j _idle_task | a "hlt" before this fails in protected mode
|*===========================================================================*
|* mpx_1hook *
|*===========================================================================*
| PUBLIC void mpx_1hook();
| Initialize klib from real mode for real or protected mode:
| Copy real mode debugger vectors to code segment.
| They are too hard to access at interrupt time.
_mpx_1hook:
push ds
mov ax,#VEC_TABLE_SEG
mov ds,ax
mov ax,4*BREAKPOINT_VECTOR
seg cs
mov cs_break_vector,ax
mov ax,4*BREAKPOINT_VECTOR+2
seg cs
mov cs_break_vector+2,ax
mov ax,4*DEBUG_VECTOR
seg cs
mov cs_sstep_vector,ax
mov ax,4*DEBUG_VECTOR+2
seg cs
mov cs_sstep_vector+2,ax
pop ds
ret
|*===========================================================================*
|* data *
|*===========================================================================*
| NB some variables are stored in code segment.
cs_break_vector: | copy of real mode breakpoint vector
.space 4
cs_sstep_vector: | copy of real mode single-step vector
.space 4
ex_number: | exception number
.space 2
|*===========================================================================*
|* variants for 286 protected mode *
|*===========================================================================*
| Most routines are different in 286 protected mode.
| The only essential difference is that an interrupt in protected mode
| (usually) switches the stack, so there is less to do in software.
| These functions are reached along jumps patched in by klib286_init():
.define p_restart | replaces _restart
.define p_save | replaces save
| These exception and software-interrupt handlers are enabled by the new
| interrupt vector table set up in protect.c:
.define _divide_error | _int00
.define _single_step_exception | _int01
.define _nmi | _int02
.define _breakpoint_exception | _int03
.define _overflow | _int04
.define _bounds_check | _int05
.define _inval_opcode | _int06
.define _copr_not_available | _int07
.define _double_fault | _int08
.define _copr_seg_overrun | _int09
.define _inval_tss | _int10
.define _segment_not_present | _int11
.define _stack_exception | _int12
.define _general_protection | _int13
.define _p_s_call | _s_call
| The hardware interrupt handlers need not be altered apart from putting
| them in the new table (save() handles the differences).
| Some of the intxx handlers (those for exceptions which don't push an
| error code) need not have been replaced, but the names here are better.
#include "protect.h"
/* Selected 286 tss offsets. */
#define TSS2_S_SP0 2
| imported variables
.bss
.extern _tss
.text
|*===========================================================================*
|* exception handlers *
|*===========================================================================*
_divide_error:
pushi8 (DIVIDE_VECTOR)
j p_exception
_single_step_exception:
seg ss
cmpb _db_enabled,#0
jz p_over_sstep
seg ss
jmpi @_sstep_vector
p_over_sstep:
pushi8 (DEBUG_VECTOR)
j p_exception
_nmi:
pushi8 (NMI_VECTOR)
j p_exception
_breakpoint_exception:
seg ss
cmpb _db_enabled,#0
jz p_over_breakpoint
seg ss
jmpi @_break_vector
p_over_breakpoint:
pushi8 (BREAKPOINT_VECTOR)
j p_exception
_overflow:
pushi8 (OVERFLOW_VECTOR)
j p_exception
_bounds_check:
pushi8 (BOUNDS_VECTOR)
j p_exception
_inval_opcode:
pushi8 (INVAL_OP_VECTOR)
j p_exception
_copr_not_available:
pushi8 (COPROC_NOT_VECTOR)
j p_exception
_double_fault:
pushi8 (DOUBLE_FAULT_VECTOR)
j errexception
_copr_seg_overrun:
pushi8 (COPROC_SEG_VECTOR)
j p_exception
_inval_tss:
pushi8 (INVAL_TSS_VECTOR)
j errexception
_segment_not_present:
pushi8 (SEG_NOT_VECTOR)
j errexception
_stack_exception:
pushi8 (STACK_FAULT_VECTOR)
j errexception
_general_protection:
pushi8 (PROTECTION_VECTOR)
j errexception
|*===========================================================================*
|* p_exception *
|*===========================================================================*
| This is called for all exceptions which don't push an error code.
p_exception:
seg ss
pop ds_ex_number
call p_save
j p1_exception
|*===========================================================================*
|* errexception *
|*===========================================================================*
| This is called for all exceptions which push an error code.
errexception:
seg ss
pop ds_ex_number
seg ss
pop trap_errno
call p_save
p1_exception: | Common for all exceptions.
push ds_ex_number
call _exception
add sp,#2
cli
ret
|*===========================================================================*
|* p_save *
|*===========================================================================*
| Save for 286 protected mode.
| This is much simpler than for 8086 mode, because the stack already points
| into process table, or has already been switched to the kernel stack.
p_save:
cld | set direction flag to a known value
pusha | save "general" registers
push ds | save ds
push es | save es
mov dx,ss | ss is kernel data segment
mov ds,dx | load rest of kernel segments
mov es,dx
movb al,#ENABLE | reenable int controller
out INT_CTL
mov bp,sp | prepare to return
incb _k_reenter | from -1 if not reentering
jnz set_p1_restart | stack is already kernel's
mov sp,#k_stktop
#if SPLIMITS
mov splimit,#k_stack+8
#endif
pushi16 (p_restart) | build return address for interrupt handler
jmp @RETADR-P_STACKBASE(bp)
set_p1_restart:
pushi16 (p1_restart)
jmp @RETADR-P_STACKBASE(bp)
|*===========================================================================*
|* p_s_call *
|*===========================================================================*
_p_s_call:
cld | set direction flag to a known value
sub sp,#6*2 | skip RETADR, ax, cx, dx, bx, st
push bp | stack already points into process table
push si
push di
push ds
push es
mov dx,ss
mov ds,dx
mov es,dx
incb _k_reenter
mov si,sp | assumes P_STACKBASE == 0
mov sp,#k_stktop
#if SPLIMITS
mov splimit,#k_stack+8
#endif
| end of inline save
sti | allow SWITCHER to be interrupted
| now set up parameters for C routine sys_call
push bx | pointer to user message
push ax | src/dest
push cx | SEND/RECEIVE/BOTH
call _sys_call | sys_call(function, src_dest, m_ptr)
| caller is now explicitly in proc_ptr
mov AXREG(si),ax | sys_call MUST PRESERVE si
cli
| Fall into code to restart proc/task running.
p_restart:
| Flush any held-up interrupts.
| This reenables interrupts, so the current interrupt handler may reenter.
| This doesn't matter, because the current handler is about to exit and no
| other handlers can reenter since flushing is only done when k_reenter == 0.
cmp _held_head,#0 | do fast test to usually avoid function call
jz p_over_call_unhold
call _unhold | this is rare so overhead is acceptable
p_over_call_unhold:
mov si,_proc_ptr
#if SPLIMITS
mov ax,P_SPLIMIT(si) | splimit = p_splimit
mov splimit,ax
#endif
deflldt (P_LDT_SEL(si)) | enable task's segment descriptors
lea ax,P_STACKTOP(si) | arrange for next interrupt
mov _tss+TSS2_S_SP0,ax | to save state in process table
mov sp,si | assumes P_STACKBASE == 0
p1_restart:
decb _k_reenter
pop es
pop ds
popa
add sp,#2 | skip RETADR
iret | continue process
|*===========================================================================*
|* mpx_2hook *
|*===========================================================================*
| PUBLIC void mpx_2hook();
| Initialize klib from protected mode for real or protected mode:
| Nothing to do.
_mpx_2hook:
ret
|*===========================================================================*
|* data *
|*===========================================================================*
.bss
ds_ex_number:
.space 2
.space 2 | try to align for 386 to take advantage of
trap_errno:
.space 2
.space 2 | align