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/Plwpregs.c | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libproc/common/Plwpregs.c')
-rw-r--r-- | usr/src/lib/libproc/common/Plwpregs.c | 500 |
1 files changed, 500 insertions, 0 deletions
diff --git a/usr/src/lib/libproc/common/Plwpregs.c b/usr/src/lib/libproc/common/Plwpregs.c new file mode 100644 index 0000000000..bb16b50370 --- /dev/null +++ b/usr/src/lib/libproc/common/Plwpregs.c @@ -0,0 +1,500 @@ +/* + * 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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/uio.h> +#include <string.h> +#include <errno.h> + +#include "Pcontrol.h" +#include "P32ton.h" + +/* + * This file implements the routines to read and write per-lwp register + * information from either a live process or core file opened with libproc. + * We build up a few common routines for reading and writing register + * information, and then the public functions are all trivial calls to these. + */ + +/* + * Utility function to return a pointer to the structure of cached information + * about an lwp in the core file, given its lwpid. + */ +static lwp_info_t * +getlwpcore(struct ps_prochandle *P, lwpid_t lwpid) +{ + lwp_info_t *lwp = list_next(&P->core->core_lwp_head); + uint_t i; + + for (i = 0; i < P->core->core_nlwp; i++, lwp = list_next(lwp)) { + if (lwp->lwp_id == lwpid) + return (lwp); + } + + errno = EINVAL; + return (NULL); +} + +/* + * Utility function to open and read the contents of a per-lwp /proc file. + * This function is used to slurp in lwpstatus, xregs, and asrs. + */ +static int +getlwpfile(struct ps_prochandle *P, lwpid_t lwpid, + const char *fbase, void *rp, size_t n) +{ + char fname[64]; + int fd; + + (void) snprintf(fname, sizeof (fname), "/proc/%d/lwp/%d/%s", + (int)P->status.pr_pid, (int)lwpid, fbase); + + if ((fd = open(fname, O_RDONLY)) >= 0) { + if (read(fd, rp, n) > 0) { + (void) close(fd); + return (0); + } + (void) close(fd); + } + return (-1); +} + +/* + * Get the lwpstatus_t for an lwp from either the live process or our + * cached information from the core file. This is used to get the + * general-purpose registers or floating point registers. + */ +int +getlwpstatus(struct ps_prochandle *P, lwpid_t lwpid, lwpstatus_t *lps) +{ + lwp_info_t *lwp; + + /* + * For both live processes and cores, our job is easy if the lwpid + * matches that of the representative lwp: + */ + if (P->status.pr_lwp.pr_lwpid == lwpid) { + (void) memcpy(lps, &P->status.pr_lwp, sizeof (lwpstatus_t)); + return (0); + } + + /* + * If this is a live process, then just read the information out + * of the per-lwp status file: + */ + if (P->state != PS_DEAD) { + return (getlwpfile(P, lwpid, "lwpstatus", + lps, sizeof (lwpstatus_t))); + } + + /* + * If this is a core file, we need to iterate through our list of + * cached lwp information and then copy out the status. + */ + if (P->core != NULL && (lwp = getlwpcore(P, lwpid)) != NULL) { + (void) memcpy(lps, &lwp->lwp_status, sizeof (lwpstatus_t)); + return (0); + } + + return (-1); +} + +/* + * Utility function to modify lwp registers. This is done using either the + * process control file or per-lwp control file as necessary. + */ +static int +setlwpregs(struct ps_prochandle *P, lwpid_t lwpid, long cmd, + const void *rp, size_t n) +{ + iovec_t iov[2]; + char fname[64]; + int fd; + + if (P->state != PS_STOP) { + errno = EBUSY; + return (-1); + } + + iov[0].iov_base = (caddr_t)&cmd; + iov[0].iov_len = sizeof (long); + iov[1].iov_base = (caddr_t)rp; + iov[1].iov_len = n; + + /* + * Writing the process control file writes the representative lwp. + * Psync before we write to make sure we are consistent with the + * primary interfaces. Similarly, make sure to update P->status + * afterward if we are modifying one of its register sets. + */ + if (P->status.pr_lwp.pr_lwpid == lwpid) { + Psync(P); + + if (writev(P->ctlfd, iov, 2) == -1) + return (-1); + + if (cmd == PCSREG) + (void) memcpy(P->status.pr_lwp.pr_reg, rp, n); + else if (cmd == PCSFPREG) + (void) memcpy(&P->status.pr_lwp.pr_fpreg, rp, n); + + return (0); + } + + /* + * If the lwp we want is not the representative lwp, we need to + * open the ctl file for that specific lwp. + */ + (void) snprintf(fname, sizeof (fname), "/proc/%d/lwp/%d/lwpctl", + (int)P->status.pr_pid, (int)lwpid); + + if ((fd = open(fname, O_WRONLY)) >= 0) { + if (writev(fd, iov, 2) > 0) { + (void) close(fd); + return (0); + } + (void) close(fd); + } + return (-1); +} + +int +Plwp_getregs(struct ps_prochandle *P, lwpid_t lwpid, prgregset_t gregs) +{ + lwpstatus_t lps; + + if (getlwpstatus(P, lwpid, &lps) == -1) + return (-1); + + (void) memcpy(gregs, lps.pr_reg, sizeof (prgregset_t)); + return (0); +} + +int +Plwp_setregs(struct ps_prochandle *P, lwpid_t lwpid, const prgregset_t gregs) +{ + return (setlwpregs(P, lwpid, PCSREG, gregs, sizeof (prgregset_t))); +} + +int +Plwp_getfpregs(struct ps_prochandle *P, lwpid_t lwpid, prfpregset_t *fpregs) +{ + lwpstatus_t lps; + + if (getlwpstatus(P, lwpid, &lps) == -1) + return (-1); + + (void) memcpy(fpregs, &lps.pr_fpreg, sizeof (prfpregset_t)); + return (0); +} + +int Plwp_setfpregs(struct ps_prochandle *P, lwpid_t lwpid, + const prfpregset_t *fpregs) +{ + return (setlwpregs(P, lwpid, PCSFPREG, fpregs, sizeof (prfpregset_t))); +} + +#if defined(sparc) || defined(__sparc) +int +Plwp_getxregs(struct ps_prochandle *P, lwpid_t lwpid, prxregset_t *xregs) +{ + lwp_info_t *lwp; + + if (P->state == PS_IDLE) { + errno = ENODATA; + return (-1); + } + + if (P->state != PS_DEAD) { + if (P->state != PS_STOP) { + errno = EBUSY; + return (-1); + } + + return (getlwpfile(P, lwpid, "xregs", + xregs, sizeof (prxregset_t))); + } + + if ((lwp = getlwpcore(P, lwpid)) != NULL && lwp->lwp_xregs != NULL) { + (void) memcpy(xregs, lwp->lwp_xregs, sizeof (prxregset_t)); + return (0); + } + + if (lwp != NULL) + errno = ENODATA; + return (-1); +} + +int +Plwp_setxregs(struct ps_prochandle *P, lwpid_t lwpid, const prxregset_t *xregs) +{ + return (setlwpregs(P, lwpid, PCSXREG, xregs, sizeof (prxregset_t))); +} + +int +Plwp_getgwindows(struct ps_prochandle *P, lwpid_t lwpid, gwindows_t *gwins) +{ + lwp_info_t *lwp; + + if (P->state == PS_IDLE) { + errno = ENODATA; + return (-1); + } + + if (P->state != PS_DEAD) { + if (P->state != PS_STOP) { + errno = EBUSY; + return (-1); + } + + return (getlwpfile(P, lwpid, "gwindows", + gwins, sizeof (gwindows_t))); + } + + if ((lwp = getlwpcore(P, lwpid)) != NULL && lwp->lwp_gwins != NULL) { + *gwins = *lwp->lwp_gwins; + return (0); + } + + if (lwp != NULL) + errno = ENODATA; + return (-1); +} + +#if defined(__sparcv9) +int +Plwp_getasrs(struct ps_prochandle *P, lwpid_t lwpid, asrset_t asrs) +{ + lwp_info_t *lwp; + + if (P->state == PS_IDLE) { + errno = ENODATA; + return (-1); + } + + if (P->state != PS_DEAD) { + if (P->state != PS_STOP) { + errno = EBUSY; + return (-1); + } + + return (getlwpfile(P, lwpid, "asrs", asrs, sizeof (asrset_t))); + } + + if ((lwp = getlwpcore(P, lwpid)) != NULL && lwp->lwp_asrs != NULL) { + (void) memcpy(asrs, lwp->lwp_asrs, sizeof (asrset_t)); + return (0); + } + + if (lwp != NULL) + errno = ENODATA; + return (-1); + +} + +int +Plwp_setasrs(struct ps_prochandle *P, lwpid_t lwpid, const asrset_t asrs) +{ + return (setlwpregs(P, lwpid, PCSASRS, asrs, sizeof (asrset_t))); +} +#endif /* __sparcv9 */ +#endif /* __sparc */ + +int +Plwp_getpsinfo(struct ps_prochandle *P, lwpid_t lwpid, lwpsinfo_t *lps) +{ + lwp_info_t *lwp; + + if (P->state == PS_IDLE) { + errno = ENODATA; + return (-1); + } + + if (P->state != PS_DEAD) { + return (getlwpfile(P, lwpid, "lwpsinfo", + lps, sizeof (lwpsinfo_t))); + } + + if ((lwp = getlwpcore(P, lwpid)) != NULL) { + (void) memcpy(lps, &lwp->lwp_psinfo, sizeof (lwpsinfo_t)); + return (0); + } + + return (-1); +} + +int +Plwp_stack(struct ps_prochandle *P, lwpid_t lwpid, stack_t *stkp) +{ + uintptr_t addr; + + if (P->state == PS_IDLE) { + errno = ENODATA; + return (-1); + } + + if (P->state != PS_DEAD) { + lwpstatus_t ls; + if (getlwpfile(P, lwpid, "lwpstatus", &ls, sizeof (ls)) != 0) + return (-1); + addr = ls.pr_ustack; + } else { + lwp_info_t *lwp; + if ((lwp = getlwpcore(P, lwpid)) == NULL) + return (-1); + addr = lwp->lwp_status.pr_ustack; + } + + + if (P->status.pr_dmodel == PR_MODEL_NATIVE) { + if (Pread(P, stkp, sizeof (*stkp), addr) != sizeof (*stkp)) + return (-1); +#ifdef _LP64 + } else { + stack32_t stk32; + + if (Pread(P, &stk32, sizeof (stk32), addr) != sizeof (stk32)) + return (-1); + + stack_32_to_n(&stk32, stkp); +#endif + } + + return (0); +} + +int +Plwp_main_stack(struct ps_prochandle *P, lwpid_t lwpid, stack_t *stkp) +{ + uintptr_t addr; + lwpstatus_t ls; + + if (P->state == PS_IDLE) { + errno = ENODATA; + return (-1); + } + + if (P->state != PS_DEAD) { + if (getlwpfile(P, lwpid, "lwpstatus", &ls, sizeof (ls)) != 0) + return (-1); + } else { + lwp_info_t *lwp; + if ((lwp = getlwpcore(P, lwpid)) == NULL) + return (-1); + ls = lwp->lwp_status; + } + + addr = ls.pr_ustack; + + /* + * Read out the current stack; if the SS_ONSTACK flag is set then + * this LWP is operating on the alternate signal stack. We can + * recover the original stack from pr_oldcontext. + */ + if (P->status.pr_dmodel == PR_MODEL_NATIVE) { + if (Pread(P, stkp, sizeof (*stkp), addr) != sizeof (*stkp)) + return (-1); + + if (stkp->ss_flags & SS_ONSTACK) + goto on_altstack; +#ifdef _LP64 + } else { + stack32_t stk32; + + if (Pread(P, &stk32, sizeof (stk32), addr) != sizeof (stk32)) + return (-1); + + if (stk32.ss_flags & SS_ONSTACK) + goto on_altstack; + + stack_32_to_n(&stk32, stkp); +#endif + } + + return (0); + +on_altstack: + + if (P->status.pr_dmodel == PR_MODEL_NATIVE) { + ucontext_t *ctxp = (void *)ls.pr_oldcontext; + + if (Pread(P, stkp, sizeof (*stkp), + (uintptr_t)&ctxp->uc_stack) != sizeof (*stkp)) + return (-1); +#ifdef _LP64 + } else { + ucontext32_t *ctxp = (void *)ls.pr_oldcontext; + stack32_t stk32; + + if (Pread(P, &stk32, sizeof (stk32), + (uintptr_t)&ctxp->uc_stack) != sizeof (stk32)) + return (-1); + + stack_32_to_n(&stk32, stkp); +#endif + } + + return (0); +} + +int +Plwp_alt_stack(struct ps_prochandle *P, lwpid_t lwpid, stack_t *stkp) +{ + if (P->state == PS_IDLE) { + errno = ENODATA; + return (-1); + } + + if (P->state != PS_DEAD) { + lwpstatus_t ls; + + if (getlwpfile(P, lwpid, "lwpstatus", &ls, sizeof (ls)) != 0) + return (-1); + + if (ls.pr_altstack.ss_flags & SS_DISABLE) { + errno = ENODATA; + return (-1); + } + + *stkp = ls.pr_altstack; + } else { + lwp_info_t *lwp; + + if ((lwp = getlwpcore(P, lwpid)) == NULL) + return (-1); + + if (lwp->lwp_status.pr_altstack.ss_flags & SS_DISABLE) { + errno = ENODATA; + return (-1); + } + + *stkp = lwp->lwp_status.pr_altstack; + } + + return (0); +} |