minix/kernel/klib386.s
2005-04-29 15:36:43 +00:00

518 lines
13 KiB
ArmAsm
Executable file

#
! sections
.sect .text; .sect .rom; .sect .data; .sect .bss
#include <minix/config.h>
#include <minix/const.h>
#include "const.h"
#include "sconst.h"
#include "protect.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 _enable_irq ! enable an irq at the 8259 controller
.define _disable_irq ! disable an irq
.define _phys_copy ! copy data from anywhere to 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_tsc ! read the cycle counter (Pentium and up)
! 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
!*===========================================================================*
!* cp_mess *
!*===========================================================================*
! PUBLIC void cp_mess(int src, phys_clicks src_clicks, vir_bytes src_offset,
! phys_clicks dst_clicks, vir_bytes dst_offset);
! This routine makes a fast copy of a message from anywhere in the address
! space to anywhere else. It also copies the source address provided as a
! parameter to the call into the first word of the destination message.
!
! Note that the message size, "Msize" is in DWORDS (not bytes) and must be set
! correctly. Changing the definition of message in the type file and not
! changing it here will lead to total disaster.
CM_ARGS = 4 + 4 + 4 + 4 + 4 ! 4 + 4 + 4 + 4 + 4
! es ds edi esi eip proc scl sof dcl dof
.align 16
_cp_mess:
cld
push esi
push edi
push ds
push es
mov eax, FLAT_DS_SELECTOR
mov ds, ax
mov es, ax
mov esi, CM_ARGS+4(esp) ! src clicks
shl esi, CLICK_SHIFT
add esi, CM_ARGS+4+4(esp) ! src offset
mov edi, CM_ARGS+4+4+4(esp) ! dst clicks
shl edi, CLICK_SHIFT
add edi, CM_ARGS+4+4+4+4(esp) ! dst offset
mov eax, CM_ARGS(esp) ! process number of sender
stos ! copy number of sender to dest message
add esi, 4 ! do not copy first word
mov ecx, Msize - 1 ! remember, first word does not count
rep
movs ! copy the message
pop es
pop ds
pop edi
pop esi
ret ! that is all folks!
!*===========================================================================*
!* 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
!*==========================================================================*
!* enable_irq *
!*==========================================================================*/
! PUBLIC void enable_irq(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
_enable_irq:
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
!*==========================================================================*
!* disable_irq *
!*==========================================================================*/
! PUBLIC int disable_irq(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
_disable_irq:
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 void 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
pop es
pop edi
pop esi
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_tsc *
!*===========================================================================*
! PUBLIC void read_tsc(unsigned long *low, unsigned long *high);
! Read the cycle counter of the CPU. Pentium and up.
.align 16
_read_tsc:
.data1 0x0f ! this is the RDTSC instruction
.data1 0x31 ! it places the TSC in EDX:EAX
push ebp
mov ebp, 8(esp)
mov (ebp), edx
mov ebp, 12(esp)
mov (ebp), eax
pop ebp
ret