1 /*- 2 * Copyright (c) 2014, 2015 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 devclass_t proto_devclass; 70 char proto_driver_name[] = "proto"; 71 72 static d_open_t proto_open; 73 static d_close_t proto_close; 74 static d_read_t proto_read; 75 static d_write_t proto_write; 76 static d_ioctl_t proto_ioctl; 77 static d_mmap_t proto_mmap; 78 79 struct cdevsw proto_devsw = { 80 .d_version = D_VERSION, 81 .d_flags = 0, 82 .d_name = proto_driver_name, 83 .d_open = proto_open, 84 .d_close = proto_close, 85 .d_read = proto_read, 86 .d_write = proto_write, 87 .d_ioctl = proto_ioctl, 88 .d_mmap = proto_mmap, 89 }; 90 91 static MALLOC_DEFINE(M_PROTO, "PROTO", "PROTO driver"); 92 93 int 94 proto_add_resource(struct proto_softc *sc, int type, int rid, 95 struct resource *res) 96 { 97 struct proto_res *r; 98 99 if (type == PROTO_RES_UNUSED) 100 return (EINVAL); 101 if (sc->sc_rescnt == PROTO_RES_MAX) 102 return (ENOSPC); 103 104 r = sc->sc_res + sc->sc_rescnt++; 105 r->r_type = type; 106 r->r_rid = rid; 107 r->r_d.res = res; 108 return (0); 109 } 110 111 #ifdef notyet 112 static int 113 proto_intr(void *arg) 114 { 115 struct proto_softc *sc = arg; 116 117 /* XXX TODO */ 118 return (FILTER_HANDLED); 119 } 120 #endif 121 122 int 123 proto_probe(device_t dev, const char *prefix, char ***devnamesp) 124 { 125 char **devnames = *devnamesp; 126 const char *dn, *ep, *ev; 127 size_t pfxlen; 128 int idx, names; 129 130 if (devnames == NULL) { 131 pfxlen = strlen(prefix); 132 names = 1; /* NULL pointer */ 133 ev = kern_getenv("hw.proto.attach"); 134 if (ev != NULL) { 135 dn = ev; 136 while (*dn != '\0') { 137 ep = dn; 138 while (*ep != ',' && *ep != '\0') 139 ep++; 140 if ((ep - dn) > pfxlen && 141 strncmp(dn, prefix, pfxlen) == 0) 142 names++; 143 dn = (*ep == ',') ? ep + 1 : ep; 144 } 145 } 146 devnames = malloc(names * sizeof(caddr_t), M_DEVBUF, 147 M_WAITOK | M_ZERO); 148 *devnamesp = devnames; 149 if (ev != NULL) { 150 dn = ev; 151 idx = 0; 152 while (*dn != '\0') { 153 ep = dn; 154 while (*ep != ',' && *ep != '\0') 155 ep++; 156 if ((ep - dn) > pfxlen && 157 strncmp(dn, prefix, pfxlen) == 0) { 158 devnames[idx] = malloc(ep - dn + 1, 159 M_DEVBUF, M_WAITOK | M_ZERO); 160 memcpy(devnames[idx], dn, ep - dn); 161 idx++; 162 } 163 dn = (*ep == ',') ? ep + 1 : ep; 164 } 165 freeenv(__DECONST(char *, ev)); 166 } 167 } 168 169 dn = device_get_desc(dev); 170 while (*devnames != NULL) { 171 if (strcmp(dn, *devnames) == 0) 172 return (BUS_PROBE_SPECIFIC); 173 devnames++; 174 } 175 return (BUS_PROBE_HOOVER); 176 } 177 178 int 179 proto_attach(device_t dev) 180 { 181 struct proto_softc *sc; 182 struct proto_res *r; 183 u_int res; 184 185 sc = device_get_softc(dev); 186 sc->sc_dev = dev; 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, 0666, 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, 0666, 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, 0666, 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 /* Don't detach if we have open device files. */ 235 for (res = 0; res < sc->sc_rescnt; res++) { 236 r = sc->sc_res + res; 237 if (r->r_opened) 238 return (EBUSY); 239 } 240 241 for (res = 0; res < sc->sc_rescnt; res++) { 242 r = sc->sc_res + res; 243 switch (r->r_type) { 244 case SYS_RES_IRQ: 245 /* XXX TODO */ 246 bus_release_resource(dev, r->r_type, r->r_rid, 247 r->r_d.res); 248 break; 249 case SYS_RES_DRQ: 250 bus_release_resource(dev, r->r_type, r->r_rid, 251 r->r_d.res); 252 break; 253 case SYS_RES_MEMORY: 254 case SYS_RES_IOPORT: 255 bus_release_resource(dev, r->r_type, r->r_rid, 256 r->r_d.res); 257 destroy_dev(r->r_u.cdev); 258 break; 259 case PROTO_RES_PCICFG: 260 destroy_dev(r->r_u.cdev); 261 break; 262 case PROTO_RES_BUSDMA: 263 proto_busdma_detach(sc, r->r_d.busdma); 264 destroy_dev(r->r_u.cdev); 265 break; 266 } 267 r->r_type = PROTO_RES_UNUSED; 268 } 269 sc->sc_rescnt = 0; 270 return (0); 271 } 272 273 /* 274 * Device functions 275 */ 276 277 static int 278 proto_open(struct cdev *cdev, int oflags, int devtype, struct thread *td) 279 { 280 struct proto_res *r; 281 282 r = cdev->si_drv2; 283 if (!atomic_cmpset_acq_ptr(&r->r_opened, 0UL, (uintptr_t)td->td_proc)) 284 return (EBUSY); 285 return (0); 286 } 287 288 static int 289 proto_close(struct cdev *cdev, int fflag, int devtype, struct thread *td) 290 { 291 struct proto_res *r; 292 struct proto_softc *sc; 293 294 sc = cdev->si_drv1; 295 r = cdev->si_drv2; 296 if (!atomic_cmpset_acq_ptr(&r->r_opened, (uintptr_t)td->td_proc, 0UL)) 297 return (ENXIO); 298 if (r->r_type == PROTO_RES_BUSDMA) 299 proto_busdma_cleanup(sc, r->r_d.busdma); 300 return (0); 301 } 302 303 static int 304 proto_read(struct cdev *cdev, struct uio *uio, int ioflag) 305 { 306 union { 307 uint8_t x1[8]; 308 uint16_t x2[4]; 309 uint32_t x4[2]; 310 uint64_t x8[1]; 311 } buf; 312 struct proto_softc *sc; 313 struct proto_res *r; 314 device_t dev; 315 off_t ofs; 316 u_long width; 317 int error; 318 319 sc = cdev->si_drv1; 320 dev = sc->sc_dev; 321 r = cdev->si_drv2; 322 323 width = uio->uio_resid; 324 if (width < 1 || width > 8 || bitcount16(width) > 1) 325 return (EIO); 326 ofs = uio->uio_offset; 327 if (ofs + width > r->r_size) 328 return (EIO); 329 330 switch (width) { 331 case 1: 332 buf.x1[0] = (r->r_type == PROTO_RES_PCICFG) ? 333 pci_read_config(dev, ofs, 1) : bus_read_1(r->r_d.res, ofs); 334 break; 335 case 2: 336 buf.x2[0] = (r->r_type == PROTO_RES_PCICFG) ? 337 pci_read_config(dev, ofs, 2) : bus_read_2(r->r_d.res, ofs); 338 break; 339 case 4: 340 buf.x4[0] = (r->r_type == PROTO_RES_PCICFG) ? 341 pci_read_config(dev, ofs, 4) : bus_read_4(r->r_d.res, ofs); 342 break; 343 #ifndef __i386__ 344 case 8: 345 if (r->r_type == PROTO_RES_PCICFG) 346 return (EINVAL); 347 buf.x8[0] = bus_read_8(r->r_d.res, ofs); 348 break; 349 #endif 350 default: 351 return (EIO); 352 } 353 354 error = uiomove(&buf, width, uio); 355 return (error); 356 } 357 358 static int 359 proto_write(struct cdev *cdev, struct uio *uio, int ioflag) 360 { 361 union { 362 uint8_t x1[8]; 363 uint16_t x2[4]; 364 uint32_t x4[2]; 365 uint64_t x8[1]; 366 } buf; 367 struct proto_softc *sc; 368 struct proto_res *r; 369 device_t dev; 370 off_t ofs; 371 u_long width; 372 int error; 373 374 sc = cdev->si_drv1; 375 dev = sc->sc_dev; 376 r = cdev->si_drv2; 377 378 width = uio->uio_resid; 379 if (width < 1 || width > 8 || bitcount16(width) > 1) 380 return (EIO); 381 ofs = uio->uio_offset; 382 if (ofs + width > r->r_size) 383 return (EIO); 384 385 error = uiomove(&buf, width, uio); 386 if (error) 387 return (error); 388 389 switch (width) { 390 case 1: 391 if (r->r_type == PROTO_RES_PCICFG) 392 pci_write_config(dev, ofs, buf.x1[0], 1); 393 else 394 bus_write_1(r->r_d.res, ofs, buf.x1[0]); 395 break; 396 case 2: 397 if (r->r_type == PROTO_RES_PCICFG) 398 pci_write_config(dev, ofs, buf.x2[0], 2); 399 else 400 bus_write_2(r->r_d.res, ofs, buf.x2[0]); 401 break; 402 case 4: 403 if (r->r_type == PROTO_RES_PCICFG) 404 pci_write_config(dev, ofs, buf.x4[0], 4); 405 else 406 bus_write_4(r->r_d.res, ofs, buf.x4[0]); 407 break; 408 #ifndef __i386__ 409 case 8: 410 if (r->r_type == PROTO_RES_PCICFG) 411 return (EINVAL); 412 bus_write_8(r->r_d.res, ofs, buf.x8[0]); 413 break; 414 #endif 415 default: 416 return (EIO); 417 } 418 419 return (0); 420 } 421 422 static int 423 proto_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, 424 struct thread *td) 425 { 426 struct proto_ioc_region *region; 427 struct proto_ioc_busdma *busdma; 428 struct proto_res *r; 429 struct proto_softc *sc; 430 int error; 431 432 sc = cdev->si_drv1; 433 r = cdev->si_drv2; 434 435 error = 0; 436 switch (cmd) { 437 case PROTO_IOC_REGION: 438 if (r->r_type == PROTO_RES_BUSDMA) { 439 error = EINVAL; 440 break; 441 } 442 region = (struct proto_ioc_region *)data; 443 region->size = r->r_size; 444 if (r->r_type == PROTO_RES_PCICFG) 445 region->address = 0; 446 else 447 region->address = rman_get_start(r->r_d.res); 448 break; 449 case PROTO_IOC_BUSDMA: 450 if (r->r_type != PROTO_RES_BUSDMA) { 451 error = EINVAL; 452 break; 453 } 454 busdma = (struct proto_ioc_busdma *)data; 455 error = proto_busdma_ioctl(sc, r->r_d.busdma, busdma, td); 456 break; 457 default: 458 error = ENOIOCTL; 459 break; 460 } 461 return (error); 462 } 463 464 static int 465 proto_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr, 466 int prot, vm_memattr_t *memattr) 467 { 468 struct proto_res *r; 469 470 if (offset & PAGE_MASK) 471 return (EINVAL); 472 if (prot & PROT_EXEC) 473 return (EACCES); 474 475 r = cdev->si_drv2; 476 477 switch (r->r_type) { 478 case SYS_RES_MEMORY: 479 if (offset >= r->r_size) 480 return (EINVAL); 481 *paddr = rman_get_start(r->r_d.res) + offset; 482 #ifndef __sparc64__ 483 *memattr = VM_MEMATTR_UNCACHEABLE; 484 #endif 485 break; 486 case PROTO_RES_BUSDMA: 487 if (!proto_busdma_mmap_allowed(r->r_d.busdma, offset)) 488 return (EINVAL); 489 *paddr = offset; 490 break; 491 default: 492 return (ENXIO); 493 } 494 return (0); 495 } 496