xref: /freebsd/sys/dev/drm2/drm_bufs.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
1592ffb21SWarner Losh /**
2592ffb21SWarner Losh  * \file drm_bufs.c
3592ffb21SWarner Losh  * Generic buffer template
4592ffb21SWarner Losh  *
5592ffb21SWarner Losh  * \author Rickard E. (Rik) Faith <faith@valinux.com>
6592ffb21SWarner Losh  * \author Gareth Hughes <gareth@valinux.com>
7592ffb21SWarner Losh  */
8592ffb21SWarner Losh 
9592ffb21SWarner Losh /*
10592ffb21SWarner Losh  * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com
11592ffb21SWarner Losh  *
12592ffb21SWarner Losh  * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
13592ffb21SWarner Losh  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
14592ffb21SWarner Losh  * All Rights Reserved.
15592ffb21SWarner Losh  *
16592ffb21SWarner Losh  * Permission is hereby granted, free of charge, to any person obtaining a
17592ffb21SWarner Losh  * copy of this software and associated documentation files (the "Software"),
18592ffb21SWarner Losh  * to deal in the Software without restriction, including without limitation
19592ffb21SWarner Losh  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
20592ffb21SWarner Losh  * and/or sell copies of the Software, and to permit persons to whom the
21592ffb21SWarner Losh  * Software is furnished to do so, subject to the following conditions:
22592ffb21SWarner Losh  *
23592ffb21SWarner Losh  * The above copyright notice and this permission notice (including the next
24592ffb21SWarner Losh  * paragraph) shall be included in all copies or substantial portions of the
25592ffb21SWarner Losh  * Software.
26592ffb21SWarner Losh  *
27592ffb21SWarner Losh  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28592ffb21SWarner Losh  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29592ffb21SWarner Losh  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
30592ffb21SWarner Losh  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
31592ffb21SWarner Losh  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
32592ffb21SWarner Losh  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
33592ffb21SWarner Losh  * OTHER DEALINGS IN THE SOFTWARE.
34592ffb21SWarner Losh  */
35592ffb21SWarner Losh 
36592ffb21SWarner Losh #include <sys/param.h>
37592ffb21SWarner Losh #include <sys/shm.h>
38592ffb21SWarner Losh 
39592ffb21SWarner Losh #include <dev/pci/pcireg.h>
40592ffb21SWarner Losh 
41592ffb21SWarner Losh #include <dev/drm2/drmP.h>
42592ffb21SWarner Losh 
43592ffb21SWarner Losh /* Allocation of PCI memory resources (framebuffer, registers, etc.) for
44592ffb21SWarner Losh  * drm_get_resource_*.  Note that they are not RF_ACTIVE, so there's no virtual
45592ffb21SWarner Losh  * address for accessing them.  Cleaned up at unload.
46592ffb21SWarner Losh  */
drm_alloc_resource(struct drm_device * dev,int resource)47592ffb21SWarner Losh static int drm_alloc_resource(struct drm_device *dev, int resource)
48592ffb21SWarner Losh {
49592ffb21SWarner Losh 	struct resource *res;
50592ffb21SWarner Losh 	int rid;
51592ffb21SWarner Losh 
52592ffb21SWarner Losh 	if (resource >= DRM_MAX_PCI_RESOURCE) {
53592ffb21SWarner Losh 		DRM_ERROR("Resource %d too large\n", resource);
54592ffb21SWarner Losh 		return 1;
55592ffb21SWarner Losh 	}
56592ffb21SWarner Losh 
57592ffb21SWarner Losh 	if (dev->pcir[resource] != NULL) {
58592ffb21SWarner Losh 		return 0;
59592ffb21SWarner Losh 	}
60592ffb21SWarner Losh 
61592ffb21SWarner Losh 	rid = PCIR_BAR(resource);
62592ffb21SWarner Losh 	res = bus_alloc_resource_any(dev->dev, SYS_RES_MEMORY, &rid,
63592ffb21SWarner Losh 	    RF_SHAREABLE);
64592ffb21SWarner Losh 	if (res == NULL) {
65592ffb21SWarner Losh 		DRM_ERROR("Couldn't find resource 0x%x\n", resource);
66592ffb21SWarner Losh 		return 1;
67592ffb21SWarner Losh 	}
68592ffb21SWarner Losh 
69592ffb21SWarner Losh 	if (dev->pcir[resource] == NULL) {
70592ffb21SWarner Losh 		dev->pcirid[resource] = rid;
71592ffb21SWarner Losh 		dev->pcir[resource] = res;
72592ffb21SWarner Losh 	}
73592ffb21SWarner Losh 
74592ffb21SWarner Losh 	return 0;
75592ffb21SWarner Losh }
76592ffb21SWarner Losh 
drm_get_resource_start(struct drm_device * dev,unsigned int resource)77592ffb21SWarner Losh unsigned long drm_get_resource_start(struct drm_device *dev,
78592ffb21SWarner Losh 				     unsigned int resource)
79592ffb21SWarner Losh {
80592ffb21SWarner Losh 	unsigned long start;
81592ffb21SWarner Losh 
82592ffb21SWarner Losh 	mtx_lock(&dev->pcir_lock);
83592ffb21SWarner Losh 
84592ffb21SWarner Losh 	if (drm_alloc_resource(dev, resource) != 0)
85592ffb21SWarner Losh 		return 0;
86592ffb21SWarner Losh 
87592ffb21SWarner Losh 	start = rman_get_start(dev->pcir[resource]);
88592ffb21SWarner Losh 
89592ffb21SWarner Losh 	mtx_unlock(&dev->pcir_lock);
90592ffb21SWarner Losh 
91592ffb21SWarner Losh 	return (start);
92592ffb21SWarner Losh }
93592ffb21SWarner Losh 
drm_get_resource_len(struct drm_device * dev,unsigned int resource)94592ffb21SWarner Losh unsigned long drm_get_resource_len(struct drm_device *dev,
95592ffb21SWarner Losh 				   unsigned int resource)
96592ffb21SWarner Losh {
97592ffb21SWarner Losh 	unsigned long len;
98592ffb21SWarner Losh 
99592ffb21SWarner Losh 	mtx_lock(&dev->pcir_lock);
100592ffb21SWarner Losh 
101592ffb21SWarner Losh 	if (drm_alloc_resource(dev, resource) != 0)
102592ffb21SWarner Losh 		return 0;
103592ffb21SWarner Losh 
104592ffb21SWarner Losh 	len = rman_get_size(dev->pcir[resource]);
105592ffb21SWarner Losh 
106592ffb21SWarner Losh 	mtx_unlock(&dev->pcir_lock);
107592ffb21SWarner Losh 
108592ffb21SWarner Losh 	return (len);
109592ffb21SWarner Losh }
110592ffb21SWarner Losh 
drm_find_matching_map(struct drm_device * dev,struct drm_local_map * map)111592ffb21SWarner Losh static struct drm_map_list *drm_find_matching_map(struct drm_device *dev,
112592ffb21SWarner Losh 						  struct drm_local_map *map)
113592ffb21SWarner Losh {
114592ffb21SWarner Losh 	struct drm_map_list *entry;
115592ffb21SWarner Losh 	list_for_each_entry(entry, &dev->maplist, head) {
116592ffb21SWarner Losh 		/*
117592ffb21SWarner Losh 		 * Because the kernel-userspace ABI is fixed at a 32-bit offset
118592ffb21SWarner Losh 		 * while PCI resources may live above that, we only compare the
119592ffb21SWarner Losh 		 * lower 32 bits of the map offset for maps of type
120592ffb21SWarner Losh 		 * _DRM_FRAMEBUFFER or _DRM_REGISTERS.
121592ffb21SWarner Losh 		 * It is assumed that if a driver have more than one resource
122592ffb21SWarner Losh 		 * of each type, the lower 32 bits are different.
123592ffb21SWarner Losh 		 */
124592ffb21SWarner Losh 		if (!entry->map ||
125592ffb21SWarner Losh 		    map->type != entry->map->type ||
126592ffb21SWarner Losh 		    entry->master != dev->primary->master)
127592ffb21SWarner Losh 			continue;
128592ffb21SWarner Losh 		switch (map->type) {
129592ffb21SWarner Losh 		case _DRM_SHM:
130592ffb21SWarner Losh 			if (map->flags != _DRM_CONTAINS_LOCK)
131592ffb21SWarner Losh 				break;
132592ffb21SWarner Losh 			return entry;
133592ffb21SWarner Losh 		case _DRM_REGISTERS:
134592ffb21SWarner Losh 		case _DRM_FRAME_BUFFER:
135592ffb21SWarner Losh 			if ((entry->map->offset & 0xffffffff) ==
136592ffb21SWarner Losh 			    (map->offset & 0xffffffff))
137592ffb21SWarner Losh 				return entry;
138592ffb21SWarner Losh 		default: /* Make gcc happy */
139592ffb21SWarner Losh 			;
140592ffb21SWarner Losh 		}
141592ffb21SWarner Losh 		if (entry->map->offset == map->offset)
142592ffb21SWarner Losh 			return entry;
143592ffb21SWarner Losh 	}
144592ffb21SWarner Losh 
145592ffb21SWarner Losh 	return NULL;
146592ffb21SWarner Losh }
147592ffb21SWarner Losh 
drm_map_handle(struct drm_device * dev,struct drm_hash_item * hash,unsigned long user_token,int hashed_handle,int shm)148592ffb21SWarner Losh static int drm_map_handle(struct drm_device *dev, struct drm_hash_item *hash,
149592ffb21SWarner Losh 			  unsigned long user_token, int hashed_handle, int shm)
150592ffb21SWarner Losh {
151592ffb21SWarner Losh 	int use_hashed_handle, shift;
152592ffb21SWarner Losh 	unsigned long add;
153592ffb21SWarner Losh 
154592ffb21SWarner Losh #if (BITS_PER_LONG == 64)
155592ffb21SWarner Losh 	use_hashed_handle = ((user_token & 0xFFFFFFFF00000000UL) || hashed_handle);
156592ffb21SWarner Losh #elif (BITS_PER_LONG == 32)
157592ffb21SWarner Losh 	use_hashed_handle = hashed_handle;
158592ffb21SWarner Losh #else
159592ffb21SWarner Losh #error Unsupported long size. Neither 64 nor 32 bits.
160592ffb21SWarner Losh #endif
161592ffb21SWarner Losh 
162592ffb21SWarner Losh 	if (!use_hashed_handle) {
163592ffb21SWarner Losh 		int ret;
164592ffb21SWarner Losh 		hash->key = user_token >> PAGE_SHIFT;
165592ffb21SWarner Losh 		ret = drm_ht_insert_item(&dev->map_hash, hash);
166592ffb21SWarner Losh 		if (ret != -EINVAL)
167592ffb21SWarner Losh 			return ret;
168592ffb21SWarner Losh 	}
169592ffb21SWarner Losh 
170592ffb21SWarner Losh 	shift = 0;
171592ffb21SWarner Losh 	add = DRM_MAP_HASH_OFFSET >> PAGE_SHIFT;
172592ffb21SWarner Losh 	if (shm && (SHMLBA > PAGE_SIZE)) {
173592ffb21SWarner Losh 		int bits = ilog2(SHMLBA >> PAGE_SHIFT) + 1;
174592ffb21SWarner Losh 
175592ffb21SWarner Losh 		/* For shared memory, we have to preserve the SHMLBA
176592ffb21SWarner Losh 		 * bits of the eventual vma->vm_pgoff value during
177592ffb21SWarner Losh 		 * mmap().  Otherwise we run into cache aliasing problems
178592ffb21SWarner Losh 		 * on some platforms.  On these platforms, the pgoff of
179592ffb21SWarner Losh 		 * a mmap() request is used to pick a suitable virtual
180592ffb21SWarner Losh 		 * address for the mmap() region such that it will not
181592ffb21SWarner Losh 		 * cause cache aliasing problems.
182592ffb21SWarner Losh 		 *
183592ffb21SWarner Losh 		 * Therefore, make sure the SHMLBA relevant bits of the
184592ffb21SWarner Losh 		 * hash value we use are equal to those in the original
185592ffb21SWarner Losh 		 * kernel virtual address.
186592ffb21SWarner Losh 		 */
187592ffb21SWarner Losh 		shift = bits;
188592ffb21SWarner Losh 		add |= ((user_token >> PAGE_SHIFT) & ((1UL << bits) - 1UL));
189592ffb21SWarner Losh 	}
190592ffb21SWarner Losh 
191592ffb21SWarner Losh 	return drm_ht_just_insert_please(&dev->map_hash, hash,
192592ffb21SWarner Losh 					 user_token, 32 - PAGE_SHIFT - 3,
193592ffb21SWarner Losh 					 shift, add);
194592ffb21SWarner Losh }
195592ffb21SWarner Losh 
196592ffb21SWarner Losh /**
197592ffb21SWarner Losh  * Core function to create a range of memory available for mapping by a
198592ffb21SWarner Losh  * non-root process.
199592ffb21SWarner Losh  *
200592ffb21SWarner Losh  * Adjusts the memory offset to its absolute value according to the mapping
201592ffb21SWarner Losh  * type.  Adds the map to the map list drm_device::maplist. Adds MTRR's where
202592ffb21SWarner Losh  * applicable and if supported by the kernel.
203592ffb21SWarner Losh  */
drm_addmap_core(struct drm_device * dev,resource_size_t offset,unsigned int size,enum drm_map_type type,enum drm_map_flags flags,struct drm_map_list ** maplist)204592ffb21SWarner Losh static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
205592ffb21SWarner Losh 			   unsigned int size, enum drm_map_type type,
206592ffb21SWarner Losh 			   enum drm_map_flags flags,
207592ffb21SWarner Losh 			   struct drm_map_list ** maplist)
208592ffb21SWarner Losh {
209592ffb21SWarner Losh 	struct drm_local_map *map;
210592ffb21SWarner Losh 	struct drm_map_list *list;
211592ffb21SWarner Losh 	drm_dma_handle_t *dmah;
212592ffb21SWarner Losh 	unsigned long user_token;
213592ffb21SWarner Losh 	int ret;
214592ffb21SWarner Losh 	int align;
215592ffb21SWarner Losh 
216592ffb21SWarner Losh 	map = malloc(sizeof(*map), DRM_MEM_MAPS, M_NOWAIT);
217592ffb21SWarner Losh 	if (!map)
218592ffb21SWarner Losh 		return -ENOMEM;
219592ffb21SWarner Losh 
220592ffb21SWarner Losh 	map->offset = offset;
221592ffb21SWarner Losh 	map->size = size;
222592ffb21SWarner Losh 	map->flags = flags;
223592ffb21SWarner Losh 	map->type = type;
224592ffb21SWarner Losh 
225592ffb21SWarner Losh 	/* Only allow shared memory to be removable since we only keep enough
226592ffb21SWarner Losh 	 * book keeping information about shared memory to allow for removal
227592ffb21SWarner Losh 	 * when processes fork.
228592ffb21SWarner Losh 	 */
229592ffb21SWarner Losh 	if ((map->flags & _DRM_REMOVABLE) && map->type != _DRM_SHM) {
230592ffb21SWarner Losh 		free(map, DRM_MEM_MAPS);
231592ffb21SWarner Losh 		return -EINVAL;
232592ffb21SWarner Losh 	}
233592ffb21SWarner Losh 	DRM_DEBUG("offset = 0x%08llx, size = 0x%08lx, type = %d\n",
234592ffb21SWarner Losh 		  (unsigned long long)map->offset, map->size, map->type);
235592ffb21SWarner Losh 
236592ffb21SWarner Losh 	/* page-align _DRM_SHM maps. They are allocated here so there is no security
237592ffb21SWarner Losh 	 * hole created by that and it works around various broken drivers that use
238592ffb21SWarner Losh 	 * a non-aligned quantity to map the SAREA. --BenH
239592ffb21SWarner Losh 	 */
240592ffb21SWarner Losh 	if (map->type == _DRM_SHM)
241592ffb21SWarner Losh 		map->size = PAGE_ALIGN(map->size);
242592ffb21SWarner Losh 
243592ffb21SWarner Losh 	/*
244592ffb21SWarner Losh 	 * FreeBSD port note: FreeBSD's PAGE_MASK is the inverse of
245592ffb21SWarner Losh 	 * Linux's one. That's why the test below doesn't inverse the
246592ffb21SWarner Losh 	 * constant.
247592ffb21SWarner Losh 	 */
248592ffb21SWarner Losh 	if ((map->offset & ((resource_size_t)PAGE_MASK)) || (map->size & (PAGE_MASK))) {
249592ffb21SWarner Losh 		free(map, DRM_MEM_MAPS);
250592ffb21SWarner Losh 		return -EINVAL;
251592ffb21SWarner Losh 	}
252592ffb21SWarner Losh 	map->mtrr = -1;
253592ffb21SWarner Losh 	map->handle = NULL;
254592ffb21SWarner Losh 
255592ffb21SWarner Losh 	switch (map->type) {
256592ffb21SWarner Losh 	case _DRM_REGISTERS:
257592ffb21SWarner Losh 	case _DRM_FRAME_BUFFER:
258592ffb21SWarner Losh #ifdef __linux__
259592ffb21SWarner Losh #if !defined(__sparc__) && !defined(__alpha__) && !defined(__ia64__) && !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__arm__)
260592ffb21SWarner Losh 		if (map->offset + (map->size-1) < map->offset ||
261592ffb21SWarner Losh 		    map->offset < virt_to_phys(high_memory)) {
262592ffb21SWarner Losh 			kfree(map);
263592ffb21SWarner Losh 			return -EINVAL;
264592ffb21SWarner Losh 		}
265592ffb21SWarner Losh #endif
266592ffb21SWarner Losh #endif
267592ffb21SWarner Losh 		/* Some drivers preinitialize some maps, without the X Server
268592ffb21SWarner Losh 		 * needing to be aware of it.  Therefore, we just return success
269592ffb21SWarner Losh 		 * when the server tries to create a duplicate map.
270592ffb21SWarner Losh 		 */
271592ffb21SWarner Losh 		list = drm_find_matching_map(dev, map);
272592ffb21SWarner Losh 		if (list != NULL) {
273592ffb21SWarner Losh 			if (list->map->size != map->size) {
274592ffb21SWarner Losh 				DRM_DEBUG("Matching maps of type %d with "
275592ffb21SWarner Losh 					  "mismatched sizes, (%ld vs %ld)\n",
276592ffb21SWarner Losh 					  map->type, map->size,
277592ffb21SWarner Losh 					  list->map->size);
278592ffb21SWarner Losh 				list->map->size = map->size;
279592ffb21SWarner Losh 			}
280592ffb21SWarner Losh 
281592ffb21SWarner Losh 			free(map, DRM_MEM_MAPS);
282592ffb21SWarner Losh 			*maplist = list;
283592ffb21SWarner Losh 			return 0;
284592ffb21SWarner Losh 		}
285592ffb21SWarner Losh 
286592ffb21SWarner Losh 		if (drm_core_has_MTRR(dev)) {
287592ffb21SWarner Losh 			if (map->type == _DRM_FRAME_BUFFER ||
288592ffb21SWarner Losh 			    (map->flags & _DRM_WRITE_COMBINING)) {
289592ffb21SWarner Losh 				if (drm_mtrr_add(
290592ffb21SWarner Losh 				    map->offset, map->size,
291592ffb21SWarner Losh 				    DRM_MTRR_WC) == 0)
292592ffb21SWarner Losh 					map->mtrr = 1;
293592ffb21SWarner Losh 			}
294592ffb21SWarner Losh 		}
295592ffb21SWarner Losh 		if (map->type == _DRM_REGISTERS) {
296592ffb21SWarner Losh 			drm_core_ioremap(map, dev);
297592ffb21SWarner Losh 			if (!map->handle) {
298592ffb21SWarner Losh 				free(map, DRM_MEM_MAPS);
299592ffb21SWarner Losh 				return -ENOMEM;
300592ffb21SWarner Losh 			}
301592ffb21SWarner Losh 		}
302592ffb21SWarner Losh 
303592ffb21SWarner Losh 		break;
304592ffb21SWarner Losh 	case _DRM_SHM:
305592ffb21SWarner Losh 		list = drm_find_matching_map(dev, map);
306592ffb21SWarner Losh 		if (list != NULL) {
307592ffb21SWarner Losh 			if(list->map->size != map->size) {
308592ffb21SWarner Losh 				DRM_DEBUG("Matching maps of type %d with "
309592ffb21SWarner Losh 					  "mismatched sizes, (%ld vs %ld)\n",
310592ffb21SWarner Losh 					  map->type, map->size, list->map->size);
311592ffb21SWarner Losh 				list->map->size = map->size;
312592ffb21SWarner Losh 			}
313592ffb21SWarner Losh 
314592ffb21SWarner Losh 			free(map, DRM_MEM_MAPS);
315592ffb21SWarner Losh 			*maplist = list;
316592ffb21SWarner Losh 			return 0;
317592ffb21SWarner Losh 		}
318592ffb21SWarner Losh 		map->handle = malloc(map->size, DRM_MEM_MAPS, M_NOWAIT);
319592ffb21SWarner Losh 		DRM_DEBUG("%lu %d %p\n",
320592ffb21SWarner Losh 			  map->size, drm_order(map->size), map->handle);
321592ffb21SWarner Losh 		if (!map->handle) {
322592ffb21SWarner Losh 			free(map, DRM_MEM_MAPS);
323592ffb21SWarner Losh 			return -ENOMEM;
324592ffb21SWarner Losh 		}
325592ffb21SWarner Losh 		map->offset = (unsigned long)map->handle;
326592ffb21SWarner Losh 		if (map->flags & _DRM_CONTAINS_LOCK) {
327592ffb21SWarner Losh 			/* Prevent a 2nd X Server from creating a 2nd lock */
328592ffb21SWarner Losh 			if (dev->primary->master->lock.hw_lock != NULL) {
329592ffb21SWarner Losh 				free(map->handle, DRM_MEM_MAPS);
330592ffb21SWarner Losh 				free(map, DRM_MEM_MAPS);
331592ffb21SWarner Losh 				return -EBUSY;
332592ffb21SWarner Losh 			}
333592ffb21SWarner Losh 			dev->sigdata.lock = dev->primary->master->lock.hw_lock = map->handle;	/* Pointer to lock */
334592ffb21SWarner Losh 		}
335592ffb21SWarner Losh 		break;
336592ffb21SWarner Losh 	case _DRM_AGP: {
337592ffb21SWarner Losh 		struct drm_agp_mem *entry;
338592ffb21SWarner Losh 		int valid = 0;
339592ffb21SWarner Losh 
340592ffb21SWarner Losh 		if (!drm_core_has_AGP(dev)) {
341592ffb21SWarner Losh 			free(map, DRM_MEM_MAPS);
342592ffb21SWarner Losh 			return -EINVAL;
343592ffb21SWarner Losh 		}
344592ffb21SWarner Losh #ifdef __linux__
345592ffb21SWarner Losh #ifdef __alpha__
346592ffb21SWarner Losh 		map->offset += dev->hose->mem_space->start;
347592ffb21SWarner Losh #endif
348592ffb21SWarner Losh #endif
349592ffb21SWarner Losh 		/* In some cases (i810 driver), user space may have already
350592ffb21SWarner Losh 		 * added the AGP base itself, because dev->agp->base previously
351592ffb21SWarner Losh 		 * only got set during AGP enable.  So, only add the base
352592ffb21SWarner Losh 		 * address if the map's offset isn't already within the
353592ffb21SWarner Losh 		 * aperture.
354592ffb21SWarner Losh 		 */
355592ffb21SWarner Losh 		if (map->offset < dev->agp->base ||
356592ffb21SWarner Losh 		    map->offset > dev->agp->base +
357592ffb21SWarner Losh 		    dev->agp->agp_info.ai_aperture_size * 1024 * 1024 - 1) {
358592ffb21SWarner Losh 			map->offset += dev->agp->base;
359592ffb21SWarner Losh 		}
360592ffb21SWarner Losh 		map->mtrr = dev->agp->agp_mtrr;	/* for getmap */
361592ffb21SWarner Losh 
362592ffb21SWarner Losh 		/* This assumes the DRM is in total control of AGP space.
363592ffb21SWarner Losh 		 * It's not always the case as AGP can be in the control
364592ffb21SWarner Losh 		 * of user space (i.e. i810 driver). So this loop will get
365592ffb21SWarner Losh 		 * skipped and we double check that dev->agp->memory is
366592ffb21SWarner Losh 		 * actually set as well as being invalid before EPERM'ing
367592ffb21SWarner Losh 		 */
368592ffb21SWarner Losh 		list_for_each_entry(entry, &dev->agp->memory, head) {
369592ffb21SWarner Losh 			if ((map->offset >= entry->bound) &&
370592ffb21SWarner Losh 			    (map->offset + map->size <= entry->bound + entry->pages * PAGE_SIZE)) {
371592ffb21SWarner Losh 				valid = 1;
372592ffb21SWarner Losh 				break;
373592ffb21SWarner Losh 			}
374592ffb21SWarner Losh 		}
375592ffb21SWarner Losh 		if (!list_empty(&dev->agp->memory) && !valid) {
376592ffb21SWarner Losh 			free(map, DRM_MEM_MAPS);
377592ffb21SWarner Losh 			return -EPERM;
378592ffb21SWarner Losh 		}
379592ffb21SWarner Losh 		DRM_DEBUG("AGP offset = 0x%08llx, size = 0x%08lx\n",
380592ffb21SWarner Losh 			  (unsigned long long)map->offset, map->size);
381592ffb21SWarner Losh 
382592ffb21SWarner Losh 		break;
383592ffb21SWarner Losh 	}
384592ffb21SWarner Losh 	case _DRM_GEM:
385592ffb21SWarner Losh 		DRM_ERROR("tried to addmap GEM object\n");
386592ffb21SWarner Losh 		break;
387592ffb21SWarner Losh 	case _DRM_SCATTER_GATHER:
388592ffb21SWarner Losh 		if (!dev->sg) {
389592ffb21SWarner Losh 			free(map, DRM_MEM_MAPS);
390592ffb21SWarner Losh 			return -EINVAL;
391592ffb21SWarner Losh 		}
392*f49fd63aSJohn Baldwin 		map->handle = (char *)dev->sg->vaddr + offset;
393*f49fd63aSJohn Baldwin 		map->offset += (uintptr_t)dev->sg->vaddr;
394592ffb21SWarner Losh 		break;
395592ffb21SWarner Losh 	case _DRM_CONSISTENT:
396592ffb21SWarner Losh 		/* dma_addr_t is 64bit on i386 with CONFIG_HIGHMEM64G,
397592ffb21SWarner Losh 		 * As we're limiting the address to 2^32-1 (or less),
398592ffb21SWarner Losh 		 * casting it down to 32 bits is no problem, but we
399592ffb21SWarner Losh 		 * need to point to a 64bit variable first. */
400592ffb21SWarner Losh 		align = map->size;
401592ffb21SWarner Losh 		if ((align & (align - 1)) != 0)
402592ffb21SWarner Losh 			align = PAGE_SIZE;
403592ffb21SWarner Losh 		dmah = drm_pci_alloc(dev, map->size, align, BUS_SPACE_MAXADDR);
404592ffb21SWarner Losh 		if (!dmah) {
405592ffb21SWarner Losh 			free(map, DRM_MEM_MAPS);
406592ffb21SWarner Losh 			return -ENOMEM;
407592ffb21SWarner Losh 		}
408592ffb21SWarner Losh 		map->handle = dmah->vaddr;
409592ffb21SWarner Losh 		map->offset = dmah->busaddr;
410592ffb21SWarner Losh 		map->dmah = dmah;
411592ffb21SWarner Losh 		break;
412592ffb21SWarner Losh 	default:
413592ffb21SWarner Losh 		free(map, DRM_MEM_MAPS);
414592ffb21SWarner Losh 		return -EINVAL;
415592ffb21SWarner Losh 	}
416592ffb21SWarner Losh 
417592ffb21SWarner Losh 	list = malloc(sizeof(*list), DRM_MEM_MAPS, M_ZERO | M_NOWAIT);
418592ffb21SWarner Losh 	if (!list) {
419592ffb21SWarner Losh 		if (map->type == _DRM_REGISTERS)
420592ffb21SWarner Losh 			drm_core_ioremapfree(map, dev);
421592ffb21SWarner Losh 		free(map, DRM_MEM_MAPS);
422592ffb21SWarner Losh 		return -EINVAL;
423592ffb21SWarner Losh 	}
424592ffb21SWarner Losh 	list->map = map;
425592ffb21SWarner Losh 
426592ffb21SWarner Losh 	DRM_LOCK(dev);
427592ffb21SWarner Losh 	list_add(&list->head, &dev->maplist);
428592ffb21SWarner Losh 
429592ffb21SWarner Losh 	/* Assign a 32-bit handle */
430592ffb21SWarner Losh 	/* We do it here so that dev->struct_mutex protects the increment */
431592ffb21SWarner Losh 	user_token = (map->type == _DRM_SHM) ? (unsigned long)map->handle :
432592ffb21SWarner Losh 		map->offset;
433592ffb21SWarner Losh 	ret = drm_map_handle(dev, &list->hash, user_token, 0,
434592ffb21SWarner Losh 			     (map->type == _DRM_SHM));
435592ffb21SWarner Losh 	if (ret) {
436592ffb21SWarner Losh 		if (map->type == _DRM_REGISTERS)
437592ffb21SWarner Losh 			drm_core_ioremapfree(map, dev);
438592ffb21SWarner Losh 		free(map, DRM_MEM_MAPS);
439592ffb21SWarner Losh 		free(list, DRM_MEM_MAPS);
440592ffb21SWarner Losh 		DRM_UNLOCK(dev);
441592ffb21SWarner Losh 		return ret;
442592ffb21SWarner Losh 	}
443592ffb21SWarner Losh 
444592ffb21SWarner Losh 	list->user_token = list->hash.key << PAGE_SHIFT;
445592ffb21SWarner Losh 	DRM_UNLOCK(dev);
446592ffb21SWarner Losh 
447592ffb21SWarner Losh 	if (!(map->flags & _DRM_DRIVER))
448592ffb21SWarner Losh 		list->master = dev->primary->master;
449592ffb21SWarner Losh 	*maplist = list;
450592ffb21SWarner Losh 	return 0;
451592ffb21SWarner Losh 	}
452592ffb21SWarner Losh 
drm_addmap(struct drm_device * dev,resource_size_t offset,unsigned int size,enum drm_map_type type,enum drm_map_flags flags,struct drm_local_map ** map_ptr)453592ffb21SWarner Losh int drm_addmap(struct drm_device * dev, resource_size_t offset,
454592ffb21SWarner Losh 	       unsigned int size, enum drm_map_type type,
455592ffb21SWarner Losh 	       enum drm_map_flags flags, struct drm_local_map ** map_ptr)
456592ffb21SWarner Losh {
457592ffb21SWarner Losh 	struct drm_map_list *list;
458592ffb21SWarner Losh 	int rc;
459592ffb21SWarner Losh 
460592ffb21SWarner Losh 	rc = drm_addmap_core(dev, offset, size, type, flags, &list);
461592ffb21SWarner Losh 	if (!rc)
462592ffb21SWarner Losh 		*map_ptr = list->map;
463592ffb21SWarner Losh 	return rc;
464592ffb21SWarner Losh }
465592ffb21SWarner Losh 
466592ffb21SWarner Losh EXPORT_SYMBOL(drm_addmap);
467592ffb21SWarner Losh 
468592ffb21SWarner Losh /**
469592ffb21SWarner Losh  * Ioctl to specify a range of memory that is available for mapping by a
470592ffb21SWarner Losh  * non-root process.
471592ffb21SWarner Losh  *
472592ffb21SWarner Losh  * \param inode device inode.
473592ffb21SWarner Losh  * \param file_priv DRM file private.
474592ffb21SWarner Losh  * \param cmd command.
475592ffb21SWarner Losh  * \param arg pointer to a drm_map structure.
476592ffb21SWarner Losh  * \return zero on success or a negative value on error.
477592ffb21SWarner Losh  *
478592ffb21SWarner Losh  */
drm_addmap_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)479592ffb21SWarner Losh int drm_addmap_ioctl(struct drm_device *dev, void *data,
480592ffb21SWarner Losh 		     struct drm_file *file_priv)
481592ffb21SWarner Losh {
482592ffb21SWarner Losh 	struct drm_map *map = data;
483592ffb21SWarner Losh 	struct drm_map_list *maplist;
484592ffb21SWarner Losh 	int err;
485592ffb21SWarner Losh 
486592ffb21SWarner Losh 	if (!(DRM_SUSER(DRM_CURPROC) || map->type == _DRM_AGP || map->type == _DRM_SHM))
487592ffb21SWarner Losh 		return -EPERM;
488592ffb21SWarner Losh 
489592ffb21SWarner Losh 	err = drm_addmap_core(dev, map->offset, map->size, map->type,
490592ffb21SWarner Losh 			      map->flags, &maplist);
491592ffb21SWarner Losh 
492592ffb21SWarner Losh 	if (err)
493592ffb21SWarner Losh 		return err;
494592ffb21SWarner Losh 
495592ffb21SWarner Losh 	/* avoid a warning on 64-bit, this casting isn't very nice, but the API is set so too late */
496592ffb21SWarner Losh 	map->handle = (void *)(unsigned long)maplist->user_token;
497592ffb21SWarner Losh 	return 0;
498592ffb21SWarner Losh }
499592ffb21SWarner Losh 
500592ffb21SWarner Losh /**
501592ffb21SWarner Losh  * Remove a map private from list and deallocate resources if the mapping
502592ffb21SWarner Losh  * isn't in use.
503592ffb21SWarner Losh  *
504592ffb21SWarner Losh  * Searches the map on drm_device::maplist, removes it from the list, see if
505592ffb21SWarner Losh  * its being used, and free any associate resource (such as MTRR's) if it's not
506592ffb21SWarner Losh  * being on use.
507592ffb21SWarner Losh  *
508592ffb21SWarner Losh  * \sa drm_addmap
509592ffb21SWarner Losh  */
drm_rmmap_locked(struct drm_device * dev,struct drm_local_map * map)510592ffb21SWarner Losh int drm_rmmap_locked(struct drm_device *dev, struct drm_local_map *map)
511592ffb21SWarner Losh {
512592ffb21SWarner Losh 	struct drm_map_list *r_list = NULL, *list_t;
513592ffb21SWarner Losh 	int found = 0;
514592ffb21SWarner Losh 	struct drm_master *master;
515592ffb21SWarner Losh 
516592ffb21SWarner Losh 	/* Find the list entry for the map and remove it */
517592ffb21SWarner Losh 	list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) {
518592ffb21SWarner Losh 		if (r_list->map == map) {
519592ffb21SWarner Losh 			master = r_list->master;
520592ffb21SWarner Losh 			list_del(&r_list->head);
521592ffb21SWarner Losh 			drm_ht_remove_key(&dev->map_hash,
522592ffb21SWarner Losh 					  r_list->user_token >> PAGE_SHIFT);
523592ffb21SWarner Losh 			free(r_list, DRM_MEM_MAPS);
524592ffb21SWarner Losh 			found = 1;
525592ffb21SWarner Losh 			break;
526592ffb21SWarner Losh 		}
527592ffb21SWarner Losh 	}
528592ffb21SWarner Losh 
529592ffb21SWarner Losh 	if (!found)
530592ffb21SWarner Losh 		return -EINVAL;
531592ffb21SWarner Losh 
532592ffb21SWarner Losh 	switch (map->type) {
533592ffb21SWarner Losh 	case _DRM_REGISTERS:
534592ffb21SWarner Losh 		drm_core_ioremapfree(map, dev);
535592ffb21SWarner Losh 		/* FALLTHROUGH */
536592ffb21SWarner Losh 	case _DRM_FRAME_BUFFER:
537592ffb21SWarner Losh 		if (drm_core_has_MTRR(dev) && map->mtrr >= 0) {
538592ffb21SWarner Losh 			int retcode;
539592ffb21SWarner Losh 			retcode = drm_mtrr_del(map->mtrr, map->offset,
540592ffb21SWarner Losh 			    map->size, DRM_MTRR_WC);
541592ffb21SWarner Losh 			DRM_DEBUG("mtrr_del=%d\n", retcode);
542592ffb21SWarner Losh 		}
543592ffb21SWarner Losh 		break;
544592ffb21SWarner Losh 	case _DRM_SHM:
545592ffb21SWarner Losh 		free(map->handle, DRM_MEM_MAPS);
546592ffb21SWarner Losh 		if (master) {
547592ffb21SWarner Losh 			if (dev->sigdata.lock == master->lock.hw_lock)
548592ffb21SWarner Losh 				dev->sigdata.lock = NULL;
549592ffb21SWarner Losh 			master->lock.hw_lock = NULL;   /* SHM removed */
550592ffb21SWarner Losh 			master->lock.file_priv = NULL;
551592ffb21SWarner Losh 			DRM_WAKEUP_INT((void *)&master->lock.lock_queue);
552592ffb21SWarner Losh 		}
553592ffb21SWarner Losh 		break;
554592ffb21SWarner Losh 	case _DRM_AGP:
555592ffb21SWarner Losh 	case _DRM_SCATTER_GATHER:
556592ffb21SWarner Losh 		break;
557592ffb21SWarner Losh 	case _DRM_CONSISTENT:
558592ffb21SWarner Losh 		drm_pci_free(dev, map->dmah);
559592ffb21SWarner Losh 		break;
560592ffb21SWarner Losh 	case _DRM_GEM:
561592ffb21SWarner Losh 		DRM_ERROR("tried to rmmap GEM object\n");
562592ffb21SWarner Losh 		break;
563592ffb21SWarner Losh 	}
564592ffb21SWarner Losh 	free(map, DRM_MEM_MAPS);
565592ffb21SWarner Losh 
566592ffb21SWarner Losh 	return 0;
567592ffb21SWarner Losh }
568592ffb21SWarner Losh EXPORT_SYMBOL(drm_rmmap_locked);
569592ffb21SWarner Losh 
drm_rmmap(struct drm_device * dev,struct drm_local_map * map)570592ffb21SWarner Losh int drm_rmmap(struct drm_device *dev, struct drm_local_map *map)
571592ffb21SWarner Losh {
572592ffb21SWarner Losh 	int ret;
573592ffb21SWarner Losh 
574592ffb21SWarner Losh 	DRM_LOCK(dev);
575592ffb21SWarner Losh 	ret = drm_rmmap_locked(dev, map);
576592ffb21SWarner Losh 	DRM_UNLOCK(dev);
577592ffb21SWarner Losh 
578592ffb21SWarner Losh 	return ret;
579592ffb21SWarner Losh }
580592ffb21SWarner Losh EXPORT_SYMBOL(drm_rmmap);
581592ffb21SWarner Losh 
582592ffb21SWarner Losh /* The rmmap ioctl appears to be unnecessary.  All mappings are torn down on
583592ffb21SWarner Losh  * the last close of the device, and this is necessary for cleanup when things
584592ffb21SWarner Losh  * exit uncleanly.  Therefore, having userland manually remove mappings seems
585592ffb21SWarner Losh  * like a pointless exercise since they're going away anyway.
586592ffb21SWarner Losh  *
587592ffb21SWarner Losh  * One use case might be after addmap is allowed for normal users for SHM and
588592ffb21SWarner Losh  * gets used by drivers that the server doesn't need to care about.  This seems
589592ffb21SWarner Losh  * unlikely.
590592ffb21SWarner Losh  *
591592ffb21SWarner Losh  * \param inode device inode.
592592ffb21SWarner Losh  * \param file_priv DRM file private.
593592ffb21SWarner Losh  * \param cmd command.
594592ffb21SWarner Losh  * \param arg pointer to a struct drm_map structure.
595592ffb21SWarner Losh  * \return zero on success or a negative value on error.
596592ffb21SWarner Losh  */
drm_rmmap_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)597592ffb21SWarner Losh int drm_rmmap_ioctl(struct drm_device *dev, void *data,
598592ffb21SWarner Losh 		    struct drm_file *file_priv)
599592ffb21SWarner Losh {
600592ffb21SWarner Losh 	struct drm_map *request = data;
601592ffb21SWarner Losh 	struct drm_local_map *map = NULL;
602592ffb21SWarner Losh 	struct drm_map_list *r_list;
603592ffb21SWarner Losh 	int ret;
604592ffb21SWarner Losh 
605592ffb21SWarner Losh 	DRM_LOCK(dev);
606592ffb21SWarner Losh 	list_for_each_entry(r_list, &dev->maplist, head) {
607592ffb21SWarner Losh 		if (r_list->map &&
608592ffb21SWarner Losh 		    r_list->user_token == (unsigned long)request->handle &&
609592ffb21SWarner Losh 		    r_list->map->flags & _DRM_REMOVABLE) {
610592ffb21SWarner Losh 			map = r_list->map;
611592ffb21SWarner Losh 			break;
612592ffb21SWarner Losh 		}
613592ffb21SWarner Losh 	}
614592ffb21SWarner Losh 
615592ffb21SWarner Losh 	/* List has wrapped around to the head pointer, or its empty we didn't
616592ffb21SWarner Losh 	 * find anything.
617592ffb21SWarner Losh 	 */
618592ffb21SWarner Losh 	if (list_empty(&dev->maplist) || !map) {
619592ffb21SWarner Losh 		DRM_UNLOCK(dev);
620592ffb21SWarner Losh 		return -EINVAL;
621592ffb21SWarner Losh 	}
622592ffb21SWarner Losh 
623592ffb21SWarner Losh 	/* Register and framebuffer maps are permanent */
624592ffb21SWarner Losh 	if ((map->type == _DRM_REGISTERS) || (map->type == _DRM_FRAME_BUFFER)) {
625592ffb21SWarner Losh 		DRM_UNLOCK(dev);
626592ffb21SWarner Losh 		return 0;
627592ffb21SWarner Losh 	}
628592ffb21SWarner Losh 
629592ffb21SWarner Losh 	ret = drm_rmmap_locked(dev, map);
630592ffb21SWarner Losh 
631592ffb21SWarner Losh 	DRM_UNLOCK(dev);
632592ffb21SWarner Losh 
633592ffb21SWarner Losh 	return ret;
634592ffb21SWarner Losh }
635592ffb21SWarner Losh 
636592ffb21SWarner Losh /**
637592ffb21SWarner Losh  * Cleanup after an error on one of the addbufs() functions.
638592ffb21SWarner Losh  *
639592ffb21SWarner Losh  * \param dev DRM device.
640592ffb21SWarner Losh  * \param entry buffer entry where the error occurred.
641592ffb21SWarner Losh  *
642592ffb21SWarner Losh  * Frees any pages and buffers associated with the given entry.
643592ffb21SWarner Losh  */
drm_cleanup_buf_error(struct drm_device * dev,struct drm_buf_entry * entry)644592ffb21SWarner Losh static void drm_cleanup_buf_error(struct drm_device * dev,
645592ffb21SWarner Losh 				  struct drm_buf_entry * entry)
646592ffb21SWarner Losh {
647592ffb21SWarner Losh 	int i;
648592ffb21SWarner Losh 
649592ffb21SWarner Losh 	if (entry->seg_count) {
650592ffb21SWarner Losh 		for (i = 0; i < entry->seg_count; i++) {
651592ffb21SWarner Losh 			if (entry->seglist[i]) {
652592ffb21SWarner Losh 				drm_pci_free(dev, entry->seglist[i]);
653592ffb21SWarner Losh 			}
654592ffb21SWarner Losh 		}
655592ffb21SWarner Losh 		free(entry->seglist, DRM_MEM_SEGS);
656592ffb21SWarner Losh 
657592ffb21SWarner Losh 		entry->seg_count = 0;
658592ffb21SWarner Losh 	}
659592ffb21SWarner Losh 
660592ffb21SWarner Losh 	if (entry->buf_count) {
661592ffb21SWarner Losh 		for (i = 0; i < entry->buf_count; i++) {
662592ffb21SWarner Losh 			free(entry->buflist[i].dev_private, DRM_MEM_BUFS);
663592ffb21SWarner Losh 		}
664592ffb21SWarner Losh 		free(entry->buflist, DRM_MEM_BUFS);
665592ffb21SWarner Losh 
666592ffb21SWarner Losh 		entry->buf_count = 0;
667592ffb21SWarner Losh 	}
668592ffb21SWarner Losh }
669592ffb21SWarner Losh 
670592ffb21SWarner Losh #if __OS_HAS_AGP
671592ffb21SWarner Losh /**
672592ffb21SWarner Losh  * Add AGP buffers for DMA transfers.
673592ffb21SWarner Losh  *
674592ffb21SWarner Losh  * \param dev struct drm_device to which the buffers are to be added.
675592ffb21SWarner Losh  * \param request pointer to a struct drm_buf_desc describing the request.
676592ffb21SWarner Losh  * \return zero on success or a negative number on failure.
677592ffb21SWarner Losh  *
678592ffb21SWarner Losh  * After some sanity checks creates a drm_buf structure for each buffer and
679592ffb21SWarner Losh  * reallocates the buffer list of the same size order to accommodate the new
680592ffb21SWarner Losh  * buffers.
681592ffb21SWarner Losh  */
drm_addbufs_agp(struct drm_device * dev,struct drm_buf_desc * request)682592ffb21SWarner Losh int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request)
683592ffb21SWarner Losh {
684592ffb21SWarner Losh 	struct drm_device_dma *dma = dev->dma;
685592ffb21SWarner Losh 	struct drm_buf_entry *entry;
686592ffb21SWarner Losh 	struct drm_agp_mem *agp_entry;
687592ffb21SWarner Losh 	struct drm_buf *buf;
688592ffb21SWarner Losh 	unsigned long offset;
689592ffb21SWarner Losh 	unsigned long agp_offset;
690592ffb21SWarner Losh 	int count;
691592ffb21SWarner Losh 	int order;
692592ffb21SWarner Losh 	int size;
693592ffb21SWarner Losh 	int alignment;
694592ffb21SWarner Losh 	int page_order;
695592ffb21SWarner Losh 	int total;
696592ffb21SWarner Losh 	int byte_count;
697592ffb21SWarner Losh 	int i, valid;
698592ffb21SWarner Losh 	struct drm_buf **temp_buflist;
699592ffb21SWarner Losh 
700592ffb21SWarner Losh 	if (!dma)
701592ffb21SWarner Losh 		return -EINVAL;
702592ffb21SWarner Losh 
703592ffb21SWarner Losh 	count = request->count;
704592ffb21SWarner Losh 	order = drm_order(request->size);
705592ffb21SWarner Losh 	size = 1 << order;
706592ffb21SWarner Losh 
707592ffb21SWarner Losh 	alignment = (request->flags & _DRM_PAGE_ALIGN)
708592ffb21SWarner Losh 	    ? PAGE_ALIGN(size) : size;
709592ffb21SWarner Losh 	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
710592ffb21SWarner Losh 	total = PAGE_SIZE << page_order;
711592ffb21SWarner Losh 
712592ffb21SWarner Losh 	byte_count = 0;
713592ffb21SWarner Losh 	agp_offset = dev->agp->base + request->agp_start;
714592ffb21SWarner Losh 
715592ffb21SWarner Losh 	DRM_DEBUG("count:      %d\n", count);
716592ffb21SWarner Losh 	DRM_DEBUG("order:      %d\n", order);
717592ffb21SWarner Losh 	DRM_DEBUG("size:       %d\n", size);
718592ffb21SWarner Losh 	DRM_DEBUG("agp_offset: %lx\n", agp_offset);
719592ffb21SWarner Losh 	DRM_DEBUG("alignment:  %d\n", alignment);
720592ffb21SWarner Losh 	DRM_DEBUG("page_order: %d\n", page_order);
721592ffb21SWarner Losh 	DRM_DEBUG("total:      %d\n", total);
722592ffb21SWarner Losh 
723592ffb21SWarner Losh 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
724592ffb21SWarner Losh 		return -EINVAL;
725592ffb21SWarner Losh 
726592ffb21SWarner Losh 	/* Make sure buffers are located in AGP memory that we own */
727592ffb21SWarner Losh 	valid = 0;
728592ffb21SWarner Losh 	list_for_each_entry(agp_entry, &dev->agp->memory, head) {
729592ffb21SWarner Losh 		if ((agp_offset >= agp_entry->bound) &&
730592ffb21SWarner Losh 		    (agp_offset + total * count <= agp_entry->bound + agp_entry->pages * PAGE_SIZE)) {
731592ffb21SWarner Losh 			valid = 1;
732592ffb21SWarner Losh 			break;
733592ffb21SWarner Losh 		}
734592ffb21SWarner Losh 	}
735592ffb21SWarner Losh 	if (!list_empty(&dev->agp->memory) && !valid) {
736592ffb21SWarner Losh 		DRM_DEBUG("zone invalid\n");
737592ffb21SWarner Losh 		return -EINVAL;
738592ffb21SWarner Losh 	}
739592ffb21SWarner Losh 	mtx_lock(&dev->count_lock);
740592ffb21SWarner Losh 	if (dev->buf_use) {
741592ffb21SWarner Losh 		mtx_unlock(&dev->count_lock);
742592ffb21SWarner Losh 		return -EBUSY;
743592ffb21SWarner Losh 	}
744592ffb21SWarner Losh 	atomic_inc(&dev->buf_alloc);
745592ffb21SWarner Losh 	mtx_unlock(&dev->count_lock);
746592ffb21SWarner Losh 
747592ffb21SWarner Losh 	DRM_LOCK(dev);
748592ffb21SWarner Losh 	entry = &dma->bufs[order];
749592ffb21SWarner Losh 	if (entry->buf_count) {
750592ffb21SWarner Losh 		DRM_UNLOCK(dev);
751592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
752592ffb21SWarner Losh 		return -ENOMEM;	/* May only call once for each order */
753592ffb21SWarner Losh 	}
754592ffb21SWarner Losh 
755592ffb21SWarner Losh 	if (count < 0 || count > 4096) {
756592ffb21SWarner Losh 		DRM_UNLOCK(dev);
757592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
758592ffb21SWarner Losh 		return -EINVAL;
759592ffb21SWarner Losh 	}
760592ffb21SWarner Losh 
761592ffb21SWarner Losh 	entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS,
762592ffb21SWarner Losh 	    M_NOWAIT | M_ZERO);
763592ffb21SWarner Losh 	if (!entry->buflist) {
764592ffb21SWarner Losh 		DRM_UNLOCK(dev);
765592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
766592ffb21SWarner Losh 		return -ENOMEM;
767592ffb21SWarner Losh 	}
768592ffb21SWarner Losh 
769592ffb21SWarner Losh 	entry->buf_size = size;
770592ffb21SWarner Losh 	entry->page_order = page_order;
771592ffb21SWarner Losh 
772592ffb21SWarner Losh 	offset = 0;
773592ffb21SWarner Losh 
774592ffb21SWarner Losh 	while (entry->buf_count < count) {
775592ffb21SWarner Losh 		buf = &entry->buflist[entry->buf_count];
776592ffb21SWarner Losh 		buf->idx = dma->buf_count + entry->buf_count;
777592ffb21SWarner Losh 		buf->total = alignment;
778592ffb21SWarner Losh 		buf->order = order;
779592ffb21SWarner Losh 		buf->used = 0;
780592ffb21SWarner Losh 
781592ffb21SWarner Losh 		buf->offset = (dma->byte_count + offset);
782592ffb21SWarner Losh 		buf->bus_address = agp_offset + offset;
783592ffb21SWarner Losh 		buf->address = (void *)(agp_offset + offset);
784592ffb21SWarner Losh 		buf->next = NULL;
785592ffb21SWarner Losh 		buf->waiting = 0;
786592ffb21SWarner Losh 		buf->pending = 0;
787592ffb21SWarner Losh 		buf->file_priv = NULL;
788592ffb21SWarner Losh 
789592ffb21SWarner Losh 		buf->dev_priv_size = dev->driver->dev_priv_size;
790592ffb21SWarner Losh 		buf->dev_private = malloc(buf->dev_priv_size, DRM_MEM_BUFS,
791592ffb21SWarner Losh 		    M_NOWAIT | M_ZERO);
792592ffb21SWarner Losh 		if (!buf->dev_private) {
793592ffb21SWarner Losh 			/* Set count correctly so we free the proper amount. */
794592ffb21SWarner Losh 			entry->buf_count = count;
795592ffb21SWarner Losh 			drm_cleanup_buf_error(dev, entry);
796592ffb21SWarner Losh 			DRM_UNLOCK(dev);
797592ffb21SWarner Losh 			atomic_dec(&dev->buf_alloc);
798592ffb21SWarner Losh 			return -ENOMEM;
799592ffb21SWarner Losh 		}
800592ffb21SWarner Losh 
801592ffb21SWarner Losh 		DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address);
802592ffb21SWarner Losh 
803592ffb21SWarner Losh 		offset += alignment;
804592ffb21SWarner Losh 		entry->buf_count++;
805592ffb21SWarner Losh 		byte_count += PAGE_SIZE << page_order;
806592ffb21SWarner Losh 	}
807592ffb21SWarner Losh 
808592ffb21SWarner Losh 	DRM_DEBUG("byte_count: %d\n", byte_count);
809592ffb21SWarner Losh 
810592ffb21SWarner Losh 	temp_buflist = realloc(dma->buflist,
811592ffb21SWarner Losh 	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
812592ffb21SWarner Losh 	    DRM_MEM_BUFS, M_NOWAIT);
813592ffb21SWarner Losh 	if (!temp_buflist) {
814592ffb21SWarner Losh 		/* Free the entry because it isn't valid */
815592ffb21SWarner Losh 		drm_cleanup_buf_error(dev, entry);
816592ffb21SWarner Losh 		DRM_UNLOCK(dev);
817592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
818592ffb21SWarner Losh 		return -ENOMEM;
819592ffb21SWarner Losh 	}
820592ffb21SWarner Losh 	dma->buflist = temp_buflist;
821592ffb21SWarner Losh 
822592ffb21SWarner Losh 	for (i = 0; i < entry->buf_count; i++) {
823592ffb21SWarner Losh 		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
824592ffb21SWarner Losh 	}
825592ffb21SWarner Losh 
826592ffb21SWarner Losh 	dma->buf_count += entry->buf_count;
827592ffb21SWarner Losh 	dma->seg_count += entry->seg_count;
828592ffb21SWarner Losh 	dma->page_count += byte_count >> PAGE_SHIFT;
829592ffb21SWarner Losh 	dma->byte_count += byte_count;
830592ffb21SWarner Losh 
831592ffb21SWarner Losh 	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
832592ffb21SWarner Losh 	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
833592ffb21SWarner Losh 
834592ffb21SWarner Losh 	DRM_UNLOCK(dev);
835592ffb21SWarner Losh 
836592ffb21SWarner Losh 	request->count = entry->buf_count;
837592ffb21SWarner Losh 	request->size = size;
838592ffb21SWarner Losh 
839592ffb21SWarner Losh 	dma->flags = _DRM_DMA_USE_AGP;
840592ffb21SWarner Losh 
841592ffb21SWarner Losh 	atomic_dec(&dev->buf_alloc);
842592ffb21SWarner Losh 	return 0;
843592ffb21SWarner Losh }
844592ffb21SWarner Losh EXPORT_SYMBOL(drm_addbufs_agp);
845592ffb21SWarner Losh #endif				/* __OS_HAS_AGP */
846592ffb21SWarner Losh 
drm_addbufs_pci(struct drm_device * dev,struct drm_buf_desc * request)847592ffb21SWarner Losh int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request)
848592ffb21SWarner Losh {
849592ffb21SWarner Losh 	struct drm_device_dma *dma = dev->dma;
850592ffb21SWarner Losh 	int count;
851592ffb21SWarner Losh 	int order;
852592ffb21SWarner Losh 	int size;
853592ffb21SWarner Losh 	int total;
854592ffb21SWarner Losh 	int page_order;
855592ffb21SWarner Losh 	struct drm_buf_entry *entry;
856592ffb21SWarner Losh 	drm_dma_handle_t *dmah;
857592ffb21SWarner Losh 	struct drm_buf *buf;
858592ffb21SWarner Losh 	int alignment;
859592ffb21SWarner Losh 	unsigned long offset;
860592ffb21SWarner Losh 	int i;
861592ffb21SWarner Losh 	int byte_count;
862592ffb21SWarner Losh 	int page_count;
863592ffb21SWarner Losh 	unsigned long *temp_pagelist;
864592ffb21SWarner Losh 	struct drm_buf **temp_buflist;
865592ffb21SWarner Losh 
866592ffb21SWarner Losh 	if (!drm_core_check_feature(dev, DRIVER_PCI_DMA))
867592ffb21SWarner Losh 		return -EINVAL;
868592ffb21SWarner Losh 
869592ffb21SWarner Losh 	if (!dma)
870592ffb21SWarner Losh 		return -EINVAL;
871592ffb21SWarner Losh 
872592ffb21SWarner Losh 	if (!DRM_SUSER(DRM_CURPROC))
873592ffb21SWarner Losh 		return -EPERM;
874592ffb21SWarner Losh 
875592ffb21SWarner Losh 	count = request->count;
876592ffb21SWarner Losh 	order = drm_order(request->size);
877592ffb21SWarner Losh 	size = 1 << order;
878592ffb21SWarner Losh 
879592ffb21SWarner Losh 	DRM_DEBUG("count=%d, size=%d (%d), order=%d\n",
880592ffb21SWarner Losh 		  request->count, request->size, size, order);
881592ffb21SWarner Losh 
882592ffb21SWarner Losh 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
883592ffb21SWarner Losh 		return -EINVAL;
884592ffb21SWarner Losh 
885592ffb21SWarner Losh 	alignment = (request->flags & _DRM_PAGE_ALIGN)
886592ffb21SWarner Losh 	    ? PAGE_ALIGN(size) : size;
887592ffb21SWarner Losh 	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
888592ffb21SWarner Losh 	total = PAGE_SIZE << page_order;
889592ffb21SWarner Losh 
890592ffb21SWarner Losh 	mtx_lock(&dev->count_lock);
891592ffb21SWarner Losh 	if (dev->buf_use) {
892592ffb21SWarner Losh 		mtx_unlock(&dev->count_lock);
893592ffb21SWarner Losh 		return -EBUSY;
894592ffb21SWarner Losh 	}
895592ffb21SWarner Losh 	atomic_inc(&dev->buf_alloc);
896592ffb21SWarner Losh 	mtx_unlock(&dev->count_lock);
897592ffb21SWarner Losh 
898592ffb21SWarner Losh 	DRM_LOCK(dev);
899592ffb21SWarner Losh 	entry = &dma->bufs[order];
900592ffb21SWarner Losh 	if (entry->buf_count) {
901592ffb21SWarner Losh 		DRM_UNLOCK(dev);
902592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
903592ffb21SWarner Losh 		return -ENOMEM;	/* May only call once for each order */
904592ffb21SWarner Losh 	}
905592ffb21SWarner Losh 
906592ffb21SWarner Losh 	if (count < 0 || count > 4096) {
907592ffb21SWarner Losh 		DRM_UNLOCK(dev);
908592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
909592ffb21SWarner Losh 		return -EINVAL;
910592ffb21SWarner Losh 	}
911592ffb21SWarner Losh 
912592ffb21SWarner Losh 	entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS,
913592ffb21SWarner Losh 	    M_NOWAIT | M_ZERO);
914592ffb21SWarner Losh 	if (!entry->buflist) {
915592ffb21SWarner Losh 		DRM_UNLOCK(dev);
916592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
917592ffb21SWarner Losh 		return -ENOMEM;
918592ffb21SWarner Losh 	}
919592ffb21SWarner Losh 
920592ffb21SWarner Losh 	entry->seglist = malloc(count * sizeof(*entry->seglist), DRM_MEM_SEGS,
921592ffb21SWarner Losh 	    M_NOWAIT | M_ZERO);
922592ffb21SWarner Losh 	if (!entry->seglist) {
923592ffb21SWarner Losh 		free(entry->buflist, DRM_MEM_BUFS);
924592ffb21SWarner Losh 		DRM_UNLOCK(dev);
925592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
926592ffb21SWarner Losh 		return -ENOMEM;
927592ffb21SWarner Losh 	}
928592ffb21SWarner Losh 
929592ffb21SWarner Losh 	/* Keep the original pagelist until we know all the allocations
930592ffb21SWarner Losh 	 * have succeeded
931592ffb21SWarner Losh 	 */
932592ffb21SWarner Losh 	temp_pagelist = malloc((dma->page_count + (count << page_order)) *
933592ffb21SWarner Losh 	    sizeof(*dma->pagelist), DRM_MEM_PAGES, M_NOWAIT);
934592ffb21SWarner Losh 	if (!temp_pagelist) {
935592ffb21SWarner Losh 		free(entry->buflist, DRM_MEM_BUFS);
936592ffb21SWarner Losh 		free(entry->seglist, DRM_MEM_SEGS);
937592ffb21SWarner Losh 		DRM_UNLOCK(dev);
938592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
939592ffb21SWarner Losh 		return -ENOMEM;
940592ffb21SWarner Losh 	}
941592ffb21SWarner Losh 	memcpy(temp_pagelist,
942592ffb21SWarner Losh 	       dma->pagelist, dma->page_count * sizeof(*dma->pagelist));
943592ffb21SWarner Losh 	DRM_DEBUG("pagelist: %d entries\n",
944592ffb21SWarner Losh 		  dma->page_count + (count << page_order));
945592ffb21SWarner Losh 
946592ffb21SWarner Losh 	entry->buf_size = size;
947592ffb21SWarner Losh 	entry->page_order = page_order;
948592ffb21SWarner Losh 	byte_count = 0;
949592ffb21SWarner Losh 	page_count = 0;
950592ffb21SWarner Losh 
951592ffb21SWarner Losh 	while (entry->buf_count < count) {
952592ffb21SWarner Losh 
953592ffb21SWarner Losh 		dmah = drm_pci_alloc(dev, PAGE_SIZE << page_order, 0x1000, BUS_SPACE_MAXADDR);
954592ffb21SWarner Losh 
955592ffb21SWarner Losh 		if (!dmah) {
956592ffb21SWarner Losh 			/* Set count correctly so we free the proper amount. */
957592ffb21SWarner Losh 			entry->buf_count = count;
958592ffb21SWarner Losh 			entry->seg_count = count;
959592ffb21SWarner Losh 			drm_cleanup_buf_error(dev, entry);
960592ffb21SWarner Losh 			free(temp_pagelist, DRM_MEM_PAGES);
961592ffb21SWarner Losh 			DRM_UNLOCK(dev);
962592ffb21SWarner Losh 			atomic_dec(&dev->buf_alloc);
963592ffb21SWarner Losh 			return -ENOMEM;
964592ffb21SWarner Losh 		}
965592ffb21SWarner Losh 		entry->seglist[entry->seg_count++] = dmah;
966592ffb21SWarner Losh 		for (i = 0; i < (1 << page_order); i++) {
967592ffb21SWarner Losh 			DRM_DEBUG("page %d @ 0x%08lx\n",
968592ffb21SWarner Losh 				  dma->page_count + page_count,
969592ffb21SWarner Losh 				  (unsigned long)dmah->vaddr + PAGE_SIZE * i);
970592ffb21SWarner Losh 			temp_pagelist[dma->page_count + page_count++]
971592ffb21SWarner Losh 				= (unsigned long)dmah->vaddr + PAGE_SIZE * i;
972592ffb21SWarner Losh 		}
973592ffb21SWarner Losh 		for (offset = 0;
974592ffb21SWarner Losh 		     offset + size <= total && entry->buf_count < count;
975592ffb21SWarner Losh 		     offset += alignment, ++entry->buf_count) {
976592ffb21SWarner Losh 			buf = &entry->buflist[entry->buf_count];
977592ffb21SWarner Losh 			buf->idx = dma->buf_count + entry->buf_count;
978592ffb21SWarner Losh 			buf->total = alignment;
979592ffb21SWarner Losh 			buf->order = order;
980592ffb21SWarner Losh 			buf->used = 0;
981592ffb21SWarner Losh 			buf->offset = (dma->byte_count + byte_count + offset);
982592ffb21SWarner Losh 			buf->address = (void *)((char *)dmah->vaddr + offset);
983592ffb21SWarner Losh 			buf->bus_address = dmah->busaddr + offset;
984592ffb21SWarner Losh 			buf->next = NULL;
985592ffb21SWarner Losh 			buf->waiting = 0;
986592ffb21SWarner Losh 			buf->pending = 0;
987592ffb21SWarner Losh 			buf->file_priv = NULL;
988592ffb21SWarner Losh 
989592ffb21SWarner Losh 			buf->dev_priv_size = dev->driver->dev_priv_size;
990592ffb21SWarner Losh 			buf->dev_private = malloc(buf->dev_priv_size,
991592ffb21SWarner Losh 			    DRM_MEM_BUFS, M_NOWAIT | M_ZERO);
992592ffb21SWarner Losh 			if (!buf->dev_private) {
993592ffb21SWarner Losh 				/* Set count correctly so we free the proper amount. */
994592ffb21SWarner Losh 				entry->buf_count = count;
995592ffb21SWarner Losh 				entry->seg_count = count;
996592ffb21SWarner Losh 				drm_cleanup_buf_error(dev, entry);
997592ffb21SWarner Losh 				free(temp_pagelist, DRM_MEM_PAGES);
998592ffb21SWarner Losh 				DRM_UNLOCK(dev);
999592ffb21SWarner Losh 				atomic_dec(&dev->buf_alloc);
1000592ffb21SWarner Losh 				return -ENOMEM;
1001592ffb21SWarner Losh 			}
1002592ffb21SWarner Losh 
1003592ffb21SWarner Losh 			DRM_DEBUG("buffer %d @ %p\n",
1004592ffb21SWarner Losh 				  entry->buf_count, buf->address);
1005592ffb21SWarner Losh 		}
1006592ffb21SWarner Losh 		byte_count += PAGE_SIZE << page_order;
1007592ffb21SWarner Losh 	}
1008592ffb21SWarner Losh 
1009592ffb21SWarner Losh 	temp_buflist = realloc(dma->buflist,
1010592ffb21SWarner Losh 	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
1011592ffb21SWarner Losh 	    DRM_MEM_BUFS, M_NOWAIT);
1012592ffb21SWarner Losh 	if (!temp_buflist) {
1013592ffb21SWarner Losh 		/* Free the entry because it isn't valid */
1014592ffb21SWarner Losh 		drm_cleanup_buf_error(dev, entry);
1015592ffb21SWarner Losh 		free(temp_pagelist, DRM_MEM_PAGES);
1016592ffb21SWarner Losh 		DRM_UNLOCK(dev);
1017592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
1018592ffb21SWarner Losh 		return -ENOMEM;
1019592ffb21SWarner Losh 	}
1020592ffb21SWarner Losh 	dma->buflist = temp_buflist;
1021592ffb21SWarner Losh 
1022592ffb21SWarner Losh 	for (i = 0; i < entry->buf_count; i++) {
1023592ffb21SWarner Losh 		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
1024592ffb21SWarner Losh 	}
1025592ffb21SWarner Losh 
1026592ffb21SWarner Losh 	/* No allocations failed, so now we can replace the original pagelist
1027592ffb21SWarner Losh 	 * with the new one.
1028592ffb21SWarner Losh 	 */
1029592ffb21SWarner Losh 	if (dma->page_count) {
1030592ffb21SWarner Losh 		free(dma->pagelist, DRM_MEM_PAGES);
1031592ffb21SWarner Losh 	}
1032592ffb21SWarner Losh 	dma->pagelist = temp_pagelist;
1033592ffb21SWarner Losh 
1034592ffb21SWarner Losh 	dma->buf_count += entry->buf_count;
1035592ffb21SWarner Losh 	dma->seg_count += entry->seg_count;
1036592ffb21SWarner Losh 	dma->page_count += entry->seg_count << page_order;
1037592ffb21SWarner Losh 	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
1038592ffb21SWarner Losh 
1039592ffb21SWarner Losh 	DRM_UNLOCK(dev);
1040592ffb21SWarner Losh 
1041592ffb21SWarner Losh 	request->count = entry->buf_count;
1042592ffb21SWarner Losh 	request->size = size;
1043592ffb21SWarner Losh 
1044592ffb21SWarner Losh 	if (request->flags & _DRM_PCI_BUFFER_RO)
1045592ffb21SWarner Losh 		dma->flags = _DRM_DMA_USE_PCI_RO;
1046592ffb21SWarner Losh 
1047592ffb21SWarner Losh 	atomic_dec(&dev->buf_alloc);
1048592ffb21SWarner Losh 	return 0;
1049592ffb21SWarner Losh 
1050592ffb21SWarner Losh }
1051592ffb21SWarner Losh EXPORT_SYMBOL(drm_addbufs_pci);
1052592ffb21SWarner Losh 
drm_addbufs_sg(struct drm_device * dev,struct drm_buf_desc * request)1053592ffb21SWarner Losh static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request)
1054592ffb21SWarner Losh {
1055592ffb21SWarner Losh 	struct drm_device_dma *dma = dev->dma;
1056592ffb21SWarner Losh 	struct drm_buf_entry *entry;
1057592ffb21SWarner Losh 	struct drm_buf *buf;
1058592ffb21SWarner Losh 	unsigned long offset;
1059592ffb21SWarner Losh 	unsigned long agp_offset;
1060592ffb21SWarner Losh 	int count;
1061592ffb21SWarner Losh 	int order;
1062592ffb21SWarner Losh 	int size;
1063592ffb21SWarner Losh 	int alignment;
1064592ffb21SWarner Losh 	int page_order;
1065592ffb21SWarner Losh 	int total;
1066592ffb21SWarner Losh 	int byte_count;
1067592ffb21SWarner Losh 	int i;
1068592ffb21SWarner Losh 	struct drm_buf **temp_buflist;
1069592ffb21SWarner Losh 
1070592ffb21SWarner Losh 	if (!drm_core_check_feature(dev, DRIVER_SG))
1071592ffb21SWarner Losh 		return -EINVAL;
1072592ffb21SWarner Losh 
1073592ffb21SWarner Losh 	if (!dma)
1074592ffb21SWarner Losh 		return -EINVAL;
1075592ffb21SWarner Losh 
1076592ffb21SWarner Losh 	if (!DRM_SUSER(DRM_CURPROC))
1077592ffb21SWarner Losh 		return -EPERM;
1078592ffb21SWarner Losh 
1079592ffb21SWarner Losh 	count = request->count;
1080592ffb21SWarner Losh 	order = drm_order(request->size);
1081592ffb21SWarner Losh 	size = 1 << order;
1082592ffb21SWarner Losh 
1083592ffb21SWarner Losh 	alignment = (request->flags & _DRM_PAGE_ALIGN)
1084592ffb21SWarner Losh 	    ? PAGE_ALIGN(size) : size;
1085592ffb21SWarner Losh 	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
1086592ffb21SWarner Losh 	total = PAGE_SIZE << page_order;
1087592ffb21SWarner Losh 
1088592ffb21SWarner Losh 	byte_count = 0;
1089592ffb21SWarner Losh 	agp_offset = request->agp_start;
1090592ffb21SWarner Losh 
1091592ffb21SWarner Losh 	DRM_DEBUG("count:      %d\n", count);
1092592ffb21SWarner Losh 	DRM_DEBUG("order:      %d\n", order);
1093592ffb21SWarner Losh 	DRM_DEBUG("size:       %d\n", size);
1094592ffb21SWarner Losh 	DRM_DEBUG("agp_offset: %lu\n", agp_offset);
1095592ffb21SWarner Losh 	DRM_DEBUG("alignment:  %d\n", alignment);
1096592ffb21SWarner Losh 	DRM_DEBUG("page_order: %d\n", page_order);
1097592ffb21SWarner Losh 	DRM_DEBUG("total:      %d\n", total);
1098592ffb21SWarner Losh 
1099592ffb21SWarner Losh 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
1100592ffb21SWarner Losh 		return -EINVAL;
1101592ffb21SWarner Losh 
1102592ffb21SWarner Losh 	mtx_lock(&dev->count_lock);
1103592ffb21SWarner Losh 	if (dev->buf_use) {
1104592ffb21SWarner Losh 		mtx_unlock(&dev->count_lock);
1105592ffb21SWarner Losh 		return -EBUSY;
1106592ffb21SWarner Losh 	}
1107592ffb21SWarner Losh 	atomic_inc(&dev->buf_alloc);
1108592ffb21SWarner Losh 	mtx_unlock(&dev->count_lock);
1109592ffb21SWarner Losh 
1110592ffb21SWarner Losh 	DRM_LOCK(dev);
1111592ffb21SWarner Losh 	entry = &dma->bufs[order];
1112592ffb21SWarner Losh 	if (entry->buf_count) {
1113592ffb21SWarner Losh 		DRM_UNLOCK(dev);
1114592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
1115592ffb21SWarner Losh 		return -ENOMEM;	/* May only call once for each order */
1116592ffb21SWarner Losh 	}
1117592ffb21SWarner Losh 
1118592ffb21SWarner Losh 	if (count < 0 || count > 4096) {
1119592ffb21SWarner Losh 		DRM_UNLOCK(dev);
1120592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
1121592ffb21SWarner Losh 		return -EINVAL;
1122592ffb21SWarner Losh 	}
1123592ffb21SWarner Losh 
1124592ffb21SWarner Losh 	entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS,
1125592ffb21SWarner Losh 	    M_NOWAIT | M_ZERO);
1126592ffb21SWarner Losh 	if (!entry->buflist) {
1127592ffb21SWarner Losh 		DRM_UNLOCK(dev);
1128592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
1129592ffb21SWarner Losh 		return -ENOMEM;
1130592ffb21SWarner Losh 	}
1131592ffb21SWarner Losh 
1132592ffb21SWarner Losh 	entry->buf_size = size;
1133592ffb21SWarner Losh 	entry->page_order = page_order;
1134592ffb21SWarner Losh 
1135592ffb21SWarner Losh 	offset = 0;
1136592ffb21SWarner Losh 
1137592ffb21SWarner Losh 	while (entry->buf_count < count) {
1138592ffb21SWarner Losh 		buf = &entry->buflist[entry->buf_count];
1139592ffb21SWarner Losh 		buf->idx = dma->buf_count + entry->buf_count;
1140592ffb21SWarner Losh 		buf->total = alignment;
1141592ffb21SWarner Losh 		buf->order = order;
1142592ffb21SWarner Losh 		buf->used = 0;
1143592ffb21SWarner Losh 
1144592ffb21SWarner Losh 		buf->offset = (dma->byte_count + offset);
1145592ffb21SWarner Losh 		buf->bus_address = agp_offset + offset;
1146592ffb21SWarner Losh 		buf->address = (void *)(agp_offset + offset
1147592ffb21SWarner Losh 					+ (unsigned long)dev->sg->vaddr);
1148592ffb21SWarner Losh 		buf->next = NULL;
1149592ffb21SWarner Losh 		buf->waiting = 0;
1150592ffb21SWarner Losh 		buf->pending = 0;
1151592ffb21SWarner Losh 		buf->file_priv = NULL;
1152592ffb21SWarner Losh 
1153592ffb21SWarner Losh 		buf->dev_priv_size = dev->driver->dev_priv_size;
1154592ffb21SWarner Losh 		buf->dev_private = malloc(buf->dev_priv_size, DRM_MEM_BUFS,
1155592ffb21SWarner Losh 		    M_NOWAIT | M_ZERO);
1156592ffb21SWarner Losh 		if (!buf->dev_private) {
1157592ffb21SWarner Losh 			/* Set count correctly so we free the proper amount. */
1158592ffb21SWarner Losh 			entry->buf_count = count;
1159592ffb21SWarner Losh 			drm_cleanup_buf_error(dev, entry);
1160592ffb21SWarner Losh 			DRM_UNLOCK(dev);
1161592ffb21SWarner Losh 			atomic_dec(&dev->buf_alloc);
1162592ffb21SWarner Losh 			return -ENOMEM;
1163592ffb21SWarner Losh 		}
1164592ffb21SWarner Losh 
1165592ffb21SWarner Losh 		DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address);
1166592ffb21SWarner Losh 
1167592ffb21SWarner Losh 		offset += alignment;
1168592ffb21SWarner Losh 		entry->buf_count++;
1169592ffb21SWarner Losh 		byte_count += PAGE_SIZE << page_order;
1170592ffb21SWarner Losh 	}
1171592ffb21SWarner Losh 
1172592ffb21SWarner Losh 	DRM_DEBUG("byte_count: %d\n", byte_count);
1173592ffb21SWarner Losh 
1174592ffb21SWarner Losh 	temp_buflist = realloc(dma->buflist,
1175592ffb21SWarner Losh 	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
1176592ffb21SWarner Losh 	    DRM_MEM_BUFS, M_NOWAIT);
1177592ffb21SWarner Losh 	if (!temp_buflist) {
1178592ffb21SWarner Losh 		/* Free the entry because it isn't valid */
1179592ffb21SWarner Losh 		drm_cleanup_buf_error(dev, entry);
1180592ffb21SWarner Losh 		DRM_UNLOCK(dev);
1181592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
1182592ffb21SWarner Losh 		return -ENOMEM;
1183592ffb21SWarner Losh 	}
1184592ffb21SWarner Losh 	dma->buflist = temp_buflist;
1185592ffb21SWarner Losh 
1186592ffb21SWarner Losh 	for (i = 0; i < entry->buf_count; i++) {
1187592ffb21SWarner Losh 		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
1188592ffb21SWarner Losh 	}
1189592ffb21SWarner Losh 
1190592ffb21SWarner Losh 	dma->buf_count += entry->buf_count;
1191592ffb21SWarner Losh 	dma->seg_count += entry->seg_count;
1192592ffb21SWarner Losh 	dma->page_count += byte_count >> PAGE_SHIFT;
1193592ffb21SWarner Losh 	dma->byte_count += byte_count;
1194592ffb21SWarner Losh 
1195592ffb21SWarner Losh 	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
1196592ffb21SWarner Losh 	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
1197592ffb21SWarner Losh 
1198592ffb21SWarner Losh 	DRM_UNLOCK(dev);
1199592ffb21SWarner Losh 
1200592ffb21SWarner Losh 	request->count = entry->buf_count;
1201592ffb21SWarner Losh 	request->size = size;
1202592ffb21SWarner Losh 
1203592ffb21SWarner Losh 	dma->flags = _DRM_DMA_USE_SG;
1204592ffb21SWarner Losh 
1205592ffb21SWarner Losh 	atomic_dec(&dev->buf_alloc);
1206592ffb21SWarner Losh 	return 0;
1207592ffb21SWarner Losh }
1208592ffb21SWarner Losh 
drm_addbufs_fb(struct drm_device * dev,struct drm_buf_desc * request)1209592ffb21SWarner Losh static int drm_addbufs_fb(struct drm_device * dev, struct drm_buf_desc * request)
1210592ffb21SWarner Losh {
1211592ffb21SWarner Losh 	struct drm_device_dma *dma = dev->dma;
1212592ffb21SWarner Losh 	struct drm_buf_entry *entry;
1213592ffb21SWarner Losh 	struct drm_buf *buf;
1214592ffb21SWarner Losh 	unsigned long offset;
1215592ffb21SWarner Losh 	unsigned long agp_offset;
1216592ffb21SWarner Losh 	int count;
1217592ffb21SWarner Losh 	int order;
1218592ffb21SWarner Losh 	int size;
1219592ffb21SWarner Losh 	int alignment;
1220592ffb21SWarner Losh 	int page_order;
1221592ffb21SWarner Losh 	int total;
1222592ffb21SWarner Losh 	int byte_count;
1223592ffb21SWarner Losh 	int i;
1224592ffb21SWarner Losh 	struct drm_buf **temp_buflist;
1225592ffb21SWarner Losh 
1226592ffb21SWarner Losh 	if (!drm_core_check_feature(dev, DRIVER_FB_DMA))
1227592ffb21SWarner Losh 		return -EINVAL;
1228592ffb21SWarner Losh 
1229592ffb21SWarner Losh 	if (!dma)
1230592ffb21SWarner Losh 		return -EINVAL;
1231592ffb21SWarner Losh 
1232592ffb21SWarner Losh 	if (!DRM_SUSER(DRM_CURPROC))
1233592ffb21SWarner Losh 		return -EPERM;
1234592ffb21SWarner Losh 
1235592ffb21SWarner Losh 	count = request->count;
1236592ffb21SWarner Losh 	order = drm_order(request->size);
1237592ffb21SWarner Losh 	size = 1 << order;
1238592ffb21SWarner Losh 
1239592ffb21SWarner Losh 	alignment = (request->flags & _DRM_PAGE_ALIGN)
1240592ffb21SWarner Losh 	    ? PAGE_ALIGN(size) : size;
1241592ffb21SWarner Losh 	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
1242592ffb21SWarner Losh 	total = PAGE_SIZE << page_order;
1243592ffb21SWarner Losh 
1244592ffb21SWarner Losh 	byte_count = 0;
1245592ffb21SWarner Losh 	agp_offset = request->agp_start;
1246592ffb21SWarner Losh 
1247592ffb21SWarner Losh 	DRM_DEBUG("count:      %d\n", count);
1248592ffb21SWarner Losh 	DRM_DEBUG("order:      %d\n", order);
1249592ffb21SWarner Losh 	DRM_DEBUG("size:       %d\n", size);
1250592ffb21SWarner Losh 	DRM_DEBUG("agp_offset: %lu\n", agp_offset);
1251592ffb21SWarner Losh 	DRM_DEBUG("alignment:  %d\n", alignment);
1252592ffb21SWarner Losh 	DRM_DEBUG("page_order: %d\n", page_order);
1253592ffb21SWarner Losh 	DRM_DEBUG("total:      %d\n", total);
1254592ffb21SWarner Losh 
1255592ffb21SWarner Losh 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
1256592ffb21SWarner Losh 		return -EINVAL;
1257592ffb21SWarner Losh 
1258592ffb21SWarner Losh 	mtx_lock(&dev->count_lock);
1259592ffb21SWarner Losh 	if (dev->buf_use) {
1260592ffb21SWarner Losh 		mtx_unlock(&dev->count_lock);
1261592ffb21SWarner Losh 		return -EBUSY;
1262592ffb21SWarner Losh 	}
1263592ffb21SWarner Losh 	atomic_inc(&dev->buf_alloc);
1264592ffb21SWarner Losh 	mtx_unlock(&dev->count_lock);
1265592ffb21SWarner Losh 
1266592ffb21SWarner Losh 	DRM_LOCK(dev);
1267592ffb21SWarner Losh 	entry = &dma->bufs[order];
1268592ffb21SWarner Losh 	if (entry->buf_count) {
1269592ffb21SWarner Losh 		DRM_UNLOCK(dev);
1270592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
1271592ffb21SWarner Losh 		return -ENOMEM;	/* May only call once for each order */
1272592ffb21SWarner Losh 	}
1273592ffb21SWarner Losh 
1274592ffb21SWarner Losh 	if (count < 0 || count > 4096) {
1275592ffb21SWarner Losh 		DRM_UNLOCK(dev);
1276592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
1277592ffb21SWarner Losh 		return -EINVAL;
1278592ffb21SWarner Losh 	}
1279592ffb21SWarner Losh 
1280592ffb21SWarner Losh 	entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS,
1281592ffb21SWarner Losh 	    M_NOWAIT | M_ZERO);
1282592ffb21SWarner Losh 	if (!entry->buflist) {
1283592ffb21SWarner Losh 		DRM_UNLOCK(dev);
1284592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
1285592ffb21SWarner Losh 		return -ENOMEM;
1286592ffb21SWarner Losh 	}
1287592ffb21SWarner Losh 
1288592ffb21SWarner Losh 	entry->buf_size = size;
1289592ffb21SWarner Losh 	entry->page_order = page_order;
1290592ffb21SWarner Losh 
1291592ffb21SWarner Losh 	offset = 0;
1292592ffb21SWarner Losh 
1293592ffb21SWarner Losh 	while (entry->buf_count < count) {
1294592ffb21SWarner Losh 		buf = &entry->buflist[entry->buf_count];
1295592ffb21SWarner Losh 		buf->idx = dma->buf_count + entry->buf_count;
1296592ffb21SWarner Losh 		buf->total = alignment;
1297592ffb21SWarner Losh 		buf->order = order;
1298592ffb21SWarner Losh 		buf->used = 0;
1299592ffb21SWarner Losh 
1300592ffb21SWarner Losh 		buf->offset = (dma->byte_count + offset);
1301592ffb21SWarner Losh 		buf->bus_address = agp_offset + offset;
1302592ffb21SWarner Losh 		buf->address = (void *)(agp_offset + offset);
1303592ffb21SWarner Losh 		buf->next = NULL;
1304592ffb21SWarner Losh 		buf->waiting = 0;
1305592ffb21SWarner Losh 		buf->pending = 0;
1306592ffb21SWarner Losh 		buf->file_priv = NULL;
1307592ffb21SWarner Losh 
1308592ffb21SWarner Losh 		buf->dev_priv_size = dev->driver->dev_priv_size;
1309592ffb21SWarner Losh 		buf->dev_private = malloc(buf->dev_priv_size, DRM_MEM_BUFS,
1310592ffb21SWarner Losh 		    M_NOWAIT | M_ZERO);
1311592ffb21SWarner Losh 		if (!buf->dev_private) {
1312592ffb21SWarner Losh 			/* Set count correctly so we free the proper amount. */
1313592ffb21SWarner Losh 			entry->buf_count = count;
1314592ffb21SWarner Losh 			drm_cleanup_buf_error(dev, entry);
1315592ffb21SWarner Losh 			DRM_UNLOCK(dev);
1316592ffb21SWarner Losh 			atomic_dec(&dev->buf_alloc);
1317592ffb21SWarner Losh 			return -ENOMEM;
1318592ffb21SWarner Losh 		}
1319592ffb21SWarner Losh 
1320592ffb21SWarner Losh 		DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address);
1321592ffb21SWarner Losh 
1322592ffb21SWarner Losh 		offset += alignment;
1323592ffb21SWarner Losh 		entry->buf_count++;
1324592ffb21SWarner Losh 		byte_count += PAGE_SIZE << page_order;
1325592ffb21SWarner Losh 	}
1326592ffb21SWarner Losh 
1327592ffb21SWarner Losh 	DRM_DEBUG("byte_count: %d\n", byte_count);
1328592ffb21SWarner Losh 
1329592ffb21SWarner Losh 	temp_buflist = realloc(dma->buflist,
1330592ffb21SWarner Losh 	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
1331592ffb21SWarner Losh 	    DRM_MEM_BUFS, M_NOWAIT);
1332592ffb21SWarner Losh 	if (!temp_buflist) {
1333592ffb21SWarner Losh 		/* Free the entry because it isn't valid */
1334592ffb21SWarner Losh 		drm_cleanup_buf_error(dev, entry);
1335592ffb21SWarner Losh 		DRM_UNLOCK(dev);
1336592ffb21SWarner Losh 		atomic_dec(&dev->buf_alloc);
1337592ffb21SWarner Losh 		return -ENOMEM;
1338592ffb21SWarner Losh 	}
1339592ffb21SWarner Losh 	dma->buflist = temp_buflist;
1340592ffb21SWarner Losh 
1341592ffb21SWarner Losh 	for (i = 0; i < entry->buf_count; i++) {
1342592ffb21SWarner Losh 		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
1343592ffb21SWarner Losh 	}
1344592ffb21SWarner Losh 
1345592ffb21SWarner Losh 	dma->buf_count += entry->buf_count;
1346592ffb21SWarner Losh 	dma->seg_count += entry->seg_count;
1347592ffb21SWarner Losh 	dma->page_count += byte_count >> PAGE_SHIFT;
1348592ffb21SWarner Losh 	dma->byte_count += byte_count;
1349592ffb21SWarner Losh 
1350592ffb21SWarner Losh 	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
1351592ffb21SWarner Losh 	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
1352592ffb21SWarner Losh 
1353592ffb21SWarner Losh 	DRM_UNLOCK(dev);
1354592ffb21SWarner Losh 
1355592ffb21SWarner Losh 	request->count = entry->buf_count;
1356592ffb21SWarner Losh 	request->size = size;
1357592ffb21SWarner Losh 
1358592ffb21SWarner Losh 	dma->flags = _DRM_DMA_USE_FB;
1359592ffb21SWarner Losh 
1360592ffb21SWarner Losh 	atomic_dec(&dev->buf_alloc);
1361592ffb21SWarner Losh 	return 0;
1362592ffb21SWarner Losh }
1363592ffb21SWarner Losh 
1364592ffb21SWarner Losh 
1365592ffb21SWarner Losh /**
1366592ffb21SWarner Losh  * Add buffers for DMA transfers (ioctl).
1367592ffb21SWarner Losh  *
1368592ffb21SWarner Losh  * \param inode device inode.
1369592ffb21SWarner Losh  * \param file_priv DRM file private.
1370592ffb21SWarner Losh  * \param cmd command.
1371592ffb21SWarner Losh  * \param arg pointer to a struct drm_buf_desc request.
1372592ffb21SWarner Losh  * \return zero on success or a negative number on failure.
1373592ffb21SWarner Losh  *
1374592ffb21SWarner Losh  * According with the memory type specified in drm_buf_desc::flags and the
1375592ffb21SWarner Losh  * build options, it dispatches the call either to addbufs_agp(),
1376592ffb21SWarner Losh  * addbufs_sg() or addbufs_pci() for AGP, scatter-gather or consistent
1377592ffb21SWarner Losh  * PCI memory respectively.
1378592ffb21SWarner Losh  */
drm_addbufs(struct drm_device * dev,void * data,struct drm_file * file_priv)1379592ffb21SWarner Losh int drm_addbufs(struct drm_device *dev, void *data,
1380592ffb21SWarner Losh 		struct drm_file *file_priv)
1381592ffb21SWarner Losh {
1382592ffb21SWarner Losh 	struct drm_buf_desc *request = data;
1383592ffb21SWarner Losh 	int ret;
1384592ffb21SWarner Losh 
1385592ffb21SWarner Losh 	if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
1386592ffb21SWarner Losh 		return -EINVAL;
1387592ffb21SWarner Losh 
1388592ffb21SWarner Losh #if __OS_HAS_AGP
1389592ffb21SWarner Losh 	if (request->flags & _DRM_AGP_BUFFER)
1390592ffb21SWarner Losh 		ret = drm_addbufs_agp(dev, request);
1391592ffb21SWarner Losh 	else
1392592ffb21SWarner Losh #endif
1393592ffb21SWarner Losh 	if (request->flags & _DRM_SG_BUFFER)
1394592ffb21SWarner Losh 		ret = drm_addbufs_sg(dev, request);
1395592ffb21SWarner Losh 	else if (request->flags & _DRM_FB_BUFFER)
1396592ffb21SWarner Losh 		ret = drm_addbufs_fb(dev, request);
1397592ffb21SWarner Losh 	else
1398592ffb21SWarner Losh 		ret = drm_addbufs_pci(dev, request);
1399592ffb21SWarner Losh 
1400592ffb21SWarner Losh 	return ret;
1401592ffb21SWarner Losh }
1402592ffb21SWarner Losh 
1403592ffb21SWarner Losh /**
1404592ffb21SWarner Losh  * Get information about the buffer mappings.
1405592ffb21SWarner Losh  *
1406592ffb21SWarner Losh  * This was originally mean for debugging purposes, or by a sophisticated
1407592ffb21SWarner Losh  * client library to determine how best to use the available buffers (e.g.,
1408592ffb21SWarner Losh  * large buffers can be used for image transfer).
1409592ffb21SWarner Losh  *
1410592ffb21SWarner Losh  * \param inode device inode.
1411592ffb21SWarner Losh  * \param file_priv DRM file private.
1412592ffb21SWarner Losh  * \param cmd command.
1413592ffb21SWarner Losh  * \param arg pointer to a drm_buf_info structure.
1414592ffb21SWarner Losh  * \return zero on success or a negative number on failure.
1415592ffb21SWarner Losh  *
1416592ffb21SWarner Losh  * Increments drm_device::buf_use while holding the drm_device::count_lock
1417592ffb21SWarner Losh  * lock, preventing of allocating more buffers after this call. Information
1418592ffb21SWarner Losh  * about each requested buffer is then copied into user space.
1419592ffb21SWarner Losh  */
drm_infobufs(struct drm_device * dev,void * data,struct drm_file * file_priv)1420592ffb21SWarner Losh int drm_infobufs(struct drm_device *dev, void *data,
1421592ffb21SWarner Losh 		 struct drm_file *file_priv)
1422592ffb21SWarner Losh {
1423592ffb21SWarner Losh 	struct drm_device_dma *dma = dev->dma;
1424592ffb21SWarner Losh 	struct drm_buf_info *request = data;
1425592ffb21SWarner Losh 	int i;
1426592ffb21SWarner Losh 	int count;
1427592ffb21SWarner Losh 
1428592ffb21SWarner Losh 	if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
1429592ffb21SWarner Losh 		return -EINVAL;
1430592ffb21SWarner Losh 
1431592ffb21SWarner Losh 	if (!dma)
1432592ffb21SWarner Losh 		return -EINVAL;
1433592ffb21SWarner Losh 
1434592ffb21SWarner Losh 	mtx_lock(&dev->count_lock);
1435592ffb21SWarner Losh 	if (atomic_read(&dev->buf_alloc)) {
1436592ffb21SWarner Losh 		mtx_unlock(&dev->count_lock);
1437592ffb21SWarner Losh 		return -EBUSY;
1438592ffb21SWarner Losh 	}
1439592ffb21SWarner Losh 	++dev->buf_use;		/* Can't allocate more after this call */
1440592ffb21SWarner Losh 	mtx_unlock(&dev->count_lock);
1441592ffb21SWarner Losh 
1442592ffb21SWarner Losh 	for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
1443592ffb21SWarner Losh 		if (dma->bufs[i].buf_count)
1444592ffb21SWarner Losh 			++count;
1445592ffb21SWarner Losh 	}
1446592ffb21SWarner Losh 
1447592ffb21SWarner Losh 	DRM_DEBUG("count = %d\n", count);
1448592ffb21SWarner Losh 
1449592ffb21SWarner Losh 	if (request->count >= count) {
1450592ffb21SWarner Losh 		for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
1451592ffb21SWarner Losh 			if (dma->bufs[i].buf_count) {
1452592ffb21SWarner Losh 				struct drm_buf_desc __user *to =
1453592ffb21SWarner Losh 				    &request->list[count];
1454592ffb21SWarner Losh 				struct drm_buf_entry *from = &dma->bufs[i];
1455592ffb21SWarner Losh 				struct drm_freelist *list = &dma->bufs[i].freelist;
1456592ffb21SWarner Losh 				if (copy_to_user(&to->count,
1457592ffb21SWarner Losh 						 &from->buf_count,
1458592ffb21SWarner Losh 						 sizeof(from->buf_count)) ||
1459592ffb21SWarner Losh 				    copy_to_user(&to->size,
1460592ffb21SWarner Losh 						 &from->buf_size,
1461592ffb21SWarner Losh 						 sizeof(from->buf_size)) ||
1462592ffb21SWarner Losh 				    copy_to_user(&to->low_mark,
1463592ffb21SWarner Losh 						 &list->low_mark,
1464592ffb21SWarner Losh 						 sizeof(list->low_mark)) ||
1465592ffb21SWarner Losh 				    copy_to_user(&to->high_mark,
1466592ffb21SWarner Losh 						 &list->high_mark,
1467592ffb21SWarner Losh 						 sizeof(list->high_mark)))
1468592ffb21SWarner Losh 					return -EFAULT;
1469592ffb21SWarner Losh 
1470592ffb21SWarner Losh 				DRM_DEBUG("%d %d %d %d %d\n",
1471592ffb21SWarner Losh 					  i,
1472592ffb21SWarner Losh 					  dma->bufs[i].buf_count,
1473592ffb21SWarner Losh 					  dma->bufs[i].buf_size,
1474592ffb21SWarner Losh 					  dma->bufs[i].freelist.low_mark,
1475592ffb21SWarner Losh 					  dma->bufs[i].freelist.high_mark);
1476592ffb21SWarner Losh 				++count;
1477592ffb21SWarner Losh 			}
1478592ffb21SWarner Losh 		}
1479592ffb21SWarner Losh 	}
1480592ffb21SWarner Losh 	request->count = count;
1481592ffb21SWarner Losh 
1482592ffb21SWarner Losh 	return 0;
1483592ffb21SWarner Losh }
1484592ffb21SWarner Losh 
1485592ffb21SWarner Losh /**
1486592ffb21SWarner Losh  * Specifies a low and high water mark for buffer allocation
1487592ffb21SWarner Losh  *
1488592ffb21SWarner Losh  * \param inode device inode.
1489592ffb21SWarner Losh  * \param file_priv DRM file private.
1490592ffb21SWarner Losh  * \param cmd command.
1491592ffb21SWarner Losh  * \param arg a pointer to a drm_buf_desc structure.
1492592ffb21SWarner Losh  * \return zero on success or a negative number on failure.
1493592ffb21SWarner Losh  *
1494592ffb21SWarner Losh  * Verifies that the size order is bounded between the admissible orders and
1495592ffb21SWarner Losh  * updates the respective drm_device_dma::bufs entry low and high water mark.
1496592ffb21SWarner Losh  *
1497592ffb21SWarner Losh  * \note This ioctl is deprecated and mostly never used.
1498592ffb21SWarner Losh  */
drm_markbufs(struct drm_device * dev,void * data,struct drm_file * file_priv)1499592ffb21SWarner Losh int drm_markbufs(struct drm_device *dev, void *data,
1500592ffb21SWarner Losh 		 struct drm_file *file_priv)
1501592ffb21SWarner Losh {
1502592ffb21SWarner Losh 	struct drm_device_dma *dma = dev->dma;
1503592ffb21SWarner Losh 	struct drm_buf_desc *request = data;
1504592ffb21SWarner Losh 	int order;
1505592ffb21SWarner Losh 	struct drm_buf_entry *entry;
1506592ffb21SWarner Losh 
1507592ffb21SWarner Losh 	if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
1508592ffb21SWarner Losh 		return -EINVAL;
1509592ffb21SWarner Losh 
1510592ffb21SWarner Losh 	if (!dma)
1511592ffb21SWarner Losh 		return -EINVAL;
1512592ffb21SWarner Losh 
1513592ffb21SWarner Losh 	DRM_DEBUG("%d, %d, %d\n",
1514592ffb21SWarner Losh 		  request->size, request->low_mark, request->high_mark);
1515592ffb21SWarner Losh 	order = drm_order(request->size);
1516592ffb21SWarner Losh 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
1517592ffb21SWarner Losh 		return -EINVAL;
1518592ffb21SWarner Losh 	entry = &dma->bufs[order];
1519592ffb21SWarner Losh 
1520592ffb21SWarner Losh 	if (request->low_mark < 0 || request->low_mark > entry->buf_count)
1521592ffb21SWarner Losh 		return -EINVAL;
1522592ffb21SWarner Losh 	if (request->high_mark < 0 || request->high_mark > entry->buf_count)
1523592ffb21SWarner Losh 		return -EINVAL;
1524592ffb21SWarner Losh 
1525592ffb21SWarner Losh 	entry->freelist.low_mark = request->low_mark;
1526592ffb21SWarner Losh 	entry->freelist.high_mark = request->high_mark;
1527592ffb21SWarner Losh 
1528592ffb21SWarner Losh 	return 0;
1529592ffb21SWarner Losh }
1530592ffb21SWarner Losh 
1531592ffb21SWarner Losh /**
1532592ffb21SWarner Losh  * Unreserve the buffers in list, previously reserved using drmDMA.
1533592ffb21SWarner Losh  *
1534592ffb21SWarner Losh  * \param inode device inode.
1535592ffb21SWarner Losh  * \param file_priv DRM file private.
1536592ffb21SWarner Losh  * \param cmd command.
1537592ffb21SWarner Losh  * \param arg pointer to a drm_buf_free structure.
1538592ffb21SWarner Losh  * \return zero on success or a negative number on failure.
1539592ffb21SWarner Losh  *
1540592ffb21SWarner Losh  * Calls free_buffer() for each used buffer.
1541592ffb21SWarner Losh  * This function is primarily used for debugging.
1542592ffb21SWarner Losh  */
drm_freebufs(struct drm_device * dev,void * data,struct drm_file * file_priv)1543592ffb21SWarner Losh int drm_freebufs(struct drm_device *dev, void *data,
1544592ffb21SWarner Losh 		 struct drm_file *file_priv)
1545592ffb21SWarner Losh {
1546592ffb21SWarner Losh 	struct drm_device_dma *dma = dev->dma;
1547592ffb21SWarner Losh 	struct drm_buf_free *request = data;
1548592ffb21SWarner Losh 	int i;
1549592ffb21SWarner Losh 	int idx;
1550592ffb21SWarner Losh 	struct drm_buf *buf;
1551592ffb21SWarner Losh 
1552592ffb21SWarner Losh 	if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
1553592ffb21SWarner Losh 		return -EINVAL;
1554592ffb21SWarner Losh 
1555592ffb21SWarner Losh 	if (!dma)
1556592ffb21SWarner Losh 		return -EINVAL;
1557592ffb21SWarner Losh 
1558592ffb21SWarner Losh 	DRM_DEBUG("%d\n", request->count);
1559592ffb21SWarner Losh 	for (i = 0; i < request->count; i++) {
1560592ffb21SWarner Losh 		if (copy_from_user(&idx, &request->list[i], sizeof(idx)))
1561592ffb21SWarner Losh 			return -EFAULT;
1562592ffb21SWarner Losh 		if (idx < 0 || idx >= dma->buf_count) {
1563592ffb21SWarner Losh 			DRM_ERROR("Index %d (of %d max)\n",
1564592ffb21SWarner Losh 				  idx, dma->buf_count - 1);
1565592ffb21SWarner Losh 			return -EINVAL;
1566592ffb21SWarner Losh 		}
1567592ffb21SWarner Losh 		buf = dma->buflist[idx];
1568592ffb21SWarner Losh 		if (buf->file_priv != file_priv) {
1569592ffb21SWarner Losh 			DRM_ERROR("Process %d freeing buffer not owned\n",
1570592ffb21SWarner Losh 				  DRM_CURRENTPID);
1571592ffb21SWarner Losh 			return -EINVAL;
1572592ffb21SWarner Losh 		}
1573592ffb21SWarner Losh 		drm_free_buffer(dev, buf);
1574592ffb21SWarner Losh 	}
1575592ffb21SWarner Losh 
1576592ffb21SWarner Losh 	return 0;
1577592ffb21SWarner Losh }
1578592ffb21SWarner Losh 
1579592ffb21SWarner Losh /**
1580592ffb21SWarner Losh  * Maps all of the DMA buffers into client-virtual space (ioctl).
1581592ffb21SWarner Losh  *
1582592ffb21SWarner Losh  * \param inode device inode.
1583592ffb21SWarner Losh  * \param file_priv DRM file private.
1584592ffb21SWarner Losh  * \param cmd command.
1585592ffb21SWarner Losh  * \param arg pointer to a drm_buf_map structure.
1586592ffb21SWarner Losh  * \return zero on success or a negative number on failure.
1587592ffb21SWarner Losh  *
1588592ffb21SWarner Losh  * Maps the AGP, SG or PCI buffer region with vm_mmap(), and copies information
1589592ffb21SWarner Losh  * about each buffer into user space. For PCI buffers, it calls vm_mmap() with
1590592ffb21SWarner Losh  * offset equal to 0, which drm_mmap() interpretes as PCI buffers and calls
1591592ffb21SWarner Losh  * drm_mmap_dma().
1592592ffb21SWarner Losh  */
drm_mapbufs(struct drm_device * dev,void * data,struct drm_file * file_priv)1593592ffb21SWarner Losh int drm_mapbufs(struct drm_device *dev, void *data,
1594592ffb21SWarner Losh 	        struct drm_file *file_priv)
1595592ffb21SWarner Losh {
1596592ffb21SWarner Losh 	struct drm_device_dma *dma = dev->dma;
1597592ffb21SWarner Losh 	int retcode = 0;
1598592ffb21SWarner Losh 	const int zero = 0;
1599592ffb21SWarner Losh 	vm_offset_t virtual;
1600592ffb21SWarner Losh 	vm_offset_t address;
1601592ffb21SWarner Losh 	struct vmspace *vms;
1602592ffb21SWarner Losh 	struct drm_buf_map *request = data;
1603592ffb21SWarner Losh 	int i;
1604592ffb21SWarner Losh 
1605592ffb21SWarner Losh 	if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
1606592ffb21SWarner Losh 		return -EINVAL;
1607592ffb21SWarner Losh 
1608592ffb21SWarner Losh 	if (!dma)
1609592ffb21SWarner Losh 		return -EINVAL;
1610592ffb21SWarner Losh 
1611592ffb21SWarner Losh 	mtx_lock(&dev->count_lock);
1612592ffb21SWarner Losh 	if (atomic_read(&dev->buf_alloc)) {
1613592ffb21SWarner Losh 		mtx_unlock(&dev->count_lock);
1614592ffb21SWarner Losh 		return -EBUSY;
1615592ffb21SWarner Losh 	}
1616592ffb21SWarner Losh 	dev->buf_use++;		/* Can't allocate more after this call */
1617592ffb21SWarner Losh 	mtx_unlock(&dev->count_lock);
1618592ffb21SWarner Losh 
1619592ffb21SWarner Losh 	vms = DRM_CURPROC->td_proc->p_vmspace;
1620592ffb21SWarner Losh 
1621592ffb21SWarner Losh 	if (request->count >= dma->buf_count) {
1622592ffb21SWarner Losh 		if ((drm_core_has_AGP(dev) && (dma->flags & _DRM_DMA_USE_AGP))
1623592ffb21SWarner Losh 		    || (drm_core_check_feature(dev, DRIVER_SG)
1624592ffb21SWarner Losh 			&& (dma->flags & _DRM_DMA_USE_SG))
1625592ffb21SWarner Losh 		    || (drm_core_check_feature(dev, DRIVER_FB_DMA)
1626592ffb21SWarner Losh 			&& (dma->flags & _DRM_DMA_USE_FB))) {
1627592ffb21SWarner Losh 			struct drm_local_map *map = dev->agp_buffer_map;
1628592ffb21SWarner Losh 			vm_ooffset_t token = dev->agp_buffer_token;
1629592ffb21SWarner Losh 
1630592ffb21SWarner Losh 			if (!map) {
1631592ffb21SWarner Losh 				retcode = -EINVAL;
1632592ffb21SWarner Losh 				goto done;
1633592ffb21SWarner Losh 			}
1634592ffb21SWarner Losh 			retcode = vm_mmap(&vms->vm_map, &virtual, map->size,
1635f34702b7SJohn Baldwin 			    VM_PROT_RW, VM_PROT_RW, MAP_SHARED | MAP_NOSYNC,
1636f34702b7SJohn Baldwin 			    OBJT_DEVICE, file_priv->minor->device, token);
1637592ffb21SWarner Losh 		} else {
1638592ffb21SWarner Losh 			retcode = vm_mmap(&vms->vm_map, &virtual, dma->byte_count,
1639f34702b7SJohn Baldwin 			    VM_PROT_RW, VM_PROT_RW, MAP_SHARED | MAP_NOSYNC,
1640f34702b7SJohn Baldwin 			    OBJT_DEVICE, file_priv->minor->device, 0);
1641592ffb21SWarner Losh 		}
1642592ffb21SWarner Losh 		if (retcode) {
1643592ffb21SWarner Losh 			/* Real error */
1644592ffb21SWarner Losh 			retcode = -retcode;
1645592ffb21SWarner Losh 			goto done;
1646592ffb21SWarner Losh 		}
1647592ffb21SWarner Losh 		request->virtual = (void __user *)virtual;
1648592ffb21SWarner Losh 
1649592ffb21SWarner Losh 		for (i = 0; i < dma->buf_count; i++) {
1650592ffb21SWarner Losh 			if (copy_to_user(&request->list[i].idx,
1651592ffb21SWarner Losh 					 &dma->buflist[i]->idx,
1652592ffb21SWarner Losh 					 sizeof(request->list[0].idx))) {
1653592ffb21SWarner Losh 				retcode = -EFAULT;
1654592ffb21SWarner Losh 				goto done;
1655592ffb21SWarner Losh 			}
1656592ffb21SWarner Losh 			if (copy_to_user(&request->list[i].total,
1657592ffb21SWarner Losh 					 &dma->buflist[i]->total,
1658592ffb21SWarner Losh 					 sizeof(request->list[0].total))) {
1659592ffb21SWarner Losh 				retcode = -EFAULT;
1660592ffb21SWarner Losh 				goto done;
1661592ffb21SWarner Losh 			}
1662592ffb21SWarner Losh 			if (copy_to_user(&request->list[i].used,
1663592ffb21SWarner Losh 					 &zero, sizeof(zero))) {
1664592ffb21SWarner Losh 				retcode = -EFAULT;
1665592ffb21SWarner Losh 				goto done;
1666592ffb21SWarner Losh 			}
1667592ffb21SWarner Losh 			address = virtual + dma->buflist[i]->offset;	/* *** */
1668592ffb21SWarner Losh 			if (copy_to_user(&request->list[i].address,
1669592ffb21SWarner Losh 					 &address, sizeof(address))) {
1670592ffb21SWarner Losh 				retcode = -EFAULT;
1671592ffb21SWarner Losh 				goto done;
1672592ffb21SWarner Losh 			}
1673592ffb21SWarner Losh 		}
1674592ffb21SWarner Losh 	}
1675592ffb21SWarner Losh       done:
1676592ffb21SWarner Losh 	request->count = dma->buf_count;
1677592ffb21SWarner Losh 	DRM_DEBUG("%d buffers, retcode = %d\n", request->count, retcode);
1678592ffb21SWarner Losh 
1679592ffb21SWarner Losh 	return retcode;
1680592ffb21SWarner Losh }
1681592ffb21SWarner Losh 
1682592ffb21SWarner Losh /**
1683592ffb21SWarner Losh  * Compute size order.  Returns the exponent of the smaller power of two which
1684592ffb21SWarner Losh  * is greater or equal to given number.
1685592ffb21SWarner Losh  *
1686592ffb21SWarner Losh  * \param size size.
1687592ffb21SWarner Losh  * \return order.
1688592ffb21SWarner Losh  *
1689592ffb21SWarner Losh  * \todo Can be made faster.
1690592ffb21SWarner Losh  */
drm_order(unsigned long size)1691592ffb21SWarner Losh int drm_order(unsigned long size)
1692592ffb21SWarner Losh {
1693592ffb21SWarner Losh 	int order;
1694592ffb21SWarner Losh 	unsigned long tmp;
1695592ffb21SWarner Losh 
1696592ffb21SWarner Losh 	for (order = 0, tmp = size >> 1; tmp; tmp >>= 1, order++) ;
1697592ffb21SWarner Losh 
1698592ffb21SWarner Losh 	if (size & (size - 1))
1699592ffb21SWarner Losh 		++order;
1700592ffb21SWarner Losh 
1701592ffb21SWarner Losh 	return order;
1702592ffb21SWarner Losh }
1703592ffb21SWarner Losh EXPORT_SYMBOL(drm_order);
1704