Print this page
15254 %ymm registers not restored after signal handler
15367 x86 getfpregs() summons corrupting %xmm ghosts
15333 want x86 /proc xregs support (libc_db, libproc, mdb, etc.)
15336 want libc functions for extended ucontext_t
15334 want ps_lwphandle-specific reg routines
15328 FPU_CW_INIT mistreats reserved bit
15335 i86pc fpu_subr.c isn't really platform-specific
15332 setcontext(2) isn't actually noreturn
15331 need <sys/stdalign.h>
Change-Id: I7060aa86042dfb989f77fc3323c065ea2eafa9ad
Conflicts:
usr/src/uts/common/fs/proc/prcontrol.c
usr/src/uts/intel/os/archdep.c
usr/src/uts/intel/sys/ucontext.h
usr/src/uts/intel/syscall/getcontext.c
*** 24,33 ****
--- 24,34 ----
*/
/*
* Copyright 2018 Joyent, Inc.
* Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright 2023 Oxide Computer Company
*/
#include <sys/types.h>
#include <sys/uio.h>
#include <string.h>
*** 37,49 ****
#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.
--- 38,56 ----
#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. It also
! * implements similar logic that is used with an lwp handle.
! *
! * The primary registers and floating point registers (e.g. regs,fpregs) are
! * retreived from the lwp and process status files. The library caches the
! * values of these files. When we perorm updates, we ensure that cached copies
! * are refreshed or updated as part of this.
*/
/*
* Utility function to return a pointer to the structure of cached information
* about an lwp in the core file, given its lwpid.
*** 58,74 ****
lwp = list_next(&core->core_lwp_head, 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)
{
--- 65,82 ----
lwp = list_next(&core->core_lwp_head, lwp)) {
if (lwp->lwp_id == lwpid)
return (lwp);
}
! errno = ENOENT;
return (NULL);
}
/*
* Utility function to open and read the contents of a per-lwp /proc file.
! * This function is used to slurp in lwpstatus, lwpname, lwpsinfo, spymaster,
! * and others.
*/
static int
getlwpfile(struct ps_prochandle *P, lwpid_t lwpid,
const char *fbase, void *rp, size_t n)
{
*** 81,96 ****
--- 89,163 ----
if ((fd = open(fname, O_RDONLY)) >= 0) {
if (read(fd, rp, n) > 0) {
(void) close(fd);
return (0);
}
+
+ int e = errno;
(void) close(fd);
+ errno = e;
}
return (-1);
}
/*
+ * This is a variant of getlwpfile that has three different semantics:
+ *
+ * o It will stat the file to determine the size and allocate that for the
+ * caller.
+ * o If the stat size is zero (e.g. traditional xregs behavior when
+ * unsupported) then it will return the libproc ENODATA error.
+ * o It is an error if not all the data is read.
+ *
+ * Currently this is just used by xregs.
+ */
+ static int
+ getlwpfile_alloc(struct ps_prochandle *P, lwpid_t lwpid, const char *fbase,
+ void **datap, size_t *sizep)
+ {
+ char fname[PATH_MAX];
+ int fd;
+
+ (void) snprintf(fname, sizeof (fname), "%s/%d/lwp/%d/%s",
+ procfs_path, (int)P->status.pr_pid, (int)lwpid, fbase);
+
+ if ((fd = open(fname, O_RDONLY)) >= 0) {
+ int e;
+ struct stat st;
+
+ if (fstat(fd, &st) == 0) {
+ prxregset_t *prx;
+
+ if (st.st_size == 0) {
+ errno = ENODATA;
+ goto clean;
+ }
+
+ prx = malloc(st.st_size);
+ if (prx == NULL) {
+ goto clean;
+ }
+
+ if (read(fd, prx, st.st_size) == st.st_size) {
+ (void) close(fd);
+ *datap = prx;
+ *sizep = st.st_size;
+ return (0);
+ }
+
+ free(prx);
+ }
+ clean:
+ e = errno;
+ (void) close(fd);
+ errno = e;
+ }
+
+ 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
*** 127,146 ****
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[PATH_MAX];
! int fd;
if (P->state != PS_STOP) {
errno = EBUSY;
return (-1);
}
--- 194,246 ----
return (-1);
}
/*
+ * libproc caches information about the registers for representative LWPs and
+ * threads which we have the thread handle for. When we do a write to certain
+ * files, we need to refresh state and take care of both the process and the
+ * representative LWP's info. Because the xregs may or may not mutate the state
+ * of the other regsiters, we just always do a refresh of the entire cached
+ * psinfo.
+ */
+ static void
+ refresh_status(struct ps_prochandle *P, lwpid_t lwpid, struct ps_lwphandle *L,
+ long cmd, const void *rp, size_t n)
+ {
+ if (P->status.pr_lwp.pr_lwpid == lwpid) {
+ 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);
+ else if (cmd == PCSXREG)
+ (void) Pstopstatus(P, PCNULL, 0);
+ }
+
+ if (L != NULL) {
+ if (cmd == PCSREG)
+ (void) memcpy(&L->lwp_status.pr_reg, rp, n);
+ else if (cmd == PCSFPREG)
+ (void) memcpy(&L->lwp_status.pr_fpreg, rp, n);
+ else if (cmd == PCSXREG)
+ (void) Lstopstatus(L, PCNULL, 0);
+ }
+ }
+
+ /*
* Utility function to modify lwp registers. This is done using either the
! * process control file or per-lwp control file as necessary. This assumes that
! * we have a process-level hold on things, which may not always be true.
*/
static int
! setlwpregs_proc(struct ps_prochandle *P, lwpid_t lwpid, long cmd,
const void *rp, size_t n)
{
iovec_t iov[2];
char fname[PATH_MAX];
! struct ps_lwphandle *L;
! int fd = -1;
if (P->state != PS_STOP) {
errno = EBUSY;
return (-1);
}
*** 149,174 ****
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
--- 249,284 ----
iov[0].iov_len = sizeof (long);
iov[1].iov_base = (caddr_t)rp;
iov[1].iov_len = n;
/*
+ * If we have an lwp handle for this thread, then make sure that we use
+ * that to update the state so cached information is updated. We sync
+ * the thread ahead of the process.
+ */
+ if ((L = Lfind(P, lwpid)) != NULL) {
+ Lsync(L);
+ fd = L->lwp_ctlfd;
+ }
+
+ /*
* 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. On some
! * platforms the xregs modify the other register states. As a result,
! * always refresh the representative LWP's status.
*/
if (P->status.pr_lwp.pr_lwpid == lwpid) {
Psync(P);
+ fd = P->ctlfd;
+ }
! if (fd > -1) {
! if (writev(fd, iov, 2) == -1)
return (-1);
! refresh_status(P, lwpid, L, cmd, rp, n);
return (0);
}
/*
* If the lwp we want is not the representative lwp, we need to
*** 180,194 ****
--- 290,333 ----
if ((fd = open(fname, O_WRONLY)) >= 0) {
if (writev(fd, iov, 2) > 0) {
(void) close(fd);
return (0);
}
+ int e = errno;
(void) close(fd);
+ errno = e;
}
return (-1);
}
+ /*
+ * This is a variant of the above that only assumes we have a hold on the thread
+ * as opposed to a process.
+ */
+ static int
+ setlwpregs_lwp(struct ps_lwphandle *L, long cmd, const void *rp, size_t n)
+ {
+ iovec_t iov[2];
+
+ if (L->lwp_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;
+
+ Lsync(L);
+ if (writev(L->lwp_ctlfd, iov, 2) == -1)
+ return (-1);
+ refresh_status(L->lwp_proc, L->lwp_id, L, cmd, rp, n);
+
+ return (0);
+ }
+
int
Plwp_getregs(struct ps_prochandle *P, lwpid_t lwpid, prgregset_t gregs)
{
lwpstatus_t lps;
*** 198,213 ****
(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)
--- 337,365 ----
(void) memcpy(gregs, lps.pr_reg, sizeof (prgregset_t));
return (0);
}
int
+ Lgetregs(struct ps_lwphandle *L, prgregset_t *gregs)
+ {
+ (void) memcpy(gregs, L->lwp_status.pr_reg, sizeof (prgregset_t));
+ return (0);
+ }
+
+ int
Plwp_setregs(struct ps_prochandle *P, lwpid_t lwpid, const prgregset_t gregs)
{
! return (setlwpregs_proc(P, lwpid, PCSREG, gregs, sizeof (prgregset_t)));
}
int
+ Lsetregs(struct ps_lwphandle *L, const prgregset_t *gregs)
+ {
+ return (setlwpregs_lwp(L, 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)
*** 215,234 ****
(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);
--- 367,413 ----
(void) memcpy(fpregs, &lps.pr_fpreg, sizeof (prfpregset_t));
return (0);
}
! int
! Lgetfpregs(struct ps_lwphandle *L, prfpregset_t *fpregs)
! {
! (void) memcpy(fpregs, &L->lwp_status.pr_fpreg, sizeof (prfpregset_t));
! return (0);
! }
!
! int
! Plwp_setfpregs(struct ps_prochandle *P, lwpid_t lwpid,
const prfpregset_t *fpregs)
{
! return (setlwpregs_proc(P, lwpid, PCSFPREG, fpregs,
! sizeof (prfpregset_t)));
}
int
! Lsetfpregs(struct ps_lwphandle *L, const prfpregset_t *fpregs)
{
+ return (setlwpregs_lwp(L, PCSFPREG, fpregs, sizeof (prfpregset_t)));
+ }
+
+ /*
+ * The reason that this is structured to take both the size and the process
+ * handle is so that way we have enough information to tie this back to its
+ * underlying source and we can eventually use umem with this.
+ */
+ void
+ Plwp_freexregs(struct ps_prochandle *P __unused, prxregset_t *prx,
+ size_t size __unused)
+ {
+ free(prx);
+ }
+
+ int
+ Plwp_getxregs(struct ps_prochandle *P, lwpid_t lwpid, prxregset_t **xregs,
+ size_t *sizep)
+ {
lwp_info_t *lwp;
if (P->state == PS_IDLE) {
errno = ENODATA;
return (-1);
*** 238,268 ****
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) {
--- 417,489 ----
if (P->state != PS_STOP) {
errno = EBUSY;
return (-1);
}
! return (getlwpfile_alloc(P, lwpid, "xregs",
! (void **)xregs, sizep));
}
! if ((lwp = getlwpcore(P, lwpid)) != NULL && lwp->lwp_xregs != NULL &&
! lwp->lwp_xregsize > 0) {
! *xregs = malloc(lwp->lwp_xregsize);
! if (*xregs == NULL)
! return (-1);
! (void) memcpy(*xregs, lwp->lwp_xregs, lwp->lwp_xregsize);
! *sizep = lwp->lwp_xregsize;
return (0);
}
if (lwp != NULL)
errno = ENODATA;
return (-1);
}
int
! Lgetxregs(struct ps_lwphandle *L, prxregset_t **xregs, size_t *sizep)
{
! lwp_info_t *lwp;
!
! if (L->lwp_state != PS_DEAD) {
! if (L->lwp_state != PS_STOP) {
! errno = EBUSY;
! return (-1);
! }
! return (getlwpfile_alloc(L->lwp_proc, L->lwp_id, "xregs",
! (void **)xregs, sizep));
! }
!
! if ((lwp = getlwpcore(L->lwp_proc, L->lwp_id)) != NULL &&
! lwp->lwp_xregs != NULL && lwp->lwp_xregsize > 0) {
! *xregs = malloc(lwp->lwp_xregsize);
! if (*xregs == NULL)
! return (-1);
! (void) memcpy(*xregs, lwp->lwp_xregs, lwp->lwp_xregsize);
! *sizep = lwp->lwp_xregsize;
! return (0);
! }
!
! if (lwp != NULL)
! errno = ENODATA;
! return (-1);
}
int
+ Plwp_setxregs(struct ps_prochandle *P, lwpid_t lwpid, const prxregset_t *xregs,
+ size_t len)
+ {
+ return (setlwpregs_proc(P, lwpid, PCSXREG, xregs, len));
+ }
+
+ int
+ Lsetxregs(struct ps_lwphandle *L, const prxregset_t *xregs, size_t len)
+ {
+ return (setlwpregs_lwp(L, PCSXREG, xregs, len));
+ }
+
+ #if defined(sparc) || defined(__sparc)
+ int
Plwp_getgwindows(struct ps_prochandle *P, lwpid_t lwpid, gwindows_t *gwins)
{
lwp_info_t *lwp;
if (P->state == PS_IDLE) {
*** 322,332 ****
}
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
--- 543,553 ----
}
int
Plwp_setasrs(struct ps_prochandle *P, lwpid_t lwpid, const asrset_t asrs)
{
! return (setlwpregs_proc(P, lwpid, PCSASRS, asrs, sizeof (asrset_t)));
}
#endif /* __sparcv9 */
#endif /* __sparc */
int