minix/kernel/arch/i386/klib386.s
Ben Gras cd8b915ed9 Primary goal for these changes is:
- no longer have kernel have its own page table that is loaded
    on every kernel entry (trap, interrupt, exception). the primary
    purpose is to reduce the number of required reloads.
Result:
  - kernel can only access memory of process that was running when
    kernel was entered
  - kernel must be mapped into every process page table, so traps to
    kernel keep working
Problem:
  - kernel must often access memory of arbitrary processes (e.g. send
    arbitrary processes messages); this can't happen directly any more;
    usually because that process' page table isn't loaded at all, sometimes
    because that memory isn't mapped in at all, sometimes because it isn't
    mapped in read-write.
So:
  - kernel must be able to map in memory of any process, in its own
    address space.
Implementation:
  - VM and kernel share a range of memory in which addresses of
    all page tables of all processes are available. This has two purposes:
      . Kernel has to know what data to copy in order to map in a range
      . Kernel has to know where to write the data in order to map it in
    That last point is because kernel has to write in the currently loaded
    page table.
  - Processes and kernel are separated through segments; kernel segments
    haven't changed.
  - The kernel keeps the process whose page table is currently loaded
    in 'ptproc.'
  - If it wants to map in a range of memory, it writes the value of the
    page directory entry for that range into the page directory entry
    in the currently loaded map. There is a slot reserved for such
    purposes. The kernel can then access this memory directly.
  - In order to do this, its segment has been increased (and the
    segments of processes start where it ends).
  - In the pagefault handler, detect if the kernel is doing
    'trappable' memory access (i.e. a pagefault isn't a fatal
     error) and if so,
       - set the saved instruction pointer to phys_copy_fault,
	 breaking out of phys_copy
       - set the saved eax register to the address of the page
	 fault, both for sanity checking and for checking in
	 which of the two ranges that phys_copy was called
	 with the fault occured
  - Some boot-time processes do not have their own page table,
    and are mapped in with the kernel, and separated with
    segments. The kernel detects this using HASPT. If such a
    process has to be scheduled, any page table will work and
    no page table switch is done.

Major changes in kernel are
  - When accessing user processes memory, kernel no longer
    explicitly checks before it does so if that memory is OK.
    It simply makes the mapping (if necessary), tries to do the
    operation, and traps the pagefault if that memory isn't present;
    if that happens, the copy function returns EFAULT.
    So all of the CHECKRANGE_OR_SUSPEND macros are gone.
  - Kernel no longer has to copy/read and parse page tables.
  - A message copying optimisation: when messages are copied, and
    the recipient isn't mapped in, they are copied into a buffer
    in the kernel. This is done in QueueMess. The next time
    the recipient is scheduled, this message is copied into
    its memory. This happens in schedcheck().
    This eliminates the mapping/copying step for messages, and makes
    it easier to deliver messages. This eliminates soft_notify.
  - Kernel no longer creates a page table at all, so the vm_setbuf
    and pagetable writing in memory.c is gone.

Minor changes in kernel are
  - ipc_stats thrown out, wasn't used
  - misc flags all renamed to MF_*
  - NOREC_* macros to enter and leave functions that should not
    be called recursively; just sanity checks really
  - code to fully decode segment selectors and descriptors
    to print on exceptions
  - lots of vmassert()s added, only executed if DEBUG_VMASSERT is 1
2009-09-21 14:31:52 +00:00

586 lines
14 KiB
ArmAsm
Executable file

#
! sections
.sect .text; .sect .rom; .sect .data; .sect .bss
#include <minix/config.h>
#include <minix/const.h>
#include <ibm/interrupt.h>
#include <archconst.h>
#include "../../const.h"
#include "sconst.h"
! This file contains a number of assembly code utility routines needed by the
! kernel. They are:
.define _monitor ! exit Minix and return to the monitor
.define _int86 ! let the monitor make an 8086 interrupt call
!.define _cp_mess ! copies messages from source to destination
.define _exit ! dummy for library routines
.define __exit ! dummy for library routines
.define ___exit ! dummy for library routines
.define ___main ! dummy for GCC
.define _phys_insw ! transfer data from (disk controller) port to memory
.define _phys_insb ! likewise byte by byte
.define _phys_outsw ! transfer data from memory to (disk controller) port
.define _phys_outsb ! likewise byte by byte
.define _intr_unmask ! enable an irq at the 8259 controller
.define _intr_mask ! disable an irq
.define _phys_copy ! copy data from anywhere to anywhere in memory
.define _phys_copy_fault! phys_copy pagefault
.define _phys_memset ! write pattern anywhere in memory
.define _mem_rdw ! copy one word from [segment:offset]
.define _reset ! reset the system
.define _idle_task ! task executed when there is no work
.define _level0 ! call a function at level 0
.define _read_cpu_flags ! read the cpu flags
.define _read_cr0 ! read cr0
.define _getcr3val
.define _write_cr0 ! write a value in cr0
.define _read_cr4
.define _thecr3
.define _write_cr4
.define _catch_pagefaults
! The routines only guarantee to preserve the registers the C compiler
! expects to be preserved (ebx, esi, edi, ebp, esp, segment registers, and
! direction bit in the flags).
.sect .text
!*===========================================================================*
!* monitor *
!*===========================================================================*
! PUBLIC void monitor();
! Return to the monitor.
_monitor:
mov esp, (_mon_sp) ! restore monitor stack pointer
o16 mov dx, SS_SELECTOR ! monitor data segment
mov ds, dx
mov es, dx
mov fs, dx
mov gs, dx
mov ss, dx
pop edi
pop esi
pop ebp
o16 retf ! return to the monitor
!*===========================================================================*
!* int86 *
!*===========================================================================*
! PUBLIC void int86();
_int86:
cmpb (_mon_return), 0 ! is the monitor there?
jnz 0f
movb ah, 0x01 ! an int 13 error seems appropriate
movb (_reg86+ 0), ah ! reg86.w.f = 1 (set carry flag)
movb (_reg86+13), ah ! reg86.b.ah = 0x01 = "invalid command"
ret
0: push ebp ! save C registers
push esi
push edi
push ebx
pushf ! save flags
cli ! no interruptions
inb INT2_CTLMASK
movb ah, al
inb INT_CTLMASK
push eax ! save interrupt masks
mov eax, (_irq_use) ! map of in-use IRQ's
and eax, ~[1<<CLOCK_IRQ] ! keep the clock ticking
outb INT_CTLMASK ! enable all unused IRQ's and vv.
movb al, ah
outb INT2_CTLMASK
mov eax, SS_SELECTOR ! monitor data segment
mov ss, ax
xchg esp, (_mon_sp) ! switch stacks
push (_reg86+36) ! parameters used in INT call
push (_reg86+32)
push (_reg86+28)
push (_reg86+24)
push (_reg86+20)
push (_reg86+16)
push (_reg86+12)
push (_reg86+ 8)
push (_reg86+ 4)
push (_reg86+ 0)
mov ds, ax ! remaining data selectors
mov es, ax
mov fs, ax
mov gs, ax
push cs
push return ! kernel return address and selector
o16 jmpf 20+2*4+10*4+2*4(esp) ! make the call
return:
pop (_reg86+ 0)
pop (_reg86+ 4)
pop (_reg86+ 8)
pop (_reg86+12)
pop (_reg86+16)
pop (_reg86+20)
pop (_reg86+24)
pop (_reg86+28)
pop (_reg86+32)
pop (_reg86+36)
lgdt (_gdt+GDT_SELECTOR) ! reload global descriptor table
jmpf CS_SELECTOR:csinit ! restore everything
csinit: mov eax, DS_SELECTOR
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
xchg esp, (_mon_sp) ! unswitch stacks
lidt (_gdt+IDT_SELECTOR) ! reload interrupt descriptor table
andb (_gdt+TSS_SELECTOR+DESC_ACCESS), ~0x02 ! clear TSS busy bit
mov eax, TSS_SELECTOR
ltr ax ! set TSS register
pop eax
outb INT_CTLMASK ! restore interrupt masks
movb al, ah
outb INT2_CTLMASK
add (_lost_ticks), ecx ! record lost clock ticks
popf ! restore flags
pop ebx ! restore C registers
pop edi
pop esi
pop ebp
ret
!*===========================================================================*
!* exit *
!*===========================================================================*
! PUBLIC void exit();
! Some library routines use exit, so provide a dummy version.
! Actual calls to exit cannot occur in the kernel.
! GNU CC likes to call ___main from main() for nonobvious reasons.
_exit:
__exit:
___exit:
sti
jmp ___exit
___main:
ret
!*===========================================================================*
!* phys_insw *
!*===========================================================================*
! PUBLIC void phys_insw(Port_t port, phys_bytes buf, size_t count);
! Input an array from an I/O port. Absolute address version of insw().
_phys_insw:
push ebp
mov ebp, esp
cld
push edi
push es
mov ecx, FLAT_DS_SELECTOR
mov es, cx
mov edx, 8(ebp) ! port to read from
mov edi, 12(ebp) ! destination addr
mov ecx, 16(ebp) ! byte count
shr ecx, 1 ! word count
rep o16 ins ! input many words
pop es
pop edi
pop ebp
ret
!*===========================================================================*
!* phys_insb *
!*===========================================================================*
! PUBLIC void phys_insb(Port_t port, phys_bytes buf, size_t count);
! Input an array from an I/O port. Absolute address version of insb().
_phys_insb:
push ebp
mov ebp, esp
cld
push edi
push es
mov ecx, FLAT_DS_SELECTOR
mov es, cx
mov edx, 8(ebp) ! port to read from
mov edi, 12(ebp) ! destination addr
mov ecx, 16(ebp) ! byte count
! shr ecx, 1 ! word count
rep insb ! input many bytes
pop es
pop edi
pop ebp
ret
!*===========================================================================*
!* phys_outsw *
!*===========================================================================*
! PUBLIC void phys_outsw(Port_t port, phys_bytes buf, size_t count);
! Output an array to an I/O port. Absolute address version of outsw().
.align 16
_phys_outsw:
push ebp
mov ebp, esp
cld
push esi
push ds
mov ecx, FLAT_DS_SELECTOR
mov ds, cx
mov edx, 8(ebp) ! port to write to
mov esi, 12(ebp) ! source addr
mov ecx, 16(ebp) ! byte count
shr ecx, 1 ! word count
rep o16 outs ! output many words
pop ds
pop esi
pop ebp
ret
!*===========================================================================*
!* phys_outsb *
!*===========================================================================*
! PUBLIC void phys_outsb(Port_t port, phys_bytes buf, size_t count);
! Output an array to an I/O port. Absolute address version of outsb().
.align 16
_phys_outsb:
push ebp
mov ebp, esp
cld
push esi
push ds
mov ecx, FLAT_DS_SELECTOR
mov ds, cx
mov edx, 8(ebp) ! port to write to
mov esi, 12(ebp) ! source addr
mov ecx, 16(ebp) ! byte count
rep outsb ! output many bytes
pop ds
pop esi
pop ebp
ret
!*==========================================================================*
!* intr_unmask *
!*==========================================================================*/
! PUBLIC void intr_unmask(irq_hook_t *hook)
! Enable an interrupt request line by clearing an 8259 bit.
! Equivalent C code for hook->irq < 8:
! if ((irq_actids[hook->irq] &= ~hook->id) == 0)
! outb(INT_CTLMASK, inb(INT_CTLMASK) & ~(1 << irq));
.align 16
_intr_unmask:
push ebp
mov ebp, esp
pushf
cli
mov eax, 8(ebp) ! hook
mov ecx, 8(eax) ! irq
mov eax, 12(eax) ! id bit
not eax
and _irq_actids(ecx*4), eax ! clear this id bit
jnz en_done ! still masked by other handlers?
movb ah, ~1
rolb ah, cl ! ah = ~(1 << (irq % 8))
mov edx, INT_CTLMASK ! enable irq < 8 at the master 8259
cmpb cl, 8
jb 0f
mov edx, INT2_CTLMASK ! enable irq >= 8 at the slave 8259
0: inb dx
andb al, ah
outb dx ! clear bit at the 8259
en_done:popf
leave
ret
!*==========================================================================*
!* intr_mask *
!*==========================================================================*/
! PUBLIC int intr_mask(irq_hook_t *hook)
! Disable an interrupt request line by setting an 8259 bit.
! Equivalent C code for irq < 8:
! irq_actids[hook->irq] |= hook->id;
! outb(INT_CTLMASK, inb(INT_CTLMASK) | (1 << irq));
! Returns true iff the interrupt was not already disabled.
.align 16
_intr_mask:
push ebp
mov ebp, esp
pushf
cli
mov eax, 8(ebp) ! hook
mov ecx, 8(eax) ! irq
mov eax, 12(eax) ! id bit
or _irq_actids(ecx*4), eax ! set this id bit
movb ah, 1
rolb ah, cl ! ah = (1 << (irq % 8))
mov edx, INT_CTLMASK ! disable irq < 8 at the master 8259
cmpb cl, 8
jb 0f
mov edx, INT2_CTLMASK ! disable irq >= 8 at the slave 8259
0: inb dx
testb al, ah
jnz dis_already ! already disabled?
orb al, ah
outb dx ! set bit at the 8259
mov eax, 1 ! disabled by this function
popf
leave
ret
dis_already:
xor eax, eax ! already disabled
popf
leave
ret
!*===========================================================================*
!* phys_copy *
!*===========================================================================*
! PUBLIC phys_bytes phys_copy(phys_bytes source, phys_bytes destination,
! phys_bytes bytecount);
! Copy a block of physical memory.
PC_ARGS = 4 + 4 + 4 + 4 ! 4 + 4 + 4
! es edi esi eip src dst len
.align 16
_phys_copy:
cld
push esi
push edi
push es
mov eax, FLAT_DS_SELECTOR
mov es, ax
mov esi, PC_ARGS(esp)
mov edi, PC_ARGS+4(esp)
mov eax, PC_ARGS+4+4(esp)
cmp eax, 10 ! avoid align overhead for small counts
jb pc_small
mov ecx, esi ! align source, hope target is too
neg ecx
and ecx, 3 ! count for alignment
sub eax, ecx
rep
eseg movsb
mov ecx, eax
shr ecx, 2 ! count of dwords
rep
eseg movs
and eax, 3
pc_small:
xchg ecx, eax ! remainder
rep
eseg movsb
mov eax, 0 ! 0 means: no fault
_phys_copy_fault: ! kernel can send us here
pop es
pop edi
pop esi
ret
!*===========================================================================*
!* phys_memset *
!*===========================================================================*
! PUBLIC void phys_memset(phys_bytes source, unsigned long pattern,
! phys_bytes bytecount);
! Fill a block of physical memory with pattern.
.align 16
_phys_memset:
push ebp
mov ebp, esp
push esi
push ebx
push ds
mov esi, 8(ebp)
mov eax, 16(ebp)
mov ebx, FLAT_DS_SELECTOR
mov ds, bx
mov ebx, 12(ebp)
shr eax, 2
fill_start:
mov (esi), ebx
add esi, 4
dec eax
jnz fill_start
! Any remaining bytes?
mov eax, 16(ebp)
and eax, 3
remain_fill:
cmp eax, 0
jz fill_done
movb bl, 12(ebp)
movb (esi), bl
add esi, 1
inc ebp
dec eax
jmp remain_fill
fill_done:
pop ds
pop ebx
pop esi
pop ebp
ret
!*===========================================================================*
!* mem_rdw *
!*===========================================================================*
! PUBLIC u16_t mem_rdw(U16_t segment, u16_t *offset);
! Load and return word at far pointer segment:offset.
.align 16
_mem_rdw:
mov cx, ds
mov ds, 4(esp) ! segment
mov eax, 4+4(esp) ! offset
movzx eax, (eax) ! word to return
mov ds, cx
ret
!*===========================================================================*
!* reset *
!*===========================================================================*
! PUBLIC void reset();
! Reset the system by loading IDT with offset 0 and interrupting.
_reset:
lidt (idt_zero)
int 3 ! anything goes, the 386 will not like it
.sect .data
idt_zero: .data4 0, 0
.sect .text
!*===========================================================================*
!* idle_task *
!*===========================================================================*
_idle_task:
! This task is called when the system has nothing else to do. The HLT
! instruction puts the processor in a state where it draws minimum power.
push halt
call _level0 ! level0(halt)
pop eax
jmp _idle_task
halt:
sti
hlt
cli
ret
!*===========================================================================*
!* level0 *
!*===========================================================================*
! PUBLIC void level0(void (*func)(void))
! Call a function at permission level 0. This allows kernel tasks to do
! things that are only possible at the most privileged CPU level.
!
_level0:
mov eax, 4(esp)
mov (_level0_func), eax
int LEVEL0_VECTOR
ret
!*===========================================================================*
!* read_flags *
!*===========================================================================*
! PUBLIC unsigned long read_cpu_flags(void);
! Read CPU status flags from C.
.align 16
_read_cpu_flags:
pushf
mov eax, (esp)
popf
ret
!*===========================================================================*
!* read_cr0 *
!*===========================================================================*
! PUBLIC unsigned long read_cr0(void);
_read_cr0:
push ebp
mov ebp, esp
mov eax, cr0
pop ebp
ret
!*===========================================================================*
!* write_cr0 *
!*===========================================================================*
! PUBLIC void write_cr0(unsigned long value);
_write_cr0:
push ebp
mov ebp, esp
mov eax, 8(ebp)
mov cr0, eax
jmp 0f ! A jump is required for some flags
0:
pop ebp
ret
!*===========================================================================*
!* read_cr4 *
!*===========================================================================*
! PUBLIC unsigned long read_cr4(void);
_read_cr4:
push ebp
mov ebp, esp
.data1 0x0f, 0x20, 0xe0 ! mov eax, cr4
pop ebp
ret
!*===========================================================================*
!* write_cr4 *
!*===========================================================================*
! PUBLIC void write_cr4(unsigned long value);
_write_cr4:
push ebp
mov ebp, esp
mov eax, 8(ebp)
.data1 0x0f, 0x22, 0xe0 ! mov cr4, eax
jmp 0f
0:
pop ebp
ret
!*===========================================================================*
!* getcr3val *
!*===========================================================================*
! PUBLIC unsigned long getcr3val(void);
_getcr3val:
mov eax, cr3
mov (_thecr3), eax
ret