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: successfully 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: successfully 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 errx(EXIT_FAILURE, "xregs error was not EBUSY, got %d!", errno); 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 } 137