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