xref: /openbsd/sys/dev/dt/dt_prov_syscall.c (revision c2c0b0cf)
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