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