Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * Framework for userspace DMA-BUF allocations
4 : : *
5 : : * Copyright (C) 2011 Google, Inc.
6 : : * Copyright (C) 2019 Linaro Ltd.
7 : : */
8 : :
9 : : #include <linux/cdev.h>
10 : : #include <linux/debugfs.h>
11 : : #include <linux/device.h>
12 : : #include <linux/dma-buf.h>
13 : : #include <linux/err.h>
14 : : #include <linux/xarray.h>
15 : : #include <linux/list.h>
16 : : #include <linux/slab.h>
17 : : #include <linux/uaccess.h>
18 : : #include <linux/syscalls.h>
19 : : #include <linux/dma-heap.h>
20 : : #include <uapi/linux/dma-heap.h>
21 : :
22 : : #define DEVNAME "dma_heap"
23 : :
24 : : #define NUM_HEAP_MINORS 128
25 : :
26 : : /**
27 : : * struct dma_heap - represents a dmabuf heap in the system
28 : : * @name: used for debugging/device-node name
29 : : * @ops: ops struct for this heap
30 : : * @heap_devt heap device node
31 : : * @list list head connecting to list of heaps
32 : : * @heap_cdev heap char device
33 : : *
34 : : * Represents a heap of memory from which buffers can be made.
35 : : */
36 : : struct dma_heap {
37 : : const char *name;
38 : : const struct dma_heap_ops *ops;
39 : : void *priv;
40 : : dev_t heap_devt;
41 : : struct list_head list;
42 : : struct cdev heap_cdev;
43 : : };
44 : :
45 : : static LIST_HEAD(heap_list);
46 : : static DEFINE_MUTEX(heap_list_lock);
47 : : static dev_t dma_heap_devt;
48 : : static struct class *dma_heap_class;
49 : : static DEFINE_XARRAY_ALLOC(dma_heap_minors);
50 : :
51 : : static int dma_heap_buffer_alloc(struct dma_heap *heap, size_t len,
52 : : unsigned int fd_flags,
53 : : unsigned int heap_flags)
54 : : {
55 : : /*
56 : : * Allocations from all heaps have to begin
57 : : * and end on page boundaries.
58 : : */
59 : 0 : len = PAGE_ALIGN(len);
60 [ # # ]: 0 : if (!len)
61 : : return -EINVAL;
62 : :
63 : 0 : return heap->ops->allocate(heap, len, fd_flags, heap_flags);
64 : : }
65 : :
66 : 0 : static int dma_heap_open(struct inode *inode, struct file *file)
67 : : {
68 : : struct dma_heap *heap;
69 : :
70 : 0 : heap = xa_load(&dma_heap_minors, iminor(inode));
71 [ # # ]: 0 : if (!heap) {
72 : 0 : pr_err("dma_heap: minor %d unknown.\n", iminor(inode));
73 : 0 : return -ENODEV;
74 : : }
75 : :
76 : : /* instance data as context */
77 : 0 : file->private_data = heap;
78 : 0 : nonseekable_open(inode, file);
79 : :
80 : 0 : return 0;
81 : : }
82 : :
83 : 0 : static long dma_heap_ioctl_allocate(struct file *file, void *data)
84 : : {
85 : : struct dma_heap_allocation_data *heap_allocation = data;
86 : 0 : struct dma_heap *heap = file->private_data;
87 : : int fd;
88 : :
89 [ # # ]: 0 : if (heap_allocation->fd)
90 : : return -EINVAL;
91 : :
92 [ # # ]: 0 : if (heap_allocation->fd_flags & ~DMA_HEAP_VALID_FD_FLAGS)
93 : : return -EINVAL;
94 : :
95 [ # # ]: 0 : if (heap_allocation->heap_flags & ~DMA_HEAP_VALID_HEAP_FLAGS)
96 : : return -EINVAL;
97 : :
98 : 0 : fd = dma_heap_buffer_alloc(heap, heap_allocation->len,
99 : : heap_allocation->fd_flags,
100 : : heap_allocation->heap_flags);
101 [ # # ]: 0 : if (fd < 0)
102 : : return fd;
103 : :
104 : 0 : heap_allocation->fd = fd;
105 : :
106 : 0 : return 0;
107 : : }
108 : :
109 : : static unsigned int dma_heap_ioctl_cmds[] = {
110 : : DMA_HEAP_IOCTL_ALLOC,
111 : : };
112 : :
113 : 0 : static long dma_heap_ioctl(struct file *file, unsigned int ucmd,
114 : : unsigned long arg)
115 : : {
116 : : char stack_kdata[128];
117 : : char *kdata = stack_kdata;
118 : : unsigned int kcmd;
119 : : unsigned int in_size, out_size, drv_size, ksize;
120 : 0 : int nr = _IOC_NR(ucmd);
121 : : int ret = 0;
122 : :
123 [ # # ]: 0 : if (nr >= ARRAY_SIZE(dma_heap_ioctl_cmds))
124 : : return -EINVAL;
125 : :
126 : : /* Get the kernel ioctl cmd that matches */
127 : 0 : kcmd = dma_heap_ioctl_cmds[nr];
128 : :
129 : : /* Figure out the delta between user cmd size and kernel cmd size */
130 : 0 : drv_size = _IOC_SIZE(kcmd);
131 : 0 : out_size = _IOC_SIZE(ucmd);
132 : : in_size = out_size;
133 [ # # ]: 0 : if ((ucmd & kcmd & IOC_IN) == 0)
134 : : in_size = 0;
135 [ # # ]: 0 : if ((ucmd & kcmd & IOC_OUT) == 0)
136 : : out_size = 0;
137 : 0 : ksize = max(max(in_size, out_size), drv_size);
138 : :
139 : : /* If necessary, allocate buffer for ioctl argument */
140 [ # # ]: 0 : if (ksize > sizeof(stack_kdata)) {
141 : : kdata = kmalloc(ksize, GFP_KERNEL);
142 [ # # ]: 0 : if (!kdata)
143 : : return -ENOMEM;
144 : : }
145 : :
146 [ # # ]: 0 : if (copy_from_user(kdata, (void __user *)arg, in_size) != 0) {
147 : : ret = -EFAULT;
148 : : goto err;
149 : : }
150 : :
151 : : /* zero out any difference between the kernel/user structure size */
152 [ # # ]: 0 : if (ksize > in_size)
153 : 0 : memset(kdata + in_size, 0, ksize - in_size);
154 : :
155 [ # # ]: 0 : switch (kcmd) {
156 : : case DMA_HEAP_IOCTL_ALLOC:
157 : 0 : ret = dma_heap_ioctl_allocate(file, kdata);
158 : : break;
159 : : default:
160 : : ret = -ENOTTY;
161 : : goto err;
162 : : }
163 : :
164 [ # # ]: 0 : if (copy_to_user((void __user *)arg, kdata, out_size) != 0)
165 : : ret = -EFAULT;
166 : : err:
167 [ # # ]: 0 : if (kdata != stack_kdata)
168 : 0 : kfree(kdata);
169 : 0 : return ret;
170 : : }
171 : :
172 : : static const struct file_operations dma_heap_fops = {
173 : : .owner = THIS_MODULE,
174 : : .open = dma_heap_open,
175 : : .unlocked_ioctl = dma_heap_ioctl,
176 : : #ifdef CONFIG_COMPAT
177 : : .compat_ioctl = dma_heap_ioctl,
178 : : #endif
179 : : };
180 : :
181 : : /**
182 : : * dma_heap_get_drvdata() - get per-subdriver data for the heap
183 : : * @heap: DMA-Heap to retrieve private data for
184 : : *
185 : : * Returns:
186 : : * The per-subdriver data for the heap.
187 : : */
188 : 0 : void *dma_heap_get_drvdata(struct dma_heap *heap)
189 : : {
190 : 0 : return heap->priv;
191 : : }
192 : :
193 : 414 : struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info)
194 : : {
195 : : struct dma_heap *heap, *h, *err_ret;
196 : : struct device *dev_ret;
197 : : unsigned int minor;
198 : : int ret;
199 : :
200 [ + - - + ]: 414 : if (!exp_info->name || !strcmp(exp_info->name, "")) {
201 : 0 : pr_err("dma_heap: Cannot add heap without a name\n");
202 : 0 : return ERR_PTR(-EINVAL);
203 : : }
204 : :
205 [ + - - + ]: 414 : if (!exp_info->ops || !exp_info->ops->allocate) {
206 : 0 : pr_err("dma_heap: Cannot add heap with invalid ops struct\n");
207 : 0 : return ERR_PTR(-EINVAL);
208 : : }
209 : :
210 : : /* check the name is unique */
211 : 414 : mutex_lock(&heap_list_lock);
212 [ + + ]: 621 : list_for_each_entry(h, &heap_list, list) {
213 [ - + ]: 207 : if (!strcmp(h->name, exp_info->name)) {
214 : 0 : mutex_unlock(&heap_list_lock);
215 : 0 : pr_err("dma_heap: Already registered heap named %s\n",
216 : : exp_info->name);
217 : 0 : return ERR_PTR(-EINVAL);
218 : : }
219 : : }
220 : 414 : mutex_unlock(&heap_list_lock);
221 : :
222 : 414 : heap = kzalloc(sizeof(*heap), GFP_KERNEL);
223 [ + - ]: 414 : if (!heap)
224 : : return ERR_PTR(-ENOMEM);
225 : :
226 : 414 : heap->name = exp_info->name;
227 : 414 : heap->ops = exp_info->ops;
228 : 414 : heap->priv = exp_info->priv;
229 : :
230 : : /* Find unused minor number */
231 : 414 : ret = xa_alloc(&dma_heap_minors, &minor, heap,
232 : 414 : XA_LIMIT(0, NUM_HEAP_MINORS - 1), GFP_KERNEL);
233 [ - + ]: 414 : if (ret < 0) {
234 : 0 : pr_err("dma_heap: Unable to get minor number for heap\n");
235 : : err_ret = ERR_PTR(ret);
236 : 0 : goto err0;
237 : : }
238 : :
239 : : /* Create device */
240 : 414 : heap->heap_devt = MKDEV(MAJOR(dma_heap_devt), minor);
241 : :
242 : 414 : cdev_init(&heap->heap_cdev, &dma_heap_fops);
243 : 414 : ret = cdev_add(&heap->heap_cdev, heap->heap_devt, 1);
244 [ - + ]: 414 : if (ret < 0) {
245 : 0 : pr_err("dma_heap: Unable to add char device\n");
246 : : err_ret = ERR_PTR(ret);
247 : 0 : goto err1;
248 : : }
249 : :
250 : 414 : dev_ret = device_create(dma_heap_class,
251 : : NULL,
252 : : heap->heap_devt,
253 : : NULL,
254 : : heap->name);
255 [ - + ]: 414 : if (IS_ERR(dev_ret)) {
256 : 0 : pr_err("dma_heap: Unable to create device\n");
257 : : err_ret = ERR_CAST(dev_ret);
258 : : goto err2;
259 : : }
260 : : /* Add heap to the list */
261 : 414 : mutex_lock(&heap_list_lock);
262 : 414 : list_add(&heap->list, &heap_list);
263 : 414 : mutex_unlock(&heap_list_lock);
264 : :
265 : 414 : return heap;
266 : :
267 : : err2:
268 : 0 : cdev_del(&heap->heap_cdev);
269 : : err1:
270 : 0 : xa_erase(&dma_heap_minors, minor);
271 : : err0:
272 : 0 : kfree(heap);
273 : 0 : return err_ret;
274 : : }
275 : :
276 : 2070 : static char *dma_heap_devnode(struct device *dev, umode_t *mode)
277 : : {
278 : 2070 : return kasprintf(GFP_KERNEL, "dma_heap/%s", dev_name(dev));
279 : : }
280 : :
281 : 207 : static int dma_heap_init(void)
282 : : {
283 : : int ret;
284 : :
285 : 207 : ret = alloc_chrdev_region(&dma_heap_devt, 0, NUM_HEAP_MINORS, DEVNAME);
286 [ + - ]: 207 : if (ret)
287 : : return ret;
288 : :
289 : 207 : dma_heap_class = class_create(THIS_MODULE, DEVNAME);
290 [ - + ]: 207 : if (IS_ERR(dma_heap_class)) {
291 : 0 : unregister_chrdev_region(dma_heap_devt, NUM_HEAP_MINORS);
292 : 0 : return PTR_ERR(dma_heap_class);
293 : : }
294 : 207 : dma_heap_class->devnode = dma_heap_devnode;
295 : :
296 : 207 : return 0;
297 : : }
298 : : subsys_initcall(dma_heap_init);
|