/* Assembly language routines for i386
   Copyright (C) 1991 Free Software Foundation

This file is part of the GNU Hurd.

The GNU Hurd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.

The GNU Hurd is distributed in the hope that it will be useful, 
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with the GNU Hurd; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* Written by Michael I. Bushnell.  */

#include "assym.h"
#include "syscall.h"

	.data

_stack_lock:		/ This locks the following three global variables
	.long	0
_regs_save_1:
	.long	0
_regs_save_2:
	.long	0
_regs_save_3:
	.long	0
_stack_list:
	.long	0

	.globl	_fork_ready
_fork_ready:
	.long	0


/* This is just a little stack to be used while we fetch real stacks.  */
	.globl	_initial_stack_base
_initial_stack_base:
	.set	., .+INT_STACK_SIZE
_initial_stack:
	
	.text
/* Emulator is called with ESP pointing at saved flags, and ESP+4
   pointing at return PC.  */

/* We want to tramp on as little as possible.  The eax and edx
   registers are used for syscall return, so we can munge those before
   we save anything.  Unfortunately, eax holds the syscall number, so
   we can only use edx while we get a stack.  */

	.globl	_syscall_vector
_syscall_vector:
	/* Acquire emulator stack lock */
	pushl	%edx
0:	movl	$1, %edx
	xchg	%edx,_stack_lock
	orl	%edx,%edx
	jne	0b

	/* Save these registers because we need them.  Eax holds the
	syscall number and ecx is guaranteed not to be tromped.  */
	movl	%eax, _regs_save_1
	movl	%ecx, _regs_save_2
	popl	_regs_save_3		/ user %edx

	/* Try to get an emulator stack */
	movl	_stack_list,%eax
	orl	%eax,%eax
	jne	3f

	/* If we didn't get one, get the initial stack and 
	   then get a real stack.  */
	movl	%esp,%edx		/ save user stack pointer
	movl	$_initial_stack, %esp
	pushl	%edx			/ push it because C will eat it
	call	_stack_alloc
	popl	%edx			/ pop it back
	movl	%edx,%esp		/ restore user stack pointer
	jmp	4f
	
3:	/* We got a stack at the top of the list */
	movl	(%eax), %ecx		/ fetch "next" pointer
	movl	%ecx, _stack_list	/ remove from list
	
4:	/* Here, eax holds the address of the stack */
	movl	%esp,(%eax)		/ put user SP at top of new stack...
	addl	$8,(%eax)		/ skipping kernel-pushed IP and flags
	movl	%esp,%ecx		/ save it in a reg
	movl	%eax,%esp		/ switch to new stack

	pushl	4(%ecx)			/ push saved IP
	pushl	(%ecx)			/ push saved flags

	/* Push saved registers */
	pushl	_regs_save_1		/ push saved eax
	pushl	_regs_save_2		/ push saved ecx

	/* Push remaining registers */
	pushl	%edi
	pushl	%esi
	pushl	%ebp
	pushl	%ebx
	pushl	_regs_save_3		/ push saved edx
	movl	$0,_stack_lock		/ release lock

	/* Push address of register block and do the syscall */
	pushl	%esp
	call	_syscall
	addl	$4, %esp		/ pop parameter

	/* Fall through... */

	/* Return from syscall */
_syscall_return:
0:	/* Acquire stack lock */
	movl	$1,%eax
	xchg	%eax,_stack_lock
	orl	%eax,%eax
	jne	0b

	/* Link stack onto list */
	movl	%esp,%eax
	addl	$36,%eax		/ SP after 9 pops below
	movl	_stack_list,%ecx
	movl	%ecx,(%eax)		/ chain onto list
	movl	%eax,_stack_list

	/* We can't release the lock yet, until we've popped all the
	   registers.  */
	popl	%edx
	popl	%ebx
	popl	%ebp
	popl	%esi
	popl	%edi
	popl	%ecx
	popl	%eax
	
	popl	_regs_save_1		/ pop flags
	popl	_regs_save_2		/ pop IP

	movl	(%esp),%esp		/ switch to user stack

	addl	$8,%esp			/ remove old IP and flags
	pushl	_regs_save_2		/ push IP
	pushl	_regs_save_1		/ push flags
	movl	$0,_stack_lock		/ release lock

	popf				/ pop flags
	ret				/ pop IP  :-)


/* Copy to/from user space */
/* SP:     return addr
   4(sp):  from address
   8(sp):  to address
   12(sp): length */

	.globl	_copyin
_copyin:
	pushl	%ebx
	pushl	%esi
	pushl	%edi
	pushl	%ebp
	movl	%esp,%ebp		/ fetch stack pointer
	andl	$STACK_MASK,%ebp	/ find base of stack
	movl	$1f,UCOPY_PC(%ebp)	/ return address
	movl	20(%esp),%eax
	movl	%eax,UCOPY_BASE(%ebp)	/ user address
	movl	28(%esp),%eax
	movl	%eax,UCOPY_LEN(%ebp)	/ length
	movl	%esp,UCOPY_SP(%ebp)	/ current stack pointer
	movl	$1,IN_UCOPY(%ebp)	/ doing copy
	movl	%esp,%eax
	pushl	28(%eax)
	pushl	24(%eax)
	pushl	20(%eax)
	call	_bcopy			/ do copy
	addl	$12,%esp		/ pop three args
	movl	$0,IN_UCOPY(%ebp)	/ not doing copy
	movl	$0,%eax			/ success!
1:	popl	%ebp
	popl	%edi
	popl	%esi
	popl	%ebx
	ret

	.globl	_copyout
_copyout:
	pushl	%ebx
	pushl	%esi
	pushl	%edi
	pushl	%ebp
	movl	%esp,%ebp		/ fetch stack pointer
	andl	$STACK_MASK,%ebp	/ find base of stack
	movl	$1f,UCOPY_PC(%ebp)	/ return address
	movl	24(%esp),%eax
	movl	%eax,UCOPY_BASE(%ebp)	/ user address
	movl	28(%esp),%eax
	movl	%eax,UCOPY_LEN(%ebp)	/ length
	movl	%esp,UCOPY_SP(%ebp)	/ current stack pointer
	movl	$1,IN_UCOPY(%ebp)	/ doing copy
	movl	%esp,%eax
	pushl	28(%eax)
	pushl	24(%eax)
	pushl	20(%eax)
	call	_bcopy			/ do copy
	addl	$12,%esp		/ pop three args
	movl	$0,IN_UCOPY(%ebp)	/ not doing copy
	movl	$0,%eax			/ success!
1:	popl	%ebp
	popl	%edi
	popl	%esi
	popl	%ebx
	ret

/* Stack frame:
   SP:   return addr
   SP+4: local address (dest)
   SP+8: user address (source) */
	.globl	_copyinstr
_copyinstr:
	pushl	%ebx
	pushl	%esi
	pushl	%edi
	pushl	%ebp
	movl	%esp,%ebp		/ fetch stack pointer
	andl	$STACK_MASK,%ebp	/ find base of stack
	movl	$1f,UCOPY_PC(%ebp)	/ return address
	movl	24(%esp),%eax
	movl	%eax,UCOPY_BASE(%ebp)	/ user address
	movl	$-1,UCOPY_LEN(%ebp)	/ and awwwaaayyy we go
	movl	%esp,UCOPY_SP(%ebp)	/ current stack pointer
	movl	$1,IN_UCOPY(%ebp)	/ doing copy
	movl	%esp,%eax
	pushl	24(%eax)
	pushl	20(%eax)
	call	_strcpy			/ do copy
	addl	$4,%esp			/ pop two args
	movl	$0,IN_UCOPY(%ebp)	/ not doing copy
	movl	$0,%eax			/ success!
1:	popl	%ebp
	popl	%edi
	popl	%esi
	popl	%ebx
	ret


/* Signal trampoline code.  This needs to call the signal handler and
   then sigreturn to the original state.  Called with stack as follows:

SP:	signal handler
SP+4:	signal number
SP+8:	signal code
SP+12:	address of signal context
SP+16:	address of signal context again
SP+20:  beginning of signal context
.
.
.
*/
	.globl	_trampoline
	.globl	_endtrampoline
_trampoline:
	popl	%eax			/ fetch handler address
	call	(%eax)			/ go to signal handler

	addl	$12,%esp		/ pop three args to signal handler

	pushl	$0			/ fake user return address
	movl	$SYS_sigreturn,%eax	/ sigreturn code
	lcall	$0x7,0x0		/ syscall trap to sigreturn

	hlt				/ firewall
_endtrampoline:


/* This is where a forked child ends up.  We have a stack, but the
   stack chain doesn't make any sense, and we don't have a reply port
   for ourselves.  EBX holds the address of our reply port so we can
   set it.  */
	.globl	_fork_init_point
_fork_init_point:
	call	_mach_reply_port
	movl	%eax,(%edx)
	movl	$0,_stack_lock
	movl	$0,_stack_list
	call	_fork_init
	movl	$1, _fork_ready
	jmp	_syscall_return	

/* This is where the new emulator thread ends up in a forked child.  
   We have no stack, nor anything else but a PC.  */
	.globl	_emul_thd_init_point
_emul_thd_init_point:
	movl	_fork_ready, %eax
	orl	%eax,%eax
	je	_emul_thd_init_point
	movl	$0, _fork_ready
	
	/* Acquire stack lock */
0:
	movl	$1,%eax
	xchg	%eax,_stack_lock
	orl	%eax,%eax
	jne 	0b

	/* Get a stack */
	movl	$_initial_stack,%esp
	call	_stack_alloc
	movl	%eax,%esp

	/* Release lock */
	movl	$0,_stack_lock

	call	_emulthread_loop


/* This is the main entry point when we start.  The child image has
   been loaded by the filesystem at the appropriate place.  The
   registers are initialized as follows:
*/
	.globl	_exec_start
_exec_start:

	movl	$_initial_stack,%esp	/ get on initial stack
	pushl	%ecx			/ eventual parameter to mman_init

	call	_mach_init		/ miscellaneous initialization
	call	_mman_init		/ initialize address space
	addl	$4,%esp			/ pop parameter
	movl	%eax,%ebp		/ save initial user SP

	call	_stack_alloc		/ get new stack
	movl	%eax,%esp		/ switch to our new stack

	pushl	%ebp
	call	_fetch_context		/ get args and initial ports
	
	/* fetch_context returns new SP in eax */
	pushl	%eax			/ push user SP
	pushl	%ebx			/ push user IP
	call	_start_user_thread	/ get user going

	call	_emulator_thread	/ become emulator thread
	
	/* emulator_thread shouldn't ever return */
	pushl	$SIGSYS
	call	_emulator_fault


