minix/kernel/arch/i386/mpx386.s
David van Moolenbroek b423d7b477 Merge of David's ptrace branch. Summary:
o Support for ptrace T_ATTACH/T_DETACH and T_SYSCALL
o PM signal handling logic should now work properly, even with debuggers
  being present
o Asynchronous PM/VFS protocol, full IPC support for senda(), and
  AMF_NOREPLY senda() flag

DETAILS

Process stop and delay call handling of PM:
o Added sys_runctl() kernel call with sys_stop() and sys_resume()
  aliases, for PM to stop and resume a process
o Added exception for sending/syscall-traced processes to sys_runctl(),
  and matching SIGKREADY pseudo-signal to PM
o Fixed PM signal logic to deal with requests from a process after
  stopping it (so-called "delay calls"), using the SIGKREADY facility
o Fixed various PM panics due to race conditions with delay calls versus
  VFS calls
o Removed special PRIO_STOP priority value
o Added SYS_LOCK RTS kernel flag, to stop an individual process from
  running while modifying its process structure

Signal and debugger handling in PM:
o Fixed debugger signals being dropped if a second signal arrives when
  the debugger has not retrieved the first one
o Fixed debugger signals being sent to the debugger more than once
o Fixed debugger signals unpausing process in VFS; removed PM_UNPAUSE_TR
  protocol message
o Detached debugger signals from general signal logic and from being
  blocked on VFS calls, meaning that even VFS can now be traced
o Fixed debugger being unable to receive more than one pending signal in
  one process stop
o Fixed signal delivery being delayed needlessly when multiple signals
  are pending
o Fixed wait test for tracer, which was returning for children that were
  not waited for
o Removed second parallel pending call from PM to VFS for any process
o Fixed process becoming runnable between exec() and debugger trap
o Added support for notifying the debugger before the parent when a
  debugged child exits
o Fixed debugger death causing child to remain stopped forever
o Fixed consistently incorrect use of _NSIG

Extensions to ptrace():
o Added T_ATTACH and T_DETACH ptrace request, to attach and detach a
  debugger to and from a process
o Added T_SYSCALL ptrace request, to trace system calls
o Added T_SETOPT ptrace request, to set trace options
o Added TO_TRACEFORK trace option, to attach automatically to children
  of a traced process
o Added TO_ALTEXEC trace option, to send SIGSTOP instead of SIGTRAP upon
  a successful exec() of the tracee
o Extended T_GETUSER ptrace support to allow retrieving a process's priv
  structure
o Removed T_STOP ptrace request again, as it does not help implementing
  debuggers properly
o Added MINIX3-specific ptrace test (test42)
o Added proper manual page for ptrace(2)

Asynchronous PM/VFS interface:
o Fixed asynchronous messages not being checked when receive() is called
  with an endpoint other than ANY
o Added AMF_NOREPLY senda() flag, preventing such messages from
  satisfying the receive part of a sendrec()
o Added asynsend3() that takes optional flags; asynsend() is now a
  #define passing in 0 as third parameter
o Made PM/VFS protocol asynchronous; reintroduced tell_fs()
o Made PM_BASE request/reply number range unique
o Hacked in a horrible temporary workaround into RS to deal with newly
  revealed RS-PM-VFS race condition triangle until VFS is asynchronous

System signal handling:
o Fixed shutdown logic of device drivers; removed old SIGKSTOP signal
o Removed is-superuser check from PM's do_procstat() (aka getsigset())
o Added sigset macros to allow system processes to deal with the full
  signal set, rather than just the POSIX subset

Miscellaneous PM fixes:
o Split do_getset into do_get and do_set, merging common code and making
  structure clearer
o Fixed setpriority() being able to put to sleep processes using an
  invalid parameter, or revive zombie processes
o Made find_proc() global; removed obsolete proc_from_pid()
o Cleanup here and there

Also included:
o Fixed false-positive boot order kernel warning
o Removed last traces of old NOTIFY_FROM code

THINGS OF POSSIBLE INTEREST

o It should now be possible to run PM at any priority, even lower than
  user processes
o No assumptions are made about communication speed between PM and VFS,
  although communication must be FIFO
o A debugger will now receive incoming debuggee signals at kill time
  only; the process may not yet be fully stopped
o A first step has been made towards making the SYSTEM task preemptible
2009-09-30 09:57:22 +00:00

612 lines
17 KiB
ArmAsm
Executable file

#
! This file, mpx386.s, is included by mpx.s when Minix is compiled for
! 32-bit Intel CPUs. The alternative mpx88.s is compiled for 16-bit CPUs.
! 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().
! Every transition to the kernel goes through this file. Transitions to the
! kernel may be nested. The initial entry may be with a system call (i.e.,
! send or receive a message), an exception or a hardware interrupt; kernel
! 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.
! 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.
! sections
#include <sys/vm_i386.h>
.sect .text
begtext:
.sect .rom
begrom:
.sect .data
begdata:
.sect .bss
begbss:
#include <minix/config.h>
#include <minix/const.h>
#include <minix/com.h>
#include <ibm/interrupt.h>
#include <archconst.h>
#include "../../const.h"
#include "sconst.h"
/* Selected 386 tss offsets. */
#define TSS3_S_SP0 4
! Exported functions
! Note: in assembly language the .define statement applied to a function name
! is loosely equivalent to a prototype in C code -- it makes it possible to
! link to an entity declared in the assembly code but does not create
! the entity.
.define _restart
.define save
.define _reload_cr3
.define _write_cr3 ! write cr3
.define errexception
.define exception1
.define exception
.define _divide_error
.define _single_step_exception
.define _nmi
.define _breakpoint_exception
.define _overflow
.define _bounds_check
.define _inval_opcode
.define _copr_not_available
.define _double_fault
.define _copr_seg_overrun
.define _inval_tss
.define _segment_not_present
.define _stack_exception
.define _general_protection
.define _page_fault
.define _copr_error
.define _params_size
.define _params_offset
.define _mon_ds
.define _schedcheck
.define _dirtypde
.define _hwint00 ! handlers for hardware interrupts
.define _hwint01
.define _hwint02
.define _hwint03
.define _hwint04
.define _hwint05
.define _hwint06
.define _hwint07
.define _hwint08
.define _hwint09
.define _hwint10
.define _hwint11
.define _hwint12
.define _hwint13
.define _hwint14
.define _hwint15
.define _s_call
.define _p_s_call
.define _level0_call
! Exported variables.
.define begbss
.define begdata
.sect .text
!*===========================================================================*
!* MINIX *
!*===========================================================================*
MINIX: ! this is the entry point for the MINIX kernel
jmp over_flags ! skip over the next few bytes
.data2 CLICK_SHIFT ! for the monitor: memory granularity
flags:
.data2 0x01FD ! boot monitor flags:
! call in 386 mode, make bss, make stack,
! load high, don't patch, will return,
! uses generic INT, memory vector,
! new boot code return
nop ! extra byte to sync up disassembler
over_flags:
! Set up a C stack frame on the monitor stack. (The monitor sets cs and ds
! right. The ss descriptor still references the monitor data segment.)
movzx esp, sp ! monitor stack is a 16 bit stack
push ebp
mov ebp, esp
push esi
push edi
cmp 4(ebp), 0 ! monitor return vector is
jz noret ! nonzero if return possible
inc (_mon_return)
noret: mov (_mon_sp), esp ! save stack pointer for later return
! Copy the monitor global descriptor table to the address space of kernel and
! switch over to it. Prot_init() can then update it with immediate effect.
sgdt (_gdt+GDT_SELECTOR) ! get the monitor gdtr
mov esi, (_gdt+GDT_SELECTOR+2) ! absolute address of GDT
mov ebx, _gdt ! address of kernel GDT
mov ecx, 8*8 ! copying eight descriptors
copygdt:
eseg movb al, (esi)
movb (ebx), al
inc esi
inc ebx
loop copygdt
mov eax, (_gdt+DS_SELECTOR+2) ! base of kernel data
and eax, 0x00FFFFFF ! only 24 bits
add eax, _gdt ! eax = vir2phys(gdt)
mov (_gdt+GDT_SELECTOR+2), eax ! set base of GDT
lgdt (_gdt+GDT_SELECTOR) ! switch over to kernel GDT
! Locate boot parameters, set up kernel segment registers and stack.
mov ebx, 8(ebp) ! boot parameters offset
mov edx, 12(ebp) ! boot parameters length
mov eax, 16(ebp) ! address of a.out headers
mov (_aout), eax
mov ax, ds ! kernel data
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, k_stktop ! set sp to point to the top of kernel stack
! Save boot parameters into these global variables for i386 code
mov (_params_size), edx
mov (_params_offset), ebx
mov (_mon_ds), SS_SELECTOR
! Call C startup code to set up a proper environment to run main().
push edx
push ebx
push SS_SELECTOR
push DS_SELECTOR
push CS_SELECTOR
call _cstart ! cstart(cs, ds, mds, parmoff, parmlen)
add esp, 5*4
! Reload gdtr, idtr and the segment registers to global descriptor table set
! up by prot_init().
lgdt (_gdt+GDT_SELECTOR)
lidt (_gdt+IDT_SELECTOR)
jmpf CS_SELECTOR:csinit
csinit:
o16 mov ax, DS_SELECTOR
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
o16 mov ax, TSS_SELECTOR ! no other TSS is used
ltr ax
push 0 ! set flags to known good state
popf ! esp, clear nested task and int enable
jmp _main ! main()
!*===========================================================================*
!* interrupt handlers *
!* interrupt handlers for 386 32-bit protected mode *
!*===========================================================================*
!*===========================================================================*
!* hwint00 - 07 *
!*===========================================================================*
! Note this is a macro, it just looks like a subroutine.
#define hwint_master(irq) \
call save /* save interrupted process state */;\
push (_irq_handlers+4*irq) /* irq_handlers[irq] */;\
call _intr_handle /* intr_handle(irq_handlers[irq]) */;\
pop ecx ;\
cmp (_irq_actids+4*irq), 0 /* interrupt still active? */;\
jz 0f ;\
inb INT_CTLMASK /* get current mask */ ;\
orb al, [1<<irq] /* mask irq */ ;\
outb INT_CTLMASK /* disable the irq */;\
0: movb al, END_OF_INT ;\
outb INT_CTL /* reenable master 8259 */;\
ret /* restart (another) process */
! Each of these entry points is an expansion of the hwint_master macro
.align 16
_hwint00: ! Interrupt routine for irq 0 (the clock).
hwint_master(0)
.align 16
_hwint01: ! Interrupt routine for irq 1 (keyboard)
hwint_master(1)
.align 16
_hwint02: ! Interrupt routine for irq 2 (cascade!)
hwint_master(2)
.align 16
_hwint03: ! Interrupt routine for irq 3 (second serial)
hwint_master(3)
.align 16
_hwint04: ! Interrupt routine for irq 4 (first serial)
hwint_master(4)
.align 16
_hwint05: ! Interrupt routine for irq 5 (XT winchester)
hwint_master(5)
.align 16
_hwint06: ! Interrupt routine for irq 6 (floppy)
hwint_master(6)
.align 16
_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) \
call save /* save interrupted process state */;\
push (_irq_handlers+4*irq) /* irq_handlers[irq] */;\
call _intr_handle /* intr_handle(irq_handlers[irq]) */;\
pop ecx ;\
cmp (_irq_actids+4*irq), 0 /* interrupt still active? */;\
jz 0f ;\
inb INT2_CTLMASK ;\
orb al, [1<<[irq-8]] ;\
outb INT2_CTLMASK /* disable the irq */;\
0: movb al, END_OF_INT ;\
outb INT_CTL /* reenable master 8259 */;\
outb INT2_CTL /* reenable slave 8259 */;\
ret /* restart (another) process */
! Each of these entry points is an expansion of the hwint_slave macro
.align 16
_hwint08: ! Interrupt routine for irq 8 (realtime clock)
hwint_slave(8)
.align 16
_hwint09: ! Interrupt routine for irq 9 (irq 2 redirected)
hwint_slave(9)
.align 16
_hwint10: ! Interrupt routine for irq 10
hwint_slave(10)
.align 16
_hwint11: ! Interrupt routine for irq 11
hwint_slave(11)
.align 16
_hwint12: ! Interrupt routine for irq 12
hwint_slave(12)
.align 16
_hwint13: ! Interrupt routine for irq 13 (FPU exception)
hwint_slave(13)
.align 16
_hwint14: ! Interrupt routine for irq 14 (AT winchester)
hwint_slave(14)
.align 16
_hwint15: ! Interrupt routine for irq 15
hwint_slave(15)
!*===========================================================================*
!* save *
!*===========================================================================*
! Save for protected mode.
! This is much simpler than for 8086 mode, because the stack already points
! into the process table, or has already been switched to the kernel stack.
.align 16
save:
cld ! set direction flag to a known value
pushad ! save "general" registers
o16 push ds ! save ds
o16 push es ! save es
o16 push fs ! save fs
o16 push gs ! save gs
mov dx, ss ! ss is kernel data segment
mov ds, dx ! load rest of kernel segments
mov es, dx ! kernel does not use fs, gs
mov eax, esp ! prepare to return
incb (_k_reenter) ! from -1 if not reentering
jnz set_restart1 ! stack is already kernel stack
mov esp, k_stktop
push _restart ! build return address for int handler
xor ebp, ebp ! for stacktrace
jmp RETADR-P_STACKBASE(eax)
.align 4
set_restart1:
push restart1
jmp RETADR-P_STACKBASE(eax)
!*===========================================================================*
!* _s_call *
!*===========================================================================*
.align 16
_s_call:
_p_s_call:
cld ! set direction flag to a known value
sub esp, 4 ! skip RETADR
pushad ! save "general" registers
o16 push ds
o16 push es
o16 push fs
o16 push gs
mov si, ss ! ss is kernel data segment
mov ds, si ! load rest of kernel segments
mov es, si ! kernel does not use fs, gs
incb (_k_reenter) ! increment kernel entry count
mov esi, esp ! assumes P_STACKBASE == 0
mov esp, k_stktop
xor ebp, ebp ! for stacktrace
! end of inline save
! now set up parameters for sys_call()
push edx ! event set or flags bit map
push ebx ! pointer to user message
push eax ! source / destination
push ecx ! call number (ipc primitive to use)
call _sys_call ! sys_call(call_nr, src_dst, m_ptr, bit_map)
! caller is now explicitly in proc_ptr
mov AXREG(esi), eax
! Fall into code to restart proc/task running.
!*===========================================================================*
!* restart *
!*===========================================================================*
_restart:
! Restart the current process or the next process if it is set.
cli
call _schedcheck ! ask C function who we're running
mov esp, (_proc_ptr) ! will assume P_STACKBASE == 0
lldt P_LDT_SEL(esp) ! enable process' segment descriptors
cmp P_CR3(esp), 0 ! process does not have its own PT
jz 0f
mov eax, P_CR3(esp)
cmp eax, (loadedcr3)
jz 0f
mov cr3, eax
mov (loadedcr3), eax
mov eax, (_proc_ptr)
mov (_ptproc), eax
mov (_dirtypde), 0
0:
lea eax, P_STACKTOP(esp) ! arrange for next interrupt
mov (_tss+TSS3_S_SP0), eax ! to save state in process table
restart1:
decb (_k_reenter)
o16 pop gs
o16 pop fs
o16 pop es
o16 pop ds
popad
add esp, 4 ! skip return adr
iretd ! continue process
!*===========================================================================*
!* exception handlers *
!*===========================================================================*
_divide_error:
push DIVIDE_VECTOR
jmp exception
_single_step_exception:
push DEBUG_VECTOR
jmp exception
_nmi:
push NMI_VECTOR
jmp exception
_breakpoint_exception:
push BREAKPOINT_VECTOR
jmp exception
_overflow:
push OVERFLOW_VECTOR
jmp exception
_bounds_check:
push BOUNDS_VECTOR
jmp exception
_inval_opcode:
push INVAL_OP_VECTOR
jmp exception
_copr_not_available:
push COPROC_NOT_VECTOR
jmp exception
_double_fault:
push DOUBLE_FAULT_VECTOR
jmp errexception
_copr_seg_overrun:
push COPROC_SEG_VECTOR
jmp exception
_inval_tss:
push INVAL_TSS_VECTOR
jmp errexception
_segment_not_present:
push SEG_NOT_VECTOR
jmp errexception
_stack_exception:
push STACK_FAULT_VECTOR
jmp errexception
_general_protection:
push PROTECTION_VECTOR
jmp errexception
_page_fault:
push PAGE_FAULT_VECTOR
push eax
mov eax, cr2
sseg mov (pagefaultcr2), eax
pop eax
jmp errexception
_copr_error:
push COPROC_ERR_VECTOR
jmp exception
!*===========================================================================*
!* exception *
!*===========================================================================*
! This is called for all exceptions which do not push an error code.
.align 16
exception:
sseg mov (trap_errno), 0 ! clear trap_errno
sseg pop (ex_number)
jmp exception1
!*===========================================================================*
!* errexception *
!*===========================================================================*
! This is called for all exceptions which push an error code.
.align 16
errexception:
sseg pop (ex_number)
sseg pop (trap_errno)
exception1: ! Common for all exceptions.
sseg mov (old_eax_ptr), esp ! where will eax be saved?
sseg sub (old_eax_ptr), PCREG-AXREG ! here
push eax ! eax is scratch register
mov eax, 0+4(esp) ! old eip
sseg mov (old_eip), eax
mov eax, esp
add eax, 4
sseg mov (old_eip_ptr), eax
movzx eax, 4+4(esp) ! old cs
sseg mov (old_cs), eax
mov eax, 8+4(esp) ! old eflags
sseg mov (old_eflags), eax
pop eax
call save
push (pagefaultcr2)
push (old_eax_ptr)
push (old_eip_ptr)
push (old_eflags)
push (old_cs)
push (old_eip)
push (trap_errno)
push (ex_number)
call _exception ! (ex_number, trap_errno, old_eip,
! old_cs, old_eflags)
add esp, 8*4
ret
!*===========================================================================*
!* write_cr3 *
!*===========================================================================*
! PUBLIC void write_cr3(unsigned long value);
_write_cr3:
push ebp
mov ebp, esp
mov eax, 8(ebp)
cmp eax, (loadedcr3)
jz 0f
mov cr3, eax
mov (loadedcr3), eax
mov (_dirtypde), 0
0:
pop ebp
ret
!*===========================================================================*
!* reload_cr3 *
!*===========================================================================*
! PUBLIC void reload_cr3(void);
_reload_cr3:
push ebp
mov ebp, esp
mov (_dirtypde), 0
mov eax, cr3
mov cr3, eax
pop ebp
ret
!*===========================================================================*
!* level0_call *
!*===========================================================================*
_level0_call:
call save
jmp (_level0_func)
!*===========================================================================*
!* data *
!*===========================================================================*
.sect .rom ! Before the string table please
.data2 0x526F ! this must be the first data entry (magic #)
.sect .bss
k_stack:
.space K_STACK_BYTES ! kernel stack
k_stktop: ! top of kernel stack
.comm ex_number, 4
.comm trap_errno, 4
.comm old_eip_ptr, 4
.comm old_eax_ptr, 4
.comm old_eip, 4
.comm old_cs, 4
.comm old_eflags, 4
.comm pagefaultcr2, 4
.comm loadedcr3, 4