diff options
author | Andrew F. Davis | 2019-09-05 08:09:16 -0500 |
---|---|---|
committer | Tero Kristo | 2019-09-06 15:30:43 -0500 |
commit | 6cc31c44e7a3f17416e56298e3393b96e2d69b11 (patch) | |
tree | 53b799f6ca3329de9604d90d473324b4d9ed696d | |
parent | 55eaa0cee89a4f16fdc47e2db4de9279e00568e0 (diff) | |
download | kernel-6cc31c44e7a3f17416e56298e3393b96e2d69b11.tar.gz kernel-6cc31c44e7a3f17416e56298e3393b96e2d69b11.tar.xz kernel-6cc31c44e7a3f17416e56298e3393b96e2d69b11.zip |
HACK: misc: Add dma-buf to physical address exporter
This is driver allows user-space to attach a DMA-BUF and
receive back its CPU physical address. This is a temporary
solution to allow CMEM like functionality from ION allocated
buffers. This is a hack and this will be removed when proper
solutions are implemented.
Signed-off-by: Andrew F. Davis <afd@ti.com>
-rw-r--r-- | drivers/misc/Kconfig | 5 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/dma-buf-phys.c | 217 | ||||
-rw-r--r-- | include/uapi/linux/dma_buf_phys.h | 35 |
4 files changed, 258 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 3726eacdf65d..6d3635552160 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig | |||
@@ -513,6 +513,11 @@ config MISC_RTSX | |||
513 | tristate | 513 | tristate |
514 | default MISC_RTSX_PCI || MISC_RTSX_USB | 514 | default MISC_RTSX_PCI || MISC_RTSX_USB |
515 | 515 | ||
516 | config DMA_BUF_PHYS | ||
517 | tristate "DMA-BUF physical address user-space exporter" | ||
518 | help | ||
519 | Exports CPU physical address of DMA-BUF to user-space. | ||
520 | |||
516 | source "drivers/misc/c2port/Kconfig" | 521 | source "drivers/misc/c2port/Kconfig" |
517 | source "drivers/misc/eeprom/Kconfig" | 522 | source "drivers/misc/eeprom/Kconfig" |
518 | source "drivers/misc/cb710/Kconfig" | 523 | source "drivers/misc/cb710/Kconfig" |
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index af22bbc3d00c..efe8447c115b 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile | |||
@@ -58,3 +58,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o | |||
58 | obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o | 58 | obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o |
59 | obj-$(CONFIG_OCXL) += ocxl/ | 59 | obj-$(CONFIG_OCXL) += ocxl/ |
60 | obj-$(CONFIG_MISC_RTSX) += cardreader/ | 60 | obj-$(CONFIG_MISC_RTSX) += cardreader/ |
61 | obj-$(CONFIG_DMA_BUF_PHYS) += dma-buf-phys.o | ||
diff --git a/drivers/misc/dma-buf-phys.c b/drivers/misc/dma-buf-phys.c new file mode 100644 index 000000000000..277546f4688b --- /dev/null +++ b/drivers/misc/dma-buf-phys.c | |||
@@ -0,0 +1,217 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * DMA-BUF contiguous buffer physical address user-space exporter | ||
4 | * | ||
5 | * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ | ||
6 | * Andrew F. Davis <afd@ti.com> | ||
7 | */ | ||
8 | |||
9 | #include <linux/device.h> | ||
10 | #include <linux/dma-buf.h> | ||
11 | #include <linux/err.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/miscdevice.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/of_device.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/uaccess.h> | ||
19 | |||
20 | #include <uapi/linux/dma_buf_phys.h> | ||
21 | |||
22 | #define DEVICE_NAME "dma-buf-phys" | ||
23 | |||
24 | struct dma_buf_phys_priv { | ||
25 | struct miscdevice miscdev; | ||
26 | }; | ||
27 | |||
28 | struct dma_buf_phys_file { | ||
29 | struct device *dev; | ||
30 | struct dma_buf *dma_buf; | ||
31 | struct dma_buf_attachment *attachment; | ||
32 | struct sg_table *sgt; | ||
33 | }; | ||
34 | |||
35 | static int dma_buf_phys_open(struct inode *inode, struct file *file) | ||
36 | { | ||
37 | struct miscdevice *miscdev = file->private_data; | ||
38 | struct device *dev = miscdev->this_device; | ||
39 | struct dma_buf_phys_file *priv; | ||
40 | |||
41 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
42 | if (!priv) | ||
43 | return -ENOMEM; | ||
44 | priv->dev = dev; | ||
45 | file->private_data = (void *)priv; | ||
46 | |||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | static int dma_buf_phys_release(struct inode *inode, struct file *file) | ||
51 | { | ||
52 | struct dma_buf_phys_file *priv = file->private_data; | ||
53 | |||
54 | if (priv->attachment && priv->sgt) | ||
55 | dma_buf_unmap_attachment(priv->attachment, priv->sgt, DMA_BIDIRECTIONAL); | ||
56 | if (priv->dma_buf && priv->attachment) | ||
57 | dma_buf_detach(priv->dma_buf, priv->attachment); | ||
58 | if (priv->dma_buf) | ||
59 | dma_buf_put(priv->dma_buf); | ||
60 | |||
61 | kfree(priv); | ||
62 | |||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static int dma_buf_phys_convert(struct dma_buf_phys_file *priv, int fd, u64 *phys) | ||
67 | { | ||
68 | struct device *dev = priv->dev; | ||
69 | struct dma_buf *dma_buf; | ||
70 | struct dma_buf_attachment *attachment; | ||
71 | struct sg_table *sgt; | ||
72 | dma_addr_t dma_addr; | ||
73 | int ret; | ||
74 | |||
75 | dma_buf = dma_buf_get(fd); | ||
76 | if (IS_ERR(dma_buf)) | ||
77 | return PTR_ERR(dma_buf); | ||
78 | |||
79 | /* Attach as the parent device as it will have the correct DMA ops set */ | ||
80 | attachment = dma_buf_attach(dma_buf, dev->parent); | ||
81 | if (IS_ERR(attachment)) { | ||
82 | ret = PTR_ERR(attachment); | ||
83 | goto fail_put; | ||
84 | } | ||
85 | |||
86 | sgt = dma_buf_map_attachment(attachment, DMA_BIDIRECTIONAL); | ||
87 | if (IS_ERR(sgt)) { | ||
88 | ret = PTR_ERR(sgt); | ||
89 | goto fail_detach; | ||
90 | } | ||
91 | |||
92 | /* Without PAT only physically contiguous buffers can be supported */ | ||
93 | if (sgt->orig_nents != 1) { | ||
94 | dev_err(dev, "DMA-BUF not contiguous\n"); | ||
95 | ret = -EINVAL; | ||
96 | goto fail_unmap; | ||
97 | } | ||
98 | |||
99 | dma_addr = sg_dma_address(sgt->sgl); | ||
100 | |||
101 | *phys = dma_addr; | ||
102 | |||
103 | priv->dma_buf = dma_buf; | ||
104 | priv->attachment = attachment; | ||
105 | priv->sgt = sgt; | ||
106 | |||
107 | return 0; | ||
108 | |||
109 | fail_unmap: | ||
110 | dma_buf_unmap_attachment(attachment, sgt, DMA_BIDIRECTIONAL); | ||
111 | fail_detach: | ||
112 | dma_buf_detach(dma_buf, attachment); | ||
113 | fail_put: | ||
114 | dma_buf_put(dma_buf); | ||
115 | |||
116 | return ret; | ||
117 | } | ||
118 | |||
119 | static long dma_buf_phys_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
120 | { | ||
121 | struct dma_buf_phys_file *priv = file->private_data; | ||
122 | |||
123 | switch (cmd) { | ||
124 | case DMA_BUF_PHYS_IOC_CONVERT: | ||
125 | { | ||
126 | struct dma_buf_phys_data data; | ||
127 | int ret; | ||
128 | |||
129 | /* | ||
130 | * TODO: this should likely be properly serialized, but I | ||
131 | * see no reason this file would ever need to be shared. | ||
132 | */ | ||
133 | /* one attachment per file */ | ||
134 | if (priv->dma_buf) | ||
135 | return -EFAULT; | ||
136 | |||
137 | if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd))) | ||
138 | return -EFAULT; | ||
139 | |||
140 | ret = dma_buf_phys_convert(priv, data.fd, &data.phys); | ||
141 | if (ret) | ||
142 | return ret; | ||
143 | |||
144 | if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) | ||
145 | return -EFAULT; | ||
146 | |||
147 | break; | ||
148 | } | ||
149 | default: | ||
150 | return -ENOTTY; | ||
151 | } | ||
152 | |||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | static const struct file_operations dma_buf_phys_fops = { | ||
157 | .owner = THIS_MODULE, | ||
158 | .open = dma_buf_phys_open, | ||
159 | .release = dma_buf_phys_release, | ||
160 | .unlocked_ioctl = dma_buf_phys_ioctl, | ||
161 | #ifdef CONFIG_COMPAT | ||
162 | .compat_ioctl = dma_buf_phys_ioctl, | ||
163 | #endif | ||
164 | }; | ||
165 | |||
166 | static int dma_buf_phys_probe(struct platform_device *pdev) | ||
167 | { | ||
168 | struct dma_buf_phys_priv *priv; | ||
169 | struct device *dev = &pdev->dev; | ||
170 | int err; | ||
171 | |||
172 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
173 | if (!priv) | ||
174 | return -ENOMEM; | ||
175 | dev_set_drvdata(dev, priv); | ||
176 | |||
177 | priv->miscdev.minor = MISC_DYNAMIC_MINOR; | ||
178 | priv->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", DEVICE_NAME); | ||
179 | priv->miscdev.fops = &dma_buf_phys_fops; | ||
180 | priv->miscdev.parent = dev; | ||
181 | err = misc_register(&priv->miscdev); | ||
182 | if (err) { | ||
183 | dev_err(dev, "unable to register DMA-BUF to Phys misc device\n"); | ||
184 | return err; | ||
185 | } | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static int dma_buf_phys_remove(struct platform_device *pdev) | ||
191 | { | ||
192 | struct dma_buf_phys_priv *priv = dev_get_drvdata(&pdev->dev); | ||
193 | |||
194 | misc_deregister(&priv->miscdev); | ||
195 | |||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | static const struct of_device_id dma_buf_phys_of_match[] = { | ||
200 | { .compatible = "ti,dma_buf_phys", }, | ||
201 | {}, | ||
202 | }; | ||
203 | MODULE_DEVICE_TABLE(of, dma_buf_phys_of_match); | ||
204 | |||
205 | static struct platform_driver dma_buf_phys_driver = { | ||
206 | .probe = dma_buf_phys_probe, | ||
207 | .remove = dma_buf_phys_remove, | ||
208 | .driver = { | ||
209 | .name = "dma_buf_phys", | ||
210 | .of_match_table = dma_buf_phys_of_match, | ||
211 | } | ||
212 | }; | ||
213 | module_platform_driver(dma_buf_phys_driver); | ||
214 | |||
215 | MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); | ||
216 | MODULE_DESCRIPTION("DMA-BUF contiguous buffer physical address user-space exporter"); | ||
217 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/include/uapi/linux/dma_buf_phys.h b/include/uapi/linux/dma_buf_phys.h new file mode 100644 index 000000000000..72d9d9e4b225 --- /dev/null +++ b/include/uapi/linux/dma_buf_phys.h | |||
@@ -0,0 +1,35 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ | ||
2 | /* | ||
3 | * DMA-BUF contiguous buffer physical address user-space exporter | ||
4 | * | ||
5 | * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ | ||
6 | * Andrew F. Davis <afd@ti.com> | ||
7 | */ | ||
8 | |||
9 | #ifndef DMA_BUF_PHYS_H | ||
10 | #define DMA_BUF_PHYS_H | ||
11 | |||
12 | #include <linux/ioctl.h> | ||
13 | #include <linux/types.h> | ||
14 | |||
15 | /** | ||
16 | * struct dma_buf_phys_data - metadata passed from userspace for conversion | ||
17 | * @fd: DMA-BUF fd for conversion | ||
18 | * @phys: populated with CPU physical address of DMA-BUF | ||
19 | */ | ||
20 | struct dma_buf_phys_data { | ||
21 | __u32 fd; | ||
22 | __u64 phys; | ||
23 | }; | ||
24 | |||
25 | #define DMA_BUF_PHYS_IOC_MAGIC 'D' | ||
26 | |||
27 | /** | ||
28 | * DOC: DMA_BUF_PHYS_IOC_CONVERT - Convert DMA-BUF to physical address | ||
29 | * | ||
30 | * Takes a dma_buf_phys_data struct containing a fd for a physicaly contigous | ||
31 | * buffer. Pins this buffer and populates phys field with the CPU physical address. | ||
32 | */ | ||
33 | #define DMA_BUF_PHYS_IOC_CONVERT _IOWR(DMA_BUF_PHYS_IOC_MAGIC, 0, struct dma_buf_phys_data) | ||
34 | |||
35 | #endif /* DMA_BUF_PHYS_H */ | ||