1 /* $OpenBSD: dt_prov_syscall.c,v 1.9 2024/04/06 11:18:02 mpi Exp $ */
2
3 /*
4 * Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/types.h>
20 #include <sys/systm.h>
21 #include <sys/param.h>
22 #include <sys/malloc.h>
23 #include <sys/atomic.h>
24 #include <sys/syscall.h>
25
26 #include <dev/dt/dtvar.h>
27
28 /* Arrays of probes per syscall. */
29 struct dt_probe **dtps_entry;
30 struct dt_probe **dtps_return;
31 unsigned int dtps_nsysent = SYS_MAXSYSCALL;
32
33 /* Flags that make sense for this provider */
34 #define DTEVT_PROV_SYSCALL (DTEVT_COMMON|DTEVT_FUNCARGS)
35
36 int dt_prov_syscall_alloc(struct dt_probe *, struct dt_softc *,
37 struct dt_pcb_list *, struct dtioc_req *);
38 int dt_prov_syscall_entry(struct dt_provider *, ...);
39 void dt_prov_syscall_return(struct dt_provider *, ...);
40
41 struct dt_provider dt_prov_syscall = {
42 .dtpv_name = "syscall",
43 .dtpv_alloc = dt_prov_syscall_alloc,
44 .dtpv_enter = dt_prov_syscall_entry,
45 .dtpv_leave = dt_prov_syscall_return,
46 .dtpv_dealloc = NULL,
47 };
48
49 int
dt_prov_syscall_init(void)50 dt_prov_syscall_init(void)
51 {
52 struct dt_probe *dtp;
53 int i, len, nprobes = 0;
54 char *sysnb;
55
56 dtps_entry = mallocarray(dtps_nsysent, sizeof(dtp), M_DT,
57 M_NOWAIT|M_ZERO);
58 if (dtps_entry == NULL)
59 return 0;
60 dtps_return = mallocarray(dtps_nsysent, sizeof(dtp), M_DT,
61 M_NOWAIT|M_ZERO);
62 if (dtps_return == NULL) {
63 free(dtps_entry, M_DT, dtps_nsysent * sizeof(dtp));
64 return 0;
65 }
66
67 for (i = 0; i < dtps_nsysent; i++) {
68 if (sysent[i].sy_call == sys_nosys)
69 continue;
70
71 len = snprintf(NULL, 0, "sys%%%u", i);
72 sysnb = malloc(len + 1, M_DT, M_NOWAIT);
73 if (sysnb == NULL)
74 break;
75 snprintf(sysnb, len + 1, "sys%%%u", i);
76 dtp = dt_dev_alloc_probe(sysnb, "entry", &dt_prov_syscall);
77 if (dtp == NULL) {
78 free(sysnb, M_DT, len);
79 break;
80 }
81 dtp->dtp_nargs = sysent[i].sy_narg;
82 dtp->dtp_sysnum = i;
83 dtps_entry[i] = dtp;
84 dt_dev_register_probe(dtp);
85 nprobes++;
86 dtp = dt_dev_alloc_probe(sysnb, "return", &dt_prov_syscall);
87 if (dtp == NULL)
88 break;
89 dtp->dtp_sysnum = i;
90 dtps_return[i] = dtp;
91 dt_dev_register_probe(dtp);
92 nprobes++;
93 }
94
95 return nprobes;
96 }
97
98 int
dt_prov_syscall_alloc(struct dt_probe * dtp,struct dt_softc * sc,struct dt_pcb_list * plist,struct dtioc_req * dtrq)99 dt_prov_syscall_alloc(struct dt_probe *dtp, struct dt_softc *sc,
100 struct dt_pcb_list *plist, struct dtioc_req *dtrq)
101 {
102 struct dt_pcb *dp;
103
104 KASSERT(TAILQ_EMPTY(plist));
105 KASSERT(dtp->dtp_prov == &dt_prov_syscall);
106 KASSERT((dtp->dtp_sysnum >= 0) && (dtp->dtp_sysnum < dtps_nsysent));
107
108 dp = dt_pcb_alloc(dtp, sc);
109 if (dp == NULL)
110 return ENOMEM;
111
112 dp->dp_evtflags = dtrq->dtrq_evtflags & DTEVT_PROV_SYSCALL;
113 TAILQ_INSERT_HEAD(plist, dp, dp_snext);
114
115
116 return 0;
117 }
118
119 int
dt_prov_syscall_entry(struct dt_provider * dtpv,...)120 dt_prov_syscall_entry(struct dt_provider *dtpv, ...)
121 {
122 struct dt_probe *dtp;
123 struct dt_pcb *dp;
124 register_t sysnum;
125 size_t argsize;
126 register_t *args;
127 va_list ap;
128
129 KASSERT(dtpv == &dt_prov_syscall);
130 va_start(ap, dtpv);
131 sysnum = va_arg(ap, register_t);
132 argsize = va_arg(ap, size_t);
133 args = va_arg(ap, register_t*);
134 va_end(ap);
135
136 KASSERT((argsize / sizeof(register_t)) <= DTMAXFUNCARGS);
137
138 if (sysnum < 0 || sysnum >= dtps_nsysent)
139 return 0;
140
141 dtp = dtps_entry[sysnum];
142 if (!dtp->dtp_recording)
143 return 0;
144
145 smr_read_enter();
146 SMR_SLIST_FOREACH(dp, &dtp->dtp_pcbs, dp_pnext) {
147 struct dt_evt *dtev;
148
149 dtev = dt_pcb_ring_get(dp, 0);
150 if (dtev == NULL)
151 continue;
152
153 if (ISSET(dp->dp_evtflags, DTEVT_FUNCARGS))
154 memcpy(dtev->dtev_args, args, argsize);
155
156 dt_pcb_ring_consume(dp, dtev);
157 }
158 smr_read_leave();
159 return 0;
160 }
161
162 void
dt_prov_syscall_return(struct dt_provider * dtpv,...)163 dt_prov_syscall_return(struct dt_provider *dtpv, ...)
164 {
165 struct dt_probe *dtp;
166 struct dt_pcb *dp;
167 register_t sysnum;
168 int error;
169 register_t retval[2];
170 va_list ap;
171
172 KASSERT(dtpv == &dt_prov_syscall);
173
174 va_start(ap, dtpv);
175 sysnum = va_arg(ap, register_t);
176 error = va_arg(ap, int);
177 retval[0] = va_arg(ap, register_t);
178 retval[1] = va_arg(ap, register_t);
179 va_end(ap);
180
181 if (sysnum < 0 || sysnum >= dtps_nsysent)
182 return;
183
184 dtp = dtps_return[sysnum];
185 if (!dtp->dtp_recording)
186 return;
187
188 smr_read_enter();
189 SMR_SLIST_FOREACH(dp, &dtp->dtp_pcbs, dp_pnext) {
190 struct dt_evt *dtev;
191
192 dtev = dt_pcb_ring_get(dp, 0);
193 if (dtev == NULL)
194 continue;
195
196 if (error) {
197 dtev->dtev_retval[0] = -1;
198 dtev->dtev_retval[1] = 0;
199 dtev->dtev_error = error;
200 } else {
201 dtev->dtev_retval[0] = retval[0];
202 dtev->dtev_retval[1] = retval[1];
203 dtev->dtev_error = 0;
204 }
205
206 dt_pcb_ring_consume(dp, dtev);
207 }
208 smr_read_leave();
209 }
210