i386/i386/locore.s Source
/*-
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * William Jolitz.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	from: @(#)locore.s	7.3 (Berkeley) 5/13/91
 *	$Id: locore.s,v 1.8 1993/10/15 10:34:19 rgrimes Exp $
 */


/*
 * locore.s:	4BSD machine support for the Intel 386
 *		Preliminary version
 *		Written by William F. Jolitz, 386BSD Project
 */

#include "npx.h"

#include "assym.s"
#include "machine/psl.h"
#include "machine/pte.h"

#include "errno.h"

#include "machine/trap.h"

#include "machine/specialreg.h"
#include "i386/isa/debug.h"
#include "machine/cputypes.h"

#define	KDSEL		0x10
#define	SEL_RPL_MASK	0x0003
#define	TRAPF_CS_OFF	(13 * 4)

/*
 * Note: This version greatly munged to avoid various assembler errors
 * that may be fixed in newer versions of gas. Perhaps newer versions
 * will have more pleasant appearance.
 */

	.set	IDXSHIFT,10

#define	ALIGN_DATA	.align	2
#define	ALIGN_TEXT	.align	2,0x90	/* 4-byte boundaries, NOP-filled */
#define	SUPERALIGN_TEXT	.align	4,0x90	/* 16-byte boundaries better for 486 */

#define	GEN_ENTRY(name)		ALIGN_TEXT; .globl name; name:
#define	NON_GPROF_ENTRY(name)	GEN_ENTRY(_/**/name)

#ifdef GPROF
/*
 * ALTENTRY() must be before a corresponding ENTRY() so that it can jump
 * over the mcounting.
 */
#define	ALTENTRY(name)		GEN_ENTRY(_/**/name); MCOUNT; jmp 2f
#define	ENTRY(name)		GEN_ENTRY(_/**/name); MCOUNT; 2:
/*
 * The call to mcount supports the usual (bad) conventions.  We allocate
 * some data and pass a pointer to it although the 386BSD doesn't use
 * the data.  We set up a frame before calling mcount because that is
 * the standard convention although it makes work for both mcount and
 * callers.
 */
#define MCOUNT			.data; ALIGN_DATA; 1:; .long 0; .text; \
				pushl %ebp; movl %esp,%ebp; \
				movl $1b,%eax; call mcount; popl %ebp
#else
/*
 * ALTENTRY() has to align because it is before a corresponding ENTRY().
 * ENTRY() has to align to because there may be no ALTENTRY() before it.
 * If there is a previous ALTENTRY() then the alignment code is empty.
 */
#define	ALTENTRY(name)		GEN_ENTRY(_/**/name)
#define	ENTRY(name)		GEN_ENTRY(_/**/name)
#endif

/* NB: NOP now preserves registers so NOPs can be inserted anywhere */
/* XXX: NOP and FASTER_NOP are misleadingly named */
#ifdef DUMMY_NOPS	/* this will break some older machines */
#define	FASTER_NOP
#define	NOP
#else
#define	FASTER_NOP	pushl %eax ; inb $0x84,%al ; popl %eax
#define	NOP	pushl %eax ; inb $0x84,%al ; inb $0x84,%al ; popl %eax
#endif

/*
 * PTmap is recursive pagemap at top of virtual address space.
 * Within PTmap, the page directory can be found (third indirection).
 */
	.globl	_PTmap,_PTD,_PTDpde,_Sysmap
	.set	_PTmap,PTDPTDI << PDRSHIFT
	.set	_PTD,_PTmap + (PTDPTDI * NBPG)
	.set	_PTDpde,_PTD + (PTDPTDI * 4)		/* XXX 4=sizeof pde */

	.set	_Sysmap,_PTmap + (KPTDI * NBPG)

/*
 * APTmap, APTD is the alternate recursive pagemap.
 * It's used when modifying another process's page tables.
 */
	.globl	_APTmap,_APTD,_APTDpde
	.set	_APTmap,APTDPTDI << PDRSHIFT
	.set	_APTD,_APTmap + (APTDPTDI * NBPG)
	.set	_APTDpde,_PTD + (APTDPTDI * 4)		/* XXX 4=sizeof pde */

/*
 * Access to each processes kernel stack is via a region of
 * per-process address space (at the beginning), immediatly above
 * the user process stack.
 */
	.set	_kstack,USRSTACK
	.globl	_kstack
	.set	PPDROFF,0x3F6
	.set	PPTEOFF,0x400-UPAGES	/* 0x3FE */


/*
 * Globals
 */
	.data
	.globl	_esym
_esym:	.long	0		/* ptr to end of syms */

	.globl	_boothowto,_bootdev,_curpcb

	.globl	_cpu,_cold,_atdevbase
_cpu:	.long	0		/* are we 386, 386sx, or 486 */
_cold:	.long	1		/* cold till we are not */
_atdevbase:	.long	0	/* location of start of iomem in virtual */
_atdevphys:	.long	0	/* location of device mapping ptes (phys) */

	.globl	_IdlePTD,_KPTphys
_IdlePTD:	.long	0
_KPTphys:	.long	0

	.globl	_cyloffset,_proc0paddr
_cyloffset:	.long	0
_proc0paddr:	.long	0

	.space 512
tmpstk:


/*
 * System Initialization
 */
	.text

/*
 * btext: beginning of text section.
 * Also the entry point (jumped to directly from the boot blocks).
 */
ENTRY(btext)
	movw	$0x1234,0x472	/* warm boot */
	jmp	1f
	.space	0x500		/* skip over warm boot shit */

	/*
	 * pass parameters on stack (howto, bootdev, unit, cyloffset, esym)
	 * note: (%esp) is return address of boot
	 * ( if we want to hold onto /boot, it's physical %esp up to _end)
	 */

 1:	movl	4(%esp),%eax
	movl	%eax,_boothowto-KERNBASE
	movl	8(%esp),%eax
	movl	%eax,_bootdev-KERNBASE
	movl	12(%esp),%eax
	movl	%eax,_cyloffset-KERNBASE
	movl	16(%esp),%eax
	addl	$KERNBASE,%eax
	movl	%eax,_esym-KERNBASE

	/* find out our CPU type. */
        pushfl
        popl    %eax
        movl    %eax,%ecx
        xorl    $0x40000,%eax
        pushl   %eax
        popfl
        pushfl
        popl    %eax
        xorl    %ecx,%eax
        shrl    $18,%eax
        andl    $1,%eax
        push    %ecx
        popfl
      
        cmpl    $0,%eax
        jne     1f
        movl    $CPU_386,_cpu-KERNBASE
	jmp	2f
1:      movl    $CPU_486,_cpu-KERNBASE
2:

	/*
	 * Finished with old stack; load new %esp now instead of later so
	 * we can trace this code without having to worry about the trace
	 * trap clobbering the memory test or the zeroing of the bss+bootstrap
	 * page tables.
	 *
	 * XXX - wdboot clears the bss after testing that this is safe.
	 * This is too wasteful - memory below 640K is scarce.  The boot
	 * program should check:
	 *	text+data <= &stack_variable - more_space_for_stack
	 *	text+data+bss+pad+space_for_page_tables <= end_of_memory
	 * Oops, the gdt is in the carcass of the boot program so clearing
	 * the rest of memory is still not possible.
	 */
	movl	$tmpstk-KERNBASE,%esp	/* bootstrap stack end location */

/*
 * Virtual address space of kernel:
 *
 *	text | data | bss | [syms] | page dir | proc0 kernel stack | usr stk map | Sysmap
 *			           0               1       2       3             4
 */

/* find end of kernel image */
	movl	$_end-KERNBASE,%ecx
	addl	$NBPG-1,%ecx	/* page align up */
	andl	$~(NBPG-1),%ecx
	movl	%ecx,%esi	/* esi=start of tables */

/* clear bss and memory for bootstrap pagetables. */
	movl	$_edata-KERNBASE,%edi
	subl	%edi,%ecx
	addl	$(UPAGES+5)*NBPG,%ecx	/* size of tables */

	xorl	%eax,%eax	/* pattern */
	cld
	rep
	stosb

/*
 * If we are loaded at 0x0 check to see if we have space for the
 * page tables pages after the kernel and before the 640K ISA memory
 * hole.  If we do not have space relocate the page table pages and
 * the kernel stack to start at 1MB.  The value that ends up in esi
 * is used by the rest of locore to build the tables.  Locore adjusts
 * esi each time it allocates a structure and then passes the final
 * value to init386(first) as the value first.  esi should ALWAYS
 * be page aligned!!
 */
	movl	%esi,%ecx	/* Get current first availiable address */
	cmpl	$0x100000,%ecx	/* Lets see if we are already above 1MB */
	jge	1f		/* yep, don't need to check for room */
	addl	$(NKPDE + 4) * NBPG,%ecx	/* XXX the 4 is for kstack */
				/* space for kstack, PTD and PTE's */
	cmpl	$(640*1024),%ecx
				/* see if it fits in low memory */
	jle	1f		/* yep, don't need to relocate it */
	movl	$0x100000,%esi	/* won't fit, so start it at 1MB */
1:

/* physical address of Idle Address space */
	movl	%esi,_IdlePTD-KERNBASE

/*
 * fillkpt
 *	eax = (page frame address | control | status) == pte
 *	ebx = address of page table
 *	ecx = how many pages to map
 */
#define	fillkpt		\
1:	movl	%eax,(%ebx)	; \
	addl	$NBPG,%eax	; /* increment physical address */ \
	addl	$4,%ebx		; /* next pte */ \
	loop	1b		;

/*
 * Map Kernel
 * N.B. don't bother with making kernel text RO, as 386
 * ignores R/W AND U/S bits on kernel access (only v works) !
 *
 * First step - build page tables
 */
	movl	%esi,%ecx		/* this much memory, */
	shrl	$PGSHIFT,%ecx		/* for this many pte s */
	addl	$UPAGES+4,%ecx		/* including our early context */
	cmpl	$0xa0,%ecx		/* XXX - cover debugger pages */
	jae	1f
	movl	$0xa0,%ecx
1:
	movl	$PG_V|PG_KW,%eax	/*  having these bits set, */
	lea	(4*NBPG)(%esi),%ebx	/*   physical address of KPT in proc 0, */
	movl	%ebx,_KPTphys-KERNBASE	/*    in the kernel page table, */
	fillkpt

/* map I/O memory map */

	movl	$0x100-0xa0,%ecx	/* for this many pte s, */
	movl	$(0xa0000|PG_V|PG_UW),%eax /* having these bits set,(perhaps URW?) XXX 06 Aug 92 */
	movl	%ebx,_atdevphys-KERNBASE	/*   remember phys addr of ptes */
	fillkpt

 /* map proc 0's kernel stack into user page table page */

	movl	$UPAGES,%ecx		/* for this many pte s, */
	lea	(1*NBPG)(%esi),%eax	/* physical address in proc 0 */
	lea	(KERNBASE)(%eax),%edx
	movl	%edx,_proc0paddr-KERNBASE
					/* remember VA for 0th process init */
	orl	$PG_V|PG_KW,%eax	/*  having these bits set, */
	lea	(3*NBPG)(%esi),%ebx	/* physical address of stack pt in proc 0 */
	addl	$(PPTEOFF*4),%ebx
	fillkpt

/*
 * Construct a page table directory
 * (of page directory elements - pde's)
 */
	/* install a pde for temporary double map of bottom of VA */
	lea	(4*NBPG)(%esi),%eax	/* physical address of kernel page table */
	orl     $PG_V|PG_UW,%eax	/* pde entry is valid XXX 06 Aug 92 */
	movl	%eax,(%esi)		/* which is where temp maps! */

	/* kernel pde's */
	movl	$(NKPDE),%ecx			/* for this many pde s, */
	lea	(KPTDI*4)(%esi),%ebx	/* offset of pde for kernel */
	fillkpt

	/* install a pde recursively mapping page directory as a page table! */
	movl	%esi,%eax		/* phys address of ptd in proc 0 */
	orl	$PG_V|PG_UW,%eax	/* pde entry is valid XXX 06 Aug 92 */
	movl	%eax,PTDPTDI*4(%esi)	/* which is where PTmap maps! */

	/* install a pde to map kernel stack for proc 0 */
	lea	(3*NBPG)(%esi),%eax	/* physical address of pt in proc 0 */
	orl	$PG_V|PG_KW,%eax	/* pde entry is valid */
	movl	%eax,PPDROFF*4(%esi)	/* which is where kernel stack maps! */

	/* copy and convert stuff from old gdt and idt for debugger */

	cmpl	$0x0375c339,0x96104	/* XXX - debugger signature */
	jne	1f
	movb	$1,_bdb_exists-KERNBASE
1:
	pushal
	subl	$2*6,%esp

	sgdt	(%esp)
	movl	2(%esp),%esi		/* base address of current gdt */
	movl	$_gdt-KERNBASE,%edi
	movl	%edi,2(%esp)
	movl	$8*18/4,%ecx
	rep				/* copy gdt */
	movsl
	movl	$_gdt-KERNBASE,-8+2(%edi)	/* adjust gdt self-ptr */
	movb	$0x92,-8+5(%edi)

	sidt	6(%esp)
	movl	6+2(%esp),%esi		/* base address of current idt */
	movl	8+4(%esi),%eax		/* convert dbg descriptor to ... */
	movw	8(%esi),%ax
	movl	%eax,bdb_dbg_ljmp+1-KERNBASE	/* ... immediate offset ... */
	movl	8+2(%esi),%eax
	movw	%ax,bdb_dbg_ljmp+5-KERNBASE	/* ... and selector for ljmp */
	movl	24+4(%esi),%eax		/* same for bpt descriptor */
	movw	24(%esi),%ax
	movl	%eax,bdb_bpt_ljmp+1-KERNBASE
	movl	24+2(%esi),%eax
	movw	%ax,bdb_bpt_ljmp+5-KERNBASE

	movl	$_idt-KERNBASE,%edi
	movl	%edi,6+2(%esp)
	movl	$8*4/4,%ecx
	rep				/* copy idt */
	movsl

	lgdt	(%esp)
	lidt	6(%esp)

	addl	$2*6,%esp
	popal

	/* load base of page directory and enable mapping */
	movl	%esi,%eax		/* phys address of ptd in proc 0 */
	orl	$I386_CR3PAT,%eax
	movl	%eax,%cr3		/* load ptd addr into mmu */
	movl	%cr0,%eax		/* get control word */
/*
 * XXX it is now safe to always (attempt to) set CR0_WP and to set up
 * the page tables assuming it works, so USE_486_WRITE_PROTECT will go
 * away.  The special 386 PTE checking needs to be conditional on
 * whatever distingiushes 486-only kernels from 386-486 kernels.
 */
#ifdef USE_486_WRITE_PROTECT
	orl	$CR0_PE|CR0_PG|CR0_WP,%eax	/* enable paging */
#else
	orl	$CR0_PE|CR0_PG,%eax	/* enable paging */
#endif
	movl	%eax,%cr0		/* and let's page NOW! */

	pushl	$begin			/* jump to high mem */
	ret

begin: /* now running relocated at KERNBASE where the system is linked to run */

	.globl _Crtat		/* XXX - locore should not know about */
	movl	_Crtat,%eax	/* variables of device drivers (pccons)! */
	subl	$(KERNBASE+0xA0000),%eax
	movl	_atdevphys,%edx	/* get pte PA */
	subl	_KPTphys,%edx	/* remove base of ptes, now have phys offset */
	shll	$PGSHIFT-2,%edx	/* corresponding to virt offset */
	addl	$KERNBASE,%edx	/* add virtual base */
	movl	%edx,_atdevbase
	addl	%eax,%edx
	movl	%edx,_Crtat

	/* set up bootstrap stack */
	movl	$_kstack+UPAGES*NBPG-4*12,%esp	/* bootstrap stack end location */
	xorl	%eax,%eax		/* mark end of frames */
	movl	%eax,%ebp
	movl	_proc0paddr,%eax
	movl	%esi,PCB_CR3(%eax)

	/* relocate debugger gdt entries */

	movl	$_gdt+8*9,%eax		/* adjust slots 9-17 */
	movl	$9,%ecx
reloc_gdt:
	movb	$0xfe,7(%eax)		/* top byte of base addresses, was 0, */
	addl	$8,%eax			/* now KERNBASE>>24 */
	loop	reloc_gdt

	cmpl	$0,_bdb_exists
	je	1f
	int	$3
1:

	/*
	 * Skip over the page tables and the kernel stack
	 * XXX 4 is kstack size
	 */
	lea	(NKPDE + 4) * NBPG(%esi),%esi

	pushl	%esi			/* value of first for init386(first) */
	call	_init386		/* wire 386 chip for unix operation */

	movl	$0,_PTD
	call	_main			/* autoconfiguration, mountroot etc */
	popl	%esi

	/*
	 * now we've run main() and determined what cpu-type we are, we can
	 * enable WP mode on i486 cpus and above.
	 * on return from main(), we are process 1
	 * set up address space and stack so that we can 'return' to user mode
	 */

	.globl	__ucodesel,__udatasel
	movl	__ucodesel,%eax
	movl	__udatasel,%ecx
	/* build outer stack frame */
	pushl	%ecx		/* user ss */
	pushl	$USRSTACK	/* user esp */
	pushl	%eax		/* user cs */
	pushl	$0		/* user ip */
	movl	%cx,%ds
	movl	%cx,%es
	movl	%ax,%fs		/* double map cs to fs */
	movl	%cx,%gs		/* and ds to gs */
	lret	/* goto user! */

	pushl	$lretmsg1	/* "should never get here!" */
	call	_panic
lretmsg1:
	.asciz	"lret: toinit\n"


	.set	exec,59
	.set	exit,1

#define	LCALL(x,y)	.byte 0x9a ; .long y; .word x
/*
 * Icode is copied out to process 1 and executed in user mode:
 *	execve("/sbin/init", argv, envp); exit(0);
 * If the execve fails, process 1 exits and the system panics.
 */
NON_GPROF_ENTRY(icode)
	pushl	$0		/* envp for execve() */

#	pushl	$argv-_icode	/* can't do this 'cos gas 1.38 is broken */
	movl	$argv,%eax
	subl	$_icode,%eax
	pushl	%eax		/* argp for execve() */

#	pushl	$init-_icode
	movl	$init,%eax
	subl	$_icode,%eax
	pushl	%eax		/* fname for execve() */

	pushl	%eax		/* dummy return address */

	movl	$exec,%eax
	LCALL(0x7,0x0)

	/* exit if something botches up in the above execve() */
	pushl	%eax		/* execve failed, the errno will do for an */
				/* exit code because errnos are < 128 */
	pushl	%eax		/* dummy return address */
	movl	$exit,%eax
	LCALL(0x7,0x0)

init:
	.asciz	"/sbin/init"
	ALIGN_DATA
argv:
	.long	init+6-_icode		/* argv[0] = "init" ("/sbin/init" + 6) */
	.long	eicode-_icode		/* argv[1] follows icode after copyout */
	.long	0
eicode:

	.globl	_szicode
_szicode:
	.long	_szicode-_icode

NON_GPROF_ENTRY(sigcode)
	call	SIGF_HANDLER(%esp)
	lea	SIGF_SC(%esp),%eax	/* scp (the call may have clobbered the */
					/* copy at 8(%esp)) */
	pushl	%eax
	pushl	%eax		/* junk to fake return address */
	movl	$103,%eax	/* XXX sigreturn() */
	LCALL(0x7,0)		/* enter kernel with args on stack */
	hlt			/* never gets here */

	.globl	_szsigcode
_szsigcode:
	.long	_szsigcode-_sigcode

/*
 * Support routines for GCC, general C-callable functions
 */
ENTRY(__udivsi3)
	movl 4(%esp),%eax
	xorl %edx,%edx
	divl 8(%esp)
	ret

ENTRY(__divsi3)
	movl 4(%esp),%eax
	cltd
	idivl 8(%esp)
	ret

	/*
	 * I/O bus instructions via C
	 */
ENTRY(inb)			/* val = inb(port) */
	movl	4(%esp),%edx
	subl	%eax,%eax
	NOP
	inb	%dx,%al
	ret

ENTRY(inw)			/* val = inw(port) */
	movl	4(%esp),%edx
	subl	%eax,%eax
	NOP
	inw	%dx,%ax
	ret

ENTRY(insb)			/* insb(port, addr, cnt) */
	pushl	%edi
	movw	8(%esp),%dx
	movl	12(%esp),%edi
	movl	16(%esp),%ecx
	cld
	NOP
	rep
	insb
	NOP
	movl	%edi,%eax
	popl	%edi
	ret

ENTRY(insw)			/* insw(port, addr, cnt) */
	pushl	%edi
	movw	8(%esp),%dx
	movl	12(%esp),%edi
	movl	16(%esp),%ecx
	cld
	NOP
	rep
	insw
	NOP
	movl	%edi,%eax
	popl	%edi
	ret

ENTRY(rtcin)			/* rtcin(val) */
	movl	4(%esp),%eax
	outb	%al,$0x70
	subl	%eax,%eax
	inb	$0x71,%al
	ret

ENTRY(outb)			/* outb(port, val) */
	movl	4(%esp),%edx
	NOP
	movl	8(%esp),%eax
	outb	%al,%dx
	NOP
	ret

ENTRY(outw)			/* outw(port, val) */
	movl	4(%esp),%edx
	NOP
	movl	8(%esp),%eax
	outw	%ax,%dx
	NOP
	ret

ENTRY(outsb)			/* outsb(port, addr, cnt) */
	pushl	%esi
	movw	8(%esp),%dx
	movl	12(%esp),%esi
	movl	16(%esp),%ecx
	cld
	NOP
	rep
	outsb
	NOP
	movl	%esi,%eax
	popl	%esi
	ret

ENTRY(outsw)			/* outsw(port, addr, cnt) */
	pushl	%esi
	movw	8(%esp),%dx
	movl	12(%esp),%esi
	movl	16(%esp),%ecx
	cld
	NOP
	rep
	outsw
	NOP
	movl	%esi,%eax
	popl	%esi
	ret

	/*
	 * bcopy family
	 */
ENTRY(bzero)			/* void bzero(void *base, u_int cnt) */
	pushl	%edi
	movl	8(%esp),%edi
	movl	12(%esp),%ecx
	xorl	%eax,%eax
	shrl	$2,%ecx
	cld
	rep
	stosl
	movl	12(%esp),%ecx
	andl	$3,%ecx
	rep
	stosb
	popl	%edi
	ret

ENTRY(fillw)			/* fillw(pat, base, cnt) */
	pushl	%edi
	movl	8(%esp),%eax
	movl	12(%esp),%edi
	movl	16(%esp),%ecx
	cld
	rep
	stosw
	popl	%edi
	ret

ENTRY(bcopyb)
bcopyb:
	pushl	%esi
	pushl	%edi
	movl	12(%esp),%esi
	movl	16(%esp),%edi
	movl	20(%esp),%ecx
	cmpl	%esi,%edi	/* potentially overlapping? */
	jnb	1f
	cld			/* nope, copy forwards */
	rep
	movsb
	popl	%edi
	popl	%esi
	ret

	ALIGN_TEXT
1:
	addl	%ecx,%edi	/* copy backwards. */
	addl	%ecx,%esi
	std
	decl	%edi
	decl	%esi
	rep
	movsb
	popl	%edi
	popl	%esi
	cld
	ret

ENTRY(bcopyw)
bcopyw:
	pushl	%esi
	pushl	%edi
	movl	12(%esp),%esi
	movl	16(%esp),%edi
	movl	20(%esp),%ecx
	cmpl	%esi,%edi	/* potentially overlapping? */
	jnb	1f
	cld			/* nope, copy forwards */
	shrl	$1,%ecx		/* copy by 16-bit words */
	rep
	movsw
	adc	%ecx,%ecx	/* any bytes left? */
	rep
	movsb
	popl	%edi
	popl	%esi
	ret

	ALIGN_TEXT
1:
	addl	%ecx,%edi	/* copy backwards */
	addl	%ecx,%esi
	std
	andl	$1,%ecx		/* any fractional bytes? */
	decl	%edi
	decl	%esi
	rep
	movsb
	movl	20(%esp),%ecx	/* copy remainder by 16-bit words */
	shrl	$1,%ecx
	decl	%esi
	decl	%edi
	rep
	movsw
	popl	%edi
	popl	%esi
	cld
	ret

ENTRY(bcopyx)
	movl	16(%esp),%eax
	cmpl	$2,%eax
	je	bcopyw		/* not _bcopyw, to avoid multiple mcounts */
	cmpl	$4,%eax
	je	bcopy
	jmp	bcopyb

	/*
	 * (ov)bcopy(src, dst, cnt)
	 *  ws@tools.de     (Wolfgang Solfrank, TooLs GmbH) +49-228-985800
	 */
ALTENTRY(ovbcopy)
ENTRY(bcopy)
bcopy:
	pushl	%esi
	pushl	%edi
	movl	12(%esp),%esi
	movl	16(%esp),%edi
	movl	20(%esp),%ecx
	cmpl	%esi,%edi	/* potentially overlapping? */
	jnb	1f
	cld			/* nope, copy forwards */
	shrl	$2,%ecx		/* copy by 32-bit words */
	rep
	movsl
	movl	20(%esp),%ecx
	andl	$3,%ecx		/* any bytes left? */
	rep
	movsb
	popl	%edi
	popl	%esi
	ret

	ALIGN_TEXT
1:
	addl	%ecx,%edi	/* copy backwards */
	addl	%ecx,%esi
	std
	andl	$3,%ecx		/* any fractional bytes? */
	decl	%edi
	decl	%esi
	rep
	movsb
	movl	20(%esp),%ecx	/* copy remainder by 32-bit words */
	shrl	$2,%ecx
	subl	$3,%esi
	subl	$3,%edi
	rep
	movsl
	popl	%edi
	popl	%esi
	cld
	ret

ALTENTRY(ntohl)
ENTRY(htonl)
	movl	4(%esp),%eax
#ifdef i486
	/* XXX */
	/* Since Gas 1.38 does not grok bswap this has been coded as the
	 * equivalent bytes.  This can be changed back to bswap when we
	 * upgrade to a newer version of Gas */
	/* bswap	%eax */
	.byte	0x0f
	.byte	0xc8
#else
	xchgb	%al,%ah
	roll	$16,%eax
	xchgb	%al,%ah
#endif
	ret

ALTENTRY(ntohs)
ENTRY(htons)
	movzwl	4(%esp),%eax
	xchgb	%al,%ah
	ret

/*****************************************************************************/
/* copyout and fubyte family                                                 */
/*****************************************************************************/
/*
 * Access user memory from inside the kernel. These routines and possibly
 * the math- and DOS emulators should be the only places that do this.
 *
 * We have to access the memory with user's permissions, so use a segment
 * selector with RPL 3. For writes to user space we have to additionally
 * check the PTE for write permission, because the 386 does not check
 * write permissions when we are executing with EPL 0. The 486 does check
 * this if the WP bit is set in CR0, so we can use a simpler version here.
 *
 * These routines set curpcb->onfault for the time they execute. When a
 * protection violation occurs inside the functions, the trap handler
 * returns to *curpcb->onfault instead of the function.
 */


ENTRY(copyout)			/* copyout(from_kernel, to_user, len) */
	movl	_curpcb,%eax
	movl	$copyout_fault,PCB_ONFAULT(%eax)
	pushl	%esi
	pushl	%edi
	pushl	%ebx
	movl	16(%esp),%esi
	movl	20(%esp),%edi
	movl	24(%esp),%ebx
	orl	%ebx,%ebx	/* anything to do? */
	jz	done_copyout

	/*
	 * Check explicitly for non-user addresses.  If 486 write protection
	 * is being used, this check is essential because we are in kernel
	 * mode so the h/w does not provide any protection against writing
	 * kernel addresses.
	 *
	 * Otherwise, it saves having to load and restore %es to get the
	 * usual segment-based protection (the destination segment for movs
	 * is always %es).  The other explicit checks for user-writablility
	 * are not quite sufficient.  They fail for the user area because
	 * we mapped the user area read/write to avoid having an #ifdef in
	 * vm_machdep.c.  They fail for user PTEs and/or PTDs!  (107
	 * addresses including 0xff800000 and 0xfc000000).  I'm not sure if
	 * this can be fixed.  Marking the PTEs supervisor mode and the
	 * PDE's user mode would almost work, but there may be a problem
	 * with the self-referential PDE.
	 */
	movl	%edi,%eax
	addl	%ebx,%eax
	jc	copyout_fault
#define VM_END_USER_ADDRESS	0xFDBFE000	/* XXX */
	cmpl	$VM_END_USER_ADDRESS,%eax
	ja	copyout_fault

#ifndef USE_486_WRITE_PROTECT
	/*
	 * We have to check each PTE for user write permission.
	 * The checking may cause a page fault, so it is important to set
	 * up everything for return via copyout_fault before here.
	 */
			/* compute number of pages */
	movl	%edi,%ecx
	andl	$NBPG-1,%ecx
	addl	%ebx,%ecx
	decl	%ecx
	shrl	$IDXSHIFT+2,%ecx
	incl	%ecx

			/* compute PTE offset for start address */
	movl	%edi,%edx
	shrl	$IDXSHIFT,%edx
	andb	$0xfc,%dl

1:			/* check PTE for each page */
	movb	_PTmap(%edx),%al
	andb	$0x07,%al	/* Pages must be VALID + USERACC + WRITABLE */
	cmpb	$0x07,%al
	je	2f

				/* simulate a trap */
	pushl	%edx
	pushl	%ecx
	shll	$IDXSHIFT,%edx
	pushl	%edx
	call	_trapwrite	/* trapwrite(addr) */
	popl	%edx
	popl	%ecx
	popl	%edx

	orl	%eax,%eax	/* if not ok, return EFAULT */
	jnz	copyout_fault

2:
	addl	$4,%edx
	decl	%ecx
	jnz	1b		/* check next page */
#endif /* ndef USE_486_WRITE_PROTECT */

			/* bcopy(%esi, %edi, %ebx) */
	cld
	movl	%ebx,%ecx
	shrl	$2,%ecx
	rep
	movsl
	movb	%bl,%cl
	andb	$3,%cl	/* XXX can we trust the rest of %ecx on clones? */
	rep
	movsb

done_copyout:
	popl	%ebx
	popl	%edi
	popl	%esi
	xorl	%eax,%eax
	movl	_curpcb,%edx
	movl	%eax,PCB_ONFAULT(%edx)
	ret

	ALIGN_TEXT
copyout_fault:
	popl	%ebx
	popl	%edi
	popl	%esi
	movl	_curpcb,%edx
	movl	$0,PCB_ONFAULT(%edx)
	movl	$EFAULT,%eax
	ret

ENTRY(copyin)			/* copyin(from_user, to_kernel, len) */
	movl	_curpcb,%eax
	movl	$copyin_fault,PCB_ONFAULT(%eax)
	pushl	%esi
	pushl	%edi
	movl	12(%esp),%esi		/* caddr_t from */
	movl	16(%esp),%edi		/* caddr_t to */
	movl	20(%esp),%ecx		/* size_t  len */

	movb	%cl,%al
	shrl	$2,%ecx			/* copy longword-wise */
	cld
	gs
	rep
	movsl
	movb	%al,%cl
	andb	$3,%cl			/* copy remaining bytes */
	gs
	rep
	movsb

	popl	%edi
	popl	%esi
	xorl	%eax,%eax
	movl	_curpcb,%edx
	movl	%eax,PCB_ONFAULT(%edx)
	ret

	ALIGN_TEXT
copyin_fault:
	popl	%edi
	popl	%esi
	movl	_curpcb,%edx
	movl	$0,PCB_ONFAULT(%edx)
	movl	$EFAULT,%eax
	ret

	/*
	 * fu{byte,sword,word} : fetch a byte(sword, word) from user memory
	 */
ALTENTRY(fuiword)
ENTRY(fuword)
	movl	_curpcb,%ecx
	movl	$fusufault,PCB_ONFAULT(%ecx)
	movl	4(%esp),%edx
	gs
	movl	(%edx),%eax
	movl	$0,PCB_ONFAULT(%ecx)
	ret

ENTRY(fusword)
	movl	_curpcb,%ecx
	movl	$fusufault,PCB_ONFAULT(%ecx)
	movl	4(%esp),%edx
	gs
	movzwl	(%edx),%eax
	movl	$0,PCB_ONFAULT(%ecx)
	ret

ALTENTRY(fuibyte)
ENTRY(fubyte)
	movl	_curpcb,%ecx
	movl	$fusufault,PCB_ONFAULT(%ecx)
	movl	4(%esp),%edx
	gs
	movzbl	(%edx),%eax
	movl	$0,PCB_ONFAULT(%ecx)
	ret

	ALIGN_TEXT
fusufault:
	movl	_curpcb,%ecx
	xorl	%eax,%eax
	movl	%eax,PCB_ONFAULT(%ecx)
	decl	%eax
	ret

	/*
	 * su{byte,sword,word}: write a byte(word, longword) to user memory
	 */
#ifdef USE_486_WRITE_PROTECT
	/*
	 * we only have to set the right segment selector.
	 */
ALTENTRY(suiword)
ENTRY(suword)
	movl	_curpcb,%ecx
	movl	$fusufault,PCB_ONFAULT(%ecx)
	movl	4(%esp),%edx
	movl	8(%esp),%eax
	gs
	movl	%eax,(%edx)
	xorl	%eax,%eax
	movl	%eax,PCB_ONFAULT(%ecx)
	ret

ENTRY(susword)
	movl	_curpcb,%ecx
	movl	$fusufault,PCB_ONFAULT(%ecx)
	movl	4(%esp),%edx
	movw	8(%esp),%ax
	gs
	movw	%ax,(%edx)
	xorl	%eax,%eax
	movl	%eax,PCB_ONFAULT(%ecx)
	ret

ALTENTRY(suibyte)
ENTRY(subyte)
	movl	_curpcb,%ecx
	movl	$fusufault,PCB_ONFAULT(%ecx)
	movl	4(%esp),%edx
	movb	8(%esp),%al
	gs
	movb	%al,(%edx)
	xorl	%eax,%eax
	movl	%eax,PCB_ONFAULT(%ecx)
	ret


#else /* USE_486_WRITE_PROTECT */
	/*
	 * here starts the trouble again: check PTE, twice if word crosses
	 * a page boundary.
	 */
	/* XXX - page boundary crossing is not handled yet */

ALTENTRY(suibyte)
ENTRY(subyte)
	movl	_curpcb,%ecx
	movl	$fusufault,PCB_ONFAULT(%ecx)
	movl	4(%esp),%edx
	movl	%edx,%eax
	shrl	$IDXSHIFT,%edx
	andb	$0xfc,%dl
	movb	_PTmap(%edx),%dl
	andb	$0x7,%dl		/* must be VALID + USERACC + WRITE */
	cmpb	$0x7,%dl
	je	1f
					/* simulate a trap */
	pushl	%eax
	call	_trapwrite
	popl	%edx
	orl	%eax,%eax
	jnz	fusufault
1:
	movl	4(%esp),%edx
	movl	8(%esp),%eax
	gs
	movb	%al,(%edx)
	xorl	%eax,%eax
	movl	_curpcb,%ecx
	movl	%eax,PCB_ONFAULT(%ecx)
	ret

ENTRY(susword)
	movl	_curpcb,%ecx
	movl	$fusufault,PCB_ONFAULT(%ecx)
	movl	4(%esp),%edx
	movl	%edx,%eax
	shrl	$IDXSHIFT,%edx
	andb	$0xfc,%dl
	movb	_PTmap(%edx),%dl
	andb	$0x7,%dl		/* must be VALID + USERACC + WRITE */
	cmpb	$0x7,%dl
	je	1f
					/* simulate a trap */
	pushl	%eax
	call	_trapwrite
	popl	%edx
	orl	%eax,%eax
	jnz	fusufault
1:
	movl	4(%esp),%edx
	movl	8(%esp),%eax
	gs
	movw	%ax,(%edx)
	xorl	%eax,%eax
	movl	_curpcb,%ecx
	movl	%eax,PCB_ONFAULT(%ecx)
	ret

ALTENTRY(suiword)
ENTRY(suword)
	movl	_curpcb,%ecx
	movl	$fusufault,PCB_ONFAULT(%ecx)
	movl	4(%esp),%edx
	movl	%edx,%eax
	shrl	$IDXSHIFT,%edx
	andb	$0xfc,%dl
	movb	_PTmap(%edx),%dl
	andb	$0x7,%dl		/* must be VALID + USERACC + WRITE */
	cmpb	$0x7,%dl
	je	1f
					/* simulate a trap */
	pushl	%eax
	call	_trapwrite
	popl	%edx
	orl	%eax,%eax
	jnz	fusufault
1:
	movl	4(%esp),%edx
	movl	8(%esp),%eax
	gs
	movl	%eax,0(%edx)
	xorl	%eax,%eax
	movl	_curpcb,%ecx
	movl	%eax,PCB_ONFAULT(%ecx)
	ret

#endif /* USE_486_WRITE_PROTECT */

/*
 * copyoutstr(from, to, maxlen, int *lencopied)
 *	copy a string from from to to, stop when a 0 character is reached.
 *	return ENAMETOOLONG if string is longer than maxlen, and
 *	EFAULT on protection violations. If lencopied is non-zero,
 *	return the actual length in *lencopied.
 */
#ifdef USE_486_WRITE_PROTECT

ENTRY(copyoutstr)
	pushl	%esi
	pushl	%edi
	movl	_curpcb,%ecx
	movl	$cpystrflt,PCB_ONFAULT(%ecx)

	movl	12(%esp),%esi			/* %esi = from */
	movl	16(%esp),%edi			/* %edi = to */
	movl	20(%esp),%edx			/* %edx = maxlen */
	incl	%edx

1:
	decl	%edx
	jz	4f
	/*
	 * gs override doesn't work for stosb.  Use the same explicit check
	 * as in copyout().  It's much slower now because it is per-char.
	 * XXX - however, it would be faster to rewrite this function to use
	 * strlen() and copyout().
	 */
	cmpl	$VM_END_USER_ADDRESS,%edi
	jae	cpystrflt
	lodsb
	gs
	stosb
	orb	%al,%al
	jnz	1b
			/* Success -- 0 byte reached */
	decl	%edx
	xorl	%eax,%eax
	jmp	6f
4:
			/* edx is zero -- return ENAMETOOLONG */
	movl	$ENAMETOOLONG,%eax
	jmp	6f

#else	/* ndef USE_486_WRITE_PROTECT */

ENTRY(copyoutstr)
	pushl	%esi
	pushl	%edi
	movl	_curpcb,%ecx
	movl	$cpystrflt,PCB_ONFAULT(%ecx)

	movl	12(%esp),%esi			/* %esi = from */
	movl	16(%esp),%edi			/* %edi = to */
	movl	20(%esp),%edx			/* %edx = maxlen */
1:
	/*
	 * It suffices to check that the first byte is in user space, because
	 * we look at a page at a time and the end address is on a page
	 * boundary.
	 */
	cmpl	$VM_END_USER_ADDRESS,%edi
	jae	cpystrflt
	movl	%edi,%eax
	shrl	$IDXSHIFT,%eax
	andb	$0xfc,%al
	movb	_PTmap(%eax),%al
	andb	$7,%al
	cmpb	$7,%al
	je	2f

			/* simulate trap */
	pushl	%edx
	pushl	%edi
	call	_trapwrite
	popl	%edi
	popl	%edx
	orl	%eax,%eax
	jnz	cpystrflt

2:			/* copy up to end of this page */
	movl	%edi,%eax
	andl	$NBPG-1,%eax
	movl	$NBPG,%ecx
	subl	%eax,%ecx	/* ecx = NBPG - (src % NBPG) */
	cmpl	%ecx,%edx
	jge	3f
	movl	%edx,%ecx	/* ecx = min(ecx, edx) */
3:
	orl	%ecx,%ecx
	jz	4f
	decl	%ecx
	decl	%edx
	lodsb
	stosb
	orb	%al,%al
	jnz	3b

			/* Success -- 0 byte reached */
	decl	%edx
	xorl	%eax,%eax
	jmp	6f

4:			/* next page */
	orl	%edx,%edx
	jnz	1b
			/* edx is zero -- return ENAMETOOLONG */
	movl	$ENAMETOOLONG,%eax
	jmp	6f

#endif /* USE_486_WRITE_PROTECT */

/*
 * copyinstr(from, to, maxlen, int *lencopied)
 *	copy a string from from to to, stop when a 0 character is reached.
 *	return ENAMETOOLONG if string is longer than maxlen, and
 *	EFAULT on protection violations. If lencopied is non-zero,
 *	return the actual length in *lencopied.
 */
ENTRY(copyinstr)
	pushl	%esi
	pushl	%edi
	movl	_curpcb,%ecx
	movl	$cpystrflt,PCB_ONFAULT(%ecx)

	movl	12(%esp),%esi			/* %esi = from */
	movl	16(%esp),%edi			/* %edi = to */
	movl	20(%esp),%edx			/* %edx = maxlen */
	incl	%edx

1:
	decl	%edx
	jz	4f
	gs
	lodsb
	stosb
	orb	%al,%al
	jnz	1b
			/* Success -- 0 byte reached */
	decl	%edx
	xorl	%eax,%eax
	jmp	6f
4:
			/* edx is zero -- return ENAMETOOLONG */
	movl	$ENAMETOOLONG,%eax
	jmp	6f

cpystrflt:
	movl	$EFAULT,%eax
6:			/* set *lencopied and return %eax */
	movl	_curpcb,%ecx
	movl	$0,PCB_ONFAULT(%ecx)
	movl	20(%esp),%ecx
	subl	%edx,%ecx
	movl	24(%esp),%edx
	orl	%edx,%edx
	jz	7f
	movl	%ecx,(%edx)
7:
	popl	%edi
	popl	%esi
	ret


/*
 * copystr(from, to, maxlen, int *lencopied)
 */
ENTRY(copystr)
	pushl	%esi
	pushl	%edi

	movl	12(%esp),%esi			/* %esi = from */
	movl	16(%esp),%edi			/* %edi = to */
	movl	20(%esp),%edx			/* %edx = maxlen */
	incl	%edx

1:
	decl	%edx
	jz	4f
	lodsb
	stosb
	orb	%al,%al
	jnz	1b
			/* Success -- 0 byte reached */
	decl	%edx
	xorl	%eax,%eax
	jmp	6f
4:
			/* edx is zero -- return ENAMETOOLONG */
	movl	$ENAMETOOLONG,%eax

6:			/* set *lencopied and return %eax */
	movl	20(%esp),%ecx
	subl	%edx,%ecx
	movl	24(%esp),%edx
	orl	%edx,%edx
	jz	7f
	movl	%ecx,(%edx)
7:
	popl	%edi
	popl	%esi
	ret

/*
 * Handling of special 386 registers and descriptor tables etc
 */
ENTRY(lgdt)	/* void lgdt(struct region_descriptor *rdp); */
	/* reload the descriptor table */
	movl	4(%esp),%eax
	lgdt	(%eax)
	/* flush the prefetch q */
	jmp	1f
	nop
1:
	/* reload "stale" selectors */
	movl	$KDSEL,%eax
	movl	%ax,%ds
	movl	%ax,%es
	movl	%ax,%ss

	/* reload code selector by turning return into intersegmental return */
	movl	(%esp),%eax
	pushl	%eax
#	movl	$KCSEL,4(%esp)
	movl	$8,4(%esp)
	lret

	/*
	 * void lidt(struct region_descriptor *rdp);
	 */
ENTRY(lidt)
	movl	4(%esp),%eax
	lidt	(%eax)
	ret

	/*
	 * void lldt(u_short sel)
	 */
ENTRY(lldt)
	lldt	4(%esp)
	ret

	/*
	 * void ltr(u_short sel)
	 */
ENTRY(ltr)
	ltr	4(%esp)
	ret

ENTRY(ssdtosd)				/* ssdtosd(*ssdp,*sdp) */
	pushl	%ebx
	movl	8(%esp),%ecx
	movl	8(%ecx),%ebx
	shll	$16,%ebx
	movl	(%ecx),%edx
	roll	$16,%edx
	movb	%dh,%bl
	movb	%dl,%bh
	rorl	$8,%ebx
	movl	4(%ecx),%eax
	movw	%ax,%dx
	andl	$0xf0000,%eax
	orl	%eax,%ebx
	movl	12(%esp),%ecx
	movl	%edx,(%ecx)
	movl	%ebx,4(%ecx)
	popl	%ebx
	ret


ENTRY(tlbflush)				/* tlbflush() */
	movl	%cr3,%eax
	orl	$I386_CR3PAT,%eax
	movl	%eax,%cr3
	ret


ENTRY(load_cr0)				/* load_cr0(cr0) */
	movl	4(%esp),%eax
	movl	%eax,%cr0
	ret


ENTRY(rcr0)				/* rcr0() */
	movl	%cr0,%eax
	ret


ENTRY(rcr2)				/* rcr2() */
	movl	%cr2,%eax
	ret


ENTRY(rcr3)				/* rcr3() */
	movl	%cr3,%eax
	ret


ENTRY(load_cr3)				/* void load_cr3(caddr_t cr3) */
	movl	4(%esp),%eax
	orl	$I386_CR3PAT,%eax
	movl	%eax,%cr3
	ret


/*****************************************************************************/
/* setjump, longjump                                                         */
/*****************************************************************************/

ENTRY(setjmp)
	movl	4(%esp),%eax
	movl	%ebx,(%eax)		/* save ebx */
	movl	%esp,4(%eax)		/* save esp */
	movl	%ebp,8(%eax)		/* save ebp */
	movl	%esi,12(%eax)		/* save esi */
	movl	%edi,16(%eax)		/* save edi */
	movl	(%esp),%edx		/* get rta */
	movl	%edx,20(%eax)		/* save eip */
	xorl	%eax,%eax		/* return(0); */
	ret

ENTRY(longjmp)
	movl	4(%esp),%eax
	movl	(%eax),%ebx		/* restore ebx */
	movl	4(%eax),%esp		/* restore esp */
	movl	8(%eax),%ebp		/* restore ebp */
	movl	12(%eax),%esi		/* restore esi */
	movl	16(%eax),%edi		/* restore edi */
	movl	20(%eax),%edx		/* get rta */
	movl	%edx,(%esp)		/* put in return frame */
	xorl	%eax,%eax		/* return(1); */
	incl	%eax
	ret


/*****************************************************************************/
/* Scheduling                                                                */
/*****************************************************************************/

/*
 * The following primitives manipulate the run queues.
 * _whichqs tells which of the 32 queues _qs
 * have processes in them.  Setrq puts processes into queues, Remrq
 * removes them from queues.  The running process is on no queue,
 * other processes are on a queue related to p->p_pri, divided by 4
 * actually to shrink the 0-127 range of priorities into the 32 available
 * queues.
 */

	.globl	_whichqs,_qs,_cnt,_panic
	.comm	_noproc,4
	.comm	_runrun,4

/*
 * Setrq(p)
 *
 * Call should be made at spl6(), and p->p_stat should be SRUN
 */
ENTRY(setrq)
	movl	4(%esp),%eax
	cmpl	$0,P_RLINK(%eax)	/* should not be on q already */
	je	set1
	pushl	$set2
	call	_panic
set1:
	movzbl	P_PRI(%eax),%edx
	shrl	$2,%edx
	btsl	%edx,_whichqs		/* set q full bit */
	shll	$3,%edx
	addl	$_qs,%edx		/* locate q hdr */
	movl	%edx,P_LINK(%eax)	/* link process on tail of q */
	movl	P_RLINK(%edx),%ecx
	movl	%ecx,P_RLINK(%eax)
	movl	%eax,P_RLINK(%edx)
	movl	%eax,P_LINK(%ecx)
	ret

set2:	.asciz	"setrq"

/*
 * Remrq(p)
 *
 * Call should be made at spl6().
 */
ENTRY(remrq)
	movl	4(%esp),%eax
	movzbl	P_PRI(%eax),%edx
	shrl	$2,%edx
	btrl	%edx,_whichqs		/* clear full bit, panic if clear already */
	jb	rem1
	pushl	$rem3
	call	_panic
rem1:
	pushl	%edx
	movl	P_LINK(%eax),%ecx	/* unlink process */
	movl	P_RLINK(%eax),%edx
	movl	%edx,P_RLINK(%ecx)
	movl	P_RLINK(%eax),%ecx
	movl	P_LINK(%eax),%edx
	movl	%edx,P_LINK(%ecx)
	popl	%edx
	movl	$_qs,%ecx
	shll	$3,%edx
	addl	%edx,%ecx
	cmpl	P_LINK(%ecx),%ecx	/* q still has something? */
	je	rem2
	shrl	$3,%edx			/* yes, set bit as still full */
	btsl	%edx,_whichqs
rem2:
	movl	$0,P_RLINK(%eax)	/* zap reverse link to indicate off list */
	ret

rem3:	.asciz	"remrq"
sw0:	.asciz	"swtch"

/*
 * When no processes are on the runq, Swtch branches to idle
 * to wait for something to come ready.
 */
	ALIGN_TEXT
Idle:
	sti
	SHOW_STI

	ALIGN_TEXT
idle_loop:
	call	_spl0
	cmpl	$0,_whichqs
	jne	sw1
	hlt				/* wait for interrupt */
	jmp	idle_loop

badsw:
	pushl	$sw0
	call	_panic
	/*NOTREACHED*/

/*
 * Swtch()
 */
	SUPERALIGN_TEXT	/* so profiling doesn't lump Idle with swtch().. */
ENTRY(swtch)

	incl	_cnt+V_SWTCH

	/* switch to new process. first, save context as needed */

	movl	_curproc,%ecx

	/* if no process to save, don't bother */
	testl	%ecx,%ecx
	je	sw1

	movl	P_ADDR(%ecx),%ecx

	movl	(%esp),%eax		/* Hardware registers */
	movl	%eax,PCB_EIP(%ecx)
	movl	%ebx,PCB_EBX(%ecx)
	movl	%esp,PCB_ESP(%ecx)
	movl	%ebp,PCB_EBP(%ecx)
	movl	%esi,PCB_ESI(%ecx)
	movl	%edi,PCB_EDI(%ecx)

#if NNPX > 0
	/* have we used fp, and need a save? */
	mov	_curproc,%eax
	cmp	%eax,_npxproc
	jne	1f
	pushl	%ecx			/* h/w bugs make saving complicated */
	leal	PCB_SAVEFPU(%ecx),%eax
	pushl	%eax
	call	_npxsave		/* do it in a big C function */
	popl	%eax
	popl	%ecx
1:
#endif	/* NNPX > 0 */

	movl	_CMAP2,%eax		/* save temporary map PTE */
	movl	%eax,PCB_CMAP2(%ecx)	/* in our context */
	movl	$0,_curproc		/*  out of process */

#	movw	_cpl,%ax
#	movw	%ax,PCB_IML(%ecx)	/* save ipl */

	/* save is done, now choose a new process or idle */
sw1:
	cli
	SHOW_CLI
	movl	_whichqs,%edi
2:
	/* XXX - bsf is sloow */
	bsfl	%edi,%eax		/* find a full q */
	je	Idle			/* if none, idle */
	/* XX update whichqs? */
swfnd:
	btrl	%eax,%edi		/* clear q full status */
	jnb	2b			/* if it was clear, look for another */
	movl	%eax,%ebx		/* save which one we are using */

	shll	$3,%eax
	addl	$_qs,%eax		/* select q */
	movl	%eax,%esi

#ifdef	DIAGNOSTIC
	cmpl	P_LINK(%eax),%eax /* linked to self? (e.g. not on list) */
	je	badsw			/* not possible */
#endif

	movl	P_LINK(%eax),%ecx	/* unlink from front of process q */
	movl	P_LINK(%ecx),%edx
	movl	%edx,P_LINK(%eax)
	movl	P_RLINK(%ecx),%eax
	movl	%eax,P_RLINK(%edx)

	cmpl	P_LINK(%ecx),%esi	/* q empty */
	je	3f
	btsl	%ebx,%edi		/* nope, set to indicate full */
3:
	movl	%edi,_whichqs		/* update q status */

	movl	$0,%eax
	movl	%eax,_want_resched

#ifdef	DIAGNOSTIC
	cmpl	%eax,P_WCHAN(%ecx)
	jne	badsw
	cmpb	$SRUN,P_STAT(%ecx)
	jne	badsw
#endif

	movl	%eax,P_RLINK(%ecx) /* isolate process to run */
	movl	P_ADDR(%ecx),%edx
	movl	PCB_CR3(%edx),%ebx

	/* switch address space */
	movl	%ebx,%cr3

	/* restore context */
	movl	PCB_EBX(%edx),%ebx
	movl	PCB_ESP(%edx),%esp
	movl	PCB_EBP(%edx),%ebp
	movl	PCB_ESI(%edx),%esi
	movl	PCB_EDI(%edx),%edi
	movl	PCB_EIP(%edx),%eax
	movl	%eax,(%esp)

	movl	PCB_CMAP2(%edx),%eax	/* get temporary map */
	movl	%eax,_CMAP2		/* reload temporary map PTE */

	movl	%ecx,_curproc		/* into next process */
	movl	%edx,_curpcb

	pushl	%edx			/* save p to return */
/*
 * XXX - 0.0 forgot to save it - is that why this was commented out in 0.1?
 * I think restoring the cpl is unnecessary, but we must turn off the cli
 * now that spl*() don't do it as a side affect.
 */
	pushl	PCB_IML(%edx)
	sti
	SHOW_STI
#if 0
	call	_splx
#endif
	addl	$4,%esp
/*
 * XXX - 0.0 gets here via swtch_to_inactive().  I think 0.1 gets here in the
 * same way.  Better return a value.
 */
	popl	%eax			/* return(p); */
	ret

ENTRY(mvesp)
	movl	%esp,%eax
	ret
/*
 * struct proc *swtch_to_inactive(p) ; struct proc *p;
 *
 * At exit of a process, move off the address space of the
 * process and onto a "safe" one. Then, on a temporary stack
 * return and run code that disposes of the old state.
 * Since this code requires a parameter from the "old" stack,
 * pass it back as a return value.
 */
ENTRY(swtch_to_inactive)
	popl	%edx			/* old pc */
	popl	%eax			/* arg, our return value */
	movl	_IdlePTD,%ecx
	movl	%ecx,%cr3		/* good bye address space */
 #write buffer?
	movl	$tmpstk-4,%esp		/* temporary stack, compensated for call */
	jmp	%edx			/* return, execute remainder of cleanup */

/*
 * savectx(pcb, altreturn)
 * Update pcb, saving current processor state and arranging
 * for alternate return ala longjmp in swtch if altreturn is true.
 */
ENTRY(savectx)
	movl	4(%esp),%ecx
	movw	_cpl,%ax
	movw	%ax,PCB_IML(%ecx)
	movl	(%esp),%eax
	movl	%eax,PCB_EIP(%ecx)
	movl	%ebx,PCB_EBX(%ecx)
	movl	%esp,PCB_ESP(%ecx)
	movl	%ebp,PCB_EBP(%ecx)
	movl	%esi,PCB_ESI(%ecx)
	movl	%edi,PCB_EDI(%ecx)

#if NNPX > 0
	/*
	 * If npxproc == NULL, then the npx h/w state is irrelevant and the
	 * state had better already be in the pcb.  This is true for forks
	 * but not for dumps (the old book-keeping with FP flags in the pcb
	 * always lost for dumps because the dump pcb has 0 flags).
	 *
	 * If npxproc != NULL, then we have to save the npx h/w state to
	 * npxproc's pcb and copy it to the requested pcb, or save to the
	 * requested pcb and reload.  Copying is easier because we would
	 * have to handle h/w bugs for reloading.  We used to lose the
	 * parent's npx state for forks by forgetting to reload.
	 */
	mov	_npxproc,%eax
	testl	%eax,%eax
	je	1f

	pushl	%ecx
	movl	P_ADDR(%eax),%eax
	leal	PCB_SAVEFPU(%eax),%eax
	pushl	%eax
	pushl	%eax
	call	_npxsave
	popl	%eax
	popl	%eax
	popl	%ecx

	pushl	%ecx
	pushl	$108+8*2	/* XXX h/w state size + padding */
	leal	PCB_SAVEFPU(%ecx),%ecx
	pushl	%ecx
	pushl	%eax
	call	_bcopy
	addl	$12,%esp
	popl	%ecx
1:
#endif	/* NNPX > 0 */

	movl	_CMAP2,%edx		/* save temporary map PTE */
	movl	%edx,PCB_CMAP2(%ecx)	/* in our context */

	cmpl	$0,8(%esp)
	je	1f
	movl	%esp,%edx		/* relocate current sp relative to pcb */
	subl	$_kstack,%edx		/*   (sp is relative to kstack): */
	addl	%edx,%ecx		/*   pcb += sp - kstack; */
	movl	%eax,(%ecx)		/* write return pc at (relocated) sp@ */
	/* this mess deals with replicating register state gcc hides */
	movl	12(%esp),%eax
	movl	%eax,12(%ecx)
	movl	16(%esp),%eax
	movl	%eax,16(%ecx)
	movl	20(%esp),%eax
	movl	%eax,20(%ecx)
	movl	24(%esp),%eax
	movl	%eax,24(%ecx)
1:
	xorl	%eax,%eax		/* return 0 */
	ret

/*
 * addupc(int pc, struct uprof *up, int ticks):
 * update profiling information for the user process.
 */
ENTRY(addupc)
	pushl %ebp
	movl %esp,%ebp
	movl 12(%ebp),%edx		/* up */
	movl 8(%ebp),%eax		/* pc */

	subl PR_OFF(%edx),%eax		/* pc -= up->pr_off */
	jl L1				/* if (pc < 0) return */

	shrl $1,%eax			/* praddr = pc >> 1 */
	imull PR_SCALE(%edx),%eax	/* praddr *= up->pr_scale */
	shrl $15,%eax			/* praddr = praddr << 15 */
	andl $-2,%eax			/* praddr &= ~1 */

	cmpl PR_SIZE(%edx),%eax		/* if (praddr > up->pr_size) return */
	ja L1

/*	addl %eax,%eax			/* praddr -> word offset */
	addl PR_BASE(%edx),%eax		/* praddr += up-> pr_base */
	movl 16(%ebp),%ecx		/* ticks */

	movl _curpcb,%edx
	movl $proffault,PCB_ONFAULT(%edx)
	addl %ecx,(%eax)		/* storage location += ticks */
	movl $0,PCB_ONFAULT(%edx)
L1:
	leave
	ret

	ALIGN_TEXT
proffault:
	/* if we get a fault, then kill profiling all together */
	movl $0,PCB_ONFAULT(%edx)	/* squish the fault handler */
	movl 12(%ebp),%ecx
	movl $0,PR_SCALE(%ecx)		/* up->pr_scale = 0 */
	leave
	ret

/* To be done: */
ENTRY(astoff)
	ret


/*****************************************************************************/
/* Trap handling                                                             */
/*****************************************************************************/
/*
 * Trap and fault vector routines
 *
 * XXX - debugger traps are now interrupt gates so at least bdb doesn't lose
 * control.  The sti's give the standard losing behaviour for ddb and kgdb.
 */
#define	IDTVEC(name)	ALIGN_TEXT; .globl _X/**/name; _X/**/name:
#define	TRAP(a)		pushl $(a) ; jmp alltraps
#ifdef KGDB
#  define BPTTRAP(a)	sti; pushl $(a) ; jmp bpttraps
#else
#  define BPTTRAP(a)	sti; TRAP(a)
#endif

IDTVEC(div)
	pushl $0; TRAP(T_DIVIDE)
IDTVEC(dbg)
#ifdef BDBTRAP
	BDBTRAP(dbg)
#endif
	pushl $0; BPTTRAP(T_TRCTRAP)
IDTVEC(nmi)
	pushl $0; TRAP(T_NMI)
IDTVEC(bpt)
#ifdef BDBTRAP
	BDBTRAP(bpt)
#endif
	pushl $0; BPTTRAP(T_BPTFLT)
IDTVEC(ofl)
	pushl $0; TRAP(T_OFLOW)
IDTVEC(bnd)
	pushl $0; TRAP(T_BOUND)
IDTVEC(ill)
	pushl $0; TRAP(T_PRIVINFLT)
IDTVEC(dna)
	pushl $0; TRAP(T_DNA)
IDTVEC(dble)
	TRAP(T_DOUBLEFLT)
	/*PANIC("Double Fault");*/
IDTVEC(fpusegm)
	pushl $0; TRAP(T_FPOPFLT)
IDTVEC(tss)
	TRAP(T_TSSFLT)
	/*PANIC("TSS not valid");*/
IDTVEC(missing)
	TRAP(T_SEGNPFLT)
IDTVEC(stk)
	TRAP(T_STKFLT)
IDTVEC(prot)
	TRAP(T_PROTFLT)
IDTVEC(page)
	TRAP(T_PAGEFLT)
IDTVEC(rsvd)
	pushl $0; TRAP(T_RESERVED)
IDTVEC(fpu)
#if NNPX > 0
	/*
	 * Handle like an interrupt so that we can call npxintr to clear the
	 * error.  It would be better to handle npx interrupts as traps but
	 * this is difficult for nested interrupts.
	 */
	pushl	$0		/* dummy error code */
	pushl	$T_ASTFLT
	pushal
	nop			/* silly, the bug is for popal and it only
				 * bites when the next instruction has a
				 * complicated address mode */
	pushl	%ds
	pushl	%es		/* now the stack frame is a trap frame */
	movl	$KDSEL,%eax
	movl	%ax,%ds
	movl	%ax,%es
	pushl	_cpl
	pushl	$0		/* dummy unit to finish building intr frame */
	incl	_cnt+V_TRAP
	call	_npxintr
	jmp	doreti
#else	/* NNPX > 0 */
	pushl $0; TRAP(T_ARITHTRAP)
#endif	/* NNPX > 0 */
	/* 17 - 31 reserved for future exp */
IDTVEC(rsvd0)
	pushl $0; TRAP(17)
IDTVEC(rsvd1)
	pushl $0; TRAP(18)
IDTVEC(rsvd2)
	pushl $0; TRAP(19)
IDTVEC(rsvd3)
	pushl $0; TRAP(20)
IDTVEC(rsvd4)
	pushl $0; TRAP(21)
IDTVEC(rsvd5)
	pushl $0; TRAP(22)
IDTVEC(rsvd6)
	pushl $0; TRAP(23)
IDTVEC(rsvd7)
	pushl $0; TRAP(24)
IDTVEC(rsvd8)
	pushl $0; TRAP(25)
IDTVEC(rsvd9)
	pushl $0; TRAP(26)
IDTVEC(rsvd10)
	pushl $0; TRAP(27)
IDTVEC(rsvd11)
	pushl $0; TRAP(28)
IDTVEC(rsvd12)
	pushl $0; TRAP(29)
IDTVEC(rsvd13)
	pushl $0; TRAP(30)
IDTVEC(rsvd14)
	pushl $0; TRAP(31)

	SUPERALIGN_TEXT
alltraps:
	pushal
	nop
	pushl	%ds
	pushl	%es
	movl	$KDSEL,%eax
	movl	%ax,%ds
	movl	%ax,%es
calltrap:
	incl	_cnt+V_TRAP
	call	_trap
	/*
	 * Return through doreti to handle ASTs.  Have to change trap frame
	 * to interrupt frame.
	 */
	movl	$T_ASTFLT,4+4+32(%esp)	/* new trap type (err code not used) */
	pushl	_cpl
	pushl	$0			/* dummy unit */
	jmp	doreti

#ifdef KGDB
/*
 * This code checks for a kgdb trap, then falls through
 * to the regular trap code.
 */
	SUPERALIGN_TEXT
bpttraps:
	pushal
	nop
	pushl	%es
	pushl	%ds
	movl	$KDSEL,%eax
	movl	%ax,%ds
	movl	%ax,%es
	testb	$SEL_RPL_MASK,TRAPF_CS_OFF(%esp)
					/* non-kernel mode? */
	jne	calltrap		/* yes */
	call	_kgdb_trap_glue
	jmp	calltrap
#endif

/*
 * Call gate entry for syscall
 */
	SUPERALIGN_TEXT
IDTVEC(syscall)
	pushfl	/* only for stupid carry bit and more stupid wait3 cc kludge */
		/* XXX - also for direction flag (bzero, etc. clear it) */
	pushal	/* only need eax,ecx,edx - trap resaves others */
	nop
	movl	$KDSEL,%eax		/* switch to kernel segments */
	movl	%ax,%ds
	movl	%ax,%es
	incl	_cnt+V_SYSCALL	/* kml 3/25/93 */
	call	_syscall
	/*
	 * Return through doreti to handle ASTs.  Have to change syscall frame
	 * to interrupt frame.
	 *
	 * XXX - we should have set up the frame earlier to avoid the
	 * following popal/pushal (not much can be done to avoid shuffling
	 * the flags).  Consistent frames would simplify things all over.
	 */
	movl	32+0(%esp),%eax	/* old flags, shuffle to above cs:eip */
	movl	32+4(%esp),%ebx	/* `int' frame should have been ef, eip, cs */
	movl	32+8(%esp),%ecx
	movl	%ebx,32+0(%esp)
	movl	%ecx,32+4(%esp)
	movl	%eax,32+8(%esp)
	popal
	nop
	pushl	$0		/* dummy error code */
	pushl	$T_ASTFLT
	pushal
	nop
	movl	__udatasel,%eax	/* switch back to user segments */
	pushl	%eax		/* XXX - better to preserve originals? */
	pushl	%eax
	pushl	_cpl
	pushl	$0
	jmp	doreti

#ifdef SHOW_A_LOT
/*
 * 'show_bits' was too big when defined as a macro.  The line length for some
 * enclosing macro was too big for gas.  Perhaps the code would have blown
 * the cache anyway.
 */
	ALIGN_TEXT
show_bits:
	pushl	%eax
	SHOW_BIT(0)
	SHOW_BIT(1)
	SHOW_BIT(2)
	SHOW_BIT(3)
	SHOW_BIT(4)
	SHOW_BIT(5)
	SHOW_BIT(6)
	SHOW_BIT(7)
	SHOW_BIT(8)
	SHOW_BIT(9)
	SHOW_BIT(10)
	SHOW_BIT(11)
	SHOW_BIT(12)
	SHOW_BIT(13)
	SHOW_BIT(14)
	SHOW_BIT(15)
	popl	%eax
	ret

	.data
bit_colors:
	.byte	GREEN,RED,0,0
	.text

#endif /* SHOW_A_LOT */


/*
 * include generated interrupt vectors and ISA intr code
 */
#include "i386/isa/vector.s"
#include "i386/isa/icu.s"