| 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