xref: /freebsd/sys/dev/thunderbolt/tb_dev.c (revision 2ed9833791f28e14843ac813f90cb030e45948dc)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2022 Scott Long
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include "opt_thunderbolt.h"
30 
31 /* Userspace control device for USB4 / TB3 */
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/bus.h>
38 #include <sys/conf.h>
39 #include <sys/malloc.h>
40 #include <sys/queue.h>
41 #include <sys/sysctl.h>
42 #include <sys/lock.h>
43 #include <sys/mutex.h>
44 #include <sys/nv.h>
45 #include <sys/taskqueue.h>
46 #include <sys/gsb_crc32.h>
47 #include <sys/endian.h>
48 #include <vm/vm.h>
49 #include <vm/pmap.h>
50 
51 #include <machine/bus.h>
52 #include <machine/stdarg.h>
53 
54 #include <dev/thunderbolt/nhi_reg.h>
55 #include <dev/thunderbolt/nhi_var.h>
56 #include <dev/thunderbolt/tb_reg.h>
57 #include <dev/thunderbolt/tb_var.h>
58 #include <dev/thunderbolt/tbcfg_reg.h>
59 #include <dev/thunderbolt/router_var.h>
60 #include <dev/thunderbolt/tb_debug.h>
61 #include <dev/thunderbolt/tb_dev.h>
62 #include <dev/thunderbolt/tb_ioctl.h>
63 
64 struct tbdev_if;
65 struct tbdev_dm;
66 struct tbdev_rt;
67 
68 struct tbdev_if {
69 	TAILQ_ENTRY(tbdev_if)	dev_next;
70 	char			name[SPECNAMELEN];
71 };
72 
73 struct tbdev_dm {
74 	TAILQ_ENTRY(tbdev_dm)	dev_next;
75 	char			uid[16];
76 };
77 
78 struct tbdev_rt {
79 	TAILQ_ENTRY(tbdev_rt)	dev_next;
80 	uint64_t		route;
81 };
82 
83 static int tbdev_static_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td);
84 
85 static struct cdevsw tbdev_static_devsw = {
86 	.d_version = D_VERSION,
87 	.d_ioctl = tbdev_static_ioctl,
88 	.d_name = "tbt"
89 };
90 static struct cdev *tb_dev = NULL;
91 
92 static TAILQ_HEAD(, tbdev_if) tbdev_head = TAILQ_HEAD_INITIALIZER(tbdev_head);
93 static TAILQ_HEAD(, tbdev_dm) tbdomain_head = TAILQ_HEAD_INITIALIZER(tbdomain_head);
94 static TAILQ_HEAD(, tbdev_rt) tbrouter_head = TAILQ_HEAD_INITIALIZER(tbrouter_head);
95 
96 static struct mtx tbdev_mtx;
97 MTX_SYSINIT(tbdev_mtx, &tbdev_mtx, "TBT Device Mutex", MTX_DEF);
98 
99 MALLOC_DEFINE(M_THUNDERBOLT, "thunderbolt", "memory for thunderbolt");
100 
101 static void
tbdev_init(void * arg)102 tbdev_init(void *arg)
103 {
104 
105 	tb_dev = make_dev(&tbdev_static_devsw, 0, UID_ROOT, GID_OPERATOR,
106 	    0644, TBT_DEVICE_NAME);
107 	if (tb_dev == NULL)
108 		printf("Cannot create Thunderbolt system device\n");
109 
110 	return;
111 }
112 
113 SYSINIT(tbdev_init, SI_SUB_KICK_SCHEDULER, SI_ORDER_FIRST, tbdev_init, NULL);
114 
115 static void
tbdev_uninit(void * arg)116 tbdev_uninit(void *arg)
117 {
118 	if (tb_dev != NULL) {
119 		destroy_dev(tb_dev);
120 		tb_dev = NULL;
121 	}
122 }
123 
124 SYSUNINIT(tbdev_uninit, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, tbdev_uninit, NULL);
125 
126 int
tbdev_add_interface(struct nhi_softc * nhi)127 tbdev_add_interface(struct nhi_softc *nhi)
128 {
129 	struct tbdev_if *ifce;
130 
131 	ifce = malloc(sizeof(struct tbdev_if), M_THUNDERBOLT, M_ZERO|M_NOWAIT);
132 	if (ifce == NULL)
133 		return (ENOMEM);
134 
135 	strlcpy(ifce->name, device_get_nameunit(nhi->dev), SPECNAMELEN);
136 	mtx_lock(&tbdev_mtx);
137 	TAILQ_INSERT_TAIL(&tbdev_head, ifce, dev_next);
138 	mtx_unlock(&tbdev_mtx);
139 
140 	return (0);
141 }
142 
143 int
tbdev_remove_interface(struct nhi_softc * nhi)144 tbdev_remove_interface(struct nhi_softc *nhi)
145 {
146 	struct tbdev_if *ifce = NULL, *if_back;
147 	const char *name;
148 
149 	name = device_get_nameunit(nhi->dev);
150 	mtx_lock(&tbdev_mtx);
151 	TAILQ_FOREACH_SAFE(ifce, &tbdev_head, dev_next, if_back) {
152 		if (strncmp(name, ifce->name, SPECNAMELEN) == 0) {
153 			TAILQ_REMOVE(&tbdev_head, ifce, dev_next);
154 			break;
155 		}
156 	}
157 	mtx_unlock(&tbdev_mtx);
158 
159 	if (ifce != NULL)
160 		free(ifce, M_THUNDERBOLT);
161 
162 	return (0);
163 }
164 
165 int
tbdev_add_domain(void * domain)166 tbdev_add_domain(void *domain)
167 {
168 
169 	return (0);
170 }
171 
172 int
tbdev_remove_domain(void * domain)173 tbdev_remove_domain(void *domain)
174 {
175 
176 	return (0);
177 }
178 
179 int
tbdev_add_router(struct router_softc * rt)180 tbdev_add_router(struct router_softc *rt)
181 {
182 
183 	return (0);
184 }
185 
186 int
tbdev_remove_router(struct router_softc * rt)187 tbdev_remove_router(struct router_softc *rt)
188 {
189 
190 	return (0);
191 }
192 
193 static int
tbdev_discover(caddr_t addr)194 tbdev_discover(caddr_t addr)
195 {
196 	nvlist_t	*nvl = NULL;
197 	struct tbt_ioc	*ioc = (struct tbt_ioc *)addr;
198 	struct tbdev_if *dev;
199 	struct tbdev_dm	*dm;
200 	struct tbdev_rt	*rt;
201 	void		*nvlpacked = NULL;
202 	const char	*cmd = NULL;
203 	int		error = 0;
204 
205 	if ((ioc->data == NULL) || (ioc->size == 0)) {
206 		printf("data or size is 0\n");
207 		return (EINVAL);
208 	}
209 
210 	if ((ioc->len == 0) || (ioc->len > TBT_IOCMAXLEN) ||
211 	    (ioc->len > ioc->size)) {
212 		printf("len is wrong\n");
213 		return (EINVAL);
214 	}
215 
216 	nvlpacked = malloc(ioc->len, M_THUNDERBOLT, M_NOWAIT);
217 	if (nvlpacked == NULL) {
218 		printf("cannot allocate nvlpacked\n");
219 		return (ENOMEM);
220 	}
221 
222 	error = copyin(ioc->data, nvlpacked, ioc->len);
223 	if (error) {
224 		free(nvlpacked, M_THUNDERBOLT);
225 		printf("error %d from copyin\n", error);
226 		return (error);
227 	}
228 
229 	nvl = nvlist_unpack(nvlpacked, ioc->len, NV_FLAG_NO_UNIQUE);
230 	if (nvl == NULL) {
231 		free(nvlpacked, M_THUNDERBOLT);
232 		printf("cannot unpack nvlist\n");
233 		return (EINVAL);
234 	}
235 	free(nvlpacked, M_THUNDERBOLT);
236 	nvlpacked = NULL;
237 
238 	if (nvlist_exists_string(nvl, TBT_DISCOVER_TYPE))
239 		cmd = nvlist_get_string(nvl, TBT_DISCOVER_TYPE);
240 	if (cmd == NULL) {
241 		printf("cannot find type string\n");
242 		error = EINVAL;
243 		goto out;
244 	}
245 
246 	mtx_lock(&tbdev_mtx);
247 	if (strncmp(cmd, TBT_DISCOVER_IFACE, TBT_NAMLEN) == 0) {
248 		TAILQ_FOREACH(dev, &tbdev_head, dev_next)
249 			nvlist_add_string(nvl, TBT_DISCOVER_IFACE, dev->name);
250 	} else if (strncmp(cmd, TBT_DISCOVER_DOMAIN, TBT_NAMLEN) == 0) {
251 		TAILQ_FOREACH(dm, &tbdomain_head, dev_next)
252 			nvlist_add_string(nvl, TBT_DISCOVER_DOMAIN, dm->uid);
253 	} else if (strncmp(cmd, TBT_DISCOVER_ROUTER, TBT_NAMLEN) == 0) {
254 		TAILQ_FOREACH(rt, &tbrouter_head, dev_next)
255 			nvlist_add_number(nvl, TBT_DISCOVER_ROUTER, rt->route);
256 	} else {
257 		printf("cannot find supported tpye\n");
258 		error = EINVAL;
259 		goto out;
260 	}
261 	mtx_unlock(&tbdev_mtx);
262 
263 	error = nvlist_error(nvl);
264 	if (error != 0) {
265 		printf("error %d state in nvlist\n", error);
266 		return (error);
267 	}
268 
269 	nvlpacked = nvlist_pack(nvl, &ioc->len);
270 	if (nvlpacked == NULL) {
271 		printf("cannot allocate new packed buffer\n");
272 		return (ENOMEM);
273 	}
274 	if (ioc->size < ioc->len) {
275 		printf("packed buffer is too big to copyout\n");
276 		return (ENOSPC);
277 	}
278 
279 	error = copyout(nvlpacked, ioc->data, ioc->len);
280 	if (error)
281 		printf("error %d on copyout\n", error);
282 
283 out:
284 	if (nvlpacked != NULL)
285 		free(nvlpacked, M_NVLIST);
286 	if (nvl != NULL)
287 		nvlist_destroy(nvl);
288 
289 	return (error);
290 }
291 
292 static int
tbdev_request(caddr_t addr)293 tbdev_request(caddr_t addr)
294 {
295 	struct tbt_ioc	*ioc = (struct tbt_ioc *)addr;
296 	nvlist_t	*nvl = NULL;
297 	void		*nvlpacked = NULL;
298 	int		error = 0;
299 
300 	if ((ioc->data == NULL) || (ioc->size == 0))
301 		return (ENOMEM);
302 
303 	nvlpacked = nvlist_pack(nvl, &ioc->len);
304 	if (nvlpacked == NULL)
305 		return (ENOMEM);
306 	if (ioc->size < ioc->len)
307 		return (ENOSPC);
308 
309 	error = copyout(nvlpacked, ioc->data, ioc->len);
310 	return (error);
311 }
312 
313 static int
tbdev_static_ioctl(struct cdev * dev,u_long cmd,caddr_t addr,int flags,struct thread * td)314 tbdev_static_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
315     struct thread *td)
316 {
317 	int error = 0;
318 
319 	switch (cmd) {
320 	case TBT_DISCOVER:
321 		error = tbdev_discover(addr);
322 		break;
323 	case TBT_REQUEST:
324 		error = tbdev_request(addr);
325 		break;
326 	default:
327 		error = EINVAL;
328 	}
329 
330 	return (error);
331 }
332