1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2023 Oxide Computer Company
  14  */
  15 
  16 /*
  17  * Verify that we can read the xregs of a thread and write them back intact.
  18  * This uses libproc as a wrapper. We then start the thread running again and
  19  * attempt to write to /proc ourselves to expect an EBUSY because the thread is
  20  * not stopped.
  21  */
  22 
  23 #include <libproc.h>
  24 #include <thread.h>
  25 #include <stdlib.h>
  26 #include <err.h>
  27 #include <errno.h>
  28 #include <string.h>
  29 #include <sys/sysmacros.h>
  30 
  31 #include "xsave_util.h"
  32 
  33 int
  34 main(void)
  35 {
  36         int ret, fd;
  37         ssize_t sret;
  38         struct ps_prochandle *P;
  39         struct ps_lwphandle *L;
  40         thread_t targ;
  41         prxregset_t *prx, *prx_alt;
  42         size_t prx_len, prx_alt_len;
  43         struct iovec iov[2];
  44         long cmd = PCSXREG;
  45 
  46         P = Pgrab(getpid(), PGRAB_RDONLY, &ret);
  47         if (P == NULL) {
  48                 errx(EXIT_FAILURE, "failed to grab ourself: %s",
  49                     Pgrab_error(ret));
  50         }
  51 
  52         ret = thr_create(NULL, 0, xsu_sleeper_thread, NULL, THR_DETACHED,
  53             &targ);
  54         if (ret != 0) {
  55                 errc(EXIT_FAILURE, ret, "failed to create sleeper thread");
  56         }
  57 
  58         L = Lgrab(P, targ, &ret);
  59         if (L == NULL) {
  60                 errx(EXIT_FAILURE, "failed to grab our sleeper thread: %s",
  61                     Lgrab_error(ret));
  62         }
  63 
  64         ret = Lstop(L, 0);
  65         if (ret != 0) {
  66                 err(EXIT_FAILURE, "failed to stop the sleeper thread");
  67         }
  68 
  69         if (Lgetxregs(L, &prx, &prx_len) != 0) {
  70                 err(EXIT_FAILURE, "failed to get xregs");
  71         }
  72 
  73         (void) printf("TEST PASSED: sucessfully got initial xregs\n");
  74 
  75         if (Lsetxregs(L, prx, prx_len) != 0) {
  76                 err(EXIT_FAILURE, "failed to set xregs");
  77         }
  78 
  79         (void) printf("TEST PASSED: sucessfully set xregs\n");
  80 
  81         if (Lgetxregs(L, &prx_alt, &prx_alt_len) != 0) {
  82                 err(EXIT_FAILURE, "failed to get xregs after write");
  83         }
  84 
  85         if (prx_len != prx_alt_len) {
  86                 errx(EXIT_FAILURE, "xreg length changed across a write: "
  87                     "originally found %zu, now %zu", prx_len, prx_alt_len);
  88         }
  89 
  90         if (memcmp(prx, prx_alt, prx_len) != 0) {
  91                 const uint8_t *a = (uint8_t *)prx;
  92                 const uint8_t *b = (uint8_t *)prx_alt;
  93                 for (size_t i = 0; i < prx_len; i++) {
  94                         if (a[i] != b[i]) {
  95                                 (void) fprintf(stderr, "prx[0x%x] = 0x%02x, "
  96                                     "prx_alt[0x%x] = 0x%02x\n", i, a[i], i,
  97                                     b[i]);
  98                         }
  99                 }
 100                 errx(EXIT_FAILURE, "xregs were not the same!");
 101         }
 102 
 103         (void) printf("TEST PASSED: round-trip xregs\n");
 104 
 105         if (Lsetrun(L, 0, 0) != 0) {
 106                 err(EXIT_FAILURE, "failed to start sleeper thread");
 107         }
 108 
 109         /*
 110          * We write to /proc directly ourselves as a way to avoid libproc's own
 111          * checks for the state of the thread.
 112          */
 113         fd = Lctlfd(L);
 114         if (fd < 0) {
 115                 errx(EXIT_FAILURE, "failed to get sleeper thread control d");
 116         }
 117 
 118         iov[0].iov_base = (char *)&cmd;
 119         iov[0].iov_len = sizeof (long);
 120         iov[1].iov_base = (char *)prx;
 121         iov[1].iov_len = prx_len;
 122         sret = writev(fd, iov, ARRAY_SIZE(iov));
 123         if (sret != -1) {
 124                 errx(EXIT_FAILURE, "writev returned %zd, but expected -1",
 125                     sret);
 126         }
 127 
 128         if (errno != EBUSY) {
 129 
 130         }
 131 
 132         (void) printf("TEST PASSED: got EBUSY with PCSXREG to running "
 133             "thread\n");
 134         Plwp_freexregs(P, prx, prx_len);
 135         return (EXIT_SUCCESS);
 136 }