1 /*- 2 * Copyright (c) 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 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 __FBSDID("$FreeBSD$"); 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <machine/bus.h> 32 #include <machine/bus_dma.h> 33 #include <machine/resource.h> 34 #include <sys/bus.h> 35 #include <sys/conf.h> 36 #include <sys/kernel.h> 37 #include <sys/malloc.h> 38 #include <sys/module.h> 39 #include <sys/proc.h> 40 #include <sys/queue.h> 41 #include <sys/rman.h> 42 #include <sys/sbuf.h> 43 #include <sys/uio.h> 44 #include <vm/vm.h> 45 #include <vm/pmap.h> 46 #include <vm/vm_map.h> 47 48 #include <dev/proto/proto.h> 49 #include <dev/proto/proto_dev.h> 50 #include <dev/proto/proto_busdma.h> 51 52 MALLOC_DEFINE(M_PROTO_BUSDMA, "proto_busdma", "DMA management data"); 53 54 struct proto_callback_bundle { 55 struct proto_busdma *busdma; 56 struct proto_md *md; 57 struct proto_ioc_busdma *ioc; 58 }; 59 60 static int 61 proto_busdma_tag_create(struct proto_busdma *busdma, struct proto_tag *parent, 62 struct proto_ioc_busdma *ioc) 63 { 64 struct proto_tag *tag; 65 66 /* 67 * If nsegs is 1, ignore maxsegsz. What this means is that if we have 68 * just 1 segment, then maxsz should be equal to maxsegsz. To keep it 69 * simple for us, limit maxsegsz to maxsz in any case. 70 */ 71 if (ioc->u.tag.maxsegsz > ioc->u.tag.maxsz || ioc->u.tag.nsegs == 1) 72 ioc->u.tag.maxsegsz = ioc->u.tag.maxsz; 73 74 /* A bndry of 0 really means ~0, or no boundary. */ 75 if (ioc->u.tag.bndry == 0) 76 ioc->u.tag.bndry = ~0U; 77 78 tag = malloc(sizeof(*tag), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); 79 if (parent != NULL) { 80 tag->parent = parent; 81 LIST_INSERT_HEAD(&parent->children, tag, peers); 82 tag->align = MAX(ioc->u.tag.align, parent->align); 83 tag->bndry = MIN(ioc->u.tag.bndry, parent->bndry); 84 tag->maxaddr = MIN(ioc->u.tag.maxaddr, parent->maxaddr); 85 tag->maxsz = MIN(ioc->u.tag.maxsz, parent->maxsz); 86 tag->maxsegsz = MIN(ioc->u.tag.maxsegsz, parent->maxsegsz); 87 tag->nsegs = MIN(ioc->u.tag.nsegs, parent->nsegs); 88 tag->datarate = MIN(ioc->u.tag.datarate, parent->datarate); 89 /* Write constraints back */ 90 ioc->u.tag.align = tag->align; 91 ioc->u.tag.bndry = tag->bndry; 92 ioc->u.tag.maxaddr = tag->maxaddr; 93 ioc->u.tag.maxsz = tag->maxsz; 94 ioc->u.tag.maxsegsz = tag->maxsegsz; 95 ioc->u.tag.nsegs = tag->nsegs; 96 ioc->u.tag.datarate = tag->datarate; 97 } else { 98 tag->align = ioc->u.tag.align; 99 tag->bndry = ioc->u.tag.bndry; 100 tag->maxaddr = ioc->u.tag.maxaddr; 101 tag->maxsz = ioc->u.tag.maxsz; 102 tag->maxsegsz = ioc->u.tag.maxsegsz; 103 tag->nsegs = ioc->u.tag.nsegs; 104 tag->datarate = ioc->u.tag.datarate; 105 } 106 LIST_INSERT_HEAD(&busdma->tags, tag, tags); 107 ioc->result = (uintptr_t)(void *)tag; 108 return (0); 109 } 110 111 static int 112 proto_busdma_tag_destroy(struct proto_busdma *busdma, struct proto_tag *tag) 113 { 114 115 if (!LIST_EMPTY(&tag->mds)) 116 return (EBUSY); 117 if (!LIST_EMPTY(&tag->children)) 118 return (EBUSY); 119 120 if (tag->parent != NULL) { 121 LIST_REMOVE(tag, peers); 122 tag->parent = NULL; 123 } 124 LIST_REMOVE(tag, tags); 125 free(tag, M_PROTO_BUSDMA); 126 return (0); 127 } 128 129 static struct proto_tag * 130 proto_busdma_tag_lookup(struct proto_busdma *busdma, u_long key) 131 { 132 struct proto_tag *tag; 133 134 LIST_FOREACH(tag, &busdma->tags, tags) { 135 if ((void *)tag == (void *)key) 136 return (tag); 137 } 138 return (NULL); 139 } 140 141 static int 142 proto_busdma_md_destroy_internal(struct proto_busdma *busdma, 143 struct proto_md *md) 144 { 145 146 LIST_REMOVE(md, mds); 147 LIST_REMOVE(md, peers); 148 if (md->physaddr) 149 bus_dmamap_unload(md->bd_tag, md->bd_map); 150 if (md->virtaddr != NULL) 151 bus_dmamem_free(md->bd_tag, md->virtaddr, md->bd_map); 152 else 153 bus_dmamap_destroy(md->bd_tag, md->bd_map); 154 bus_dma_tag_destroy(md->bd_tag); 155 free(md, M_PROTO_BUSDMA); 156 return (0); 157 } 158 159 static void 160 proto_busdma_mem_alloc_callback(void *arg, bus_dma_segment_t *segs, int nseg, 161 int error) 162 { 163 struct proto_callback_bundle *pcb = arg; 164 165 pcb->ioc->u.md.bus_nsegs = nseg; 166 pcb->ioc->u.md.bus_addr = segs[0].ds_addr; 167 } 168 169 static int 170 proto_busdma_mem_alloc(struct proto_busdma *busdma, struct proto_tag *tag, 171 struct proto_ioc_busdma *ioc) 172 { 173 struct proto_callback_bundle pcb; 174 struct proto_md *md; 175 int error; 176 177 md = malloc(sizeof(*md), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); 178 md->tag = tag; 179 180 error = bus_dma_tag_create(busdma->bd_roottag, tag->align, tag->bndry, 181 tag->maxaddr, BUS_SPACE_MAXADDR, NULL, NULL, tag->maxsz, 182 tag->nsegs, tag->maxsegsz, 0, NULL, NULL, &md->bd_tag); 183 if (error) { 184 free(md, M_PROTO_BUSDMA); 185 return (error); 186 } 187 error = bus_dmamem_alloc(md->bd_tag, &md->virtaddr, 0, &md->bd_map); 188 if (error) { 189 bus_dma_tag_destroy(md->bd_tag); 190 free(md, M_PROTO_BUSDMA); 191 return (error); 192 } 193 md->physaddr = pmap_kextract((uintptr_t)(md->virtaddr)); 194 pcb.busdma = busdma; 195 pcb.md = md; 196 pcb.ioc = ioc; 197 error = bus_dmamap_load(md->bd_tag, md->bd_map, md->virtaddr, 198 tag->maxsz, proto_busdma_mem_alloc_callback, &pcb, BUS_DMA_NOWAIT); 199 if (error) { 200 bus_dmamem_free(md->bd_tag, md->virtaddr, md->bd_map); 201 bus_dma_tag_destroy(md->bd_tag); 202 free(md, M_PROTO_BUSDMA); 203 return (error); 204 } 205 LIST_INSERT_HEAD(&tag->mds, md, peers); 206 LIST_INSERT_HEAD(&busdma->mds, md, mds); 207 ioc->u.md.virt_addr = (uintptr_t)md->virtaddr; 208 ioc->u.md.virt_size = tag->maxsz; 209 ioc->u.md.phys_nsegs = 1; 210 ioc->u.md.phys_addr = md->physaddr; 211 ioc->result = (uintptr_t)(void *)md; 212 return (0); 213 } 214 215 static int 216 proto_busdma_mem_free(struct proto_busdma *busdma, struct proto_md *md) 217 { 218 219 if (md->virtaddr == NULL) 220 return (ENXIO); 221 return (proto_busdma_md_destroy_internal(busdma, md)); 222 } 223 224 static int 225 proto_busdma_md_create(struct proto_busdma *busdma, struct proto_tag *tag, 226 struct proto_ioc_busdma *ioc) 227 { 228 struct proto_md *md; 229 int error; 230 231 md = malloc(sizeof(*md), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); 232 md->tag = tag; 233 234 error = bus_dma_tag_create(busdma->bd_roottag, tag->align, tag->bndry, 235 tag->maxaddr, BUS_SPACE_MAXADDR, NULL, NULL, tag->maxsz, 236 tag->nsegs, tag->maxsegsz, 0, NULL, NULL, &md->bd_tag); 237 if (error) { 238 free(md, M_PROTO_BUSDMA); 239 return (error); 240 } 241 error = bus_dmamap_create(md->bd_tag, 0, &md->bd_map); 242 if (error) { 243 bus_dma_tag_destroy(md->bd_tag); 244 free(md, M_PROTO_BUSDMA); 245 return (error); 246 } 247 248 LIST_INSERT_HEAD(&tag->mds, md, peers); 249 LIST_INSERT_HEAD(&busdma->mds, md, mds); 250 ioc->result = (uintptr_t)(void *)md; 251 return (0); 252 } 253 254 static int 255 proto_busdma_md_destroy(struct proto_busdma *busdma, struct proto_md *md) 256 { 257 258 if (md->virtaddr != NULL) 259 return (ENXIO); 260 return (proto_busdma_md_destroy_internal(busdma, md)); 261 } 262 263 static void 264 proto_busdma_md_load_callback(void *arg, bus_dma_segment_t *segs, int nseg, 265 bus_size_t sz, int error) 266 { 267 struct proto_callback_bundle *pcb = arg; 268 269 pcb->ioc->u.md.bus_nsegs = nseg; 270 pcb->ioc->u.md.bus_addr = segs[0].ds_addr; 271 } 272 273 static int 274 proto_busdma_md_load(struct proto_busdma *busdma, struct proto_md *md, 275 struct proto_ioc_busdma *ioc, struct thread *td) 276 { 277 struct proto_callback_bundle pcb; 278 struct iovec iov; 279 struct uio uio; 280 pmap_t pmap; 281 int error; 282 283 iov.iov_base = (void *)(uintptr_t)ioc->u.md.virt_addr; 284 iov.iov_len = ioc->u.md.virt_size; 285 uio.uio_iov = &iov; 286 uio.uio_iovcnt = 1; 287 uio.uio_offset = 0; 288 uio.uio_resid = iov.iov_len; 289 uio.uio_segflg = UIO_USERSPACE; 290 uio.uio_rw = UIO_READ; 291 uio.uio_td = td; 292 293 pcb.busdma = busdma; 294 pcb.md = md; 295 pcb.ioc = ioc; 296 error = bus_dmamap_load_uio(md->bd_tag, md->bd_map, &uio, 297 proto_busdma_md_load_callback, &pcb, BUS_DMA_NOWAIT); 298 if (error) 299 return (error); 300 301 /* XXX determine *all* physical memory segments */ 302 pmap = vmspace_pmap(td->td_proc->p_vmspace); 303 md->physaddr = pmap_extract(pmap, ioc->u.md.virt_addr); 304 ioc->u.md.phys_nsegs = 1; /* XXX */ 305 ioc->u.md.phys_addr = md->physaddr; 306 return (0); 307 } 308 309 static int 310 proto_busdma_md_unload(struct proto_busdma *busdma, struct proto_md *md) 311 { 312 313 if (!md->physaddr) 314 return (ENXIO); 315 bus_dmamap_unload(md->bd_tag, md->bd_map); 316 md->physaddr = 0; 317 return (0); 318 } 319 320 static int 321 proto_busdma_sync(struct proto_busdma *busdma, struct proto_md *md, 322 struct proto_ioc_busdma *ioc) 323 { 324 325 if (!md->physaddr) 326 return (ENXIO); 327 bus_dmamap_sync(md->bd_tag, md->bd_map, ioc->u.sync.op); 328 return (0); 329 } 330 331 static struct proto_md * 332 proto_busdma_md_lookup(struct proto_busdma *busdma, u_long key) 333 { 334 struct proto_md *md; 335 336 LIST_FOREACH(md, &busdma->mds, mds) { 337 if ((void *)md == (void *)key) 338 return (md); 339 } 340 return (NULL); 341 } 342 343 struct proto_busdma * 344 proto_busdma_attach(struct proto_softc *sc) 345 { 346 struct proto_busdma *busdma; 347 348 busdma = malloc(sizeof(*busdma), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); 349 return (busdma); 350 } 351 352 int 353 proto_busdma_detach(struct proto_softc *sc, struct proto_busdma *busdma) 354 { 355 356 proto_busdma_cleanup(sc, busdma); 357 free(busdma, M_PROTO_BUSDMA); 358 return (0); 359 } 360 361 int 362 proto_busdma_cleanup(struct proto_softc *sc, struct proto_busdma *busdma) 363 { 364 struct proto_md *md, *md1; 365 struct proto_tag *tag, *tag1; 366 367 LIST_FOREACH_SAFE(md, &busdma->mds, mds, md1) 368 proto_busdma_md_destroy_internal(busdma, md); 369 LIST_FOREACH_SAFE(tag, &busdma->tags, tags, tag1) 370 proto_busdma_tag_destroy(busdma, tag); 371 return (0); 372 } 373 374 int 375 proto_busdma_ioctl(struct proto_softc *sc, struct proto_busdma *busdma, 376 struct proto_ioc_busdma *ioc, struct thread *td) 377 { 378 struct proto_tag *tag; 379 struct proto_md *md; 380 int error; 381 382 error = 0; 383 switch (ioc->request) { 384 case PROTO_IOC_BUSDMA_TAG_CREATE: 385 busdma->bd_roottag = bus_get_dma_tag(sc->sc_dev); 386 error = proto_busdma_tag_create(busdma, NULL, ioc); 387 break; 388 case PROTO_IOC_BUSDMA_TAG_DERIVE: 389 tag = proto_busdma_tag_lookup(busdma, ioc->key); 390 if (tag == NULL) { 391 error = EINVAL; 392 break; 393 } 394 error = proto_busdma_tag_create(busdma, tag, ioc); 395 break; 396 case PROTO_IOC_BUSDMA_TAG_DESTROY: 397 tag = proto_busdma_tag_lookup(busdma, ioc->key); 398 if (tag == NULL) { 399 error = EINVAL; 400 break; 401 } 402 error = proto_busdma_tag_destroy(busdma, tag); 403 break; 404 case PROTO_IOC_BUSDMA_MEM_ALLOC: 405 tag = proto_busdma_tag_lookup(busdma, ioc->u.md.tag); 406 if (tag == NULL) { 407 error = EINVAL; 408 break; 409 } 410 error = proto_busdma_mem_alloc(busdma, tag, ioc); 411 break; 412 case PROTO_IOC_BUSDMA_MEM_FREE: 413 md = proto_busdma_md_lookup(busdma, ioc->key); 414 if (md == NULL) { 415 error = EINVAL; 416 break; 417 } 418 error = proto_busdma_mem_free(busdma, md); 419 break; 420 case PROTO_IOC_BUSDMA_MD_CREATE: 421 tag = proto_busdma_tag_lookup(busdma, ioc->u.md.tag); 422 if (tag == NULL) { 423 error = EINVAL; 424 break; 425 } 426 error = proto_busdma_md_create(busdma, tag, ioc); 427 break; 428 case PROTO_IOC_BUSDMA_MD_DESTROY: 429 md = proto_busdma_md_lookup(busdma, ioc->key); 430 if (md == NULL) { 431 error = EINVAL; 432 break; 433 } 434 error = proto_busdma_md_destroy(busdma, md); 435 break; 436 case PROTO_IOC_BUSDMA_MD_LOAD: 437 md = proto_busdma_md_lookup(busdma, ioc->key); 438 if (md == NULL) { 439 error = EINVAL; 440 break; 441 } 442 error = proto_busdma_md_load(busdma, md, ioc, td); 443 break; 444 case PROTO_IOC_BUSDMA_MD_UNLOAD: 445 md = proto_busdma_md_lookup(busdma, ioc->key); 446 if (md == NULL) { 447 error = EINVAL; 448 break; 449 } 450 error = proto_busdma_md_unload(busdma, md); 451 break; 452 case PROTO_IOC_BUSDMA_SYNC: 453 md = proto_busdma_md_lookup(busdma, ioc->key); 454 if (md == NULL) { 455 error = EINVAL; 456 break; 457 } 458 error = proto_busdma_sync(busdma, md, ioc); 459 break; 460 default: 461 error = EINVAL; 462 break; 463 } 464 return (error); 465 } 466 467 int 468 proto_busdma_mmap_allowed(struct proto_busdma *busdma, vm_paddr_t physaddr) 469 { 470 struct proto_md *md; 471 472 LIST_FOREACH(md, &busdma->mds, mds) { 473 if (physaddr >= trunc_page(md->physaddr) && 474 physaddr <= trunc_page(md->physaddr + md->tag->maxsz)) 475 return (1); 476 } 477 return (0); 478 } 479