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