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