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