xref: /openbsd/sys/dev/pci/drm/drm_scatter.c (revision ad8b1aaf)
1c349dbc7Sjsg /*
2c349dbc7Sjsg  * \file drm_scatter.c
3c349dbc7Sjsg  * IOCTLs to manage scatter/gather memory
4c349dbc7Sjsg  *
5c349dbc7Sjsg  * \author Gareth Hughes <gareth@valinux.com>
6c349dbc7Sjsg  */
7c349dbc7Sjsg 
8c349dbc7Sjsg /*
9c349dbc7Sjsg  * Created: Mon Dec 18 23:20:54 2000 by gareth@valinux.com
10c349dbc7Sjsg  *
11c349dbc7Sjsg  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
12c349dbc7Sjsg  * All Rights Reserved.
13c349dbc7Sjsg  *
14c349dbc7Sjsg  * Permission is hereby granted, free of charge, to any person obtaining a
15c349dbc7Sjsg  * copy of this software and associated documentation files (the "Software"),
16c349dbc7Sjsg  * to deal in the Software without restriction, including without limitation
17c349dbc7Sjsg  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18c349dbc7Sjsg  * and/or sell copies of the Software, and to permit persons to whom the
19c349dbc7Sjsg  * Software is furnished to do so, subject to the following conditions:
20c349dbc7Sjsg  *
21c349dbc7Sjsg  * The above copyright notice and this permission notice (including the next
22c349dbc7Sjsg  * paragraph) shall be included in all copies or substantial portions of the
23c349dbc7Sjsg  * Software.
24c349dbc7Sjsg  *
25c349dbc7Sjsg  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26c349dbc7Sjsg  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27c349dbc7Sjsg  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
28c349dbc7Sjsg  * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
29c349dbc7Sjsg  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
30c349dbc7Sjsg  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31c349dbc7Sjsg  * DEALINGS IN THE SOFTWARE.
32c349dbc7Sjsg  */
33c349dbc7Sjsg 
34c349dbc7Sjsg #include <linux/mm.h>
35c349dbc7Sjsg #include <linux/slab.h>
36c349dbc7Sjsg #include <linux/vmalloc.h>
37c349dbc7Sjsg 
38c349dbc7Sjsg #include <drm/drm.h>
39c349dbc7Sjsg #include <drm/drm_drv.h>
40c349dbc7Sjsg #include <drm/drm_print.h>
41c349dbc7Sjsg 
42c349dbc7Sjsg #include "drm_legacy.h"
43c349dbc7Sjsg 
44c349dbc7Sjsg #define DEBUG_SCATTER 0
45c349dbc7Sjsg 
drm_sg_cleanup(struct drm_sg_mem * entry)46c349dbc7Sjsg static void drm_sg_cleanup(struct drm_sg_mem * entry)
47c349dbc7Sjsg {
48c349dbc7Sjsg 	struct vm_page *page;
49c349dbc7Sjsg 	int i;
50c349dbc7Sjsg 
51c349dbc7Sjsg 	for (i = 0; i < entry->pages; i++) {
52c349dbc7Sjsg 		page = entry->pagelist[i];
53c349dbc7Sjsg 		if (page)
54c349dbc7Sjsg 			ClearPageReserved(page);
55c349dbc7Sjsg 	}
56c349dbc7Sjsg 
57c349dbc7Sjsg 	vfree(entry->virtual);
58c349dbc7Sjsg 
59c349dbc7Sjsg 	kfree(entry->busaddr);
60c349dbc7Sjsg 	kfree(entry->pagelist);
61c349dbc7Sjsg 	kfree(entry);
62c349dbc7Sjsg }
63c349dbc7Sjsg 
drm_legacy_sg_cleanup(struct drm_device * dev)64c349dbc7Sjsg void drm_legacy_sg_cleanup(struct drm_device *dev)
65c349dbc7Sjsg {
66c349dbc7Sjsg 	if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg &&
67c349dbc7Sjsg 	    drm_core_check_feature(dev, DRIVER_LEGACY)) {
68c349dbc7Sjsg 		drm_sg_cleanup(dev->sg);
69c349dbc7Sjsg 		dev->sg = NULL;
70c349dbc7Sjsg 	}
71c349dbc7Sjsg }
72c349dbc7Sjsg #ifdef _LP64
73c349dbc7Sjsg # define ScatterHandle(x) (unsigned int)((x >> 32) + (x & ((1L << 32) - 1)))
74c349dbc7Sjsg #else
75c349dbc7Sjsg # define ScatterHandle(x) (unsigned int)(x)
76c349dbc7Sjsg #endif
77c349dbc7Sjsg 
drm_legacy_sg_alloc(struct drm_device * dev,void * data,struct drm_file * file_priv)78c349dbc7Sjsg int drm_legacy_sg_alloc(struct drm_device *dev, void *data,
79c349dbc7Sjsg 			struct drm_file *file_priv)
80c349dbc7Sjsg {
81c349dbc7Sjsg 	struct drm_scatter_gather *request = data;
82c349dbc7Sjsg 	struct drm_sg_mem *entry;
83c349dbc7Sjsg 	unsigned long pages, i, j;
84c349dbc7Sjsg 
85c349dbc7Sjsg 	DRM_DEBUG("\n");
86c349dbc7Sjsg 
87c349dbc7Sjsg 	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
88c349dbc7Sjsg 		return -EOPNOTSUPP;
89c349dbc7Sjsg 
90c349dbc7Sjsg 	if (!drm_core_check_feature(dev, DRIVER_SG))
91c349dbc7Sjsg 		return -EOPNOTSUPP;
92c349dbc7Sjsg 
93c349dbc7Sjsg 	if (request->size > SIZE_MAX - PAGE_SIZE)
94c349dbc7Sjsg 		return -EINVAL;
95c349dbc7Sjsg 
96c349dbc7Sjsg 	if (dev->sg)
97c349dbc7Sjsg 		return -EINVAL;
98c349dbc7Sjsg 
99c349dbc7Sjsg 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
100c349dbc7Sjsg 	if (!entry)
101c349dbc7Sjsg 		return -ENOMEM;
102c349dbc7Sjsg 
103c349dbc7Sjsg 	pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE;
104c349dbc7Sjsg 	DRM_DEBUG("size=%ld pages=%ld\n", request->size, pages);
105c349dbc7Sjsg 
106c349dbc7Sjsg 	entry->pages = pages;
107c349dbc7Sjsg 	entry->pagelist = kcalloc(pages, sizeof(*entry->pagelist), GFP_KERNEL);
108c349dbc7Sjsg 	if (!entry->pagelist) {
109c349dbc7Sjsg 		kfree(entry);
110c349dbc7Sjsg 		return -ENOMEM;
111c349dbc7Sjsg 	}
112c349dbc7Sjsg 
113c349dbc7Sjsg 	entry->busaddr = kcalloc(pages, sizeof(*entry->busaddr), GFP_KERNEL);
114c349dbc7Sjsg 	if (!entry->busaddr) {
115c349dbc7Sjsg 		kfree(entry->pagelist);
116c349dbc7Sjsg 		kfree(entry);
117c349dbc7Sjsg 		return -ENOMEM;
118c349dbc7Sjsg 	}
119c349dbc7Sjsg 
120*ad8b1aafSjsg 	entry->virtual = vmalloc_32(pages << PAGE_SHIFT);
121c349dbc7Sjsg 	if (!entry->virtual) {
122c349dbc7Sjsg 		kfree(entry->busaddr);
123c349dbc7Sjsg 		kfree(entry->pagelist);
124c349dbc7Sjsg 		kfree(entry);
125c349dbc7Sjsg 		return -ENOMEM;
126c349dbc7Sjsg 	}
127c349dbc7Sjsg 
128c349dbc7Sjsg 	/* This also forces the mapping of COW pages, so our page list
129c349dbc7Sjsg 	 * will be valid.  Please don't remove it...
130c349dbc7Sjsg 	 */
131c349dbc7Sjsg 	memset(entry->virtual, 0, pages << PAGE_SHIFT);
132c349dbc7Sjsg 
133c349dbc7Sjsg 	entry->handle = ScatterHandle((unsigned long)entry->virtual);
134c349dbc7Sjsg 
135c349dbc7Sjsg 	DRM_DEBUG("handle  = %08lx\n", entry->handle);
136c349dbc7Sjsg 	DRM_DEBUG("virtual = %p\n", entry->virtual);
137c349dbc7Sjsg 
138c349dbc7Sjsg 	for (i = (unsigned long)entry->virtual, j = 0; j < pages;
139c349dbc7Sjsg 	     i += PAGE_SIZE, j++) {
140c349dbc7Sjsg 		entry->pagelist[j] = vmalloc_to_page((void *)i);
141c349dbc7Sjsg 		if (!entry->pagelist[j])
142c349dbc7Sjsg 			goto failed;
143c349dbc7Sjsg 		SetPageReserved(entry->pagelist[j]);
144c349dbc7Sjsg 	}
145c349dbc7Sjsg 
146c349dbc7Sjsg 	request->handle = entry->handle;
147c349dbc7Sjsg 
148c349dbc7Sjsg 	dev->sg = entry;
149c349dbc7Sjsg 
150c349dbc7Sjsg #if DEBUG_SCATTER
151c349dbc7Sjsg 	/* Verify that each page points to its virtual address, and vice
152c349dbc7Sjsg 	 * versa.
153c349dbc7Sjsg 	 */
154c349dbc7Sjsg 	{
155c349dbc7Sjsg 		int error = 0;
156c349dbc7Sjsg 
157c349dbc7Sjsg 		for (i = 0; i < pages; i++) {
158c349dbc7Sjsg 			unsigned long *tmp;
159c349dbc7Sjsg 
160c349dbc7Sjsg 			tmp = page_address(entry->pagelist[i]);
161c349dbc7Sjsg 			for (j = 0;
162c349dbc7Sjsg 			     j < PAGE_SIZE / sizeof(unsigned long);
163c349dbc7Sjsg 			     j++, tmp++) {
164c349dbc7Sjsg 				*tmp = 0xcafebabe;
165c349dbc7Sjsg 			}
166c349dbc7Sjsg 			tmp = (unsigned long *)((u8 *) entry->virtual +
167c349dbc7Sjsg 						(PAGE_SIZE * i));
168c349dbc7Sjsg 			for (j = 0;
169c349dbc7Sjsg 			     j < PAGE_SIZE / sizeof(unsigned long);
170c349dbc7Sjsg 			     j++, tmp++) {
171c349dbc7Sjsg 				if (*tmp != 0xcafebabe && error == 0) {
172c349dbc7Sjsg 					error = 1;
173c349dbc7Sjsg 					DRM_ERROR("Scatter allocation error, "
174c349dbc7Sjsg 						  "pagelist does not match "
175c349dbc7Sjsg 						  "virtual mapping\n");
176c349dbc7Sjsg 				}
177c349dbc7Sjsg 			}
178c349dbc7Sjsg 			tmp = page_address(entry->pagelist[i]);
179c349dbc7Sjsg 			for (j = 0;
180c349dbc7Sjsg 			     j < PAGE_SIZE / sizeof(unsigned long);
181c349dbc7Sjsg 			     j++, tmp++) {
182c349dbc7Sjsg 				*tmp = 0;
183c349dbc7Sjsg 			}
184c349dbc7Sjsg 		}
185c349dbc7Sjsg 		if (error == 0)
186c349dbc7Sjsg 			DRM_ERROR("Scatter allocation matches pagelist\n");
187c349dbc7Sjsg 	}
188c349dbc7Sjsg #endif
189c349dbc7Sjsg 
190c349dbc7Sjsg 	return 0;
191c349dbc7Sjsg 
192c349dbc7Sjsg       failed:
193c349dbc7Sjsg 	drm_sg_cleanup(entry);
194c349dbc7Sjsg 	return -ENOMEM;
195c349dbc7Sjsg }
196c349dbc7Sjsg 
drm_legacy_sg_free(struct drm_device * dev,void * data,struct drm_file * file_priv)197c349dbc7Sjsg int drm_legacy_sg_free(struct drm_device *dev, void *data,
198c349dbc7Sjsg 		       struct drm_file *file_priv)
199c349dbc7Sjsg {
200c349dbc7Sjsg 	struct drm_scatter_gather *request = data;
201c349dbc7Sjsg 	struct drm_sg_mem *entry;
202c349dbc7Sjsg 
203c349dbc7Sjsg 	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
204c349dbc7Sjsg 		return -EOPNOTSUPP;
205c349dbc7Sjsg 
206c349dbc7Sjsg 	if (!drm_core_check_feature(dev, DRIVER_SG))
207c349dbc7Sjsg 		return -EOPNOTSUPP;
208c349dbc7Sjsg 
209c349dbc7Sjsg 	entry = dev->sg;
210c349dbc7Sjsg 	dev->sg = NULL;
211c349dbc7Sjsg 
212c349dbc7Sjsg 	if (!entry || entry->handle != request->handle)
213c349dbc7Sjsg 		return -EINVAL;
214c349dbc7Sjsg 
215c349dbc7Sjsg 	DRM_DEBUG("virtual  = %p\n", entry->virtual);
216c349dbc7Sjsg 
217c349dbc7Sjsg 	drm_sg_cleanup(entry);
218c349dbc7Sjsg 
219c349dbc7Sjsg 	return 0;
220c349dbc7Sjsg }
221