xref: /freebsd/tools/bus_space/busdma.c (revision ca987d4641cdcd7f27e153db17c5bf064934faf5)
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  *
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/ioctl.h>
31 #include <sys/mman.h>
32 #include <assert.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <limits.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 
41 #include "busdma.h"
42 
43 #include "../../sys/dev/proto/proto_dev.h"
44 
45 struct obj {
46 	int	oid;
47 	u_int	type;
48 #define	OBJ_TYPE_NONE	0
49 #define	OBJ_TYPE_TAG	1
50 #define	OBJ_TYPE_MD	2
51 #define	OBJ_TYPE_SEG	3
52 	u_int	refcnt;
53 	int	fd;
54 	struct obj *parent;
55 	u_long	key;
56 	union {
57 		struct {
58 			unsigned long	align;
59 			unsigned long	bndry;
60 			unsigned long	maxaddr;
61 			unsigned long	maxsz;
62 			unsigned long	maxsegsz;
63 			unsigned long	nsegs;
64 			unsigned long	datarate;
65 		} tag;
66 		struct {
67 			struct obj	*seg[3];
68 			int		nsegs[3];
69 #define	BUSDMA_MD_BUS	0
70 #define	BUSDMA_MD_PHYS	1
71 #define	BUSDMA_MD_VIRT	2
72 		} md;
73 		struct {
74 			struct obj	*next;
75 			unsigned long	address;
76 			unsigned long	size;
77 		} seg;
78 	} u;
79 };
80 
81 static struct obj **oidtbl = NULL;
82 static int noids = 0;
83 
84 static struct obj *
85 obj_alloc(u_int type)
86 {
87 	struct obj **newtbl, *obj;
88 	int oid;
89 
90 	obj = calloc(1, sizeof(struct obj));
91 	obj->type = type;
92 
93 	for (oid = 0; oid < noids; oid++) {
94 		if (oidtbl[oid] == 0)
95 			break;
96 	}
97 	if (oid == noids) {
98 		newtbl = realloc(oidtbl, sizeof(struct obj *) * (noids + 1));
99 		if (newtbl == NULL) {
100 			free(obj);
101 			return (NULL);
102 		}
103 		oidtbl = newtbl;
104 		noids++;
105 	}
106 	oidtbl[oid] = obj;
107 	obj->oid = oid;
108 	return (obj);
109 }
110 
111 static int
112 obj_free(struct obj *obj)
113 {
114 
115 	oidtbl[obj->oid] = NULL;
116 	free(obj);
117 	return (0);
118 }
119 
120 static struct obj *
121 obj_lookup(int oid, u_int type)
122 {
123 	struct obj *obj;
124 
125 	if (oid < 0 || oid >= noids) {
126 		errno = EINVAL;
127 		return (NULL);
128 	}
129 	obj = oidtbl[oid];
130 	if (obj->refcnt == 0) {
131 		errno = ENXIO;
132 		return (NULL);
133 	}
134 	if (type != OBJ_TYPE_NONE && obj->type != type) {
135 		errno = ENODEV;
136 		return (NULL);
137 	}
138 	return (obj);
139 }
140 
141 static struct obj *
142 bd_tag_new(struct obj *ptag, int fd, u_long align, u_long bndry,
143     u_long maxaddr, u_long maxsz, u_int nsegs, u_long maxsegsz,
144     u_int datarate, u_int flags)
145 {
146 	struct proto_ioc_busdma ioc;
147 	struct obj *tag;
148 
149 	tag = obj_alloc(OBJ_TYPE_TAG);
150 	if (tag == NULL)
151 		return (NULL);
152 
153 	memset(&ioc, 0, sizeof(ioc));
154 	ioc.request = (ptag != NULL) ? PROTO_IOC_BUSDMA_TAG_DERIVE :
155 	    PROTO_IOC_BUSDMA_TAG_CREATE;
156 	ioc.key = (ptag != NULL) ? ptag->key : 0;
157 	ioc.u.tag.align = align;
158 	ioc.u.tag.bndry = bndry;
159 	ioc.u.tag.maxaddr = maxaddr;
160 	ioc.u.tag.maxsz = maxsz;
161 	ioc.u.tag.nsegs = nsegs;
162 	ioc.u.tag.maxsegsz = maxsegsz;
163 	ioc.u.tag.datarate = datarate;
164 	ioc.u.tag.flags = flags;
165 	if (ioctl(fd, PROTO_IOC_BUSDMA, &ioc) == -1) {
166 		obj_free(tag);
167 		return (NULL);
168 	}
169 	tag->refcnt = 1;
170 	tag->fd = fd;
171 	tag->parent = ptag;
172 	tag->key = ioc.result;
173 	tag->u.tag.align = ioc.u.tag.align;
174 	tag->u.tag.bndry = ioc.u.tag.bndry;
175 	tag->u.tag.maxaddr = ioc.u.tag.maxaddr;
176 	tag->u.tag.maxsz = ioc.u.tag.maxsz;
177 	tag->u.tag.maxsegsz = ioc.u.tag.maxsegsz;
178 	tag->u.tag.nsegs = ioc.u.tag.nsegs;
179 	tag->u.tag.datarate = ioc.u.tag.datarate;
180 	return (tag);
181 }
182 
183 int
184 bd_tag_create(const char *dev, u_long align, u_long bndry, u_long maxaddr,
185     u_long maxsz, u_int nsegs, u_long maxsegsz, u_int datarate, u_int flags)
186 {
187 	char path[PATH_MAX];
188 	struct obj *tag;
189 	int fd, len;
190 
191 	len = snprintf(path, PATH_MAX, "/dev/proto/%s/busdma", dev);
192 	if (len >= PATH_MAX) {
193 		errno = EINVAL;
194 		return (-1);
195 	}
196 	fd = open(path, O_RDWR);
197 	if (fd == -1)
198 		return (-1);
199 
200 	tag = bd_tag_new(NULL, fd, align, bndry, maxaddr, maxsz, nsegs,
201 	    maxsegsz, datarate, flags);
202 	if (tag == NULL) {
203 		close(fd);
204 		return (-1);
205 	}
206 	return (tag->oid);
207 }
208 
209 int
210 bd_tag_derive(int ptid, u_long align, u_long bndry, u_long maxaddr,
211     u_long maxsz, u_int nsegs, u_long maxsegsz, u_int datarate, u_int flags)
212 {
213 	struct obj *ptag, *tag;
214 
215 	ptag = obj_lookup(ptid, OBJ_TYPE_TAG);
216 	if (ptag == NULL)
217 		return (-1);
218 
219 	tag = bd_tag_new(ptag, ptag->fd, align, bndry, maxaddr, maxsz, nsegs,
220 	    maxsegsz, datarate, flags);
221 	if (tag == NULL)
222 		return (-1);
223 	ptag->refcnt++;
224 	return (tag->oid);
225 }
226 
227 int
228 bd_tag_destroy(int tid)
229 {
230 	struct proto_ioc_busdma ioc;
231 	struct obj *ptag, *tag;
232 
233 	tag = obj_lookup(tid, OBJ_TYPE_TAG);
234 	if (tag == NULL)
235 		return (errno);
236 	if (tag->refcnt > 1)
237 		return (EBUSY);
238 
239 	memset(&ioc, 0, sizeof(ioc));
240 	ioc.request = PROTO_IOC_BUSDMA_TAG_DESTROY;
241 	ioc.key = tag->key;
242 	if (ioctl(tag->fd, PROTO_IOC_BUSDMA, &ioc) == -1)
243 		return (errno);
244 
245 	if (tag->parent != NULL)
246 		tag->parent->refcnt--;
247 	else
248 		close(tag->fd);
249 	obj_free(tag);
250 	return (0);
251 }
252 
253 static int
254 bd_md_add_seg(struct obj *md, int type, u_long addr, u_long size)
255 {
256 	struct obj *seg;
257 
258 	seg = obj_alloc(OBJ_TYPE_SEG);
259 	if (seg == NULL)
260 		return (errno);
261 	seg->refcnt = 1;
262 	seg->parent = md;
263 	seg->u.seg.address = addr;
264 	seg->u.seg.size = size;
265 
266 	md->u.md.seg[type] = seg;
267 	md->u.md.nsegs[type] = 1;
268 	return (0);
269 }
270 
271 static int
272 bd_md_del_segs(struct obj *md, int type, int unmap)
273 {
274 	struct obj *seg, *seg0;
275 
276 	for (seg = md->u.md.seg[type]; seg != NULL; seg = seg0) {
277 		if (unmap)
278 			munmap((void *)seg->u.seg.address, seg->u.seg.size);
279 		seg0 = seg->u.seg.next;
280 		obj_free(seg);
281 	}
282 	return (0);
283 }
284 
285 int
286 bd_md_create(int tid, u_int flags)
287 {
288 	struct proto_ioc_busdma ioc;
289 	struct obj *md, *tag;
290 
291 	tag = obj_lookup(tid, OBJ_TYPE_TAG);
292 	if (tag == NULL)
293 		return (-1);
294 
295 	md = obj_alloc(OBJ_TYPE_MD);
296 	if (md == NULL)
297 		return (-1);
298 
299 	memset(&ioc, 0, sizeof(ioc));
300 	ioc.request = PROTO_IOC_BUSDMA_MD_CREATE;
301 	ioc.u.md.tag = tag->key;
302 	ioc.u.md.flags = flags;
303 	if (ioctl(tag->fd, PROTO_IOC_BUSDMA, &ioc) == -1) {
304 		obj_free(md);
305 		return (-1);
306 	}
307 
308 	md->refcnt = 1;
309 	md->fd = tag->fd;
310 	md->parent = tag;
311 	tag->refcnt++;
312 	md->key = ioc.result;
313 	return (md->oid);
314 }
315 
316 int
317 bd_md_destroy(int mdid)
318 {
319 	struct proto_ioc_busdma ioc;
320 	struct obj *md;
321 
322 	md = obj_lookup(mdid, OBJ_TYPE_MD);
323 	if (md == NULL)
324 		return (errno);
325 
326 	memset(&ioc, 0, sizeof(ioc));
327 	ioc.request = PROTO_IOC_BUSDMA_MD_DESTROY;
328 	ioc.key = md->key;
329 	if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1)
330 		return (errno);
331 
332 	md->parent->refcnt--;
333 	obj_free(md);
334 	return (0);
335 }
336 
337 int
338 bd_md_load(int mdid, void *buf, u_long len, u_int flags)
339 {
340 	struct proto_ioc_busdma ioc;
341 	struct obj *md;
342 	int error;
343 
344 	md = obj_lookup(mdid, OBJ_TYPE_MD);
345 	if (md == NULL)
346 		return (errno);
347 
348 	memset(&ioc, 0, sizeof(ioc));
349 	ioc.request = PROTO_IOC_BUSDMA_MD_LOAD;
350 	ioc.key = md->key;
351 	ioc.u.md.flags = flags;
352 	ioc.u.md.virt_addr = (uintptr_t)buf;
353 	ioc.u.md.virt_size = len;
354 	if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1)
355 		return (errno);
356 
357 	error = bd_md_add_seg(md, BUSDMA_MD_VIRT, ioc.u.md.virt_addr, len);
358 	error = bd_md_add_seg(md, BUSDMA_MD_PHYS, ioc.u.md.phys_addr, len);
359 	error = bd_md_add_seg(md, BUSDMA_MD_BUS, ioc.u.md.bus_addr, len);
360 	return (error);
361 }
362 
363 int
364 bd_md_unload(int mdid)
365 {
366 	struct proto_ioc_busdma ioc;
367 	struct obj *md;
368 	int error;
369 
370 	md = obj_lookup(mdid, OBJ_TYPE_MD);
371 	if (md == NULL)
372 		return (errno);
373 
374 	memset(&ioc, 0, sizeof(ioc));
375 	ioc.request = PROTO_IOC_BUSDMA_MD_UNLOAD;
376 	ioc.key = md->key;
377 	if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1)
378 		return (errno);
379 
380 	bd_md_del_segs(md, BUSDMA_MD_VIRT, 0);
381 	bd_md_del_segs(md, BUSDMA_MD_PHYS, 0);
382 	bd_md_del_segs(md, BUSDMA_MD_BUS, 0);
383 	return (0);
384 }
385 
386 int
387 bd_mem_alloc(int tid, u_int flags)
388 {
389 	struct proto_ioc_busdma ioc;
390 	struct obj *md, *tag;
391 	uintptr_t addr;
392 	int error;
393 
394 	tag = obj_lookup(tid, OBJ_TYPE_TAG);
395 	if (tag == NULL)
396 		return (-1);
397 
398 	md = obj_alloc(OBJ_TYPE_MD);
399 	if (md == NULL)
400 		return (-1);
401 
402 	memset(&ioc, 0, sizeof(ioc));
403 	ioc.request = PROTO_IOC_BUSDMA_MEM_ALLOC;
404 	ioc.u.md.tag = tag->key;
405 	ioc.u.md.flags = flags;
406 	if (ioctl(tag->fd, PROTO_IOC_BUSDMA, &ioc) == -1) {
407 		obj_free(md);
408 		return (-1);
409 	}
410 
411 	md->refcnt = 1;
412 	md->fd = tag->fd;
413 	md->parent = tag;
414 	tag->refcnt++;
415 	md->key = ioc.result;
416 
417 	/* XXX we need to support multiple segments */
418 	assert(ioc.u.md.phys_nsegs == 1);
419 	assert(ioc.u.md.bus_nsegs == 1);
420 	error = bd_md_add_seg(md, BUSDMA_MD_PHYS, ioc.u.md.phys_addr,
421 	    tag->u.tag.maxsz);
422 	error = bd_md_add_seg(md, BUSDMA_MD_BUS, ioc.u.md.bus_addr,
423 	    tag->u.tag.maxsz);
424 
425 	addr = (uintptr_t)mmap(NULL, tag->u.tag.maxsz, PROT_READ | PROT_WRITE,
426 	    MAP_NOCORE | MAP_SHARED, md->fd, ioc.u.md.phys_addr);
427 	if (addr == (uintptr_t)MAP_FAILED)
428 		goto fail;
429 	error = bd_md_add_seg(md, BUSDMA_MD_VIRT, addr, tag->u.tag.maxsz);
430 
431 	return (md->oid);
432 
433  fail:
434 	memset(&ioc, 0, sizeof(ioc));
435 	ioc.request = PROTO_IOC_BUSDMA_MEM_FREE;
436 	ioc.key = md->key;
437 	ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc);
438 	md->parent->refcnt--;
439 	obj_free(md);
440 	return (-1);
441 }
442 
443 int
444 bd_mem_free(int mdid)
445 {
446 	struct proto_ioc_busdma ioc;
447 	struct obj *md;
448 
449 	md = obj_lookup(mdid, OBJ_TYPE_MD);
450 	if (md == NULL)
451 		return (errno);
452 
453 	memset(&ioc, 0, sizeof(ioc));
454 	ioc.request = PROTO_IOC_BUSDMA_MEM_FREE;
455 	ioc.key = md->key;
456 	if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1)
457 		return (errno);
458 
459 	bd_md_del_segs(md, BUSDMA_MD_VIRT, 1);
460 	bd_md_del_segs(md, BUSDMA_MD_PHYS, 0);
461 	bd_md_del_segs(md, BUSDMA_MD_BUS, 0);
462 	md->parent->refcnt--;
463 	obj_free(md);
464 	return (0);
465 }
466 
467 int
468 bd_md_first_seg(int mdid, int space)
469 {
470 	struct obj *md, *seg;
471 
472 	md = obj_lookup(mdid, OBJ_TYPE_MD);
473 	if (md == NULL)
474 		return (-1);
475 
476 	if (space != BUSDMA_MD_BUS && space != BUSDMA_MD_PHYS &&
477 	    space != BUSDMA_MD_VIRT) {
478 		errno = EINVAL;
479 		return (-1);
480 	}
481 	seg = md->u.md.seg[space];
482 	if (seg == NULL) {
483 		errno = ENXIO;
484 		return (-1);
485 	}
486 	return (seg->oid);
487 }
488 
489 int
490 bd_md_next_seg(int mdid, int sid)
491 {
492 	struct obj *seg;
493 
494 	seg = obj_lookup(sid, OBJ_TYPE_SEG);
495 	if (seg == NULL)
496 		return (-1);
497 
498 	seg = seg->u.seg.next;
499 	if (seg == NULL) {
500 		errno = ENXIO;
501 		return (-1);
502 	}
503 	return (seg->oid);
504 }
505 
506 int
507 bd_seg_get_addr(int sid, u_long *addr_p)
508 {
509 	struct obj *seg;
510 
511 	if (addr_p == NULL)
512 		return (EINVAL);
513 
514 	seg = obj_lookup(sid, OBJ_TYPE_SEG);
515 	if (seg == NULL)
516 		return (errno);
517 
518 	*addr_p = seg->u.seg.address;
519 	return (0);
520 }
521 
522 int
523 bd_seg_get_size(int sid, u_long *size_p)
524 {
525 	struct obj *seg;
526 
527 	if (size_p == NULL)
528 		return (EINVAL);
529 
530 	seg = obj_lookup(sid, OBJ_TYPE_SEG);
531 	if (seg == NULL)
532 		return (errno);
533 
534 	*size_p = seg->u.seg.size;
535 	return (0);
536 }
537 
538 int
539 bd_sync(int mdid, u_int op, u_long ofs, u_long len)
540 {
541 	struct proto_ioc_busdma ioc;
542 	struct obj *md;
543 
544 	md = obj_lookup(mdid, OBJ_TYPE_MD);
545 	if (md == NULL)
546 		return (errno);
547 
548 	memset(&ioc, 0, sizeof(ioc));
549 	ioc.request = PROTO_IOC_BUSDMA_SYNC;
550 	ioc.key = md->key;
551 	ioc.u.sync.op = op;
552 	ioc.u.sync.base = ofs;
553 	ioc.u.sync.size = len;
554 	if (ioctl(md->fd, PROTO_IOC_BUSDMA, &ioc) == -1)
555 		return (errno);
556 
557 	return (0);
558 }
559