summaryrefslogtreecommitdiff
path: root/usr/src/lib/libproc/common/Psyscall.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libproc/common/Psyscall.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libproc/common/Psyscall.c')
-rw-r--r--usr/src/lib/libproc/common/Psyscall.c555
1 files changed, 555 insertions, 0 deletions
diff --git a/usr/src/lib/libproc/common/Psyscall.c b/usr/src/lib/libproc/common/Psyscall.c
new file mode 100644
index 0000000000..c34a17a04c
--- /dev/null
+++ b/usr/src/lib/libproc/common/Psyscall.c
@@ -0,0 +1,555 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <string.h>
+#include <memory.h>
+#include <errno.h>
+#include <dirent.h>
+#include <limits.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <sys/param.h>
+#include <sys/stack.h>
+#include <sys/fault.h>
+#include <sys/syscall.h>
+#include <sys/sysmacros.h>
+
+#include "libproc.h"
+#include "Pcontrol.h"
+#include "Putil.h"
+#include "P32ton.h"
+#include "Pisadep.h"
+
+extern sigset_t blockable_sigs;
+
+static void
+Pabort_agent(struct ps_prochandle *P)
+{
+ int sysnum = P->status.pr_lwp.pr_syscall;
+ int stop;
+
+ dprintf("agent LWP is asleep in syscall %d\n", sysnum);
+ (void) Pstop(P, 0);
+ stop = Psysexit(P, sysnum, TRUE);
+
+ if (Psetrun(P, 0, PRSABORT) == 0) {
+ while (Pwait(P, 0) == -1 && errno == EINTR)
+ continue;
+ (void) Psysexit(P, sysnum, stop);
+ dprintf("agent LWP system call aborted\n");
+ }
+}
+
+/*
+ * Create the /proc agent LWP for further operations.
+ */
+int
+Pcreate_agent(struct ps_prochandle *P)
+{
+ int fd;
+ char pathname[100];
+ char *fname;
+ struct {
+ long cmd;
+ prgregset_t regs;
+ } cmd;
+
+ /*
+ * If not first reference, we already have the /proc agent LWP active.
+ */
+ if (P->agentcnt > 0) {
+ P->agentcnt++;
+ return (0);
+ }
+
+ /*
+ * The agent is not available for use as a mortician or as an
+ * obstetrician.
+ */
+ if (P->state == PS_DEAD || P->state == PS_UNDEAD ||
+ P->state == PS_IDLE) {
+ errno = ENOENT;
+ return (-1);
+ }
+
+ /*
+ * Create the special /proc agent LWP if it doesn't already exist.
+ * Give it the registers of the representative LWP.
+ */
+ (void) Pstop(P, 0);
+ Psync(P);
+ if (!(P->status.pr_lwp.pr_flags & PR_AGENT)) {
+ cmd.cmd = PCAGENT;
+ (void) memcpy(&cmd.regs, &P->status.pr_lwp.pr_reg[0],
+ sizeof (P->status.pr_lwp.pr_reg));
+ if (write(P->ctlfd, &cmd, sizeof (cmd)) != sizeof (cmd))
+ goto bad;
+ }
+
+ /* refresh the process status */
+ (void) Pstopstatus(P, PCNULL, 0);
+
+ /* open the agent LWP files */
+ (void) sprintf(pathname, "/proc/%d/lwp/agent/", (int)P->pid);
+ fname = pathname + strlen(pathname);
+ (void) set_minfd();
+
+ /*
+ * It is difficult to know how to recover from the two errors
+ * that follow. The agent LWP exists and we need to kill it,
+ * but we can't because we need it active in order to kill it.
+ * We just hope that these failures never occur.
+ */
+ (void) strcpy(fname, "lwpstatus");
+ if ((fd = open(pathname, O_RDONLY)) < 0 ||
+ (fd = dupfd(fd, 0)) < 0)
+ goto bad;
+ P->agentstatfd = fd;
+
+ (void) strcpy(fname, "lwpctl");
+ if ((fd = open(pathname, O_WRONLY)) < 0 ||
+ (fd = dupfd(fd, 0)) < 0)
+ goto bad;
+ P->agentctlfd = fd;
+
+ /*
+ * If the agent is currently asleep in a system call, attempt
+ * to abort the system call so it's ready to serve.
+ */
+ if (P->status.pr_lwp.pr_flags & PR_ASLEEP) {
+ dprintf("Pcreate_agent: aborting agent syscall\n");
+ Pabort_agent(P);
+ }
+
+ /* get the agent LWP status */
+ P->agentcnt++;
+ if (Pstopstatus(P, PCNULL, 0) != 0) {
+ Pdestroy_agent(P);
+ return (-1);
+ }
+
+ return (0);
+
+bad:
+ if (P->agentstatfd >= 0)
+ (void) close(P->agentstatfd);
+ if (P->agentctlfd >= 0)
+ (void) close(P->agentctlfd);
+ P->agentstatfd = -1;
+ P->agentctlfd = -1;
+ /* refresh the process status */
+ (void) Pstopstatus(P, PCNULL, 0);
+ return (-1);
+}
+
+/*
+ * Decrement the /proc agent agent reference count.
+ * On last reference, destroy the agent.
+ */
+void
+Pdestroy_agent(struct ps_prochandle *P)
+{
+ if (P->agentcnt > 1)
+ P->agentcnt--;
+ else {
+ int flags;
+
+ Psync(P); /* Flush out any pending changes */
+
+ (void) Pstopstatus(P, PCNULL, 0);
+ flags = P->status.pr_lwp.pr_flags;
+
+ /*
+ * If the agent is currently asleep in a system call, attempt
+ * to abort the system call so we can terminate the agent.
+ */
+ if ((flags & (PR_AGENT|PR_ASLEEP)) == (PR_AGENT|PR_ASLEEP)) {
+ dprintf("Pdestroy_agent: aborting agent syscall\n");
+ Pabort_agent(P);
+ }
+
+ /*
+ * The agent itself is destroyed by forcing it to execute
+ * the _lwp_exit(2) system call. Close our agent descriptors
+ * regardless of whether this is successful.
+ */
+ (void) pr_lwp_exit(P);
+ (void) close(P->agentctlfd);
+ (void) close(P->agentstatfd);
+ P->agentctlfd = -1;
+ P->agentstatfd = -1;
+ P->agentcnt = 0;
+
+ /*
+ * Now that (hopefully) the agent has exited, refresh the
+ * status: the representative LWP is no longer the agent.
+ */
+ (void) Pstopstatus(P, PCNULL, 0);
+ }
+}
+
+/*
+ * Execute the syscall instruction.
+ */
+static int
+execute(struct ps_prochandle *P, int sysindex)
+{
+ int ctlfd = (P->agentctlfd >= 0)? P->agentctlfd : P->ctlfd;
+ int washeld = FALSE;
+ sigset_t hold; /* mask of held signals */
+ int cursig;
+ struct {
+ long cmd;
+ siginfo_t siginfo;
+ } ctl;
+ int sentry; /* old value of stop-on-syscall-entry */
+
+ sentry = Psysentry(P, sysindex, TRUE); /* set stop-on-syscall-entry */
+
+ /*
+ * If not already blocked, block all signals now.
+ */
+ if (memcmp(&P->status.pr_lwp.pr_lwphold, &blockable_sigs,
+ sizeof (sigset_t)) != 0) {
+ hold = P->status.pr_lwp.pr_lwphold;
+ P->status.pr_lwp.pr_lwphold = blockable_sigs;
+ P->flags |= SETHOLD;
+ washeld = TRUE;
+ }
+
+ /*
+ * If there is a current signal, remember it and cancel it.
+ */
+ if ((cursig = P->status.pr_lwp.pr_cursig) != 0) {
+ ctl.cmd = PCSSIG;
+ ctl.siginfo = P->status.pr_lwp.pr_info;
+ }
+
+ if (Psetrun(P, 0, PRCSIG | PRCFAULT) == -1)
+ goto bad;
+
+ while (P->state == PS_RUN) {
+ (void) Pwait(P, 0);
+ }
+ if (P->state != PS_STOP)
+ goto bad;
+
+ if (cursig) /* restore cursig */
+ (void) write(ctlfd, &ctl, sizeof (ctl));
+ if (washeld) { /* restore the signal mask if we set it */
+ P->status.pr_lwp.pr_lwphold = hold;
+ P->flags |= SETHOLD;
+ }
+
+ (void) Psysentry(P, sysindex, sentry); /* restore sysentry stop */
+
+ if (P->status.pr_lwp.pr_why == PR_SYSENTRY &&
+ P->status.pr_lwp.pr_what == sysindex)
+ return (0);
+bad:
+ return (-1);
+}
+
+
+/*
+ * Perform system call in controlled process.
+ */
+int
+Psyscall(struct ps_prochandle *P,
+ sysret_t *rval, /* syscall return values */
+ int sysindex, /* system call index */
+ uint_t nargs, /* number of arguments to system call */
+ argdes_t *argp) /* argument descriptor array */
+{
+ int agent_created = FALSE;
+ pstatus_t save_pstatus;
+ argdes_t *adp; /* pointer to argument descriptor */
+ int i; /* general index value */
+ int model; /* data model */
+ int error = 0; /* syscall errno */
+ int Perr = 0; /* local error number */
+ int sexit; /* old value of stop-on-syscall-exit */
+ prgreg_t sp; /* adjusted stack pointer */
+ prgreg_t ap; /* adjusted argument pointer */
+ sigset_t unblock;
+
+ (void) sigprocmask(SIG_BLOCK, &blockable_sigs, &unblock);
+
+ rval->sys_rval1 = 0; /* initialize return values */
+ rval->sys_rval2 = 0;
+
+ if (sysindex <= 0 || sysindex > PRMAXSYS || nargs > MAXARGS)
+ goto bad1; /* programming error */
+
+ if (P->state == PS_DEAD || P->state == PS_UNDEAD || P->state == PS_IDLE)
+ goto bad1; /* dead processes can't perform system calls */
+
+ model = P->status.pr_dmodel;
+#ifndef _LP64
+ /* We must be a 64-bit process to deal with a 64-bit process */
+ if (model == PR_MODEL_LP64)
+ goto bad9;
+#endif
+
+ /*
+ * Create the /proc agent LWP in the process to do all the work.
+ * (It may already exist; nested create/destroy is permitted
+ * by virtue of the reference count.)
+ */
+ if (Pcreate_agent(P) != 0)
+ goto bad8;
+
+ /*
+ * Save agent's status to restore on exit.
+ */
+ agent_created = TRUE;
+ save_pstatus = P->status;
+
+ if (P->state != PS_STOP || /* check state of LWP */
+ (P->status.pr_flags & PR_ASLEEP))
+ goto bad2;
+
+ if (Pscantext(P)) /* bad text ? */
+ goto bad3;
+
+ /*
+ * Validate arguments and compute the stack frame parameters.
+ * Begin with the current stack pointer.
+ */
+#ifdef _LP64
+ if (model == PR_MODEL_LP64) {
+ sp = P->status.pr_lwp.pr_reg[R_SP] + STACK_BIAS;
+ sp = PSTACK_ALIGN64(sp);
+ } else {
+#endif
+ sp = (uint32_t)P->status.pr_lwp.pr_reg[R_SP];
+ sp = PSTACK_ALIGN32(sp);
+#ifdef _LP64
+ }
+#endif
+
+ /*
+ * For each AT_BYREF argument, compute the necessary
+ * stack space and the object's stack address.
+ */
+ for (i = 0, adp = argp; i < nargs; i++, adp++) {
+ rval->sys_rval1 = i; /* in case of error */
+ switch (adp->arg_type) {
+ default: /* programming error */
+ goto bad4;
+ case AT_BYVAL: /* simple argument */
+ break;
+ case AT_BYREF: /* must allocate space */
+ switch (adp->arg_inout) {
+ case AI_INPUT:
+ case AI_OUTPUT:
+ case AI_INOUT:
+ if (adp->arg_object == NULL)
+ goto bad5; /* programming error */
+ break;
+ default: /* programming error */
+ goto bad6;
+ }
+ /* allocate stack space for BYREF argument */
+ if (adp->arg_size == 0 || adp->arg_size > MAXARGL)
+ goto bad7; /* programming error */
+#ifdef _LP64
+ if (model == PR_MODEL_LP64)
+ sp = PSTACK_ALIGN64(sp - adp->arg_size);
+ else
+#endif
+ sp = PSTACK_ALIGN32(sp - adp->arg_size);
+ adp->arg_value = sp; /* stack address for object */
+ break;
+ }
+ }
+ rval->sys_rval1 = 0; /* in case of error */
+ /*
+ * Point of no return.
+ * Perform the system call entry, adjusting %sp.
+ * This moves the LWP to the stopped-on-syscall-entry state
+ * just before the arguments to the system call are fetched.
+ */
+ ap = Psyscall_setup(P, nargs, sysindex, sp);
+ P->flags |= SETREGS; /* set registers before continuing */
+ dprintf("Psyscall(): execute(sysindex = %d)\n", sysindex);
+
+ /*
+ * Execute the syscall instruction and stop on syscall entry.
+ */
+ if (execute(P, sysindex) != 0 ||
+ (!Pissyscall(P, P->status.pr_lwp.pr_reg[R_PC]) &&
+ !Pissyscall_prev(P, P->status.pr_lwp.pr_reg[R_PC], NULL)))
+ goto bad10;
+
+ dprintf("Psyscall(): copying arguments\n");
+
+ /*
+ * The LWP is stopped at syscall entry.
+ * Copy objects to stack frame for each argument.
+ */
+ for (i = 0, adp = argp; i < nargs; i++, adp++) {
+ rval->sys_rval1 = i; /* in case of error */
+ if (adp->arg_type != AT_BYVAL &&
+ adp->arg_inout != AI_OUTPUT) {
+ /* copy input byref parameter to process */
+ if (Pwrite(P, adp->arg_object, adp->arg_size,
+ (uintptr_t)adp->arg_value) != adp->arg_size)
+ goto bad17;
+ }
+ }
+ rval->sys_rval1 = 0; /* in case of error */
+ if (Psyscall_copyinargs(P, nargs, argp, ap) != 0)
+ goto bad18;
+
+ /*
+ * Complete the system call.
+ * This moves the LWP to the stopped-on-syscall-exit state.
+ */
+ dprintf("Psyscall(): set running at sysentry\n");
+
+ sexit = Psysexit(P, sysindex, TRUE); /* catch this syscall exit */
+ do {
+ if (Psetrun(P, 0, 0) == -1)
+ goto bad21;
+ while (P->state == PS_RUN)
+ (void) Pwait(P, 0);
+ } while (P->state == PS_STOP && P->status.pr_lwp.pr_why != PR_SYSEXIT);
+ (void) Psysexit(P, sysindex, sexit); /* restore original setting */
+
+ /*
+ * If the system call was _lwp_exit(), we expect that our last call
+ * to Pwait() will yield ENOENT because the LWP no longer exists.
+ */
+ if (sysindex == SYS_lwp_exit && errno == ENOENT) {
+ dprintf("Psyscall(): _lwp_exit successful\n");
+ rval->sys_rval1 = rval->sys_rval2 = 0;
+ goto out;
+ }
+
+ if (P->state != PS_STOP || P->status.pr_lwp.pr_why != PR_SYSEXIT)
+ goto bad22;
+
+ if (P->status.pr_lwp.pr_what != sysindex)
+ goto bad23;
+
+ if (!Pissyscall_prev(P, P->status.pr_lwp.pr_reg[R_PC], NULL)) {
+ dprintf("Pissyscall_prev() failed\n");
+ goto bad24;
+ }
+
+ dprintf("Psyscall(): caught at sysexit\n");
+
+ /*
+ * For each argument.
+ */
+ for (i = 0, adp = argp; i < nargs; i++, adp++) {
+ rval->sys_rval1 = i; /* in case of error */
+ if (adp->arg_type != AT_BYVAL &&
+ adp->arg_inout != AI_INPUT) {
+ /* copy output byref parameter from process */
+ if (Pread(P, adp->arg_object, adp->arg_size,
+ (uintptr_t)adp->arg_value) != adp->arg_size)
+ goto bad25;
+ }
+ }
+
+ if (Psyscall_copyoutargs(P, nargs, argp, ap) != 0)
+ goto bad26;
+
+ /*
+ * Get the return values from the syscall.
+ */
+ if (P->status.pr_lwp.pr_errno) { /* error return */
+ error = P->status.pr_lwp.pr_errno;
+ rval->sys_rval1 = -1L;
+ rval->sys_rval2 = -1L;
+ dprintf("Psyscall(%d) fails with errno %d\n",
+ sysindex, error);
+ } else { /* normal return */
+ rval->sys_rval1 = P->status.pr_lwp.pr_rval1;
+ rval->sys_rval2 = P->status.pr_lwp.pr_rval2;
+ dprintf("Psyscall(%d) returns 0x%lx 0x%lx\n", sysindex,
+ P->status.pr_lwp.pr_rval1, P->status.pr_lwp.pr_rval2);
+ }
+
+ goto out;
+
+bad26: Perr++;
+bad25: Perr++;
+bad24: Perr++;
+bad23: Perr++;
+bad22: Perr++;
+bad21: Perr++;
+ Perr++;
+ Perr++;
+bad18: Perr++;
+bad17: Perr++;
+ Perr++;
+ Perr++;
+ Perr++;
+ Perr++;
+ Perr++;
+ Perr++;
+bad10: Perr++;
+bad9: Perr++;
+bad8: Perr++;
+bad7: Perr++;
+bad6: Perr++;
+bad5: Perr++;
+bad4: Perr++;
+bad3: Perr++;
+bad2: Perr++;
+bad1: Perr++;
+ error = -1;
+ dprintf("Psyscall(%d) fails with local error %d\n", sysindex, Perr);
+
+out:
+ /*
+ * Destroy the /proc agent LWP now (or just bump down the ref count).
+ */
+ if (agent_created) {
+ if (P->state != PS_UNDEAD) {
+ P->status = save_pstatus;
+ P->flags |= SETREGS;
+ Psync(P);
+ }
+ Pdestroy_agent(P);
+ }
+
+ (void) sigprocmask(SIG_SETMASK, &unblock, NULL);
+ return (error);
+}