NetBSD-5.0.2/sys/compat/irix/irix_prctl.c

Compare this file to the similar file:
Show the results in this format:

/*	$NetBSD: irix_prctl.c,v 1.48 2008/07/02 19:49:58 rmind Exp $ */

/*-
 * Copyright (c) 2001-2002 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Emmanuel Dreyfus.
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: irix_prctl.c,v 1.48 2008/07/02 19:49:58 rmind Exp $");

#include <sys/errno.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/signal.h>
#include <sys/signalvar.h>
#include <sys/systm.h>
#include <sys/exec.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/rwlock.h>
#include <sys/filedesc.h>
#include <sys/vnode.h>
#include <sys/resourcevar.h>
#include <sys/kauth.h>

#include <uvm/uvm_extern.h>
#include <uvm/uvm_map.h>

#include <machine/regnum.h>
#include <machine/vmparam.h>

#include <compat/svr4/svr4_types.h>

#include <compat/irix/irix_types.h>
#include <compat/irix/irix_exec.h>
#include <compat/irix/irix_prctl.h>
#include <compat/irix/irix_signal.h>
#include <compat/irix/irix_syscallargs.h>

struct irix_sproc_child_args {
	struct proc **isc_proc;
	void *isc_entry;
	void *isc_arg;
	size_t isc_len;
	int isc_inh;
	struct lwp *isc_parent_lwp;
	struct irix_share_group *isc_share_group;
	int isc_child_done;
};
static void irix_sproc_child(struct irix_sproc_child_args *);
static int irix_sproc(void *, unsigned int, void *, void *, size_t,
    pid_t, struct lwp *, register_t *);
static struct irix_shared_regions_rec *irix_isrr_create(vaddr_t,
    vsize_t, int);
#ifdef DEBUG_IRIX
static void irix_isrr_debug(struct proc *);
#endif
static void irix_isrr_cleanup(struct proc *);

int
irix_sys_prctl(struct lwp *l, const struct irix_sys_prctl_args *uap, register_t *retval)
{
	/* {
		syscallarg(unsigned) option;
		syscallarg(void *) arg1;
	} */
	struct proc *p = l->l_proc;
	unsigned int option = SCARG(uap, option);

#ifdef DEBUG_IRIX
	printf("irix_sys_prctl(): option = %d\n", option);
#endif

	switch(option) {
	case IRIX_PR_GETSHMASK:	{	/* Get shared resources */
		struct proc *p2;
		int shmask = 0;
		struct irix_emuldata *ied;

		p2 = pfind((pid_t)SCARG(uap, arg1));

		if (p2 == p || SCARG(uap, arg1) == 0) {
			/* XXX return our own shmask */
			return 0;
		}

		if (p2 == NULL)
			return EINVAL;

		ied = (struct irix_emuldata *)p->p_emuldata;
		if (ied->ied_shareaddr)
			shmask |= IRIX_PR_SADDR;
		if (p->p_fd == p2->p_fd)
			shmask |= IRIX_PR_SFDS;
		if (p->p_cwdi == p2->p_cwdi)
			shmask |= (IRIX_PR_SDIR|IRIX_PR_SUMASK);

		*retval = (register_t)shmask;
		return 0;
		break;
	}

	case IRIX_PR_LASTSHEXIT:  	/* "Last sproc exit" */
		/* We no nothing */
		break;

	case IRIX_PR_GETNSHARE: {	/* Number of sproc share group memb.*/
		struct irix_emuldata *ied;
		struct irix_emuldata *iedp;
		struct irix_share_group *isg;
		int count;

		ied = (struct irix_emuldata *)p->p_emuldata;
		if ((isg = ied->ied_share_group) == NULL) {
			*retval = 0;
			return 0;
		}

		count = 0;
		rw_enter(&isg->isg_lock, RW_READER);
		LIST_FOREACH(iedp, &isg->isg_head, ied_sglist)
			count++;
		rw_exit(&isg->isg_lock);

		*retval = count;
		return 0;
		break;
	}

	case IRIX_PR_TERMCHILD: {	/* Get SIGHUP when parent's exit */
		struct irix_emuldata *ied;

		ied = (struct irix_emuldata *)(p->p_emuldata);
		ied->ied_termchild = 1;
		break;
	}

	case IRIX_PR_ISBLOCKED: {	/* Is process blocked? */
		pid_t pid = (pid_t)SCARG(uap, arg1);
		struct irix_emuldata *ied;
		struct proc *target;

		if (pid == 0)
			pid = p->p_pid;

		if ((target = pfind(pid)) == NULL)
			return ESRCH;

		if (irix_check_exec(target) == 0)
			return 0;

		if (kauth_authorize_process(l->l_cred, KAUTH_PROCESS_CANSEE,
		    target, KAUTH_ARG(KAUTH_REQ_PROCESS_CANSEE_ENTRY), NULL,
		    NULL) != 0)
			return EPERM;

		ied = (struct irix_emuldata *)(target->p_emuldata);
		*retval = (ied->ied_procblk_count < 0);
		return 0;
		break;
	}

	default:
		printf("Warning: call to unimplemented prctl() command %d\n",
		    option);
		return EINVAL;
		break;
	}
	return 0;
}


int
irix_sys_pidsprocsp(struct lwp *l, const struct irix_sys_pidsprocsp_args *uap, register_t *retval)
{
	/* {
		syscallarg(void *) entry;
		syscallarg(unsigned) inh;
		syscallarg(void *) arg;
		syscallarg(void *) sp;
		syscallarg(irix_size_t) len;
		syscallarg(irix_pid_t) pid;
	} */

	/* pid is ignored for now */
	printf("Warning: unsupported pid argument to IRIX sproc\n");

	return irix_sproc(SCARG(uap, entry), SCARG(uap, inh), SCARG(uap, arg),
	    SCARG(uap, sp), SCARG(uap, len), SCARG(uap, pid), l, retval);
}

int
irix_sys_sprocsp(struct lwp *l, const struct irix_sys_sprocsp_args *uap, register_t *retval)
{
	/* {
		syscallarg(void *) entry;
		syscallarg(unsigned) inh;
		syscallarg(void *) arg;
		syscallarg(void *) sp;
		syscallarg(irix_size_t) len;
	} */

	return irix_sproc(SCARG(uap, entry), SCARG(uap, inh), SCARG(uap, arg),
	    SCARG(uap, sp), SCARG(uap, len), 0, l, retval);
}

int
irix_sys_sproc(struct lwp *l, const struct irix_sys_sproc_args *uap, register_t *retval)
{
	/* {
		syscallarg(void *) entry;
		syscallarg(unsigned) inh;
		syscallarg(void *) arg;
	} */
	struct proc *p = l->l_proc;

	return irix_sproc(SCARG(uap, entry), SCARG(uap, inh), SCARG(uap, arg),
	    NULL, p->p_rlimit[RLIMIT_STACK].rlim_cur, 0, l, retval);
}


static int
irix_sproc(void *entry, unsigned int inh, void *arg, void *sp, size_t len, pid_t pid, struct lwp *l, register_t *retval)
{
	struct proc *p = l->l_proc;
	int bsd_flags = 0;
	struct exec_vmcmd vmc;
	int error;
	struct proc *p2;
	struct irix_sproc_child_args *isc;
	struct irix_emuldata *ied;
	struct irix_emuldata *iedp;
	struct irix_share_group *isg = NULL;
	segsz_t stacksize;

#ifdef DEBUG_IRIX
	printf("irix_sproc(): entry = %p, inh = %x, arg = %p, sp = 0x%08lx, len = 0x%08lx, pid = %d\n", entry, inh, arg, (u_long)sp, (u_long)len, pid);
#endif

	if (len == 0)
		return EINVAL;

	if (inh & IRIX_PR_SFDS)
		bsd_flags |= FORK_SHAREFILES;
	if (inh & IRIX_PR_SUMASK && inh & IRIX_PR_SDIR) {
		bsd_flags |= FORK_SHARECWD;
		/* Forget them so that we don't get warning below */
		inh &= ~(IRIX_PR_SUMASK|IRIX_PR_SDIR);
	}
	/* We know how to do PR_SUMASK and PR_SDIR together only */
	if (inh & IRIX_PR_SUMASK)
		printf("Warning: unimplemented IRIX sproc flag PR_SUMASK\n");
	if (inh & IRIX_PR_SDIR)
		printf("Warning: unimplemented IRIX sproc flag PR_SDIR\n");
	if (inh & IRIX_PR_SULIMIT)
		bsd_flags |= FORK_SHARELIMIT;

	/*
	 * If relevant, initialize the share group structure
	 */
	ied = (struct irix_emuldata *)(p->p_emuldata);
	if (ied->ied_share_group == NULL) {
		isg = malloc(sizeof(struct irix_share_group),
		    M_EMULDATA, M_WAITOK);
		rw_init(&isg->isg_lock);
		isg->isg_refcount = 0;

		rw_enter(&isg->isg_lock, RW_WRITER);
		LIST_INIT(&isg->isg_head);
		LIST_INSERT_HEAD(&isg->isg_head, ied, ied_sglist);
		isg->isg_refcount++;
		rw_exit(&isg->isg_lock);

		ied->ied_share_group = isg;
	}

	/*
	 * Setting up child stack
	 */
	if (inh & IRIX_PR_SADDR) {
		if (sp == NULL) {
			/*
			 * All share group  members have vm_maxsaddr set
			 * to the bottom of the lowest stack in address space,
			 * therefore we map the new stack there.
			 */
			sp = p->p_vmspace->vm_maxsaddr;

			/* Compute new stacks's bottom address */
			sp = (void *)trunc_page((u_long)sp - len);
		}

		/* Now map the new stack */
		bzero(&vmc, sizeof(vmc));
		vmc.ev_addr = trunc_page((u_long)sp);
		vmc.ev_len = round_page(len);
		vmc.ev_prot = UVM_PROT_RWX;
		vmc.ev_flags = UVM_FLAG_COPYONW|UVM_FLAG_FIXED|UVM_FLAG_OVERLAY;
		vmc.ev_proc = vmcmd_map_zero;
#ifdef DEBUG_IRIX
		printf("irix_sproc(): new stack addr=0x%08lx, len=0x%08lx\n",
		    (u_long)sp, (u_long)len);
#endif
		/* Normally it cannot be NULL since we just initialized it */
		if ((isg = ied->ied_share_group) == NULL)
			panic("irix_sproc: NULL ied->ied_share_group");

		IRIX_VM_SYNC(p, error = (*vmc.ev_proc)(l, &vmc));
		if (error)
			return error;

		/* Update stack parameters for the share group members */
		ied = (struct irix_emuldata *)p->p_emuldata;
		stacksize = ((char *)p->p_vmspace->vm_minsaddr - (char *)sp)
		    / PAGE_SIZE;


		rw_enter(&isg->isg_lock, RW_WRITER);
		LIST_FOREACH(iedp, &isg->isg_head, ied_sglist) {
			iedp->ied_p->p_vmspace->vm_maxsaddr = (void *)sp;
			iedp->ied_p->p_vmspace->vm_ssize = stacksize;
		}
		rw_exit(&isg->isg_lock);
	}

	/*
	 * Arguments for irix_sproc_child()
	 * This will be freed by the child.
	 */
	isc = malloc(sizeof(*isc), M_TEMP, M_WAITOK);
	isc->isc_proc = &p2;
	isc->isc_entry = entry;
	isc->isc_arg = arg;
	isc->isc_len = len;
	isc->isc_inh = inh;
	isc->isc_parent_lwp = l;
	isc->isc_share_group = isg;
	isc->isc_child_done = 0;

	if (inh & IRIX_PR_SADDR) {
		ied->ied_shareaddr = 1;
	}

	if ((error = fork1(l, bsd_flags, SIGCHLD, (void *)sp, len,
	    (void *)irix_sproc_child, (void *)isc, retval, &p2)) != 0)
		return error;

	/*
	 * The child needs the parent to stay alive until it has
	 * copied a few things from it. We sleep whatever happen
	 * until the child is done.
	 */
	while (!isc->isc_child_done)
		(void)tsleep(&isc->isc_child_done, PZERO, "sproc", 0);
	free(isc, M_TEMP);

	retval[0] = (register_t)p2->p_pid;
	retval[1] = 0;

	return 0;
}

static void
irix_sproc_child(struct irix_sproc_child_args *isc)
{
	struct proc *p2 = *isc->isc_proc;
	struct lwp *l2 = curlwp;
	int inh = isc->isc_inh;
	struct lwp *lparent = isc->isc_parent_lwp;
	struct proc *parent = lparent->l_proc;
	struct frame *tf = (struct frame *)l2->l_md.md_regs;
	struct frame *ptf = (struct frame *)lparent->l_md.md_regs;
	kauth_cred_t pc;
	struct irix_emuldata *ied;
	struct irix_emuldata *parent_ied;

#ifdef DEBUG_IRIX
	printf("irix_sproc_child()\n");
#endif
	/*
	 * Handle shared VM space. The process private arena is not shared
	 */
	if (inh & IRIX_PR_SADDR) {
		int error;
		vaddr_t minp, maxp;
		vsize_t len;
		struct irix_shared_regions_rec *isrr;

		/*
		 * First, unmap the whole address space
		 */
		minp = vm_map_min(&p2->p_vmspace->vm_map);
		maxp = vm_map_max(&p2->p_vmspace->vm_map);
		uvm_unmap(&p2->p_vmspace->vm_map, minp, maxp);

		/*
		 * Now, copy the mapping from the parent for shared regions
		 */
		parent_ied = (struct irix_emuldata *)parent->p_emuldata;
		LIST_FOREACH(isrr, &parent_ied->ied_shared_regions, isrr_list) {
			minp = isrr->isrr_start;
			len = isrr->isrr_len;
			maxp = minp + len;
			/* If this is a private region, skip */
			if (isrr->isrr_shared == IRIX_ISRR_PRIVATE)
				continue;

			/* Copy the new mapping from the parent */
			error = uvm_map_extract(&parent->p_vmspace->vm_map,
			    minp, len, &p2->p_vmspace->vm_map, &minp, 0);
			if (error != 0) {
#ifdef DEBUG_IRIX
				printf("irix_sproc_child(): error %d\n", error);
#endif
				isc->isc_child_done = 1;
				wakeup(&isc->isc_child_done);
				mutex_enter(proc_lock);
				killproc(p2,
				    "failed to initialize share group VM");
				mutex_exit(proc_lock);
			}
		}

		/* Map and initialize the process private arena (unshared) */
		error = irix_prda_init(p2);
		if (error != 0) {
			isc->isc_child_done = 1;
			wakeup(&isc->isc_child_done);
			mutex_enter(proc_lock);
			killproc(p2, "failed to initialize the PRDA");
			mutex_exit(proc_lock);
		}
	}

	/*
	 * Handle shared process UID/GID
	 */
	if (inh & IRIX_PR_SID) {
		pc = p2->p_cred;
		kauth_cred_hold(parent->p_cred);
		p2->p_cred = parent->p_cred;
		kauth_cred_free(pc);
	}

	/*
	 * Setup PC to return to the child entry point
	 */
	tf->f_regs[_R_PC] = (unsigned long)isc->isc_entry;
	tf->f_regs[_R_RA] = 0;

	/*
	 * Setup child arguments
	 */
	tf->f_regs[_R_A0] = (unsigned long)isc->isc_arg;
	tf->f_regs[_R_A1] = 0;
	tf->f_regs[_R_A2] = 0;
	tf->f_regs[_R_A3] = 0;
	if (ptf->f_regs[_R_S3] == (unsigned long)isc->isc_len) {
		tf->f_regs[_R_S0] = ptf->f_regs[_R_S0];
		tf->f_regs[_R_S1] = ptf->f_regs[_R_S1];
		tf->f_regs[_R_S2] = ptf->f_regs[_R_S2];
		tf->f_regs[_R_S3] = ptf->f_regs[_R_S3];
	}

	/*
	 * Join the share group
	 */
	ied = (struct irix_emuldata *)(p2->p_emuldata);
	parent_ied = (struct irix_emuldata *)(parent->p_emuldata);
	ied->ied_share_group = parent_ied->ied_share_group;

	rw_enter(&ied->ied_share_group->isg_lock, RW_WRITER);
	LIST_INSERT_HEAD(&ied->ied_share_group->isg_head, ied, ied_sglist);
	ied->ied_share_group->isg_refcount++;
	rw_exit(&ied->ied_share_group->isg_lock);

	if (inh & IRIX_PR_SADDR)
		ied->ied_shareaddr = 1;

	/*
	 * wakeup the parent as it can now die without
	 * causing a panic in the child.
	 */
	isc->isc_child_done = 1;
	wakeup(&isc->isc_child_done);

	/*
	 * Return to userland for a newly created process
	 */
	child_return((void *)l2);
	return;
}

int
irix_sys_procblk(struct lwp *l, const struct irix_sys_procblk_args *uap, register_t *retval)
{
	/* {
		syscallarg(int) cmd;
		syscallarg(pid_t) pid;
		syscallarg(int) count;
	} */
	int cmd = SCARG(uap, cmd);
	struct irix_emuldata *ied;
	struct irix_emuldata *iedp;
	struct irix_share_group *isg;
	struct proc *target;
	int oldcount;
	struct lwp *ied_lwp;
	int error, last_error;
	struct irix_sys_procblk_args cup;

	/* Find the process */
	if ((target = pfind(SCARG(uap, pid))) == NULL)
		return ESRCH;

	/* May we stop it? */
	/* XXX-elad: Is hardcoding SIGSTOP here correct? */
	if (kauth_authorize_process(l->l_cred, KAUTH_PROCESS_SIGNAL, target,
	    KAUTH_ARG(SIGSTOP), NULL, NULL) != 0)
		return EPERM;

	/* Is it an IRIX process? */
	if (irix_check_exec(target) == 0)
		return EPERM;

	ied = (struct irix_emuldata *)(target->p_emuldata);
	oldcount = ied->ied_procblk_count;

	switch (cmd) {
	case IRIX_PROCBLK_BLOCK:
		ied->ied_procblk_count--;
		break;

	case IRIX_PROCBLK_UNBLOCK:
		ied->ied_procblk_count++;
		break;

	case IRIX_PROCBLK_COUNT:
		if (SCARG(uap, count) > IRIX_PR_MAXBLOCKCNT ||
		    SCARG(uap, count) < IRIX_PR_MINBLOCKCNT)
			return EINVAL;
		ied->ied_procblk_count = SCARG(uap, count);
		break;

	case IRIX_PROCBLK_BLOCKALL:
	case IRIX_PROCBLK_UNBLOCKALL:
	case IRIX_PROCBLK_COUNTALL:
		SCARG(&cup, cmd) = cmd -IRIX_PROCBLK_ONLYONE;
		SCARG(&cup, count) = SCARG(uap, count);
		last_error = 0;

		/*
		 * If the process does not belong to a
		 * share group, do it just for the process
		 */
		if ((isg = ied->ied_share_group) == NULL) {
			SCARG(&cup, pid) = SCARG(uap, pid);
			return irix_sys_procblk(l, &cup, retval);
		}

		rw_enter(&isg->isg_lock, RW_READER);
		LIST_FOREACH(iedp, &isg->isg_head, ied_sglist) {
			struct proc *p;
			/* Recall procblk for this process */
			p = iedp->ied_p;
			SCARG(&cup, pid) = p->p_pid;
			ied_lwp = LIST_FIRST(&p->p_lwps);
			KASSERT(ied_lwp != NULL);
			error = irix_sys_procblk(ied_lwp, &cup, retval);
			if (error != 0)
				last_error = error;
		}
		rw_exit(&isg->isg_lock);

		return last_error;
		break;
	default:
		printf("Warning: unimplemented IRIX procblk command %d\n", cmd);
		return EINVAL;
		break;
	}

	/*
	 * We emulate the process block/unblock using SIGSTOP and SIGCONT
	 * signals. This is not very accurate, since on IRIX theses way
	 * of blocking a process are completely separated.
	 */
	if (oldcount >= 0 && ied->ied_procblk_count < 0) /* blocked */
		psignal(target, SIGSTOP);

	if (oldcount < 0 && ied->ied_procblk_count >= 0) /* unblocked */
		psignal(target, SIGCONT);

	return 0;
}

int
irix_prda_init(struct proc *p)
{
	int error;
	struct exec_vmcmd evc;
	struct irix_prda *ip;
	struct irix_prda_sys ips;
	struct lwp *l;

	bzero(&evc, sizeof(evc));
	evc.ev_addr = (u_long)IRIX_PRDA;
	evc.ev_len = sizeof(struct irix_prda);
	evc.ev_prot = UVM_PROT_RW;
	evc.ev_proc = *vmcmd_map_zero;

	/* XXXSMP */
	l = LIST_FIRST(&p->p_lwps);
	KASSERT(l != NULL);

	if ((error = (*evc.ev_proc)(l, &evc)) != 0)
		return error;

	ip = (struct irix_prda *)IRIX_PRDA;
	bzero(&ips, sizeof(ips));

	ips.t_pid = p->p_pid;
	/*
	 * The PRDA ID must be unique for a PRDA. IRIX uses a small
	 * integer, but we don't know how it is chosen. The PID
	 * should be unique enough to get the work done.
	 */
	ips.t_prid = p->p_pid;

	error = copyout(&ips, (void *)&ip->sys_prda.prda_sys, sizeof(ips));
	if (error)
		return error;

	/* Remeber the PRDA is private */
	irix_isrr_insert((vaddr_t)IRIX_PRDA, sizeof(ips), IRIX_ISRR_PRIVATE, p);

	return 0;
}

int
irix_vm_fault(struct proc *p, vaddr_t vaddr, vm_prot_t access_type)
{
	int error;
	struct irix_emuldata *ied;
	struct vm_map *map;

	ied = (struct irix_emuldata *)p->p_emuldata;
	map = &p->p_vmspace->vm_map;

	if (ied->ied_share_group == NULL || ied->ied_shareaddr == 0)
		return uvm_fault(map, vaddr, access_type);

	/* share group version */
	rw_enter(&ied->ied_share_group->isg_lock, RW_WRITER);
	error = uvm_fault(map, vaddr, access_type);
	irix_vm_sync(p);
	rw_exit(&ied->ied_share_group->isg_lock);

	return error;
}

/*
 * Propagate changes to address space to other members of the share group
 */
void
irix_vm_sync(struct proc *p)
{
	struct proc *pp;
	struct irix_emuldata *iedp;
	struct irix_emuldata *ied = (struct irix_emuldata *)p->p_emuldata;
	struct irix_shared_regions_rec *isrr;
	vaddr_t minp;
	vaddr_t maxp;
	vsize_t len;
	int error;

	LIST_FOREACH(iedp, &ied->ied_share_group->isg_head, ied_sglist) {
		if (iedp->ied_shareaddr != 1 || iedp->ied_p == p)
			continue;

		pp = iedp->ied_p;

		error = 0;
		/* for each region in the target process ... */
		LIST_FOREACH(isrr, &iedp->ied_shared_regions, isrr_list) {
			/* skip regions private to the target process */
			if (isrr->isrr_shared == IRIX_ISRR_PRIVATE)
				continue;

			/*
			 * XXX We should also skip regions private to the
			 * original process...
			 */

			/* The region is shared */
			minp = isrr->isrr_start;
			len = isrr->isrr_len;
			maxp = minp + len;

			/* Drop the region */
			uvm_unmap(&pp->p_vmspace->vm_map, minp, maxp);

			/* Clone it from the parent */
			error = uvm_map_extract(&p->p_vmspace->vm_map,
			    minp, len, &pp->p_vmspace->vm_map, &minp, 0);

			if (error)
				break;
		}

		if (error) {
			mutex_enter(proc_lock);
			killproc(pp, "failed to keep share group VM in sync");
			mutex_exit(proc_lock);
		}
	}

	return;
}

static struct irix_shared_regions_rec *
irix_isrr_create(vaddr_t start, vsize_t len, int shared)
{
	struct irix_shared_regions_rec *new_isrr;

	new_isrr = malloc(sizeof(struct irix_shared_regions_rec),
	    M_EMULDATA, M_WAITOK);
	new_isrr->isrr_start = start;
	new_isrr->isrr_len = len;
	new_isrr->isrr_shared = shared;

	return new_isrr;
}

/*
 * Insert a record for a new region in the list. The new region may be
 * overlaping or be included in an existing region.
 */
void
irix_isrr_insert(vaddr_t start, vsize_t len, int shared, struct proc *p)
{
	struct irix_emuldata *ied = (struct irix_emuldata *)p->p_emuldata;
	struct irix_shared_regions_rec *isrr;
	struct irix_shared_regions_rec *new_isrr;
	vaddr_t end, cur_start, cur_end;
	int cur_shared;

	start = trunc_page(start);
	len = round_page(len);
	end = start + len;

	new_isrr = irix_isrr_create(start, len, shared);

	/* Do we need to insert the new region at the begining of the list? */
	if (LIST_EMPTY(&ied->ied_shared_regions) ||
	    LIST_FIRST(&ied->ied_shared_regions)->isrr_start > start) {
		LIST_INSERT_HEAD(&ied->ied_shared_regions, new_isrr, isrr_list);
	} else {
		/* Find the place where to insert it */
		LIST_FOREACH(isrr, &ied->ied_shared_regions, isrr_list) {
			cur_start = isrr->isrr_start;
			cur_end = isrr->isrr_start + isrr->isrr_len;
			cur_shared = isrr->isrr_shared;

			/*
			 * if there is no intersection between inserted
			 * and current region: skip to next region
			 */
			if (cur_end <= start)
				continue;

			/*
			 * if new region is included into the current
			 * region. Right-crop the current region,
			 * insert a new one, and insert a new region
			 * for the end of the split region
			 */
			if (cur_end > end && cur_start < start) {
				isrr->isrr_len = start - isrr->isrr_start;
				LIST_INSERT_AFTER(isrr, new_isrr, isrr_list);
				isrr = new_isrr;

				new_isrr = irix_isrr_create(end,
				    cur_end - end, cur_shared);
				LIST_INSERT_AFTER(isrr, new_isrr, isrr_list);

				/* Nothing more to do, exit now */
#ifdef DEBUG_IRIX
				irix_isrr_debug(p);
#endif
				irix_isrr_cleanup(p);
#ifdef DEBUG_IRIX
				irix_isrr_debug(p);
#endif
				return;
			}

			/*
			 * if inserted block overlap some part
			 * of current region: right-crop current region
			 * and insert the new region
			 */
			if (start < cur_end) {
				isrr->isrr_len = start - cur_start;
				LIST_INSERT_AFTER(isrr, new_isrr, isrr_list);

				/* exit the FOREACH loop */
				break;
			}
		}
	}

	/*
	 * At this point, we inserted the new region (new_isrr) but
	 * it may be overlaping with next regions, so we need to clean
	 * this up and remove or crop next regions
	 */
	LIST_FOREACH(isrr, &ied->ied_shared_regions, isrr_list) {
		cur_start = isrr->isrr_start;
		cur_end = isrr->isrr_start + isrr->isrr_len;

		/* skip until we get beyond new_isrr */
		if (cur_start <= start)
			continue;

		if (end >= cur_end) { /* overlap */
			LIST_REMOVE(isrr, isrr_list);
			free(isrr, M_EMULDATA);
			/* isrr is now invalid */
			isrr = new_isrr;
			continue;
		}

		/*
		 * Here end < cur_end, therefore we need to
		 * right-crop the current region
		 */
		 isrr->isrr_start = end;
		 isrr->isrr_len = cur_end - end;
		 break;
	}
#ifdef DEBUG_IRIX
	irix_isrr_debug(p);
#endif
	irix_isrr_cleanup(p);
#ifdef DEBUG_IRIX
	irix_isrr_debug(p);
#endif
	return;
}

/*
 * Cleanup the region list by
 * (1) removing regions with length 0, and
 * (2) merging contiguous regions with the same status
 */
static void
irix_isrr_cleanup(struct proc *p)
{
	struct irix_emuldata *ied = (struct irix_emuldata *)p->p_emuldata;
	struct irix_shared_regions_rec *isrr;
	struct irix_shared_regions_rec *new_isrr;

	isrr = LIST_FIRST(&ied->ied_shared_regions);
	do {
		new_isrr = LIST_NEXT(isrr, isrr_list);

		if (isrr->isrr_len == 0) {
			LIST_REMOVE(isrr, isrr_list);
			free(isrr, M_EMULDATA);
			isrr = new_isrr;
			if (isrr == NULL)
				break;
		}

		if (new_isrr == NULL)
			break;

		if (isrr->isrr_shared == new_isrr->isrr_shared) {
			isrr->isrr_len += new_isrr->isrr_len;
			new_isrr->isrr_len = 0;
		}

		isrr = new_isrr;
	} while (1);

	return;
}


#ifdef DEBUG_IRIX
static void
irix_isrr_debug(struct proc *p)
{
	struct irix_emuldata *ied = (struct irix_emuldata *)p->p_emuldata;
	struct irix_shared_regions_rec *isrr;

	printf("isrr for pid %d\n", p->p_pid);
	LIST_FOREACH(isrr, &ied->ied_shared_regions, isrr_list) {
		printf("  addr = %p, len = %p, shared = %d\n",
		    (void *)isrr->isrr_start,
		    (void *)isrr->isrr_len,
		    isrr->isrr_shared);
	}
}
#endif /* DEBUG_IRIX */