Files
2015-04-16 09:16:48 +02:00

1318 lines
38 KiB
C

/*
* This file is part of ixemul.library for the Amiga.
* Copyright (C) 1991, 1992 Markus M. Wild
* Portions Copyright (C) 1994 Rafael W. Luebbert
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: vfork.c,v 1.2 2005/03/31 20:47:00 piru Exp $
*/
#define _KERNEL
#include "ixemul.h"
#include "kprintf.h"
#include <sys/syscall.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <stddef.h>
#include <setjmp.h>
#include <string.h>
#include <stdlib.h>
#include <utility/tagitem.h>
#include <dos/dostags.h>
#ifdef MORPHOS
#include <emul/emulinterface.h>
#endif
#include "version.h"
#ifdef NATIVE_MORPHOS
void set_68k_sp(u_int sp) { REG_A7 = sp; }
u_int get_68k_sp(void) { return REG_A7; }
#endif
void vfork_own_malloc ();
void volatile vfork_longjmp (jmp_buf, int);
void ruadd(struct rusage *ru, struct rusage *ru2);
struct death_msg {
struct ixnode dm_node;
struct Process *dm_child;
int dm_pgrp;
int dm_status;
struct rusage dm_rusage;
};
/* this is the new process generated by vfork () ! */
static void
launcher (void)
{
void *ixb;
struct Process *me = (struct Process *) SysBase->ThisTask;
struct user *u_ptr;
struct vfork_msg *vm = NULL;
int omask;
KPRINTF(("vforked: opening ixemul\n"));
//kprintf("%ld\n",me->pr_Task.tc_UserData);
ixb = OpenLibrary ("ixemul.library", IX_VERSION);
u_ptr = getuser(me);
//kprintf("%ld\n",me->pr_Task.tc_UserData);
KPRINTF(("vforked: waiting for startup msg\n"));
/*{int* p=(int*)get_sp();
KPRINTF(("r2=%lx ret=%lx\n",MyEmulHandle,(*(int**)p)[1]));
KPRINTF(("r1=%lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
p=*(int **)p;
KPRINTF(("nxt=%lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
p=(int*)get_68k_sp();
KPRINTF(("a7=%lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
}*/
do
{
if (!vm)
WaitPort (& me->pr_MsgPort);
vm = (struct vfork_msg *) GetMsg (&me->pr_MsgPort);
}
while (!vm || vm->vm_self != me);
KPRINTF(("vforked: initializing\n"));
if (ixb)
{
/* get parents user area */
volatile struct user *pu = getuser(vm->vm_pptr);
/* `my' user area. This way we don't have to recalculate it too often */
volatile struct user *mu = &u;
/* reaping the dup function of execve() ;-)) */
int fd;
int a4_size = pu->u_a4_pointers_size * 4;
#ifdef NATIVE_MORPHOS
mu->u_is_ppc = pu->u_is_ppc;
KPRINTF(("is_ppc = %ld, u = %lx, pu = %lx, u->r13 = %lx, pu->r13 = %lx\n", mu->u_is_ppc, mu, pu, mu->u_a4, pu->u_a4));
#endif
/* link ourselves into the parents process lists. Guarantee single
* threaded access to those lists by locking out any other users of
* the library (nicer than to just call Forbid()) */
ix_lock_base ();
#ifdef NATIVE_MORPHOS
vm->vm_clowerppc = (int)me->pr_Task.tc_ETask->PPCSPLower;
vm->vm_cupperppc = (int)me->pr_Task.tc_ETask->PPCSPUpper;
#endif
vm->vm_clower = (int)me->pr_Task.tc_SPLower;
vm->vm_cupper = (int)me->pr_Task.tc_SPUpper;
/* our older sybling is the last recently created child of the parent */
mu->p_osptr = pu->p_cptr;
mu->u_parent_userdata = 0;
/* we have no younger sybling */
mu->p_ysptr = 0;
/* if we have an older sybling, point its `younger sybling' field at us */
if (mu->p_osptr)
safe_getuser(mu->p_osptr)->p_ysptr = me;
/* set the parents `last recently created child' field at us */
pu->p_cptr = me;
/* inherit the session of our parent */
mu->u_session = pu->u_session;
if (mu->u_session)
mu->u_session->s_count++; /* and increase use count */
/* inherit the process group of our parent */
mu->p_pgrp = pu->p_pgrp;
mu->p_pptr = vm->vm_pptr;
/* inherit the uid/gid information from parent */
mu->u_ruid = pu->u_ruid;
mu->u_euid = pu->u_euid;
mu->u_rgid = pu->u_rgid;
mu->u_egid = pu->u_egid;
if ((mu->u_ngroups = pu->u_ngroups))
bcopy((char *)pu->u_grouplist, (char *)mu->u_grouplist, pu->u_ngroups * sizeof(int));
if ((mu->u_logname_valid = pu->u_logname_valid))
strcpy((char *)mu->u_logname, (char *)pu->u_logname);
shmfork((struct user *)pu, (struct user *)mu);
/* if we got our own malloc list already, it is safe to call malloc here.
If not, the stuff done here is postponed to either vfork_resume, or
execve */
if (vm->vm_own_malloc)
{
/* inherit these global variables. */
mu->u_environ = (char ***) malloc (4);
* mu->u_environ = dupvec (* pu->u_environ);
mu->u_errno = (int *) malloc (4);
*mu->u_errno = 0;
mu->u_h_errno = (int *) malloc (4);
*mu->u_h_errno = 0;
}
else
{
/* borrow the variables of the parent */
mu->u_environ = pu->u_environ;
mu->u_errno = pu->u_errno;
/* tell malloc to use the parents malloc lists */
mu->u_mdp = pu->u_mdp;
}
/* and inherit several other things as well, upto not including u_md */
bcopy ((void *)&pu->u_a4_pointers_size, (void *)&mu->u_a4_pointers_size,
offsetof (struct user, u_md) - offsetof (struct user, u_a4_pointers_size));
bcopy ((char *)pu - a4_size, (char *)mu - a4_size, a4_size);
/* some things have been copied that should be reset */
mu->p_flag &= ~(SFREEA4 | STRC);
mu->p_xstat = 0;
bzero ((void *)&mu->u_ru, sizeof (struct rusage));
bzero ((void *)&mu->u_prof, sizeof (struct uprof));
mu->u_prof_last_pc = 0;
bzero ((void *)&mu->u_cru, sizeof (struct rusage));
bzero ((void *)&mu->u_timer[0], sizeof (struct itimerval)); /* just the REAL timer! */
syscall (SYS_gettimeofday, & mu->u_start, 0);
omask = vm->vm_rc; /* signal mask to restore at the end */
/* and adjust the open count of each of the copied filedescriptors */
for (fd = 0; fd < NOFILE; fd++)
if (mu->u_ofile[fd])
{
/* obtain (and create a new fd) for INET sockets */
if (mu->u_ofile[fd]->f_type == DTYPE_SOCKET)
{
/* Was this socket released? */
if (mu->u_ofile[fd]->f_socket_id)
{
/* Yes, it was. So we now obtain a new socket from the underlying TCP/IP stack */
int newfd = syscall(SYS_ix_obtain_socket, mu->u_ofile[fd]->f_socket_id,
mu->u_ofile[fd]->f_socket_domain,
mu->u_ofile[fd]->f_socket_type,
mu->u_ofile[fd]->f_socket_protocol);
mu->u_ofile[fd]->f_socket_id = 0;
/* move the newly created fd to the old one */
if (newfd != -1)
{
/* Tricky bit: we have to remember that for dupped sockets all file descriptors
point to the same file structure: obtaining one released file descriptor will
obtain them all. So we have to scan for file descriptors that point to the
same file structure for which we just obtained the socket and copy the new
file structure into the dupped file descriptors.
Note that the newly created file structure has the field f_socket_id set to 0,
so we won't come here again because of the if-statement above that tests whether
the socket was released. */
int fd2;
/* scan the file descriptors */
for (fd2 = fd + 1; fd2 < NOFILE; fd2++)
{
/* do they point to the same file structure? */
if (mu->u_ofile[fd] == mu->u_ofile[fd2])
{
/* in that case copy the newly obtained socket into this file descriptor
and increase the open count */
mu->u_ofile[fd2] = mu->u_ofile[newfd];
mu->u_ofile[newfd]->f_count++;
}
}
/* and finally do the same for fd */
mu->u_ofile[fd] = mu->u_ofile[newfd];
mu->u_ofile[newfd] = 0;
}
}
}
else
mu->u_ofile[fd]->f_count++;
}
/* also copy u_segs, after all, the child will run initially in the
same SegList as the parent. */
mu->u_segs = pu->u_segs;
mu->u_start_pc = pu->u_start_pc;
mu->u_end_pc = pu->u_end_pc;
mu->u_is_root = pu->u_is_root;
mu->u_a4 = pu->u_a4;
/* copying finished, allow other processes to vfork() as well ;-)) */
ix_unlock_base ();
/* remember the message we have to reply when either _exit() or
* execve() is called */
mu->p_vfork_msg = vm;
vm->vm_rc = 0;
#ifdef NATIVE_MORPHOS
mu->u_save_sp = mu->u_is_ppc ? (void *) get_sp () : (void *) get_68k_sp();
#else
mu->u_save_sp = (void *) get_sp ();
#endif
KPRINTF(("sp = %lx\n", mu->u_save_sp));
/*#ifdef NATIVE_MORPHOS
mu->u_save_68k_sp = (void *) get_68k_sp ();
KPRINTF(("r1 = %lx, a7 = %lx\n", mu->u_save_sp, mu->u_save_68k_sp));
#endif*/
/*{int* p=(int*)get_sp();
KPRINTF(("r1=%lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
p=(int*)get_68k_sp();
KPRINTF(("a7=%lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
}*/
/* we get here when the user does an _exit()
* (so as well after execve() terminates !) */
if (_setjmp ((void *)mu->u_jmp_buf))
{
int i;
/*{int* p=(int*)get_sp();
KPRINTF(("r1=%lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
p=(int*)get_68k_sp();
KPRINTF(("a7=%lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
}*/
/* reset `mu' in here, setjmp() might have clobbered it */
me = (struct Process *)SysBase->ThisTask;
u_ptr = getuser(me);
mu = &u;
if (mu->p_vfork_msg)
{
#ifdef NATIVE_MORPHOS
me->pr_Task.tc_ETask->PPCSPLower = (void *)mu->p_vfork_msg->vm_clowerppc;
me->pr_Task.tc_ETask->PPCSPUpper = (void *)mu->p_vfork_msg->vm_cupperppc;
#endif
me->pr_Task.tc_SPLower = (void *)mu->p_vfork_msg->vm_clower;
me->pr_Task.tc_SPUpper = (void *)mu->p_vfork_msg->vm_cupper;
}
/* overkill? */
vfork_own_malloc ();
/* although this is done in CloseLibrary(), files should
really be closed *before* a death-message is sent to
the parent. */
for (i = 0; i < NOFILE; i++)
if (u.u_ofile[i])
syscall (SYS_close, i);
/* free memory (look that most, best all memory is freed here, as
long as we're not inside Forbid. If memory is freed in CloseLibrary,
it may potentially have to wait for the memory semaphore in buddy-alloc.c,
thus breaking the Forbid! */
all_free ();
KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
/* this whole thing only happens if our parent is still alive ! */
Forbid ();
KPRINTF(("p_ptr = %lx\n", mu->p_pptr));
if (mu->p_pptr && mu->p_pptr != (struct Process *) 1)
send_death_msg((struct user *)mu);
else
{
KPRINTF (("vforked: couldn't send death_msg\n"));
}
KPRINTF (("vforked: now closing library\n"));
CloseLibrary (ixb);
/*{int* p=(int*)get_sp();
KPRINTF(("r2=%lx ret=%lx\n",MyEmulHandle,(*(int**)p)[1]));
KPRINTF(("r1=%lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
p=*(int **)p;
KPRINTF(("nxt=%lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
p=(int*)get_68k_sp();
KPRINTF(("a7=%lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
}*/
KPRINTF (("vforked: falling off the edge of the world.\n"));
/* just fall off the edge of the world, this is a process */
return;
}
syscall (SYS_sigsetmask, omask);
KPRINTF (("vforked: jumping back\n"));
vm = *&vm; /* uh ? */
#ifdef NATIVE_MORPHOS
/* don't change r2 */
vm->vm_regs->jb[3] = (LONG)MyEmulHandle;
if (mu->u_is_ppc)
{
/* do a ppc longjmp() without restoring the 68k context */
me->pr_Task.tc_ETask->PPCSPLower = (void *)vm->vm_plower;
me->pr_Task.tc_ETask->PPCSPUpper = (void *)vm->vm_pupper;
/* don't change a7 */
vm->vm_regs->jb[75] = get_68k_sp();
/* jump into nevereverland ;-) */
vfork_longjmp (vm->vm_regs->jb, 0);
/* NOTREACHED */
}
else
{
/* do a 68k longjmp() without restoring the ppc context */
/* binary code for:
move.l a0,a7
rts
*/
static const UWORD gate[] = { 0x2E48, 0x4E75 };
me->pr_Task.tc_SPLower = (void *)vm->vm_plower;
me->pr_Task.tc_SPUpper = (void *)vm->vm_pupper;
REG_D0 = 0;
REG_A0 = vm->vm_regs->jb[75]; /* a7 */
memcpy(&REG_D2, &vm->vm_regs->jb[64], 6*4);
memcpy(&REG_A2, &vm->vm_regs->jb[70], 5*4);
KPRINTF(("r1 = %lx, a7 = %lx\n", get_sp(), REG_A0));
{int* p=REG_A0;
KPRINTF(("%08lx %08lx %08lx %08lx %08lx %08lx\n",p[0],p[1],p[2],p[3],p[4],p[5]));}
(void)MyEmulHandle->EmulCallDirect68k((APTR)gate);
/* NOTREACHED */
}
#else
me->pr_Task.tc_SPLower = (void *)vm->vm_plower;
me->pr_Task.tc_SPUpper = (void *)vm->vm_pupper;
/* jump into nevereverland ;-) */
vfork_longjmp (vm->vm_regs->jb, 0);
/* NOTREACHED */
#endif
}
Forbid();
vm->vm_rc = ENOMEM; /* can't imagine any other reason why the OpenLib should fail */
ReplyMsg ((struct Message *) vm);
/* fall off the edge of the world ;-) */
}
#ifdef NATIVE_MORPHOS
struct EmulLibEntry _gate_launcher = {
TRAP_LIBNR, 0, (void(*)())launcher
};
#endif
void
send_death_msg(struct user *mu)
{
struct death_msg *dm = 0;
struct user *pu = safe_getuser(mu->p_pptr);
/* We are in forbid state */
/* KPRINTF (("vforked: parent alive, zombie-sig = %ld, vfork_msg = $%lx.\n",
pu->p_zombie_sig, mu->p_vfork_msg));*/
/* send the parent a death message with our return code */
dm = (struct death_msg *) kmalloc (sizeof (struct death_msg));
if (dm)
{
dm->dm_status = mu->p_xstat;
dm->dm_rusage = mu->u_ru;
ruadd (&dm->dm_rusage, (struct rusage *)&mu->u_cru);
dm->dm_child = (struct Process *) SysBase->ThisTask;
dm->dm_pgrp = mu->p_pgrp;
KPRINTF (("vfork-exit: Adding child $%lx to $%lx\n", dm->dm_child, mu->p_pptr));
ixaddtail ((struct ixlist *) &pu->p_zombies, (struct ixnode *) dm);
}
_psignal ((struct Task *)mu->p_pptr, SIGCHLD);
/* have to wakeup the parent `by hand' to make sure it gets
out of its sleep, since it might have SIGCHLD masked out or
ignored at the moment */
if (pu->p_stat == SSLEEP && pu->p_wchan == (caddr_t) pu)
{KPRINTF(("ix_wakeup()\n"));
ix_wakeup ((u_int)pu);}
if (mu->p_vfork_msg)
{KPRINTF(("replymsg()\n"));
ReplyMsg ((struct Message *) mu->p_vfork_msg);}
/* this is necessary for process synchronisation, this process
will be unlinked from the process chain by wait4(), which will
also take care of reparenting the process if it was PT_ATTACHed
by a debugger */
if (dm)
ix_sleep ((caddr_t)dm, "vfork-dm");
KPRINTF(("end send_death_msg()\n"));
}
/* This function is used by vfork_resume and execve. Perhaps it should be made
externally available? It causes the process to switch to its own malloc
list, and copies errno and environ into private space. */
void
vfork_own_malloc (void)
{
usetup;
/* use volatile here, or the compiler might do wrong `optimization' .. */
volatile struct user *p = &u;
if (p->u_mdp != &p->u_md)
{
char **parent_environ = *p->u_environ;
/* switch to our memory list (which is initialized by OpenLibrary) */
p->u_mdp = (void *)&p->u_md;
/* dupvec now uses malloc() on our list */
p->u_environ = (char ***) malloc (4);
*p->u_environ = dupvec (parent_environ);
p->u_errno = (int *) malloc (4);
*p->u_errno = 0;
p->u_h_errno = (int *) malloc (4);
*p->u_h_errno = 0;
}
}
#define _MAKESTR(o) #o
#define MAKESTR(o) _MAKESTR(o)
#ifdef NATIVE_MORPHOS
asm("
.globl vfork
.globl ix_vfork
.globl ix_vfork_resume
vfork:
/* store a setjmp () compatible frame on the stack to pass to _vfork () */
stwu 1,-384(1) /* _JBLEN longs + prolog on the stack */
mflr 0
addi 3,1,8
stw 0,388(1)
bl setjmp
/* now patch sp and pc, since they differ */
lwz 0,388(1)
addi 4,1,384 /* account for buffer space */
stw 0,28(1) /* insert real PC */
stw 4,16(1)
/* tell _vfork *not* yet to switch to own malloc-list */
li 3,0
addi 4,1,8
b _vfork
/* bl _vfork
lwz 0,388(1)
mtlr 0
addi 1,1,384
stw 3,0(2)
blr*/
/*_trampoline_vfork:
stwu 1,-80(1)
mflr 0
stmw 16,8(1)
stw 0,84(1)
lmw 16,0(2)
bl vfork
lwz 31,60(2)
mr 16,3
lwz 0,84(1)
stmw 16,0(2)
mtlr 0
lmw 16,8(1)
addi 1,1,80
blr */
ix_vfork:
/* this is the vfork used in older versions of the library */
stwu 1,-384(1) /* _JBLEN longs + prolog on the stack */
mflr 0
addi 3,1,8
stw 0,388(1)
bl setjmp
/* now patch sp and pc, since they differ */
lwz 0,388(1)
addi 4,1,384 /* account for buffer space */
stw 0,28(1) /* insert real PC */
stw 4,16(1)
/* tell _vfork to have the child run with own malloc-list. */
li 3,1
addi 4,1,8
b _vfork
/* bl _vfork
lwz 0,388(1)
mtlr 0
addi 1,1,384
stw 3,0(2)
blr*/
/*_trampoline_ix_vfork:
stwu 1,-80(1)
mflr 0
stmw 16,8(1)
stw 0,84(1)
lmw 16,0(2)
bl ix_vfork
lwz 31,60(2)
mr 16,3
lwz 0,84(1)
stmw 16,0(2)
mtlr 0
lmw 16,8(1)
addi 1,1,80
blr */
/* the following is longjmp(), with the subtle difference that this */
/* thing doesn't insist in returning something non-zero... */
vfork_longjmp:
lwz 5,8(3)
cmpi 0,5,0 /* ensure non-zero SP */
beq- Lbotch /* oops! */
lwz 2,12(3)
lmw 14,304(3) /* fp2-fp7 */
lwz 6,252(3) /* *a7 */
stmw 14,0x88(2)
lmw 26,280(3) /* a2-a7 */
lwz 5,248(3) /* pc */
stmw 26,40(2)
stw 6,0(31)
lmw 26,256(3) /* d2-d7 */
stmw 26,8(2)
lmw 12,24(3)
lfd 14,104(3)
lfd 15,112(3)
lfd 16,120(3)
lfd 17,128(3)
lfd 18,136(3)
lfd 19,144(3)
lfd 20,152(3)
lfd 21,160(3)
lfd 22,168(3)
lfd 23,176(3)
lfd 24,184(3)
lfd 25,192(3)
lfd 26,200(3)
lfd 27,208(3)
lfd 28,216(3)
lfd 29,224(3)
lfd 30,232(3)
lfd 31,240(3)
mtcr 12
b sigreturn
Lbotch:
b longjmperror
ix_vfork_resume:
/* pass the stack pointer */
lwz 3,0(1) /* lr will be in the next frame. r1+8 would probably be enough */
addi 3,3,12
b _vfork_resume
_trampoline_ix_vfork_resume:
stwu 1,-64(1)
mflr 0
stmw 18,8(1)
lwz 3,60(2) /* r3 = a7 */
lmw 18,8(2) /* save 68k regs */
stw 0,68(1)
addi 3,3,128 /* add some space to be a little safer */
bl _vfork_resume
lwz 31,60(2) /* r31 = a7. It was changed, don't restore it. */
lwz 0,68(1)
stmw 18,8(2) /* restore 68k regs */
mtlr 0
lmw 18,8(1)
addi 1,1,64
blr
");
pid_t vfork(void);
pid_t ix_vfork(void);
void ix_vfork_resume(void);
/*void _trampoline_vfork(void);
void _trampoline_ix_vfork(void);*/
void _trampoline_ix_vfork_resume(void);
struct EmulLibEntry _gate_vfork = {
TRAP_LIBRESTORE, 0, (void(*)())/*_trampoline_*/vfork
};
struct EmulLibEntry _gate_ix_vfork = {
TRAP_LIBRESTORE, 0, (void(*)())/*_trampoline_*/ix_vfork
};
struct EmulLibEntry _gate_ix_vfork_resume = {
TRAP_LIBRESTORE, 0, (void(*)())_trampoline_ix_vfork_resume
};
#else
asm (" \n\
.globl _vfork \n\
.globl _ix_vfork \n\
.globl _ix_vfork_resume \n\
_vfork: \n\
| store a setjmp () compatible frame on the stack to pass to _vfork () \n\
lea sp@(-18*4),sp | _JBLEN (17) longs on the stack \n\
pea sp@ \n\
jbsr _setjmp \n\
addqw #4,sp \n\
| now patch sp and pc, since they differ \n\
addl #20*4,sp@(8) | account for buffer space \n\
movel sp@(18*4),sp@(20) | insert real PC (return addr on stack) \n\
| tell _vfork *not* yet to switch to own malloc-list \n\
pea 0:W \n\
bsr __vfork \n\
lea sp@(18*4 + 4),sp \n\
rts \n\
\n\
_ix_vfork: \n\
| this is the vfork used in older versions of the library \n\
lea sp@(-18*4),sp | _JBLEN (17) longs on the stack \n\
pea sp@ \n\
jbsr _setjmp \n\
addqw #4,sp \n\
| now patch sp and pc, since they differ \n\
addl #20*4,sp@(8) | account for buffer space \n\
movel sp@(18*4),sp@(20) | insert real PC (return addr on stack) \n\
| tell _vfork to have the child run with own malloc-list. \n\
pea 1:W \n\
bsr __vfork \n\
lea sp@(18*4 + 4),sp \n\
rts \n\
\n\
| the following is longjmp(), with the subtle difference that this \n\
| thing doesn't insist in returning something non-zero... \n\
_vfork_longjmp: \n\
movel sp@(4),a0 /* save area pointer */ \n\
tstl a0@(8) /* ensure non-zero SP */ \n\
jeq Lbotch /* oops! */ \n\
movel sp@(8),d0 /* grab return value */ \n\
moveml a0@(28),d2-d7/a2-a4/a6 /* restore non-scratch regs */ \n\
movel a0,sp@- /* let sigreturn */ \n\
jbsr _sigreturn /* finish for us */ \n\
\n\
Lbotch: \n\
jbsr _longjmperror \n\
stop #0 \n\
\n\
_ix_vfork_resume: \n\
pea sp@ | pass the sp containing the return address \n\
bsr __vfork_resume \n\
addqw #4,sp \n\
rts \n\
");
#endif
/*
* this is an implementation extension to the `real' vfork2(). Normally you
* can only cause the parent to resume by calling _exit() or execve() from
* the child. Since I can't provide a real fork() on the Amiga, this function
* is a third possibility to make the parent resume. You have then two
* concurrent processes sharing the same frame and global data... Please be
* EXTREMELY careful what you may do and what not. vfork2() itself is a hack,
* this is an even greater one...
*
* DO NOT use this function in combination with vfork(), or you'll get a big
* memory leak. Only use it with vfork2().
*/
/*
* don't make this function static, gcc doesn't notice that it's used from the
* assembly code above, so make sure it isn't optimized away...
*/
#ifdef NATIVE_MORPHOS
static void _reply_msg(struct Message *msg)
{
ReplyMsg(msg);
}
#endif
void
_vfork_resume (u_int *copy_from_sp)
{
struct Process *me = (struct Process *) SysBase->ThisTask;
struct user *u_ptr = getuser(me);
/* vm is used after stack-switching, put it in a register */
#ifdef NATIVE_MORPHOS
register struct vfork_msg **vm asm("r14") = &u.p_vfork_msg;
ULONG save[13];
int k;
for (k = 2; k < 15; ++k)
save[k - 2] = ((ULONG *)MyEmulHandle)[k];
#else
register struct vfork_msg **vm asm("a2") = &u.p_vfork_msg;
#endif
/* KPRINTF(("resume: me = %lx, vm = %lx, u_ptr = %lx\n", me, vm, u_ptr));
{int* p=(int*)get_sp();
KPRINTF(("r1=%lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
p=(int*)get_68k_sp();
KPRINTF(("a7=%lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
p=(int*)u.u_save_sp;
KPRINTF(("orig a7=%lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
}*/
if (*vm)
{
u_int *sp = (u_int *)u.u_save_sp;
u_int *copy_till_sp;
u_int *p;
#ifdef NATIVE_MORPHOS
/* u_int *sp_68k = (u_int *)u.u_save_68k_sp;
u_int *copy_till_68k_sp = (u_int *)get_68k_sp ();
u_int *copy_from_68k_sp = copy_from_sp;*/
int offset;
/* copy_from_sp = (u_int *)getuser((*vm)->vm_pptr)->u_vfork_frame[2] + 4;
KPRINTF(("r1 = %lx, save_r1 = %lx, a7 = %lx, save_a7 = %lx\n", copy_till_sp, sp, copy_till_68k_sp, sp_68k));*/
offset = (int)sp - (int)copy_from_sp - 4;
if (u.u_is_ppc)
{
copy_till_sp = (u_int *)get_sp ();
}
else
{
copy_till_sp = (u_int *)get_68k_sp();
}
#else
copy_till_sp = (u_int *)get_sp ();
#endif
/* be sure to switch to our memory list */
vfork_own_malloc ();
KPRINTF(("memlist switched\n"));
/* copy the stack frame */
KPRINTF(("copy stack %lx-%lx to %lx\n", copy_till_sp, copy_from_sp, sp));
p = copy_from_sp + 1;
do
*--sp = *--p;
while (p > copy_till_sp);
#ifdef NATIVE_MORPHOS
if (u.u_is_ppc)
{
/* Major hack below. If that works, we're lucky ! */
/* adjust the frame pointers */
p = sp;
while (*p >= (u_int)copy_till_sp && *p < (u_int)copy_from_sp)
{
KPRINTF(("adjusting frame %lx -> %lx\n", *p, *p+offset));
*p += offset;
p = (u_int *)*p;
}
/* if the EmulHandle was in the copied part of the stack, adjust the pointer */
if ((u_int *)MyEmulHandle < copy_from_sp &&
(u_int *)MyEmulHandle > copy_till_sp)
{ MyEmulHandle = (void *)((int)MyEmulHandle + offset);
KPRINTF(("EmulHandle adjusted: %lx\n", MyEmulHandle));}
/*KPRINTF(("ppc stack:\n"));
p = sp;
while (p < sp + 256)
{
KPRINTF(("%08lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p,p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7]));
p+=8;
}*/
}
KPRINTF(("state: r1=%lx, lr=%lx\n", get_sp(), __builtin_return_address(0)));
#endif
me->pr_Task.tc_SPLower = (void *)(*vm)->vm_clower;
me->pr_Task.tc_SPUpper = (void *)(*vm)->vm_cupper;
#ifdef NATIVE_MORPHOS
me->pr_Task.tc_ETask->PPCSPUpper = (void *)(*vm)->vm_cupperppc;
me->pr_Task.tc_ETask->PPCSPLower = (void *)(*vm)->vm_clowerppc;
if (u.u_is_ppc)
{
set_sp ((u_int) sp);
}
else
{
set_68k_sp ((u_int) sp);
}
KPRINTF(("stack switched: r1=%lx, lr=%lx, a7=%lx\n", get_sp(), __builtin_return_address(0), get_68k_sp()));
#else
set_sp ((u_int) sp);
#endif
/*{int* p=(int*)get_sp();
KPRINTF(("r1=%lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
p=(int*)get_68k_sp();
KPRINTF(("a7=%lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
}*/
#ifdef NATIVE_MORPHOS
_reply_msg ((struct Message *) *vm);
#else
ReplyMsg ((struct Message *) *vm);
#endif
*vm = 0;
}
/*{int* p=(int*)get_sp();
KPRINTF(("r1=%lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
p=(int*)get_68k_sp();
KPRINTF(("a7=%lx: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]));
}*/
#ifdef NATIVE_MORPHOS
for (k = 2; k < 15; ++k)
((ULONG *)MyEmulHandle)[k] = save[k - 2];
#endif
KPRINTF(("done\n"));
}
#define STACKNAME ("IXSTACK") /* Environment variable for stack size */
static int get_stack_size(struct Process *proc)
{
int stack_size,stack_size2=512000;
char *tmp;
struct CommandLineInterface *CLI = BTOCPTR (proc->pr_CLI);
if ((tmp = getenv (STACKNAME)))stack_size2 = atoi (tmp);
stack_size = CLI ? CLI->cli_DefaultStack * 4 : proc->pr_StackSize;
if (stack_size2 > stack_size)stack_size = stack_size2;
if (stack_size < STACKSIZE)
return STACKSIZE;
return stack_size;
}
/*
* don't make this function static, gcc doesn't notice that it's used from the
* assembly code above, so make sure it isn't optimized away...
*/
int
_vfork (int own_malloc, struct reg_parms rp)
{
struct Process *me = (struct Process *) SysBase->ThisTask;
struct user *u_ptr = getuser(me);
struct CommandLineInterface *CLI = (void *)(me->pr_CLI);
u_int stack_size = get_stack_size(me);
u_int plower, pupper;
/*#ifdef NATIVE_MORPHOS
u_int plowerppc, pupperppc;
#endif*/
/* those *have* to be in registers to survive the stack deallocation */
#ifndef __PPC__
register struct vfork_msg *vm asm ("a2");
#else
register struct vfork_msg *vm asm ("r14");
register u_int return_addr_68k asm ("r15");
#endif
struct Process *child;
int i;
struct TagItem tags [] = {
#ifdef NATIVE_MORPHOS
{ NP_Entry, (ULONG) &_gate_launcher },
#else
{ NP_Entry, (ULONG) launcher },
#endif
{ NP_Name, (ULONG) "vfork()'d process" }, /* to be overridden by execve() */
{ NP_StackSize, stack_size }, /* same size we use */
{ NP_Cli, TRUE }, /* same thing we are */
//{ NP_Cli, (ULONG) (CLI ? -1 : 0) }, /* same thing we are */
{ TAG_END, 0 }
};
KPRINTF(("vfork: user=%lx, a4_size=%ld, a4=%lx\n",u_ptr,u_ptr->u_a4_pointers_size*4,u_ptr->u_a4));
KPRINTF (("vfork: creating child with stacksize = $%lx[$%lx,$%lx]\n", stack_size, tags[3]));
vm = (struct vfork_msg *) kmalloc (sizeof (struct vfork_msg));
if (!vm)
{
errno = ENOMEM;
KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
vfork_longjmp (u.u_vfork_frame, -1);
return -1; /* not reached */
}
bzero(vm, sizeof(*vm));
vm->vm_msg.mn_ReplyPort = u.u_sync_mp;
vm->vm_msg.mn_Node.ln_Type = NT_MESSAGE;
vm->vm_msg.mn_Length = sizeof (struct vfork_msg);
vm->vm_pptr = me;
vm->vm_own_malloc = own_malloc;
vm->vm_regs = &rp;
#ifdef NATIVE_MORPHOS
if (u.u_is_ppc)
{
vm->vm_plower = (int)me->pr_Task.tc_ETask->PPCSPLower;
vm->vm_pupper = (int)me->pr_Task.tc_ETask->PPCSPUpper;
}
else
{
vm->vm_plower = (int)me->pr_Task.tc_SPLower;
vm->vm_pupper = (int)me->pr_Task.tc_SPUpper;
return_addr_68k = *(u_int *)get_68k_sp();
}
#else
vm->vm_plower = (int)me->pr_Task.tc_SPLower;
vm->vm_pupper = (int)me->pr_Task.tc_SPUpper;
#endif
/* we have to block all signals as long as the child uses our resources.
* but since the child needs to start with the signal mask BEFORE this
* general blocking, we have to pass it the old signal mask. This is a
* way to do it */
vm->vm_rc = vm->vm_omask = syscall (SYS_sigsetmask, ~0);
/* save the passed frame in our user structure, since the child will
deallocate it from the stack when it `returns' to user code */
bcopy (&rp, &u.u_vfork_frame, sizeof (rp));
/* if some fd's are sockets, release them so the child can obtain them */
for (i = 0; i < NOFILE; i++)
{
if (u.u_ofile[i] && u.u_ofile[i]->f_type == DTYPE_SOCKET)
u.u_ofile[i]->f_socket_id = syscall(SYS_ix_release_socket, i);
}
KPRINTF (("vfork: creating child with stacksize = $%lx[$%lx,$%lx]\n", stack_size, tags[3]));
vm->vm_self = CreateNewProc (tags);
if (!vm->vm_self)
{
/* do I have to close input/output here? Or does the startup close
them no matter whether it succeeds or not ? */
syscall (SYS_sigsetmask, vm->vm_omask);
kfree (vm);
errno = EPROCLIM;
KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
vfork_longjmp (u.u_vfork_frame, -1);
return -1; /* not reached */
}
/* As soon as this message is dispatched, the child will `return' and
deallocate the stack we're running on. So afterwards, *only* use
register variables and then longjmp () back.
Since we don't have a stack until after the longjmp(), temporarily
switch to our mini-stack */
#ifdef NATIVE_MORPHOS
if (u.u_is_ppc)
{
me->pr_Task.tc_ETask->PPCSPLower = (void *)(((int)u.u_mini_stack));
me->pr_Task.tc_ETask->PPCSPUpper = (void *)(((int)u.u_mini_stack) + sizeof(u.u_mini_stack));
set_sp(((u_int)me->pr_Task.tc_ETask->PPCSPUpper & ~15) - 512);
}
else
{
me->pr_Task.tc_SPLower = (void *)u.u_mini_stack;
me->pr_Task.tc_SPUpper = (void *)(((int)u.u_mini_stack) + sizeof(u.u_mini_stack));
set_68k_sp((u_int)me->pr_Task.tc_SPUpper);
}
#else
me->pr_Task.tc_SPLower = (void *)u.u_mini_stack;
me->pr_Task.tc_SPUpper = (void *)(((int)u.u_mini_stack) + sizeof(u.u_mini_stack));
set_sp((u_int)me->pr_Task.tc_SPUpper); // need to be inline, so crash when not optimize
#endif
PutMsg (&vm->vm_self->pr_MsgPort, (struct Message *) vm);
/* wait until the child does execve() or _exit() */
KPRINTF (("waiting for child\n"));
WaitPort (getuser(vm->vm_pptr)->u_sync_mp);
GetMsg (getuser(vm->vm_pptr)->u_sync_mp);
KPRINTF (("child returned or called execve()\n"));
syscall (SYS_sigsetmask, vm->vm_omask);
plower = vm->vm_plower;
pupper = vm->vm_pupper;
me = vm->vm_pptr;
child = vm->vm_self;
// restore u_ptr
u_ptr = getuser(me);
if (vm->vm_rc)
{
errno = (int) vm->vm_rc;
KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
vm->vm_self = (struct Process *) -1;
}
/* this is the parent return, so we pass the id of the new child */
kfree (vm);
#ifdef NATIVE_MORPHOS
if (u.u_is_ppc)
{
me->pr_Task.tc_ETask->PPCSPLower = (void *)plower;
me->pr_Task.tc_ETask->PPCSPUpper = (void *)pupper;
}
else
{
me->pr_Task.tc_SPLower = (void *)plower;
me->pr_Task.tc_SPUpper = (void *)pupper;
*(u_int *)u.u_vfork_frame[75] = return_addr_68k;
}
#else
me->pr_Task.tc_SPLower = (void *)plower;
me->pr_Task.tc_SPUpper = (void *)pupper;
#endif
KPRINTF(("returning\n"));
/* could use longjmp() here, but since we already *have* the local one.. */
vfork_longjmp (u.u_vfork_frame, (int) child);
return 0; /* not reached */
}
void ruadd(struct rusage *ru, struct rusage *ru2)
{
register long *ip, *ip2;
register int i;
timevaladd(&ru->ru_utime, &ru2->ru_utime);
timevaladd(&ru->ru_stime, &ru2->ru_stime);
if (ru->ru_maxrss < ru2->ru_maxrss)
ru->ru_maxrss = ru2->ru_maxrss;
ip = &ru->ru_first; ip2 = &ru2->ru_first;
for (i = &ru->ru_last - &ru->ru_first; i > 0; i--)
*ip++ += *ip2++;
}
/*
* make process 'parent' the new parent of process 'child'.
*/
void
proc_reparent(struct Process *child, struct Process *parent)
{
register struct Process *o;
register struct Process *y;
struct user *cu = safe_getuser(child);
struct user *pu = (parent != (struct Process *)1 ? safe_getuser(parent) : 0);
if (cu->p_pptr == parent)
return;
KPRINTF(("proc_reparent(%lx, %lx)\n", child, parent));
/* fix up the child linkage for the old parent */
o = cu->p_osptr;
y = cu->p_ysptr;
if (y)
safe_getuser(y)->p_osptr = o;
if (o)
safe_getuser(o)->p_ysptr = y;
if (cu->p_pptr && cu->p_pptr != (struct Process *)1)
{
struct user *tu = safe_getuser(cu->p_pptr);
if (tu->p_cptr == child)
tu->p_cptr = o;
}
if (pu)
{
/* fix up child linkage for new parent, if there is one */
o = pu->p_cptr;
if (o)
safe_getuser(o)->p_ysptr = child;
cu->p_osptr = o;
cu->p_ysptr = NULL;
pu->p_cptr = child;
cu->p_pptr = parent;
}
}
static int
possible_childs(int pid, struct Process *cptr)
{
usetup;
while (cptr)
{
/*
* Seems things are getting filled in too slowly, so we will wait till they do
* get filled. [this solves a NULL pointer problem] -dz-
*/
/* while (!getuser(cptr))
{
Delay(5);
}*/
struct user *u_ptr = safe_getuser(cptr);
if (u_ptr->p_stat != SZOMB)
if (pid == -1 ||
(pid == 0 && u_ptr->p_pgrp == u.p_pgrp) ||
(pid < -1 && u_ptr->p_pgrp == -pid) ||
(pid == (int)cptr))
return TRUE;
cptr = u_ptr->p_osptr;
}
return FALSE;
}
/* This function is in desperate need of redesign !!!! */
int
wait4 (int pid, int *status, int options, struct rusage *rusage)
{
struct Process *me = (struct Process *) SysBase->ThisTask;
struct user *u_ptr = getuser(me);
KPRINTF(("wait4(%lx, %lx)\n", pid, options));
for (;;)
{
int err = 0;
struct death_msg *dm;
int got_node = 0;
Forbid ();
for (dm = (struct death_msg *)u.p_zombies.head;
dm;
dm = (struct death_msg *)dm->dm_node.next)
{
if (pid == -1 ||
(pid == 0 && dm->dm_pgrp == u.p_pgrp) ||
(pid < -1 && dm->dm_pgrp == - pid) ||
(pid == (int) dm->dm_child))
{
got_node = 1;
break;
}
}
if (!got_node && !possible_childs(pid, u.p_cptr))
err = ECHILD;
if (got_node)
/* Handle exited children first. */
{
struct Process *child = dm->dm_child;
struct user *cu = getuser(child);
struct Task *t;
if(!cu){ cu=safe_getuser(child);
KPRINTF(("********** cu==0 !! real = %lx\n", cu));}
/*
* If we got the child through a ptrace 'attach',
* we need to give it back to the old parent.
*/
if (cu->p_opptr && (t = pfind((pid_t)cu->p_opptr)))
{
proc_reparent(child, (struct Process *)t);
cu->p_opptr = 0;
_psignal(t, SIGCHLD);
ix_wakeup((u_int)t);
Permit();
return (int)child;
}
KPRINTF (("wait4: unlinking child $%lx\n", dm->dm_child));
ixremove ((struct ixlist *)&u.p_zombies, (struct ixnode *) dm);
cu->p_stat = SZOMB;
ix_wakeup ((u_int)dm);
Permit ();
if (status)
{
*status = dm->dm_status;
}
if (rusage)
*rusage = dm->dm_rusage;
kfree (dm);
return (int) child;
}
else
/* No child processes have died for now.
Do we have a traced child process to handle? */
{
struct Process *p;
struct user *pu;
KPRINTF(("wait4: checking traced children, pid=%lx, u.p_cptr=%lx\n",
pid, u.p_cptr));
for (p = u.p_cptr; p; p = pu->p_osptr)
{
KPRINTF(("wait4: checking pid p=%lx\n", p));
pu = safe_getuser(p);
if (pid == -1
|| ((int) p) == pid
|| pu->p_pgrp == -pid
|| (pid == 0
&& u.p_pgrp == pu->p_pgrp))
{
KPRINTF(("wait4: pu->p_stat=%lx, pu->flag=%lx\n",
pu->p_stat, pu->p_flag));
if (pu->p_stat == SSTOP
&& (pu->p_flag & SWTED) == 0
&& (pu->p_flag & STRC || options & WUNTRACED))
{
KPRINTF(("wait4: SSTOPed; p_xstat=0x%lx, W_STOPCODEd=0x%lx\n",
pu->p_xstat, W_STOPCODE (pu->p_xstat)));
pu->p_flag |= SWTED;
if (status)
{
*status = W_STOPCODE (pu->p_xstat);
KPRINTF(("wait4: status was set to %ld\n", *status));
}
Permit();
return (int)p;
}
}
}
}
if (!got_node && err)
{
Permit();
errno = err;
KPRINTF(("&errno = %lx, errno = %ld\n", &errno, errno));
return -1;
}
if (options & WNOHANG)
{
Permit();
return 0;
}
ix_sleep((caddr_t)&u, "wait4");
if (u.p_sig)
err = EINTR;
Permit();
if (CURSIG(&u))
setrun((struct Task *)me);
}
}