1 /*- 2 * Copyright (c) 2014, 2015, 2019 Marcel Moolenaar 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/bus.h> 33 #include <sys/conf.h> 34 #include <sys/cons.h> 35 #include <sys/fcntl.h> 36 #include <sys/interrupt.h> 37 #include <sys/kdb.h> 38 #include <sys/kernel.h> 39 #include <sys/malloc.h> 40 #include <sys/mman.h> 41 #include <sys/proc.h> 42 #include <sys/queue.h> 43 #include <sys/reboot.h> 44 #include <machine/bus.h> 45 #include <sys/rman.h> 46 #include <sys/uio.h> 47 #include <machine/resource.h> 48 #include <machine/stdarg.h> 49 50 #include <dev/pci/pcivar.h> 51 52 #include <dev/proto/proto.h> 53 #include <dev/proto/proto_dev.h> 54 #include <dev/proto/proto_busdma.h> 55 56 CTASSERT(SYS_RES_IRQ != PROTO_RES_UNUSED && 57 SYS_RES_DRQ != PROTO_RES_UNUSED && 58 SYS_RES_MEMORY != PROTO_RES_UNUSED && 59 SYS_RES_IOPORT != PROTO_RES_UNUSED); 60 CTASSERT(SYS_RES_IRQ != PROTO_RES_PCICFG && 61 SYS_RES_DRQ != PROTO_RES_PCICFG && 62 SYS_RES_MEMORY != PROTO_RES_PCICFG && 63 SYS_RES_IOPORT != PROTO_RES_PCICFG); 64 CTASSERT(SYS_RES_IRQ != PROTO_RES_BUSDMA && 65 SYS_RES_DRQ != PROTO_RES_BUSDMA && 66 SYS_RES_MEMORY != PROTO_RES_BUSDMA && 67 SYS_RES_IOPORT != PROTO_RES_BUSDMA); 68 69 char proto_driver_name[] = "proto"; 70 71 static d_open_t proto_open; 72 static d_close_t proto_close; 73 static d_read_t proto_read; 74 static d_write_t proto_write; 75 static d_ioctl_t proto_ioctl; 76 static d_mmap_t proto_mmap; 77 78 struct cdevsw proto_devsw = { 79 .d_version = D_VERSION, 80 .d_flags = 0, 81 .d_name = proto_driver_name, 82 .d_open = proto_open, 83 .d_close = proto_close, 84 .d_read = proto_read, 85 .d_write = proto_write, 86 .d_ioctl = proto_ioctl, 87 .d_mmap = proto_mmap, 88 }; 89 90 static MALLOC_DEFINE(M_PROTO, "PROTO", "PROTO driver"); 91 92 int 93 proto_add_resource(struct proto_softc *sc, int type, int rid, 94 struct resource *res) 95 { 96 struct proto_res *r; 97 98 if (type == PROTO_RES_UNUSED) 99 return (EINVAL); 100 if (sc->sc_rescnt == PROTO_RES_MAX) 101 return (ENOSPC); 102 103 r = sc->sc_res + sc->sc_rescnt++; 104 r->r_type = type; 105 r->r_rid = rid; 106 r->r_d.res = res; 107 return (0); 108 } 109 110 #ifdef notyet 111 static int 112 proto_intr(void *arg) 113 { 114 struct proto_softc *sc = arg; 115 116 /* XXX TODO */ 117 return (FILTER_HANDLED); 118 } 119 #endif 120 121 int 122 proto_probe(device_t dev, const char *prefix, char ***devnamesp) 123 { 124 char **devnames = *devnamesp; 125 const char *dn, *ep, *ev; 126 size_t pfxlen; 127 int idx, names; 128 129 if (devnames == NULL) { 130 pfxlen = strlen(prefix); 131 names = 1; /* NULL pointer */ 132 ev = kern_getenv("hw.proto.attach"); 133 if (ev != NULL) { 134 dn = ev; 135 while (*dn != '\0') { 136 ep = dn; 137 while (*ep != ',' && *ep != '\0') 138 ep++; 139 if ((ep - dn) > pfxlen && 140 strncmp(dn, prefix, pfxlen) == 0) 141 names++; 142 dn = (*ep == ',') ? ep + 1 : ep; 143 } 144 } 145 devnames = malloc(names * sizeof(caddr_t), M_DEVBUF, 146 M_WAITOK | M_ZERO); 147 *devnamesp = devnames; 148 if (ev != NULL) { 149 dn = ev; 150 idx = 0; 151 while (*dn != '\0') { 152 ep = dn; 153 while (*ep != ',' && *ep != '\0') 154 ep++; 155 if ((ep - dn) > pfxlen && 156 strncmp(dn, prefix, pfxlen) == 0) { 157 devnames[idx] = malloc(ep - dn + 1, 158 M_DEVBUF, M_WAITOK | M_ZERO); 159 memcpy(devnames[idx], dn, ep - dn); 160 idx++; 161 } 162 dn = (*ep == ',') ? ep + 1 : ep; 163 } 164 freeenv(__DECONST(char *, ev)); 165 } 166 } 167 168 dn = device_get_desc(dev); 169 while (*devnames != NULL) { 170 if (strcmp(dn, *devnames) == 0) 171 return (BUS_PROBE_SPECIFIC); 172 devnames++; 173 } 174 return (BUS_PROBE_HOOVER); 175 } 176 177 int 178 proto_attach(device_t dev) 179 { 180 struct proto_softc *sc; 181 struct proto_res *r; 182 u_int res; 183 184 sc = device_get_softc(dev); 185 sc->sc_dev = dev; 186 mtx_init(&sc->sc_mtx, "proto-softc", NULL, MTX_DEF); 187 188 for (res = 0; res < sc->sc_rescnt; res++) { 189 r = sc->sc_res + res; 190 switch (r->r_type) { 191 case SYS_RES_IRQ: 192 /* XXX TODO */ 193 break; 194 case SYS_RES_DRQ: 195 break; 196 case SYS_RES_MEMORY: 197 case SYS_RES_IOPORT: 198 r->r_size = rman_get_size(r->r_d.res); 199 r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0600, 200 "proto/%s/%02x.%s", device_get_desc(dev), r->r_rid, 201 (r->r_type == SYS_RES_IOPORT) ? "io" : "mem"); 202 r->r_u.cdev->si_drv1 = sc; 203 r->r_u.cdev->si_drv2 = r; 204 break; 205 case PROTO_RES_PCICFG: 206 r->r_size = 4096; 207 r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0600, 208 "proto/%s/pcicfg", device_get_desc(dev)); 209 r->r_u.cdev->si_drv1 = sc; 210 r->r_u.cdev->si_drv2 = r; 211 break; 212 case PROTO_RES_BUSDMA: 213 r->r_d.busdma = proto_busdma_attach(sc); 214 r->r_size = 0; /* no read(2) nor write(2) */ 215 r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0600, 216 "proto/%s/busdma", device_get_desc(dev)); 217 r->r_u.cdev->si_drv1 = sc; 218 r->r_u.cdev->si_drv2 = r; 219 break; 220 } 221 } 222 return (0); 223 } 224 225 int 226 proto_detach(device_t dev) 227 { 228 struct proto_softc *sc; 229 struct proto_res *r; 230 u_int res; 231 232 sc = device_get_softc(dev); 233 234 mtx_lock(&sc->sc_mtx); 235 if (sc->sc_opencnt == 0) 236 sc->sc_opencnt = -1; 237 mtx_unlock(&sc->sc_mtx); 238 if (sc->sc_opencnt > 0) 239 return (EBUSY); 240 241 for (res = 0; res < sc->sc_rescnt; res++) { 242 r = sc->sc_res + res; 243 244 switch (r->r_type) { 245 case SYS_RES_IRQ: 246 /* XXX TODO */ 247 bus_release_resource(dev, r->r_type, r->r_rid, 248 r->r_d.res); 249 break; 250 case SYS_RES_DRQ: 251 bus_release_resource(dev, r->r_type, r->r_rid, 252 r->r_d.res); 253 break; 254 case SYS_RES_MEMORY: 255 case SYS_RES_IOPORT: 256 destroy_dev(r->r_u.cdev); 257 bus_release_resource(dev, r->r_type, r->r_rid, 258 r->r_d.res); 259 break; 260 case PROTO_RES_PCICFG: 261 destroy_dev(r->r_u.cdev); 262 break; 263 case PROTO_RES_BUSDMA: 264 destroy_dev(r->r_u.cdev); 265 proto_busdma_detach(sc, r->r_d.busdma); 266 break; 267 } 268 r->r_type = PROTO_RES_UNUSED; 269 } 270 mtx_lock(&sc->sc_mtx); 271 sc->sc_rescnt = 0; 272 sc->sc_opencnt = 0; 273 mtx_unlock(&sc->sc_mtx); 274 mtx_destroy(&sc->sc_mtx); 275 return (0); 276 } 277 278 /* 279 * Device functions 280 */ 281 282 static int 283 proto_open(struct cdev *cdev, int oflags, int devtype, struct thread *td) 284 { 285 struct proto_res *r; 286 struct proto_softc *sc; 287 int error; 288 289 sc = cdev->si_drv1; 290 mtx_lock(&sc->sc_mtx); 291 if (sc->sc_opencnt >= 0) { 292 r = cdev->si_drv2; 293 if (!r->r_opened) { 294 r->r_opened = 1; 295 sc->sc_opencnt++; 296 error = 0; 297 } else 298 error = EBUSY; 299 } else 300 error = ENXIO; 301 mtx_unlock(&sc->sc_mtx); 302 return (error); 303 } 304 305 static int 306 proto_close(struct cdev *cdev, int fflag, int devtype, struct thread *td) 307 { 308 struct proto_res *r; 309 struct proto_softc *sc; 310 int error; 311 312 sc = cdev->si_drv1; 313 mtx_lock(&sc->sc_mtx); 314 if (sc->sc_opencnt > 0) { 315 r = cdev->si_drv2; 316 if (r->r_opened) { 317 if (r->r_type == PROTO_RES_BUSDMA) 318 proto_busdma_cleanup(sc, r->r_d.busdma); 319 r->r_opened = 0; 320 sc->sc_opencnt--; 321 error = 0; 322 } else 323 error = ENXIO; 324 } else 325 error = ENXIO; 326 mtx_unlock(&sc->sc_mtx); 327 return (error); 328 } 329 330 static int 331 proto_read(struct cdev *cdev, struct uio *uio, int ioflag) 332 { 333 union { 334 uint8_t x1[8]; 335 uint16_t x2[4]; 336 uint32_t x4[2]; 337 uint64_t x8[1]; 338 } buf; 339 struct proto_softc *sc; 340 struct proto_res *r; 341 device_t dev; 342 off_t ofs; 343 u_long width; 344 int error; 345 346 sc = cdev->si_drv1; 347 dev = sc->sc_dev; 348 r = cdev->si_drv2; 349 350 width = uio->uio_resid; 351 if (width < 1 || width > 8 || bitcount16(width) > 1) 352 return (EIO); 353 ofs = uio->uio_offset; 354 if (ofs + width > r->r_size) 355 return (EIO); 356 357 switch (width) { 358 case 1: 359 buf.x1[0] = (r->r_type == PROTO_RES_PCICFG) ? 360 pci_read_config(dev, ofs, 1) : bus_read_1(r->r_d.res, ofs); 361 break; 362 case 2: 363 buf.x2[0] = (r->r_type == PROTO_RES_PCICFG) ? 364 pci_read_config(dev, ofs, 2) : bus_read_2(r->r_d.res, ofs); 365 break; 366 case 4: 367 buf.x4[0] = (r->r_type == PROTO_RES_PCICFG) ? 368 pci_read_config(dev, ofs, 4) : bus_read_4(r->r_d.res, ofs); 369 break; 370 #ifndef __i386__ 371 case 8: 372 if (r->r_type == PROTO_RES_PCICFG) 373 return (EINVAL); 374 buf.x8[0] = bus_read_8(r->r_d.res, ofs); 375 break; 376 #endif 377 default: 378 return (EIO); 379 } 380 381 error = uiomove(&buf, width, uio); 382 return (error); 383 } 384 385 static int 386 proto_write(struct cdev *cdev, struct uio *uio, int ioflag) 387 { 388 union { 389 uint8_t x1[8]; 390 uint16_t x2[4]; 391 uint32_t x4[2]; 392 uint64_t x8[1]; 393 } buf; 394 struct proto_softc *sc; 395 struct proto_res *r; 396 device_t dev; 397 off_t ofs; 398 u_long width; 399 int error; 400 401 sc = cdev->si_drv1; 402 dev = sc->sc_dev; 403 r = cdev->si_drv2; 404 405 width = uio->uio_resid; 406 if (width < 1 || width > 8 || bitcount16(width) > 1) 407 return (EIO); 408 ofs = uio->uio_offset; 409 if (ofs + width > r->r_size) 410 return (EIO); 411 412 error = uiomove(&buf, width, uio); 413 if (error) 414 return (error); 415 416 switch (width) { 417 case 1: 418 if (r->r_type == PROTO_RES_PCICFG) 419 pci_write_config(dev, ofs, buf.x1[0], 1); 420 else 421 bus_write_1(r->r_d.res, ofs, buf.x1[0]); 422 break; 423 case 2: 424 if (r->r_type == PROTO_RES_PCICFG) 425 pci_write_config(dev, ofs, buf.x2[0], 2); 426 else 427 bus_write_2(r->r_d.res, ofs, buf.x2[0]); 428 break; 429 case 4: 430 if (r->r_type == PROTO_RES_PCICFG) 431 pci_write_config(dev, ofs, buf.x4[0], 4); 432 else 433 bus_write_4(r->r_d.res, ofs, buf.x4[0]); 434 break; 435 #ifndef __i386__ 436 case 8: 437 if (r->r_type == PROTO_RES_PCICFG) 438 return (EINVAL); 439 bus_write_8(r->r_d.res, ofs, buf.x8[0]); 440 break; 441 #endif 442 default: 443 return (EIO); 444 } 445 446 return (0); 447 } 448 449 static int 450 proto_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, 451 struct thread *td) 452 { 453 struct proto_ioc_region *region; 454 struct proto_ioc_busdma *busdma; 455 struct proto_res *r; 456 struct proto_softc *sc; 457 int error; 458 459 sc = cdev->si_drv1; 460 r = cdev->si_drv2; 461 462 error = 0; 463 switch (cmd) { 464 case PROTO_IOC_REGION: 465 if (r->r_type == PROTO_RES_BUSDMA) { 466 error = EINVAL; 467 break; 468 } 469 region = (struct proto_ioc_region *)data; 470 region->size = r->r_size; 471 if (r->r_type == PROTO_RES_PCICFG) 472 region->address = 0; 473 else 474 region->address = rman_get_start(r->r_d.res); 475 break; 476 case PROTO_IOC_BUSDMA: 477 if (r->r_type != PROTO_RES_BUSDMA) { 478 error = EINVAL; 479 break; 480 } 481 busdma = (struct proto_ioc_busdma *)data; 482 error = proto_busdma_ioctl(sc, r->r_d.busdma, busdma, td); 483 break; 484 default: 485 error = ENOIOCTL; 486 break; 487 } 488 return (error); 489 } 490 491 static int 492 proto_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr, 493 int prot, vm_memattr_t *memattr) 494 { 495 struct proto_res *r; 496 497 if (offset & PAGE_MASK) 498 return (EINVAL); 499 if (prot & PROT_EXEC) 500 return (EACCES); 501 502 r = cdev->si_drv2; 503 504 switch (r->r_type) { 505 case SYS_RES_MEMORY: 506 if (offset >= r->r_size) 507 return (EINVAL); 508 *paddr = rman_get_start(r->r_d.res) + offset; 509 *memattr = VM_MEMATTR_UNCACHEABLE; 510 break; 511 case PROTO_RES_BUSDMA: 512 if (!proto_busdma_mmap_allowed(r->r_d.busdma, offset)) 513 return (EINVAL); 514 *paddr = offset; 515 break; 516 default: 517 return (ENXIO); 518 } 519 return (0); 520 } 521