cd8b915ed9
- 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
585 lines
14 KiB
ArmAsm
Executable file
585 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
|
|
|