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

Split Close
Expand all
Collapse all
          --- old/usr/src/lib/libproc/common/Plwpregs.c
          +++ new/usr/src/lib/libproc/common/Plwpregs.c
↓ open down ↓ 18 lines elided ↑ open up ↑
  19   19   * CDDL HEADER END
  20   20   */
  21   21  /*
  22   22   * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  23   23   * Use is subject to license terms.
  24   24   */
  25   25  
  26   26  /*
  27   27   * Copyright 2018 Joyent, Inc.
  28   28   * Copyright (c) 2013 by Delphix. All rights reserved.
       29 + * Copyright 2023 Oxide Computer Company
  29   30   */
  30   31  
  31   32  #include <sys/types.h>
  32   33  #include <sys/uio.h>
  33   34  #include <string.h>
  34   35  #include <errno.h>
  35   36  #include <limits.h>
  36   37  
  37   38  #include "Pcontrol.h"
  38   39  #include "P32ton.h"
  39   40  
  40   41  /*
  41   42   * This file implements the routines to read and write per-lwp register
  42      - * information from either a live process or core file opened with libproc.
  43      - * We build up a few common routines for reading and writing register
  44      - * information, and then the public functions are all trivial calls to these.
       43 + * information from either a live process or core file opened with libproc.  We
       44 + * build up a few common routines for reading and writing register information,
       45 + * and then the public functions are all trivial calls to these.  It also
       46 + * implements similar logic that is used with an lwp handle.
       47 + *
       48 + * The primary registers and floating point registers (e.g. regs,fpregs) are
       49 + * retreived from the lwp and process status files.  The library caches the
       50 + * values of these files.  When we perorm updates, we ensure that cached copies
       51 + * are refreshed or updated as part of this.
  45   52   */
  46   53  
  47   54  /*
  48   55   * Utility function to return a pointer to the structure of cached information
  49   56   * about an lwp in the core file, given its lwpid.
  50   57   */
  51   58  static lwp_info_t *
  52   59  getlwpcore(struct ps_prochandle *P, lwpid_t lwpid)
  53   60  {
  54   61          core_info_t *core = P->data;
  55   62          lwp_info_t *lwp;
  56   63  
  57   64          for (lwp = list_head(&core->core_lwp_head); lwp != NULL;
  58   65              lwp = list_next(&core->core_lwp_head, lwp)) {
  59   66                  if (lwp->lwp_id == lwpid)
  60   67                          return (lwp);
  61   68          }
  62   69  
  63      -        errno = EINVAL;
       70 +        errno = ENOENT;
  64   71          return (NULL);
  65   72  }
  66   73  
  67   74  /*
  68   75   * Utility function to open and read the contents of a per-lwp /proc file.
  69      - * This function is used to slurp in lwpstatus, xregs, and asrs.
       76 + * This function is used to slurp in lwpstatus, lwpname, lwpsinfo, spymaster,
       77 + * and others.
  70   78   */
  71   79  static int
  72   80  getlwpfile(struct ps_prochandle *P, lwpid_t lwpid,
  73   81      const char *fbase, void *rp, size_t n)
  74   82  {
  75   83          char fname[PATH_MAX];
  76   84          int fd;
  77   85  
  78   86          (void) snprintf(fname, sizeof (fname), "%s/%d/lwp/%d/%s",
  79   87              procfs_path, (int)P->status.pr_pid, (int)lwpid, fbase);
  80   88  
  81   89          if ((fd = open(fname, O_RDONLY)) >= 0) {
  82   90                  if (read(fd, rp, n) > 0) {
  83   91                          (void) close(fd);
  84   92                          return (0);
  85   93                  }
       94 +
       95 +                int e = errno;
  86   96                  (void) close(fd);
       97 +                errno = e;
  87   98          }
  88   99          return (-1);
  89  100  }
  90  101  
  91  102  /*
      103 + * This is a variant of getlwpfile that has three different semantics:
      104 + *
      105 + *  o It will stat the file to determine the size and allocate that for the
      106 + *    caller.
      107 + *  o If the stat size is zero (e.g. traditional xregs behavior when
      108 + *    unsupported) then it will return the libproc ENODATA error.
      109 + *  o It is an error if not all the data is read.
      110 + *
      111 + * Currently this is just used by xregs.
      112 + */
      113 +static int
      114 +getlwpfile_alloc(struct ps_prochandle *P, lwpid_t lwpid, const char *fbase,
      115 +    void **datap, size_t *sizep)
      116 +{
      117 +        char fname[PATH_MAX];
      118 +        int fd;
      119 +
      120 +        (void) snprintf(fname, sizeof (fname), "%s/%d/lwp/%d/%s",
      121 +            procfs_path, (int)P->status.pr_pid, (int)lwpid, fbase);
      122 +
      123 +        if ((fd = open(fname, O_RDONLY)) >= 0) {
      124 +                int e;
      125 +                struct stat st;
      126 +
      127 +                if (fstat(fd, &st) == 0) {
      128 +                        prxregset_t *prx;
      129 +
      130 +                        if (st.st_size == 0) {
      131 +                                errno = ENODATA;
      132 +                                goto clean;
      133 +                        }
      134 +
      135 +                        prx = malloc(st.st_size);
      136 +                        if (prx == NULL) {
      137 +                                goto clean;
      138 +                        }
      139 +
      140 +                        if (read(fd, prx, st.st_size) == st.st_size) {
      141 +                                (void) close(fd);
      142 +                                *datap = prx;
      143 +                                *sizep = st.st_size;
      144 +                                return (0);
      145 +                        }
      146 +
      147 +                        free(prx);
      148 +                }
      149 +clean:
      150 +                e = errno;
      151 +                (void) close(fd);
      152 +                errno = e;
      153 +        }
      154 +
      155 +        return (-1);
      156 +}
      157 +
      158 +/*
  92  159   * Get the lwpstatus_t for an lwp from either the live process or our
  93  160   * cached information from the core file.  This is used to get the
  94  161   * general-purpose registers or floating point registers.
  95  162   */
  96  163  int
  97  164  getlwpstatus(struct ps_prochandle *P, lwpid_t lwpid, lwpstatus_t *lps)
  98  165  {
  99  166          lwp_info_t *lwp;
 100  167  
 101  168          /*
↓ open down ↓ 20 lines elided ↑ open up ↑
 122  189           */
 123  190          if (P->data != NULL && (lwp = getlwpcore(P, lwpid)) != NULL) {
 124  191                  (void) memcpy(lps, &lwp->lwp_status, sizeof (lwpstatus_t));
 125  192                  return (0);
 126  193          }
 127  194  
 128  195          return (-1);
 129  196  }
 130  197  
 131  198  /*
      199 + * libproc caches information about the registers for representative LWPs and
      200 + * threads which we have the thread handle for. When we do a write to certain
      201 + * files, we need to refresh state and take care of both the process and the
      202 + * representative LWP's info. Because the xregs may or may not mutate the state
      203 + * of the other regsiters, we just always do a refresh of the entire cached
      204 + * psinfo.
      205 + */
      206 +static void
      207 +refresh_status(struct ps_prochandle *P, lwpid_t lwpid, struct ps_lwphandle *L,
      208 +    long cmd, const void *rp, size_t n)
      209 +{
      210 +        if (P->status.pr_lwp.pr_lwpid == lwpid) {
      211 +                if (cmd == PCSREG)
      212 +                        (void) memcpy(P->status.pr_lwp.pr_reg, rp, n);
      213 +                else if (cmd == PCSFPREG)
      214 +                        (void) memcpy(&P->status.pr_lwp.pr_fpreg, rp, n);
      215 +                else if (cmd == PCSXREG)
      216 +                        (void) Pstopstatus(P, PCNULL, 0);
      217 +        }
      218 +
      219 +        if (L != NULL) {
      220 +                if (cmd == PCSREG)
      221 +                        (void) memcpy(&L->lwp_status.pr_reg, rp, n);
      222 +                else if (cmd == PCSFPREG)
      223 +                        (void) memcpy(&L->lwp_status.pr_fpreg, rp, n);
      224 +                else if (cmd == PCSXREG)
      225 +                        (void) Lstopstatus(L, PCNULL, 0);
      226 +        }
      227 +}
      228 +
      229 +/*
 132  230   * Utility function to modify lwp registers.  This is done using either the
 133      - * process control file or per-lwp control file as necessary.
      231 + * process control file or per-lwp control file as necessary.  This assumes that
      232 + * we have a process-level hold on things, which may not always be true.
 134  233   */
 135  234  static int
 136      -setlwpregs(struct ps_prochandle *P, lwpid_t lwpid, long cmd,
      235 +setlwpregs_proc(struct ps_prochandle *P, lwpid_t lwpid, long cmd,
 137  236      const void *rp, size_t n)
 138  237  {
 139  238          iovec_t iov[2];
 140  239          char fname[PATH_MAX];
 141      -        int fd;
      240 +        struct ps_lwphandle *L;
      241 +        int fd = -1;
 142  242  
 143  243          if (P->state != PS_STOP) {
 144  244                  errno = EBUSY;
 145  245                  return (-1);
 146  246          }
 147  247  
 148  248          iov[0].iov_base = (caddr_t)&cmd;
 149  249          iov[0].iov_len = sizeof (long);
 150  250          iov[1].iov_base = (caddr_t)rp;
 151  251          iov[1].iov_len = n;
 152  252  
 153  253          /*
      254 +         * If we have an lwp handle for this thread, then make sure that we use
      255 +         * that to update the state so cached information is updated.  We sync
      256 +         * the thread ahead of the process.
      257 +         */
      258 +        if ((L = Lfind(P, lwpid)) != NULL) {
      259 +                Lsync(L);
      260 +                fd = L->lwp_ctlfd;
      261 +        }
      262 +
      263 +        /*
 154  264           * Writing the process control file writes the representative lwp.
 155  265           * Psync before we write to make sure we are consistent with the
 156  266           * primary interfaces.  Similarly, make sure to update P->status
 157      -         * afterward if we are modifying one of its register sets.
      267 +         * afterward if we are modifying one of its register sets.  On some
      268 +         * platforms the xregs modify the other register states.  As a result,
      269 +         * always refresh the representative LWP's status.
 158  270           */
 159  271          if (P->status.pr_lwp.pr_lwpid == lwpid) {
 160  272                  Psync(P);
      273 +                fd = P->ctlfd;
      274 +        }
 161  275  
 162      -                if (writev(P->ctlfd, iov, 2) == -1)
      276 +        if (fd > -1) {
      277 +                if (writev(fd, iov, 2) == -1)
 163  278                          return (-1);
 164      -
 165      -                if (cmd == PCSREG)
 166      -                        (void) memcpy(P->status.pr_lwp.pr_reg, rp, n);
 167      -                else if (cmd == PCSFPREG)
 168      -                        (void) memcpy(&P->status.pr_lwp.pr_fpreg, rp, n);
 169      -
      279 +                refresh_status(P, lwpid, L, cmd, rp, n);
 170  280                  return (0);
 171  281          }
 172  282  
 173  283          /*
 174  284           * If the lwp we want is not the representative lwp, we need to
 175  285           * open the ctl file for that specific lwp.
 176  286           */
 177  287          (void) snprintf(fname, sizeof (fname), "%s/%d/lwp/%d/lwpctl",
 178  288              procfs_path, (int)P->status.pr_pid, (int)lwpid);
 179  289  
 180  290          if ((fd = open(fname, O_WRONLY)) >= 0) {
 181  291                  if (writev(fd, iov, 2) > 0) {
 182  292                          (void) close(fd);
 183  293                          return (0);
 184  294                  }
      295 +                int e = errno;
 185  296                  (void) close(fd);
      297 +                errno = e;
 186  298          }
 187  299          return (-1);
 188  300  }
 189  301  
      302 +/*
      303 + * This is a variant of the above that only assumes we have a hold on the thread
      304 + * as opposed to a process.
      305 + */
      306 +static int
      307 +setlwpregs_lwp(struct ps_lwphandle *L, long cmd, const void *rp, size_t n)
      308 +{
      309 +        iovec_t iov[2];
      310 +
      311 +        if (L->lwp_state != PS_STOP) {
      312 +                errno = EBUSY;
      313 +                return (-1);
      314 +        }
      315 +
      316 +        iov[0].iov_base = (caddr_t)&cmd;
      317 +        iov[0].iov_len = sizeof (long);
      318 +        iov[1].iov_base = (caddr_t)rp;
      319 +        iov[1].iov_len = n;
      320 +
      321 +        Lsync(L);
      322 +        if (writev(L->lwp_ctlfd, iov, 2) == -1)
      323 +                return (-1);
      324 +        refresh_status(L->lwp_proc, L->lwp_id, L, cmd, rp, n);
      325 +
      326 +        return (0);
      327 +}
      328 +
 190  329  int
 191  330  Plwp_getregs(struct ps_prochandle *P, lwpid_t lwpid, prgregset_t gregs)
 192  331  {
 193  332          lwpstatus_t lps;
 194  333  
 195  334          if (getlwpstatus(P, lwpid, &lps) == -1)
 196  335                  return (-1);
 197  336  
 198  337          (void) memcpy(gregs, lps.pr_reg, sizeof (prgregset_t));
 199  338          return (0);
 200  339  }
 201  340  
 202  341  int
      342 +Lgetregs(struct ps_lwphandle *L, prgregset_t *gregs)
      343 +{
      344 +        (void) memcpy(gregs, L->lwp_status.pr_reg, sizeof (prgregset_t));
      345 +        return (0);
      346 +}
      347 +
      348 +int
 203  349  Plwp_setregs(struct ps_prochandle *P, lwpid_t lwpid, const prgregset_t gregs)
 204  350  {
 205      -        return (setlwpregs(P, lwpid, PCSREG, gregs, sizeof (prgregset_t)));
      351 +        return (setlwpregs_proc(P, lwpid, PCSREG, gregs, sizeof (prgregset_t)));
 206  352  }
 207  353  
 208  354  int
      355 +Lsetregs(struct ps_lwphandle *L, const prgregset_t *gregs)
      356 +{
      357 +        return (setlwpregs_lwp(L, PCSREG, gregs, sizeof (prgregset_t)));
      358 +}
      359 +
      360 +int
 209  361  Plwp_getfpregs(struct ps_prochandle *P, lwpid_t lwpid, prfpregset_t *fpregs)
 210  362  {
 211  363          lwpstatus_t lps;
 212  364  
 213  365          if (getlwpstatus(P, lwpid, &lps) == -1)
 214  366                  return (-1);
 215  367  
 216  368          (void) memcpy(fpregs, &lps.pr_fpreg, sizeof (prfpregset_t));
 217  369          return (0);
 218  370  }
 219  371  
 220      -int Plwp_setfpregs(struct ps_prochandle *P, lwpid_t lwpid,
      372 +int
      373 +Lgetfpregs(struct ps_lwphandle *L, prfpregset_t *fpregs)
      374 +{
      375 +        (void) memcpy(fpregs, &L->lwp_status.pr_fpreg, sizeof (prfpregset_t));
      376 +        return (0);
      377 +}
      378 +
      379 +int
      380 +Plwp_setfpregs(struct ps_prochandle *P, lwpid_t lwpid,
 221  381      const prfpregset_t *fpregs)
 222  382  {
 223      -        return (setlwpregs(P, lwpid, PCSFPREG, fpregs, sizeof (prfpregset_t)));
      383 +        return (setlwpregs_proc(P, lwpid, PCSFPREG, fpregs,
      384 +            sizeof (prfpregset_t)));
 224  385  }
 225  386  
 226      -#if defined(sparc) || defined(__sparc)
 227  387  int
 228      -Plwp_getxregs(struct ps_prochandle *P, lwpid_t lwpid, prxregset_t *xregs)
      388 +Lsetfpregs(struct ps_lwphandle *L, const prfpregset_t *fpregs)
 229  389  {
      390 +        return (setlwpregs_lwp(L, PCSFPREG, fpregs, sizeof (prfpregset_t)));
      391 +}
      392 +
      393 +/*
      394 + * The reason that this is structured to take both the size and the process
      395 + * handle is so that way we have enough information to tie this back to its
      396 + * underlying source and we can eventually use umem with this.
      397 + */
      398 +void
      399 +Plwp_freexregs(struct ps_prochandle *P __unused, prxregset_t *prx,
      400 +    size_t size __unused)
      401 +{
      402 +        free(prx);
      403 +}
      404 +
      405 +int
      406 +Plwp_getxregs(struct ps_prochandle *P, lwpid_t lwpid, prxregset_t **xregs,
      407 +    size_t *sizep)
      408 +{
 230  409          lwp_info_t *lwp;
 231  410  
 232  411          if (P->state == PS_IDLE) {
 233  412                  errno = ENODATA;
 234  413                  return (-1);
 235  414          }
 236  415  
 237  416          if (P->state != PS_DEAD) {
 238  417                  if (P->state != PS_STOP) {
 239  418                          errno = EBUSY;
 240  419                          return (-1);
 241  420                  }
 242  421  
 243      -                return (getlwpfile(P, lwpid, "xregs",
 244      -                    xregs, sizeof (prxregset_t)));
      422 +                return (getlwpfile_alloc(P, lwpid, "xregs",
      423 +                    (void **)xregs, sizep));
 245  424          }
 246  425  
 247      -        if ((lwp = getlwpcore(P, lwpid)) != NULL && lwp->lwp_xregs != NULL) {
 248      -                (void) memcpy(xregs, lwp->lwp_xregs, sizeof (prxregset_t));
      426 +        if ((lwp = getlwpcore(P, lwpid)) != NULL && lwp->lwp_xregs != NULL &&
      427 +            lwp->lwp_xregsize > 0) {
      428 +                *xregs = malloc(lwp->lwp_xregsize);
      429 +                if (*xregs == NULL)
      430 +                        return (-1);
      431 +                (void) memcpy(*xregs, lwp->lwp_xregs, lwp->lwp_xregsize);
      432 +                *sizep = lwp->lwp_xregsize;
 249  433                  return (0);
 250  434          }
 251  435  
 252  436          if (lwp != NULL)
 253  437                  errno = ENODATA;
 254  438          return (-1);
 255  439  }
 256  440  
 257  441  int
 258      -Plwp_setxregs(struct ps_prochandle *P, lwpid_t lwpid, const prxregset_t *xregs)
      442 +Lgetxregs(struct ps_lwphandle *L, prxregset_t **xregs, size_t *sizep)
 259  443  {
 260      -        return (setlwpregs(P, lwpid, PCSXREG, xregs, sizeof (prxregset_t)));
      444 +        lwp_info_t *lwp;
      445 +
      446 +        if (L->lwp_state != PS_DEAD) {
      447 +                if (L->lwp_state != PS_STOP) {
      448 +                        errno = EBUSY;
      449 +                        return (-1);
      450 +                }
      451 +                return (getlwpfile_alloc(L->lwp_proc, L->lwp_id, "xregs",
      452 +                    (void **)xregs, sizep));
      453 +        }
      454 +
      455 +        if ((lwp = getlwpcore(L->lwp_proc, L->lwp_id)) != NULL &&
      456 +            lwp->lwp_xregs != NULL && lwp->lwp_xregsize > 0) {
      457 +                *xregs = malloc(lwp->lwp_xregsize);
      458 +                if (*xregs == NULL)
      459 +                        return (-1);
      460 +                (void) memcpy(*xregs, lwp->lwp_xregs, lwp->lwp_xregsize);
      461 +                *sizep = lwp->lwp_xregsize;
      462 +                return (0);
      463 +        }
      464 +
      465 +        if (lwp != NULL)
      466 +                errno = ENODATA;
      467 +        return (-1);
 261  468  }
 262  469  
 263  470  int
      471 +Plwp_setxregs(struct ps_prochandle *P, lwpid_t lwpid, const prxregset_t *xregs,
      472 +    size_t len)
      473 +{
      474 +        return (setlwpregs_proc(P, lwpid, PCSXREG, xregs, len));
      475 +}
      476 +
      477 +int
      478 +Lsetxregs(struct ps_lwphandle *L, const prxregset_t *xregs, size_t len)
      479 +{
      480 +        return (setlwpregs_lwp(L, PCSXREG, xregs, len));
      481 +}
      482 +
      483 +#if defined(sparc) || defined(__sparc)
      484 +int
 264  485  Plwp_getgwindows(struct ps_prochandle *P, lwpid_t lwpid, gwindows_t *gwins)
 265  486  {
 266  487          lwp_info_t *lwp;
 267  488  
 268  489          if (P->state == PS_IDLE) {
 269  490                  errno = ENODATA;
 270  491                  return (-1);
 271  492          }
 272  493  
 273  494          if (P->state != PS_DEAD) {
↓ open down ↓ 43 lines elided ↑ open up ↑
 317  538  
 318  539          if (lwp != NULL)
 319  540                  errno = ENODATA;
 320  541          return (-1);
 321  542  
 322  543  }
 323  544  
 324  545  int
 325  546  Plwp_setasrs(struct ps_prochandle *P, lwpid_t lwpid, const asrset_t asrs)
 326  547  {
 327      -        return (setlwpregs(P, lwpid, PCSASRS, asrs, sizeof (asrset_t)));
      548 +        return (setlwpregs_proc(P, lwpid, PCSASRS, asrs, sizeof (asrset_t)));
 328  549  }
 329  550  #endif  /* __sparcv9 */
 330  551  #endif  /* __sparc */
 331  552  
 332  553  int
 333  554  Plwp_getpsinfo(struct ps_prochandle *P, lwpid_t lwpid, lwpsinfo_t *lps)
 334  555  {
 335  556          lwp_info_t *lwp;
 336  557  
 337  558          if (P->state == PS_IDLE) {
↓ open down ↓ 232 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX