]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/bus_space/busdma.c
Make linux_ptrace() use linux_msg() instead of printf().
[FreeBSD/FreeBSD.git] / tools / bus_space / busdma.c
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 }