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 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 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 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 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 166 tbdev_add_domain(void *domain) 167 { 168 169 return (0); 170 } 171 172 int 173 tbdev_remove_domain(void *domain) 174 { 175 176 return (0); 177 } 178 179 int 180 tbdev_add_router(struct router_softc *rt) 181 { 182 183 return (0); 184 } 185 186 int 187 tbdev_remove_router(struct router_softc *rt) 188 { 189 190 return (0); 191 } 192 193 static int 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 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 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