diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libproc/common/Psyscall.c | |
download | illumos-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.c | 555 |
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); +} |