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