xref: /dflybsd-src/sys/dev/drm/drm_bufs.c (revision 88fc0f688c71bd9bdaa7df1abbdeb57492b3744e)
1df4baf3dSFrançois Tigeot /*
21b13d190SFrançois Tigeot  * Legacy: Generic DRM Buffer Management
3df4baf3dSFrançois Tigeot  *
47f3c3d6fSHasso Tepper  * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
57f3c3d6fSHasso Tepper  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
67f3c3d6fSHasso Tepper  * All Rights Reserved.
77f3c3d6fSHasso Tepper  *
81b13d190SFrançois Tigeot  * Author: Rickard E. (Rik) Faith <faith@valinux.com>
91b13d190SFrançois Tigeot  * Author: Gareth Hughes <gareth@valinux.com>
101b13d190SFrançois Tigeot  *
117f3c3d6fSHasso Tepper  * Permission is hereby granted, free of charge, to any person obtaining a
127f3c3d6fSHasso Tepper  * copy of this software and associated documentation files (the "Software"),
137f3c3d6fSHasso Tepper  * to deal in the Software without restriction, including without limitation
147f3c3d6fSHasso Tepper  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
157f3c3d6fSHasso Tepper  * and/or sell copies of the Software, and to permit persons to whom the
167f3c3d6fSHasso Tepper  * Software is furnished to do so, subject to the following conditions:
177f3c3d6fSHasso Tepper  *
187f3c3d6fSHasso Tepper  * The above copyright notice and this permission notice (including the next
197f3c3d6fSHasso Tepper  * paragraph) shall be included in all copies or substantial portions of the
207f3c3d6fSHasso Tepper  * Software.
217f3c3d6fSHasso Tepper  *
227f3c3d6fSHasso Tepper  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
237f3c3d6fSHasso Tepper  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
247f3c3d6fSHasso Tepper  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
257f3c3d6fSHasso Tepper  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
267f3c3d6fSHasso Tepper  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
277f3c3d6fSHasso Tepper  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
287f3c3d6fSHasso Tepper  * OTHER DEALINGS IN THE SOFTWARE.
297f3c3d6fSHasso Tepper  */
307f3c3d6fSHasso Tepper 
31*88fc0f68SFrançois Tigeot #include <linux/vmalloc.h>
32*88fc0f68SFrançois Tigeot #include <linux/log2.h>
33df4baf3dSFrançois Tigeot #include <linux/export.h>
34*88fc0f68SFrançois Tigeot #include <asm/shmparam.h>
3518e26a6dSFrançois Tigeot #include <drm/drmP.h>
361b13d190SFrançois Tigeot #include "drm_legacy.h"
377f3c3d6fSHasso Tepper 
38*88fc0f68SFrançois Tigeot #if 0
39*88fc0f68SFrançois Tigeot static struct drm_map_list *drm_find_matching_map(struct drm_device *dev,
40*88fc0f68SFrançois Tigeot 						  struct drm_local_map *map)
41*88fc0f68SFrançois Tigeot {
42*88fc0f68SFrançois Tigeot 	struct drm_map_list *entry;
43*88fc0f68SFrançois Tigeot 	list_for_each_entry(entry, &dev->maplist, head) {
44*88fc0f68SFrançois Tigeot 		/*
45*88fc0f68SFrançois Tigeot 		 * Because the kernel-userspace ABI is fixed at a 32-bit offset
46*88fc0f68SFrançois Tigeot 		 * while PCI resources may live above that, we only compare the
47*88fc0f68SFrançois Tigeot 		 * lower 32 bits of the map offset for maps of type
48*88fc0f68SFrançois Tigeot 		 * _DRM_FRAMEBUFFER or _DRM_REGISTERS.
49*88fc0f68SFrançois Tigeot 		 * It is assumed that if a driver have more than one resource
50*88fc0f68SFrançois Tigeot 		 * of each type, the lower 32 bits are different.
51*88fc0f68SFrançois Tigeot 		 */
52*88fc0f68SFrançois Tigeot 		if (!entry->map ||
53*88fc0f68SFrançois Tigeot 		    map->type != entry->map->type ||
54*88fc0f68SFrançois Tigeot 		    entry->master != dev->primary->master)
55*88fc0f68SFrançois Tigeot 			continue;
56*88fc0f68SFrançois Tigeot 		switch (map->type) {
57*88fc0f68SFrançois Tigeot 		case _DRM_SHM:
58*88fc0f68SFrançois Tigeot 			if (map->flags != _DRM_CONTAINS_LOCK)
59*88fc0f68SFrançois Tigeot 				break;
60*88fc0f68SFrançois Tigeot 			return entry;
61*88fc0f68SFrançois Tigeot 		case _DRM_REGISTERS:
62*88fc0f68SFrançois Tigeot 		case _DRM_FRAME_BUFFER:
63*88fc0f68SFrançois Tigeot 			if ((entry->map->offset & 0xffffffff) ==
64*88fc0f68SFrançois Tigeot 			    (map->offset & 0xffffffff))
65*88fc0f68SFrançois Tigeot 				return entry;
66*88fc0f68SFrançois Tigeot 		default: /* Make gcc happy */
67*88fc0f68SFrançois Tigeot 			;
68*88fc0f68SFrançois Tigeot 		}
69*88fc0f68SFrançois Tigeot 		if (entry->map->offset == map->offset)
70*88fc0f68SFrançois Tigeot 			return entry;
71*88fc0f68SFrançois Tigeot 	}
72*88fc0f68SFrançois Tigeot 
73*88fc0f68SFrançois Tigeot 	return NULL;
74*88fc0f68SFrançois Tigeot }
75*88fc0f68SFrançois Tigeot 
76*88fc0f68SFrançois Tigeot static int drm_map_handle(struct drm_device *dev, struct drm_hash_item *hash,
77*88fc0f68SFrançois Tigeot 			  unsigned long user_token, int hashed_handle, int shm)
78*88fc0f68SFrançois Tigeot {
79*88fc0f68SFrançois Tigeot }
80*88fc0f68SFrançois Tigeot #endif
81*88fc0f68SFrançois Tigeot 
82*88fc0f68SFrançois Tigeot /**
83*88fc0f68SFrançois Tigeot  * Core function to create a range of memory available for mapping by a
84*88fc0f68SFrançois Tigeot  * non-root process.
85*88fc0f68SFrançois Tigeot  *
86*88fc0f68SFrançois Tigeot  * Adjusts the memory offset to its absolute value according to the mapping
87*88fc0f68SFrançois Tigeot  * type.  Adds the map to the map list drm_device::maplist. Adds MTRR's where
88*88fc0f68SFrançois Tigeot  * applicable and if supported by the kernel.
89*88fc0f68SFrançois Tigeot  */
90*88fc0f68SFrançois Tigeot static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
91df4baf3dSFrançois Tigeot 			   unsigned int size, enum drm_map_type type,
92*88fc0f68SFrançois Tigeot 			   enum drm_map_flags flags,
93*88fc0f68SFrançois Tigeot 			   struct drm_map_list ** maplist)
947f3c3d6fSHasso Tepper {
95f599cd46SFrançois Tigeot 	struct drm_local_map *map;
96*88fc0f68SFrançois Tigeot 	struct drm_map_list *list = NULL;
975c002123SFrançois Tigeot 	drm_dma_handle_t *dmah;
9879d1f0c0SFrançois Tigeot 
9979d1f0c0SFrançois Tigeot 	/* Allocate a new map structure, fill it in, and do any type-specific
10079d1f0c0SFrançois Tigeot 	 * initialization necessary.
10179d1f0c0SFrançois Tigeot 	 */
102f8677ba6SMatthew Dillon 	map = kmalloc(sizeof(*map), M_DRM, M_ZERO | M_WAITOK | M_NULLOK);
103*88fc0f68SFrançois Tigeot 	if (!map)
104b922632fSImre Vadász 		return -ENOMEM;
10579d1f0c0SFrançois Tigeot 
10679d1f0c0SFrançois Tigeot 	map->offset = offset;
10779d1f0c0SFrançois Tigeot 	map->size = size;
10879d1f0c0SFrançois Tigeot 	map->flags = flags;
109*88fc0f68SFrançois Tigeot 	map->type = type;
1107f3c3d6fSHasso Tepper 
1117f3c3d6fSHasso Tepper 	/* Only allow shared memory to be removable since we only keep enough
1127f3c3d6fSHasso Tepper 	 * book keeping information about shared memory to allow for removal
1137f3c3d6fSHasso Tepper 	 * when processes fork.
1147f3c3d6fSHasso Tepper 	 */
115*88fc0f68SFrançois Tigeot 	if ((map->flags & _DRM_REMOVABLE) && map->type != _DRM_SHM) {
116175896dfSzrj 		kfree(map);
117b922632fSImre Vadász 		return -EINVAL;
1187f3c3d6fSHasso Tepper 	}
1197f3c3d6fSHasso Tepper 	if ((offset & PAGE_MASK) || (size & PAGE_MASK)) {
1202ffc4fa7SSascha Wildner 		DRM_ERROR("offset/size not page aligned: 0x%jx/0x%04x\n",
1212ffc4fa7SSascha Wildner 		    (uintmax_t)offset, size);
122175896dfSzrj 		kfree(map);
123b922632fSImre Vadász 		return -EINVAL;
1247f3c3d6fSHasso Tepper 	}
1257f3c3d6fSHasso Tepper 	if (offset + size < offset) {
1262ffc4fa7SSascha Wildner 		DRM_ERROR("offset and size wrap around: 0x%jx/0x%04x\n",
1272ffc4fa7SSascha Wildner 		    (uintmax_t)offset, size);
128175896dfSzrj 		kfree(map);
129b922632fSImre Vadász 		return -EINVAL;
1307f3c3d6fSHasso Tepper 	}
1317f3c3d6fSHasso Tepper 
132df4baf3dSFrançois Tigeot 	DRM_DEBUG("offset = 0x%08llx, size = 0x%08lx, type = %d\n",
133df4baf3dSFrançois Tigeot 		  (unsigned long long)map->offset, map->size, map->type);
1347f3c3d6fSHasso Tepper 
1357f3c3d6fSHasso Tepper 	/* Check if this is just another version of a kernel-allocated map, and
1367f3c3d6fSHasso Tepper 	 * just hand that back if so.
1377f3c3d6fSHasso Tepper 	 */
1387f3c3d6fSHasso Tepper 	if (type == _DRM_REGISTERS || type == _DRM_FRAME_BUFFER ||
1397f3c3d6fSHasso Tepper 	    type == _DRM_SHM) {
140*88fc0f68SFrançois Tigeot 		list_for_each_entry(list, &dev->maplist, head) {
141*88fc0f68SFrançois Tigeot 			if (list->map->type == type && (list->map->offset == offset ||
142*88fc0f68SFrançois Tigeot 			    (list->map->type == _DRM_SHM &&
143*88fc0f68SFrançois Tigeot 			    list->map->flags == _DRM_CONTAINS_LOCK))) {
144*88fc0f68SFrançois Tigeot 				list->map->size = size;
1457f3c3d6fSHasso Tepper 				DRM_DEBUG("Found kernel map %d\n", type);
1467f3c3d6fSHasso Tepper 				goto done;
1477f3c3d6fSHasso Tepper 			}
1487f3c3d6fSHasso Tepper 		}
1497f3c3d6fSHasso Tepper 	}
150*88fc0f68SFrançois Tigeot 	map->mtrr = -1;
151*88fc0f68SFrançois Tigeot 	map->handle = NULL;
1527f3c3d6fSHasso Tepper 
1537f3c3d6fSHasso Tepper 	switch (map->type) {
1547f3c3d6fSHasso Tepper 	case _DRM_REGISTERS:
1557f3c3d6fSHasso Tepper 	case _DRM_FRAME_BUFFER:
1566431cd91SFrançois Tigeot 
1576431cd91SFrançois Tigeot 		if (map->type == _DRM_FRAME_BUFFER ||
1586431cd91SFrançois Tigeot 		    (map->flags & _DRM_WRITE_COMBINING)) {
1596431cd91SFrançois Tigeot 			map->mtrr =
1606431cd91SFrançois Tigeot 				arch_phys_wc_add(map->offset, map->size);
1616431cd91SFrançois Tigeot 		}
1626431cd91SFrançois Tigeot 		if (map->type == _DRM_REGISTERS) {
1636431cd91SFrançois Tigeot 			if (map->flags & _DRM_WRITE_COMBINING)
1646431cd91SFrançois Tigeot 				map->handle = ioremap_wc(map->offset,
1656431cd91SFrançois Tigeot 							 map->size);
1666431cd91SFrançois Tigeot 			else
1676431cd91SFrançois Tigeot 				map->handle = ioremap(map->offset, map->size);
1686431cd91SFrançois Tigeot 			if (!map->handle) {
1696431cd91SFrançois Tigeot 				kfree(map);
1706431cd91SFrançois Tigeot 				return -ENOMEM;
1716431cd91SFrançois Tigeot 			}
1726431cd91SFrançois Tigeot 		}
1736431cd91SFrançois Tigeot 
1747f3c3d6fSHasso Tepper 		break;
1757f3c3d6fSHasso Tepper 	case _DRM_SHM:
176*88fc0f68SFrançois Tigeot 		map->handle = vmalloc_user(map->size);
1777f3c3d6fSHasso Tepper 		DRM_DEBUG("%lu %d %p\n",
1784cd92098Szrj 			  map->size, order_base_2(map->size), map->handle);
1795c002123SFrançois Tigeot 		if (!map->handle) {
180175896dfSzrj 			kfree(map);
181b922632fSImre Vadász 			return -ENOMEM;
1827f3c3d6fSHasso Tepper 		}
1835c002123SFrançois Tigeot 		map->offset = (unsigned long)map->handle;
1847f3c3d6fSHasso Tepper 		if (map->flags & _DRM_CONTAINS_LOCK) {
1857f3c3d6fSHasso Tepper 			/* Prevent a 2nd X Server from creating a 2nd lock */
18679f713b0SFrançois Tigeot 			if (dev->lock.hw_lock != NULL) {
187*88fc0f68SFrançois Tigeot 				vfree(map->handle);
188175896dfSzrj 				kfree(map);
189b922632fSImre Vadász 				return -EBUSY;
1907f3c3d6fSHasso Tepper 			}
19179f713b0SFrançois Tigeot 			dev->lock.hw_lock = map->handle; /* Pointer to lock */
1927f3c3d6fSHasso Tepper 		}
1937f3c3d6fSHasso Tepper 		break;
194*88fc0f68SFrançois Tigeot 	case _DRM_AGP: {
19553e4e524Szrj 
19653e4e524Szrj 		if (!dev->agp) {
19753e4e524Szrj 			kfree(map);
19853e4e524Szrj 			return -EINVAL;
19953e4e524Szrj 		}
2007f3c3d6fSHasso Tepper 		/*valid = 0;*/
2017f3c3d6fSHasso Tepper 		/* In some cases (i810 driver), user space may have already
2027f3c3d6fSHasso Tepper 		 * added the AGP base itself, because dev->agp->base previously
2037f3c3d6fSHasso Tepper 		 * only got set during AGP enable.  So, only add the base
2047f3c3d6fSHasso Tepper 		 * address if the map's offset isn't already within the
2057f3c3d6fSHasso Tepper 		 * aperture.
2067f3c3d6fSHasso Tepper 		 */
2077f3c3d6fSHasso Tepper 		if (map->offset < dev->agp->base ||
2087f3c3d6fSHasso Tepper 		    map->offset > dev->agp->base +
2099d567857SJean-Sébastien Pédron 		    dev->agp->agp_info.ai_aperture_size - 1) {
2107f3c3d6fSHasso Tepper 			map->offset += dev->agp->base;
2117f3c3d6fSHasso Tepper 		}
2129d567857SJean-Sébastien Pédron 		map->mtrr   = dev->agp->agp_mtrr; /* for getmap */
2137f3c3d6fSHasso Tepper 		/*for (entry = dev->agp->memory; entry; entry = entry->next) {
2147f3c3d6fSHasso Tepper 			if ((map->offset >= entry->bound) &&
2157f3c3d6fSHasso Tepper 			    (map->offset + map->size <=
2167f3c3d6fSHasso Tepper 			    entry->bound + entry->pages * PAGE_SIZE)) {
2177f3c3d6fSHasso Tepper 				valid = 1;
2187f3c3d6fSHasso Tepper 				break;
2197f3c3d6fSHasso Tepper 			}
2207f3c3d6fSHasso Tepper 		}
2217f3c3d6fSHasso Tepper 		if (!valid) {
222175896dfSzrj 			kfree(map);
223b922632fSImre Vadász 			return -EACCES;
2247f3c3d6fSHasso Tepper 		}*/
2257f3c3d6fSHasso Tepper 		break;
226*88fc0f68SFrançois Tigeot 	}
2277f3c3d6fSHasso Tepper 	case _DRM_SCATTER_GATHER:
2287f3c3d6fSHasso Tepper 		if (!dev->sg) {
229175896dfSzrj 			kfree(map);
230b922632fSImre Vadász 			return -EINVAL;
2317f3c3d6fSHasso Tepper 		}
2325c002123SFrançois Tigeot 		map->handle = (void *)(uintptr_t)(dev->sg->vaddr + offset);
23399f70504SFrançois Tigeot 		map->offset = dev->sg->vaddr + offset;
2347f3c3d6fSHasso Tepper 		break;
2357f3c3d6fSHasso Tepper 	case _DRM_CONSISTENT:
236b31e9d59SFrançois Tigeot 		/* dma_addr_t is 64bit on i386 with CONFIG_HIGHMEM64G,
237b31e9d59SFrançois Tigeot 		 * As we're limiting the address to 2^32-1 (or less),
238b31e9d59SFrançois Tigeot 		 * casting it down to 32 bits is no problem, but we
239b31e9d59SFrançois Tigeot 		 * need to point to a 64bit variable first. */
240b31e9d59SFrançois Tigeot 		dmah = drm_pci_alloc(dev, map->size, map->size);
2415c002123SFrançois Tigeot 		if (!dmah) {
242158486a6SFrançois Tigeot 			kfree(map);
243b31e9d59SFrançois Tigeot 			return -ENOMEM;
2447f3c3d6fSHasso Tepper 		}
2455c002123SFrançois Tigeot 		map->handle = dmah->vaddr;
246*88fc0f68SFrançois Tigeot 		map->offset = (unsigned long)dmah->busaddr;
247*88fc0f68SFrançois Tigeot 		kfree(dmah);
2487f3c3d6fSHasso Tepper 		break;
2497f3c3d6fSHasso Tepper 	default:
2507f3c3d6fSHasso Tepper 		DRM_ERROR("Bad map type %d\n", map->type);
251175896dfSzrj 		kfree(map);
252b922632fSImre Vadász 		return -EINVAL;
2537f3c3d6fSHasso Tepper 	}
2547f3c3d6fSHasso Tepper 
255*88fc0f68SFrançois Tigeot 	list = kzalloc(sizeof(*list), GFP_KERNEL);
256*88fc0f68SFrançois Tigeot 	if (!list) {
257*88fc0f68SFrançois Tigeot 		if (map->type == _DRM_REGISTERS)
258*88fc0f68SFrançois Tigeot 			iounmap(map->handle);
259*88fc0f68SFrançois Tigeot 		kfree(map);
260*88fc0f68SFrançois Tigeot 		return -EINVAL;
261*88fc0f68SFrançois Tigeot 	}
262*88fc0f68SFrançois Tigeot 	list->map = map;
263*88fc0f68SFrançois Tigeot 
264*88fc0f68SFrançois Tigeot 	mutex_lock(&dev->struct_mutex);
265*88fc0f68SFrançois Tigeot 	list_add(&list->head, &dev->maplist);
266*88fc0f68SFrançois Tigeot 	mutex_unlock(&dev->struct_mutex);
2677f3c3d6fSHasso Tepper 
2687f3c3d6fSHasso Tepper done:
2697f3c3d6fSHasso Tepper 	/* Jumped to, with lock held, when a kernel map is found. */
2707f3c3d6fSHasso Tepper 
2717f3c3d6fSHasso Tepper 	DRM_DEBUG("Added map %d 0x%lx/0x%lx\n", map->type, map->offset,
2727f3c3d6fSHasso Tepper 	    map->size);
2737f3c3d6fSHasso Tepper 
274*88fc0f68SFrançois Tigeot 	*maplist = list;
2757f3c3d6fSHasso Tepper 
2767f3c3d6fSHasso Tepper 	return 0;
2777f3c3d6fSHasso Tepper }
2787f3c3d6fSHasso Tepper 
279*88fc0f68SFrançois Tigeot int drm_legacy_addmap(struct drm_device * dev, resource_size_t offset,
280*88fc0f68SFrançois Tigeot 		      unsigned int size, enum drm_map_type type,
281*88fc0f68SFrançois Tigeot 		      enum drm_map_flags flags, struct drm_local_map **map_ptr)
282*88fc0f68SFrançois Tigeot {
283*88fc0f68SFrançois Tigeot 	struct drm_map_list *list;
284*88fc0f68SFrançois Tigeot 	int rc;
285*88fc0f68SFrançois Tigeot 
286*88fc0f68SFrançois Tigeot 	rc = drm_addmap_core(dev, offset, size, type, flags, &list);
287*88fc0f68SFrançois Tigeot 	if (!rc)
288*88fc0f68SFrançois Tigeot 		*map_ptr = list->map;
289*88fc0f68SFrançois Tigeot 	return rc;
290*88fc0f68SFrançois Tigeot }
291*88fc0f68SFrançois Tigeot EXPORT_SYMBOL(drm_legacy_addmap);
292*88fc0f68SFrançois Tigeot 
293df4baf3dSFrançois Tigeot /**
294df4baf3dSFrançois Tigeot  * Ioctl to specify a range of memory that is available for mapping by a
295df4baf3dSFrançois Tigeot  * non-root process.
296df4baf3dSFrançois Tigeot  *
297df4baf3dSFrançois Tigeot  * \param inode device inode.
298df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
299df4baf3dSFrançois Tigeot  * \param cmd command.
300df4baf3dSFrançois Tigeot  * \param arg pointer to a drm_map structure.
301df4baf3dSFrançois Tigeot  * \return zero on success or a negative value on error.
302df4baf3dSFrançois Tigeot  *
303df4baf3dSFrançois Tigeot  */
3041b13d190SFrançois Tigeot int drm_legacy_addmap_ioctl(struct drm_device *dev, void *data,
305b3705d71SHasso Tepper 			    struct drm_file *file_priv)
3067f3c3d6fSHasso Tepper {
307b3705d71SHasso Tepper 	struct drm_map *request = data;
3087f3c3d6fSHasso Tepper 	drm_local_map_t *map;
3097f3c3d6fSHasso Tepper 	int err;
3107f3c3d6fSHasso Tepper 
3117f3c3d6fSHasso Tepper 	if (!(dev->flags & (FREAD|FWRITE)))
312b922632fSImre Vadász 		return -EACCES; /* Require read/write */
3137f3c3d6fSHasso Tepper 
314c6f73aabSFrançois Tigeot 	if (!capable(CAP_SYS_ADMIN) && request->type != _DRM_AGP)
315b922632fSImre Vadász 		return -EACCES;
3167f3c3d6fSHasso Tepper 
31779f713b0SFrançois Tigeot 	DRM_LOCK(dev);
3181b13d190SFrançois Tigeot 	err = drm_legacy_addmap(dev, request->offset, request->size, request->type,
3197f3c3d6fSHasso Tepper 	    request->flags, &map);
32079f713b0SFrançois Tigeot 	DRM_UNLOCK(dev);
32179f713b0SFrançois Tigeot 	if (err != 0)
3227f3c3d6fSHasso Tepper 		return err;
3237f3c3d6fSHasso Tepper 
3247f3c3d6fSHasso Tepper 	request->offset = map->offset;
3257f3c3d6fSHasso Tepper 	request->size = map->size;
3267f3c3d6fSHasso Tepper 	request->type = map->type;
3277f3c3d6fSHasso Tepper 	request->flags = map->flags;
3287f3c3d6fSHasso Tepper 	request->mtrr   = map->mtrr;
32999f70504SFrançois Tigeot 	request->handle = (void *)map->handle;
3307f3c3d6fSHasso Tepper 
3317f3c3d6fSHasso Tepper 	return 0;
3327f3c3d6fSHasso Tepper }
3337f3c3d6fSHasso Tepper 
3348621f407SFrançois Tigeot /*
3358621f407SFrançois Tigeot  * Get a mapping information.
3368621f407SFrançois Tigeot  *
3378621f407SFrançois Tigeot  * \param inode device inode.
3388621f407SFrançois Tigeot  * \param file_priv DRM file private.
3398621f407SFrançois Tigeot  * \param cmd command.
3408621f407SFrançois Tigeot  * \param arg user argument, pointing to a drm_map structure.
3418621f407SFrançois Tigeot  *
3428621f407SFrançois Tigeot  * \return zero on success or a negative number on failure.
3438621f407SFrançois Tigeot  *
3448621f407SFrançois Tigeot  * Searches for the mapping with the specified offset and copies its information
3458621f407SFrançois Tigeot  * into userspace
3468621f407SFrançois Tigeot  */
3478621f407SFrançois Tigeot int drm_legacy_getmap_ioctl(struct drm_device *dev, void *data,
3488621f407SFrançois Tigeot 			    struct drm_file *file_priv)
3498621f407SFrançois Tigeot {
3508621f407SFrançois Tigeot 	struct drm_map *map = data;
3518621f407SFrançois Tigeot 	struct drm_map_list *r_list = NULL;
3528621f407SFrançois Tigeot 	struct list_head *list;
3538621f407SFrançois Tigeot 	int idx;
3548621f407SFrançois Tigeot 	int i;
3558621f407SFrançois Tigeot 
3568621f407SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
3578621f407SFrançois Tigeot 	    drm_core_check_feature(dev, DRIVER_MODESET))
3588621f407SFrançois Tigeot 		return -EINVAL;
3598621f407SFrançois Tigeot 
3608621f407SFrançois Tigeot 	idx = map->offset;
3618621f407SFrançois Tigeot 	if (idx < 0)
3628621f407SFrançois Tigeot 		return -EINVAL;
3638621f407SFrançois Tigeot 
3648621f407SFrançois Tigeot 	i = 0;
3658621f407SFrançois Tigeot 	mutex_lock(&dev->struct_mutex);
3668621f407SFrançois Tigeot 	list_for_each(list, &dev->maplist) {
3678621f407SFrançois Tigeot 		if (i == idx) {
3688621f407SFrançois Tigeot 			r_list = list_entry(list, struct drm_map_list, head);
3698621f407SFrançois Tigeot 			break;
3708621f407SFrançois Tigeot 		}
3718621f407SFrançois Tigeot 		i++;
3728621f407SFrançois Tigeot 	}
3738621f407SFrançois Tigeot 	if (!r_list || !r_list->map) {
3748621f407SFrançois Tigeot 		mutex_unlock(&dev->struct_mutex);
3758621f407SFrançois Tigeot 		return -EINVAL;
3768621f407SFrançois Tigeot 	}
3778621f407SFrançois Tigeot 
3788621f407SFrançois Tigeot 	map->offset = r_list->map->offset;
3798621f407SFrançois Tigeot 	map->size = r_list->map->size;
3808621f407SFrançois Tigeot 	map->type = r_list->map->type;
3818621f407SFrançois Tigeot 	map->flags = r_list->map->flags;
3828621f407SFrançois Tigeot 	map->handle = (void *)(unsigned long) r_list->user_token;
3838621f407SFrançois Tigeot 	map->mtrr = r_list->map->mtrr;
3848621f407SFrançois Tigeot 
3858621f407SFrançois Tigeot 	mutex_unlock(&dev->struct_mutex);
3868621f407SFrançois Tigeot 
3878621f407SFrançois Tigeot 	return 0;
3888621f407SFrançois Tigeot }
3898621f407SFrançois Tigeot 
3901b13d190SFrançois Tigeot /**
3911b13d190SFrançois Tigeot  * Remove a map private from list and deallocate resources if the mapping
3921b13d190SFrançois Tigeot  * isn't in use.
3931b13d190SFrançois Tigeot  *
3941b13d190SFrançois Tigeot  * Searches the map on drm_device::maplist, removes it from the list, see if
3951b13d190SFrançois Tigeot  * its being used, and free any associate resource (such as MTRR's) if it's not
3961b13d190SFrançois Tigeot  * being on use.
3971b13d190SFrançois Tigeot  *
3981b13d190SFrançois Tigeot  * \sa drm_legacy_addmap
3991b13d190SFrançois Tigeot  */
4001b13d190SFrançois Tigeot int drm_legacy_rmmap_locked(struct drm_device *dev, struct drm_local_map *map)
4017f3c3d6fSHasso Tepper {
402f599cd46SFrançois Tigeot 	struct drm_map_list *r_list = NULL, *list_t;
4035c002123SFrançois Tigeot 	drm_dma_handle_t dmah;
404f599cd46SFrançois Tigeot 	int found = 0;
40579f713b0SFrançois Tigeot 
406f599cd46SFrançois Tigeot 	/* Find the list entry for the map and remove it */
407f599cd46SFrançois Tigeot 	list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) {
408f599cd46SFrançois Tigeot 		if (r_list->map == map) {
409f599cd46SFrançois Tigeot 			list_del(&r_list->head);
4101b13d190SFrançois Tigeot 			kfree(r_list);
411f599cd46SFrançois Tigeot 			found = 1;
412f599cd46SFrançois Tigeot 			break;
413f599cd46SFrançois Tigeot 		}
414f599cd46SFrançois Tigeot 	}
415f599cd46SFrançois Tigeot 
416f599cd46SFrançois Tigeot 	if (!found)
4171b13d190SFrançois Tigeot 		return -EINVAL;
4187f3c3d6fSHasso Tepper 
4197f3c3d6fSHasso Tepper 	switch (map->type) {
4207f3c3d6fSHasso Tepper 	case _DRM_REGISTERS:
4216431cd91SFrançois Tigeot 		drm_legacy_ioremapfree(map, dev);
4227f3c3d6fSHasso Tepper 		/* FALLTHROUGH */
4237f3c3d6fSHasso Tepper 	case _DRM_FRAME_BUFFER:
4246431cd91SFrançois Tigeot 		arch_phys_wc_del(map->mtrr);
4257f3c3d6fSHasso Tepper 		break;
4267f3c3d6fSHasso Tepper 	case _DRM_SHM:
427175896dfSzrj 		kfree(map->handle);
4287f3c3d6fSHasso Tepper 		break;
4297f3c3d6fSHasso Tepper 	case _DRM_AGP:
4307f3c3d6fSHasso Tepper 	case _DRM_SCATTER_GATHER:
4317f3c3d6fSHasso Tepper 		break;
4327f3c3d6fSHasso Tepper 	case _DRM_CONSISTENT:
4335c002123SFrançois Tigeot 		dmah.vaddr = map->handle;
4345c002123SFrançois Tigeot 		dmah.busaddr = map->offset;
4351b13d190SFrançois Tigeot 		dmah.size = map->size;
4361b13d190SFrançois Tigeot 		__drm_legacy_pci_free(dev, &dmah);
4377f3c3d6fSHasso Tepper 		break;
4381b13d190SFrançois Tigeot 	}
4391b13d190SFrançois Tigeot 	kfree(map);
4401b13d190SFrançois Tigeot 
4411b13d190SFrançois Tigeot 	return 0;
4427f3c3d6fSHasso Tepper }
44379f713b0SFrançois Tigeot 
4441b13d190SFrançois Tigeot int drm_legacy_rmmap(struct drm_device *dev, struct drm_local_map *map)
4451b13d190SFrançois Tigeot {
4461b13d190SFrançois Tigeot 	int ret;
4471b13d190SFrançois Tigeot 
4488621f407SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
4498621f407SFrançois Tigeot 	    drm_core_check_feature(dev, DRIVER_MODESET))
4508621f407SFrançois Tigeot 		return -EINVAL;
4518621f407SFrançois Tigeot 
4521b13d190SFrançois Tigeot 	mutex_lock(&dev->struct_mutex);
4531b13d190SFrançois Tigeot 	ret = drm_legacy_rmmap_locked(dev, map);
4541b13d190SFrançois Tigeot 	mutex_unlock(&dev->struct_mutex);
4551b13d190SFrançois Tigeot 
4561b13d190SFrançois Tigeot 	return ret;
4577f3c3d6fSHasso Tepper }
4581b13d190SFrançois Tigeot EXPORT_SYMBOL(drm_legacy_rmmap);
4597f3c3d6fSHasso Tepper 
460f599cd46SFrançois Tigeot /* The rmmap ioctl appears to be unnecessary.  All mappings are torn down on
461f599cd46SFrançois Tigeot  * the last close of the device, and this is necessary for cleanup when things
462f599cd46SFrançois Tigeot  * exit uncleanly.  Therefore, having userland manually remove mappings seems
463f599cd46SFrançois Tigeot  * like a pointless exercise since they're going away anyway.
464f599cd46SFrançois Tigeot  *
465f599cd46SFrançois Tigeot  * One use case might be after addmap is allowed for normal users for SHM and
466f599cd46SFrançois Tigeot  * gets used by drivers that the server doesn't need to care about.  This seems
467f599cd46SFrançois Tigeot  * unlikely.
468f599cd46SFrançois Tigeot  *
469f599cd46SFrançois Tigeot  * \param inode device inode.
470f599cd46SFrançois Tigeot  * \param file_priv DRM file private.
471f599cd46SFrançois Tigeot  * \param cmd command.
472f599cd46SFrançois Tigeot  * \param arg pointer to a struct drm_map structure.
473f599cd46SFrançois Tigeot  * \return zero on success or a negative value on error.
4747f3c3d6fSHasso Tepper  */
4751b13d190SFrançois Tigeot int drm_legacy_rmmap_ioctl(struct drm_device *dev, void *data,
476b3705d71SHasso Tepper 			   struct drm_file *file_priv)
4777f3c3d6fSHasso Tepper {
478b3705d71SHasso Tepper 	struct drm_map *request = data;
479f599cd46SFrançois Tigeot 	struct drm_local_map *map = NULL;
480f599cd46SFrançois Tigeot 	struct drm_map_list *r_list;
481*88fc0f68SFrançois Tigeot 	int ret;
4827f3c3d6fSHasso Tepper 
483*88fc0f68SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
484*88fc0f68SFrançois Tigeot 	    drm_core_check_feature(dev, DRIVER_MODESET))
485*88fc0f68SFrançois Tigeot 		return -EINVAL;
486*88fc0f68SFrançois Tigeot 
487*88fc0f68SFrançois Tigeot 	mutex_lock(&dev->struct_mutex);
488f599cd46SFrançois Tigeot 	list_for_each_entry(r_list, &dev->maplist, head) {
489f599cd46SFrançois Tigeot 		if (r_list->map &&
490f599cd46SFrançois Tigeot 		    r_list->user_token == (unsigned long)request->handle &&
491f599cd46SFrançois Tigeot 		    r_list->map->flags & _DRM_REMOVABLE) {
492f599cd46SFrançois Tigeot 			map = r_list->map;
4937f3c3d6fSHasso Tepper 			break;
4947f3c3d6fSHasso Tepper 		}
495f599cd46SFrançois Tigeot 	}
4967f3c3d6fSHasso Tepper 
497f599cd46SFrançois Tigeot 	/* List has wrapped around to the head pointer, or its empty we didn't
498f599cd46SFrançois Tigeot 	 * find anything.
499f599cd46SFrançois Tigeot 	 */
500f599cd46SFrançois Tigeot 	if (list_empty(&dev->maplist) || !map) {
501*88fc0f68SFrançois Tigeot 		mutex_unlock(&dev->struct_mutex);
502f599cd46SFrançois Tigeot 		return -EINVAL;
503f599cd46SFrançois Tigeot 	}
504f599cd46SFrançois Tigeot 
505f599cd46SFrançois Tigeot 	/* Register and framebuffer maps are permanent */
506f599cd46SFrançois Tigeot 	if ((map->type == _DRM_REGISTERS) || (map->type == _DRM_FRAME_BUFFER)) {
507*88fc0f68SFrançois Tigeot 		mutex_unlock(&dev->struct_mutex);
508f599cd46SFrançois Tigeot 		return 0;
5097f3c3d6fSHasso Tepper 	}
5107f3c3d6fSHasso Tepper 
511*88fc0f68SFrançois Tigeot 	ret = drm_legacy_rmmap_locked(dev, map);
5127f3c3d6fSHasso Tepper 
513*88fc0f68SFrançois Tigeot 	mutex_unlock(&dev->struct_mutex);
5147f3c3d6fSHasso Tepper 
515*88fc0f68SFrançois Tigeot 	return ret;
5167f3c3d6fSHasso Tepper }
5177f3c3d6fSHasso Tepper 
518df4baf3dSFrançois Tigeot /**
519df4baf3dSFrançois Tigeot  * Cleanup after an error on one of the addbufs() functions.
520df4baf3dSFrançois Tigeot  *
521df4baf3dSFrançois Tigeot  * \param dev DRM device.
522df4baf3dSFrançois Tigeot  * \param entry buffer entry where the error occurred.
523df4baf3dSFrançois Tigeot  *
524df4baf3dSFrançois Tigeot  * Frees any pages and buffers associated with the given entry.
525df4baf3dSFrançois Tigeot  */
526b3705d71SHasso Tepper static void drm_cleanup_buf_error(struct drm_device * dev,
527df4baf3dSFrançois Tigeot 				  struct drm_buf_entry * entry)
5287f3c3d6fSHasso Tepper {
5297f3c3d6fSHasso Tepper 	int i;
5307f3c3d6fSHasso Tepper 
5317f3c3d6fSHasso Tepper 	if (entry->seg_count) {
5327f3c3d6fSHasso Tepper 		for (i = 0; i < entry->seg_count; i++) {
5337f3c3d6fSHasso Tepper 			drm_pci_free(dev, entry->seglist[i]);
5347f3c3d6fSHasso Tepper 		}
535175896dfSzrj 		kfree(entry->seglist);
5367f3c3d6fSHasso Tepper 
5377f3c3d6fSHasso Tepper 		entry->seg_count = 0;
5387f3c3d6fSHasso Tepper 	}
5397f3c3d6fSHasso Tepper 
5407f3c3d6fSHasso Tepper 	if (entry->buf_count) {
5417f3c3d6fSHasso Tepper 		for (i = 0; i < entry->buf_count; i++) {
542175896dfSzrj 			kfree(entry->buflist[i].dev_private);
5437f3c3d6fSHasso Tepper 		}
544175896dfSzrj 		kfree(entry->buflist);
5457f3c3d6fSHasso Tepper 
5467f3c3d6fSHasso Tepper 		entry->buf_count = 0;
5477f3c3d6fSHasso Tepper 	}
5487f3c3d6fSHasso Tepper }
5497f3c3d6fSHasso Tepper 
550b3705d71SHasso Tepper static int drm_do_addbufs_agp(struct drm_device *dev, struct drm_buf_desc *request)
5517f3c3d6fSHasso Tepper {
5524250aa95Szrj 	struct drm_device_dma *dma = dev->dma;
5534250aa95Szrj 	struct drm_buf_entry *entry;
5544250aa95Szrj 	/* struct drm_agp_mem *agp_entry; */
5554250aa95Szrj 	/* int valid */
5564250aa95Szrj 	struct drm_buf *buf;
5577f3c3d6fSHasso Tepper 	unsigned long offset;
5587f3c3d6fSHasso Tepper 	unsigned long agp_offset;
5597f3c3d6fSHasso Tepper 	int count;
5607f3c3d6fSHasso Tepper 	int order;
5617f3c3d6fSHasso Tepper 	int size;
5627f3c3d6fSHasso Tepper 	int alignment;
5637f3c3d6fSHasso Tepper 	int page_order;
5647f3c3d6fSHasso Tepper 	int total;
5657f3c3d6fSHasso Tepper 	int byte_count;
5667f3c3d6fSHasso Tepper 	int i;
5674250aa95Szrj 	struct drm_buf **temp_buflist;
5687f3c3d6fSHasso Tepper 
5697f3c3d6fSHasso Tepper 	count = request->count;
5704cd92098Szrj 	order = order_base_2(request->size);
5717f3c3d6fSHasso Tepper 	size = 1 << order;
5727f3c3d6fSHasso Tepper 
5737f3c3d6fSHasso Tepper 	alignment  = (request->flags & _DRM_PAGE_ALIGN)
5747f3c3d6fSHasso Tepper 	    ? round_page(size) : size;
5757f3c3d6fSHasso Tepper 	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
5767f3c3d6fSHasso Tepper 	total = PAGE_SIZE << page_order;
5777f3c3d6fSHasso Tepper 
5787f3c3d6fSHasso Tepper 	byte_count = 0;
5797f3c3d6fSHasso Tepper 	agp_offset = dev->agp->base + request->agp_start;
5807f3c3d6fSHasso Tepper 
5817f3c3d6fSHasso Tepper 	DRM_DEBUG("count:      %d\n",  count);
5827f3c3d6fSHasso Tepper 	DRM_DEBUG("order:      %d\n",  order);
5837f3c3d6fSHasso Tepper 	DRM_DEBUG("size:       %d\n",  size);
5847f3c3d6fSHasso Tepper 	DRM_DEBUG("agp_offset: 0x%lx\n", agp_offset);
5857f3c3d6fSHasso Tepper 	DRM_DEBUG("alignment:  %d\n",  alignment);
5867f3c3d6fSHasso Tepper 	DRM_DEBUG("page_order: %d\n",  page_order);
5877f3c3d6fSHasso Tepper 	DRM_DEBUG("total:      %d\n",  total);
5887f3c3d6fSHasso Tepper 
5897f3c3d6fSHasso Tepper 	/* Make sure buffers are located in AGP memory that we own */
5907f3c3d6fSHasso Tepper 	/* Breaks MGA due to drm_alloc_agp not setting up entries for the
5917f3c3d6fSHasso Tepper 	 * memory.  Safe to ignore for now because these ioctls are still
5927f3c3d6fSHasso Tepper 	 * root-only.
5937f3c3d6fSHasso Tepper 	 */
5947f3c3d6fSHasso Tepper 	/*valid = 0;
5957f3c3d6fSHasso Tepper 	for (agp_entry = dev->agp->memory; agp_entry;
5967f3c3d6fSHasso Tepper 	    agp_entry = agp_entry->next) {
5977f3c3d6fSHasso Tepper 		if ((agp_offset >= agp_entry->bound) &&
5987f3c3d6fSHasso Tepper 		    (agp_offset + total * count <=
5997f3c3d6fSHasso Tepper 		    agp_entry->bound + agp_entry->pages * PAGE_SIZE)) {
6007f3c3d6fSHasso Tepper 			valid = 1;
6017f3c3d6fSHasso Tepper 			break;
6027f3c3d6fSHasso Tepper 		}
6037f3c3d6fSHasso Tepper 	}
6047f3c3d6fSHasso Tepper 	if (!valid) {
6057f3c3d6fSHasso Tepper 		DRM_DEBUG("zone invalid\n");
606b922632fSImre Vadász 		return -EINVAL;
6077f3c3d6fSHasso Tepper 	}*/
6087f3c3d6fSHasso Tepper 
6097f3c3d6fSHasso Tepper 	entry = &dma->bufs[order];
6107f3c3d6fSHasso Tepper 
6115a3b77d5SFrançois Tigeot 	entry->buflist = kmalloc(count * sizeof(*entry->buflist), M_DRM,
612f8677ba6SMatthew Dillon 				 M_WAITOK | M_NULLOK | M_ZERO);
6137f3c3d6fSHasso Tepper 	if (!entry->buflist) {
614b922632fSImre Vadász 		return -ENOMEM;
6157f3c3d6fSHasso Tepper 	}
6167f3c3d6fSHasso Tepper 
6177f3c3d6fSHasso Tepper 	entry->buf_size = size;
6187f3c3d6fSHasso Tepper 	entry->page_order = page_order;
6197f3c3d6fSHasso Tepper 
6207f3c3d6fSHasso Tepper 	offset = 0;
6217f3c3d6fSHasso Tepper 
6227f3c3d6fSHasso Tepper 	while (entry->buf_count < count) {
6237f3c3d6fSHasso Tepper 		buf          = &entry->buflist[entry->buf_count];
6247f3c3d6fSHasso Tepper 		buf->idx     = dma->buf_count + entry->buf_count;
6257f3c3d6fSHasso Tepper 		buf->total   = alignment;
6267f3c3d6fSHasso Tepper 		buf->order   = order;
6277f3c3d6fSHasso Tepper 		buf->used    = 0;
6287f3c3d6fSHasso Tepper 
6297f3c3d6fSHasso Tepper 		buf->offset  = (dma->byte_count + offset);
6307f3c3d6fSHasso Tepper 		buf->bus_address = agp_offset + offset;
6317f3c3d6fSHasso Tepper 		buf->address = (void *)(agp_offset + offset);
6327f3c3d6fSHasso Tepper 		buf->next    = NULL;
6337f3c3d6fSHasso Tepper 		buf->pending = 0;
6347f3c3d6fSHasso Tepper 		buf->file_priv = NULL;
6357f3c3d6fSHasso Tepper 
636ba55f2f5SFrançois Tigeot 		buf->dev_priv_size = dev->driver->dev_priv_size;
6375a3b77d5SFrançois Tigeot 		buf->dev_private = kmalloc(buf->dev_priv_size, M_DRM,
638f8677ba6SMatthew Dillon 					   M_WAITOK | M_NULLOK | M_ZERO);
6397f3c3d6fSHasso Tepper 		if (buf->dev_private == NULL) {
6407f3c3d6fSHasso Tepper 			/* Set count correctly so we free the proper amount. */
6417f3c3d6fSHasso Tepper 			entry->buf_count = count;
6427f3c3d6fSHasso Tepper 			drm_cleanup_buf_error(dev, entry);
643b922632fSImre Vadász 			return -ENOMEM;
6447f3c3d6fSHasso Tepper 		}
6457f3c3d6fSHasso Tepper 
6467f3c3d6fSHasso Tepper 		offset += alignment;
6477f3c3d6fSHasso Tepper 		entry->buf_count++;
6487f3c3d6fSHasso Tepper 		byte_count += PAGE_SIZE << page_order;
6497f3c3d6fSHasso Tepper 	}
6507f3c3d6fSHasso Tepper 
6517f3c3d6fSHasso Tepper 	DRM_DEBUG("byte_count: %d\n", byte_count);
6527f3c3d6fSHasso Tepper 
6535718399fSFrançois Tigeot 	temp_buflist = krealloc(dma->buflist,
654b3705d71SHasso Tepper 	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
655f8677ba6SMatthew Dillon 	    M_DRM, M_WAITOK | M_NULLOK);
6567f3c3d6fSHasso Tepper 	if (temp_buflist == NULL) {
6577f3c3d6fSHasso Tepper 		/* Free the entry because it isn't valid */
6587f3c3d6fSHasso Tepper 		drm_cleanup_buf_error(dev, entry);
659b922632fSImre Vadász 		return -ENOMEM;
6607f3c3d6fSHasso Tepper 	}
6617f3c3d6fSHasso Tepper 	dma->buflist = temp_buflist;
6627f3c3d6fSHasso Tepper 
6637f3c3d6fSHasso Tepper 	for (i = 0; i < entry->buf_count; i++) {
6647f3c3d6fSHasso Tepper 		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
6657f3c3d6fSHasso Tepper 	}
6667f3c3d6fSHasso Tepper 
6677f3c3d6fSHasso Tepper 	dma->buf_count += entry->buf_count;
6687f3c3d6fSHasso Tepper 	dma->byte_count += byte_count;
6697f3c3d6fSHasso Tepper 
6707f3c3d6fSHasso Tepper 	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
6717f3c3d6fSHasso Tepper 	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
6727f3c3d6fSHasso Tepper 
6737f3c3d6fSHasso Tepper 	request->count = entry->buf_count;
6747f3c3d6fSHasso Tepper 	request->size = size;
6757f3c3d6fSHasso Tepper 
6767f3c3d6fSHasso Tepper 	dma->flags = _DRM_DMA_USE_AGP;
6777f3c3d6fSHasso Tepper 
6787f3c3d6fSHasso Tepper 	return 0;
6797f3c3d6fSHasso Tepper }
6807f3c3d6fSHasso Tepper 
681b3705d71SHasso Tepper static int drm_do_addbufs_pci(struct drm_device *dev, struct drm_buf_desc *request)
6827f3c3d6fSHasso Tepper {
6834250aa95Szrj 	struct drm_device_dma *dma = dev->dma;
6847f3c3d6fSHasso Tepper 	int count;
6857f3c3d6fSHasso Tepper 	int order;
6867f3c3d6fSHasso Tepper 	int size;
6877f3c3d6fSHasso Tepper 	int total;
6887f3c3d6fSHasso Tepper 	int page_order;
6894250aa95Szrj 	struct drm_buf_entry *entry;
690b31e9d59SFrançois Tigeot 	drm_dma_handle_t *dmah;
6914250aa95Szrj 	struct drm_buf *buf;
6927f3c3d6fSHasso Tepper 	int alignment;
6937f3c3d6fSHasso Tepper 	unsigned long offset;
6947f3c3d6fSHasso Tepper 	int i;
6957f3c3d6fSHasso Tepper 	int byte_count;
6967f3c3d6fSHasso Tepper 	int page_count;
6977f3c3d6fSHasso Tepper 	unsigned long *temp_pagelist;
6984250aa95Szrj 	struct drm_buf **temp_buflist;
6997f3c3d6fSHasso Tepper 
7007f3c3d6fSHasso Tepper 	count = request->count;
7014cd92098Szrj 	order = order_base_2(request->size);
7027f3c3d6fSHasso Tepper 	size = 1 << order;
7037f3c3d6fSHasso Tepper 
7047f3c3d6fSHasso Tepper 	DRM_DEBUG("count=%d, size=%d (%d), order=%d\n",
7057f3c3d6fSHasso Tepper 	    request->count, request->size, size, order);
7067f3c3d6fSHasso Tepper 
7077f3c3d6fSHasso Tepper 	alignment = (request->flags & _DRM_PAGE_ALIGN)
7087f3c3d6fSHasso Tepper 	    ? round_page(size) : size;
7097f3c3d6fSHasso Tepper 	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
7107f3c3d6fSHasso Tepper 	total = PAGE_SIZE << page_order;
7117f3c3d6fSHasso Tepper 
7127f3c3d6fSHasso Tepper 	entry = &dma->bufs[order];
7137f3c3d6fSHasso Tepper 
7145a3b77d5SFrançois Tigeot 	entry->buflist = kmalloc(count * sizeof(*entry->buflist), M_DRM,
715f8677ba6SMatthew Dillon 				 M_WAITOK | M_NULLOK | M_ZERO);
7165a3b77d5SFrançois Tigeot 	entry->seglist = kmalloc(count * sizeof(*entry->seglist), M_DRM,
717f8677ba6SMatthew Dillon 				 M_WAITOK | M_NULLOK | M_ZERO);
7187f3c3d6fSHasso Tepper 
7197f3c3d6fSHasso Tepper 	/* Keep the original pagelist until we know all the allocations
7207f3c3d6fSHasso Tepper 	 * have succeeded
7217f3c3d6fSHasso Tepper 	 */
7225718399fSFrançois Tigeot 	temp_pagelist = kmalloc((dma->page_count + (count << page_order)) *
723f8677ba6SMatthew Dillon 				sizeof(*dma->pagelist),
724f8677ba6SMatthew Dillon 				M_DRM, M_WAITOK | M_NULLOK);
7257f3c3d6fSHasso Tepper 
7267f3c3d6fSHasso Tepper 	if (entry->buflist == NULL || entry->seglist == NULL ||
7277f3c3d6fSHasso Tepper 	    temp_pagelist == NULL) {
728175896dfSzrj 		kfree(temp_pagelist);
729175896dfSzrj 		kfree(entry->seglist);
730175896dfSzrj 		kfree(entry->buflist);
731b922632fSImre Vadász 		return -ENOMEM;
7327f3c3d6fSHasso Tepper 	}
7337f3c3d6fSHasso Tepper 
7347f3c3d6fSHasso Tepper 	memcpy(temp_pagelist, dma->pagelist, dma->page_count *
7357f3c3d6fSHasso Tepper 	    sizeof(*dma->pagelist));
7367f3c3d6fSHasso Tepper 
7377f3c3d6fSHasso Tepper 	DRM_DEBUG("pagelist: %d entries\n",
7387f3c3d6fSHasso Tepper 	    dma->page_count + (count << page_order));
7397f3c3d6fSHasso Tepper 
7407f3c3d6fSHasso Tepper 	entry->buf_size	= size;
7417f3c3d6fSHasso Tepper 	entry->page_order = page_order;
7427f3c3d6fSHasso Tepper 	byte_count = 0;
7437f3c3d6fSHasso Tepper 	page_count = 0;
7447f3c3d6fSHasso Tepper 
7457f3c3d6fSHasso Tepper 	while (entry->buf_count < count) {
746b31e9d59SFrançois Tigeot 		dmah = drm_pci_alloc(dev, PAGE_SIZE << page_order, 0x1000);
747b31e9d59SFrançois Tigeot 
748b31e9d59SFrançois Tigeot 		if (!dmah) {
7497f3c3d6fSHasso Tepper 			/* Set count correctly so we free the proper amount. */
7507f3c3d6fSHasso Tepper 			entry->buf_count = count;
7517f3c3d6fSHasso Tepper 			entry->seg_count = count;
7527f3c3d6fSHasso Tepper 			drm_cleanup_buf_error(dev, entry);
753175896dfSzrj 			kfree(temp_pagelist);
754b31e9d59SFrançois Tigeot 			return -ENOMEM;
7557f3c3d6fSHasso Tepper 		}
7567f3c3d6fSHasso Tepper 		entry->seglist[entry->seg_count++] = dmah;
7577f3c3d6fSHasso Tepper 		for (i = 0; i < (1 << page_order); i++) {
758b31e9d59SFrançois Tigeot 			DRM_DEBUG("page %d @ 0x%08lx\n",
7597f3c3d6fSHasso Tepper 				  dma->page_count + page_count,
760b31e9d59SFrançois Tigeot 				  (unsigned long)dmah->vaddr + PAGE_SIZE * i);
761b31e9d59SFrançois Tigeot 			temp_pagelist[dma->page_count + page_count++]
762b31e9d59SFrançois Tigeot 				= (unsigned long)dmah->vaddr + PAGE_SIZE * i;
7637f3c3d6fSHasso Tepper 		}
7647f3c3d6fSHasso Tepper 		for (offset = 0;
7657f3c3d6fSHasso Tepper 		    offset + size <= total && entry->buf_count < count;
7667f3c3d6fSHasso Tepper 		    offset += alignment, ++entry->buf_count) {
7677f3c3d6fSHasso Tepper 			buf	     = &entry->buflist[entry->buf_count];
7687f3c3d6fSHasso Tepper 			buf->idx     = dma->buf_count + entry->buf_count;
7697f3c3d6fSHasso Tepper 			buf->total   = alignment;
7707f3c3d6fSHasso Tepper 			buf->order   = order;
7717f3c3d6fSHasso Tepper 			buf->used    = 0;
7727f3c3d6fSHasso Tepper 			buf->offset  = (dma->byte_count + byte_count + offset);
7737f3c3d6fSHasso Tepper 			buf->address = ((char *)dmah->vaddr + offset);
7747f3c3d6fSHasso Tepper 			buf->bus_address = dmah->busaddr + offset;
7757f3c3d6fSHasso Tepper 			buf->next    = NULL;
7767f3c3d6fSHasso Tepper 			buf->pending = 0;
7777f3c3d6fSHasso Tepper 			buf->file_priv = NULL;
7787f3c3d6fSHasso Tepper 
779ba55f2f5SFrançois Tigeot 			buf->dev_priv_size = dev->driver->dev_priv_size;
7805718399fSFrançois Tigeot 			buf->dev_private = kmalloc(buf->dev_priv_size,
781f8677ba6SMatthew Dillon 						   M_DRM,
782f8677ba6SMatthew Dillon 						   M_WAITOK | M_NULLOK |
783f8677ba6SMatthew Dillon 						    M_ZERO);
7847f3c3d6fSHasso Tepper 			if (buf->dev_private == NULL) {
7857f3c3d6fSHasso Tepper 				/* Set count correctly so we free the proper amount. */
7867f3c3d6fSHasso Tepper 				entry->buf_count = count;
7877f3c3d6fSHasso Tepper 				entry->seg_count = count;
7887f3c3d6fSHasso Tepper 				drm_cleanup_buf_error(dev, entry);
789175896dfSzrj 				kfree(temp_pagelist);
790b922632fSImre Vadász 				return -ENOMEM;
7917f3c3d6fSHasso Tepper 			}
7927f3c3d6fSHasso Tepper 
7937f3c3d6fSHasso Tepper 			DRM_DEBUG("buffer %d @ %p\n",
7947f3c3d6fSHasso Tepper 			    entry->buf_count, buf->address);
7957f3c3d6fSHasso Tepper 		}
7967f3c3d6fSHasso Tepper 		byte_count += PAGE_SIZE << page_order;
7977f3c3d6fSHasso Tepper 	}
7987f3c3d6fSHasso Tepper 
7995718399fSFrançois Tigeot 	temp_buflist = krealloc(dma->buflist,
800b3705d71SHasso Tepper 	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
801f8677ba6SMatthew Dillon 	    M_DRM, M_WAITOK | M_NULLOK);
8027f3c3d6fSHasso Tepper 	if (temp_buflist == NULL) {
8037f3c3d6fSHasso Tepper 		/* Free the entry because it isn't valid */
8047f3c3d6fSHasso Tepper 		drm_cleanup_buf_error(dev, entry);
805175896dfSzrj 		kfree(temp_pagelist);
806b922632fSImre Vadász 		return -ENOMEM;
8077f3c3d6fSHasso Tepper 	}
8087f3c3d6fSHasso Tepper 	dma->buflist = temp_buflist;
8097f3c3d6fSHasso Tepper 
8107f3c3d6fSHasso Tepper 	for (i = 0; i < entry->buf_count; i++) {
8117f3c3d6fSHasso Tepper 		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
8127f3c3d6fSHasso Tepper 	}
8137f3c3d6fSHasso Tepper 
814175896dfSzrj 	/* No allocations failed, so now we can replace the original pagelist
8157f3c3d6fSHasso Tepper 	 * with the new one.
8167f3c3d6fSHasso Tepper 	 */
817175896dfSzrj 	kfree(dma->pagelist);
8187f3c3d6fSHasso Tepper 	dma->pagelist = temp_pagelist;
8197f3c3d6fSHasso Tepper 
8207f3c3d6fSHasso Tepper 	dma->buf_count += entry->buf_count;
8217f3c3d6fSHasso Tepper 	dma->seg_count += entry->seg_count;
8227f3c3d6fSHasso Tepper 	dma->page_count += entry->seg_count << page_order;
8237f3c3d6fSHasso Tepper 	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
8247f3c3d6fSHasso Tepper 
8257f3c3d6fSHasso Tepper 	request->count = entry->buf_count;
8267f3c3d6fSHasso Tepper 	request->size = size;
8277f3c3d6fSHasso Tepper 
8287f3c3d6fSHasso Tepper 	return 0;
8297f3c3d6fSHasso Tepper 
8307f3c3d6fSHasso Tepper }
8317f3c3d6fSHasso Tepper 
832b3705d71SHasso Tepper static int drm_do_addbufs_sg(struct drm_device *dev, struct drm_buf_desc *request)
8337f3c3d6fSHasso Tepper {
8344250aa95Szrj 	struct drm_device_dma *dma = dev->dma;
8354250aa95Szrj 	struct drm_buf_entry *entry;
8364250aa95Szrj 	struct drm_buf *buf;
8377f3c3d6fSHasso Tepper 	unsigned long offset;
8387f3c3d6fSHasso Tepper 	unsigned long agp_offset;
8397f3c3d6fSHasso Tepper 	int count;
8407f3c3d6fSHasso Tepper 	int order;
8417f3c3d6fSHasso Tepper 	int size;
8427f3c3d6fSHasso Tepper 	int alignment;
8437f3c3d6fSHasso Tepper 	int page_order;
8447f3c3d6fSHasso Tepper 	int total;
8457f3c3d6fSHasso Tepper 	int byte_count;
8467f3c3d6fSHasso Tepper 	int i;
8474250aa95Szrj 	struct drm_buf **temp_buflist;
8487f3c3d6fSHasso Tepper 
8497f3c3d6fSHasso Tepper 	count = request->count;
8504cd92098Szrj 	order = order_base_2(request->size);
8517f3c3d6fSHasso Tepper 	size = 1 << order;
8527f3c3d6fSHasso Tepper 
8537f3c3d6fSHasso Tepper 	alignment  = (request->flags & _DRM_PAGE_ALIGN)
8547f3c3d6fSHasso Tepper 	    ? round_page(size) : size;
8557f3c3d6fSHasso Tepper 	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
8567f3c3d6fSHasso Tepper 	total = PAGE_SIZE << page_order;
8577f3c3d6fSHasso Tepper 
8587f3c3d6fSHasso Tepper 	byte_count = 0;
8597f3c3d6fSHasso Tepper 	agp_offset = request->agp_start;
8607f3c3d6fSHasso Tepper 
8617f3c3d6fSHasso Tepper 	DRM_DEBUG("count:      %d\n",  count);
8627f3c3d6fSHasso Tepper 	DRM_DEBUG("order:      %d\n",  order);
8637f3c3d6fSHasso Tepper 	DRM_DEBUG("size:       %d\n",  size);
8647f3c3d6fSHasso Tepper 	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
8657f3c3d6fSHasso Tepper 	DRM_DEBUG("alignment:  %d\n",  alignment);
8667f3c3d6fSHasso Tepper 	DRM_DEBUG("page_order: %d\n",  page_order);
8677f3c3d6fSHasso Tepper 	DRM_DEBUG("total:      %d\n",  total);
8687f3c3d6fSHasso Tepper 
8697f3c3d6fSHasso Tepper 	entry = &dma->bufs[order];
8707f3c3d6fSHasso Tepper 
8715a3b77d5SFrançois Tigeot 	entry->buflist = kmalloc(count * sizeof(*entry->buflist), M_DRM,
872f8677ba6SMatthew Dillon 				 M_WAITOK | M_NULLOK | M_ZERO);
8737f3c3d6fSHasso Tepper 	if (entry->buflist == NULL)
874b922632fSImre Vadász 		return -ENOMEM;
8757f3c3d6fSHasso Tepper 
8767f3c3d6fSHasso Tepper 	entry->buf_size = size;
8777f3c3d6fSHasso Tepper 	entry->page_order = page_order;
8787f3c3d6fSHasso Tepper 
8797f3c3d6fSHasso Tepper 	offset = 0;
8807f3c3d6fSHasso Tepper 
8817f3c3d6fSHasso Tepper 	while (entry->buf_count < count) {
8827f3c3d6fSHasso Tepper 		buf          = &entry->buflist[entry->buf_count];
8837f3c3d6fSHasso Tepper 		buf->idx     = dma->buf_count + entry->buf_count;
8847f3c3d6fSHasso Tepper 		buf->total   = alignment;
8857f3c3d6fSHasso Tepper 		buf->order   = order;
8867f3c3d6fSHasso Tepper 		buf->used    = 0;
8877f3c3d6fSHasso Tepper 
8887f3c3d6fSHasso Tepper 		buf->offset  = (dma->byte_count + offset);
8897f3c3d6fSHasso Tepper 		buf->bus_address = agp_offset + offset;
89099f70504SFrançois Tigeot 		buf->address = (void *)(agp_offset + offset + dev->sg->vaddr);
8917f3c3d6fSHasso Tepper 		buf->next    = NULL;
8927f3c3d6fSHasso Tepper 		buf->pending = 0;
8937f3c3d6fSHasso Tepper 		buf->file_priv = NULL;
8947f3c3d6fSHasso Tepper 
895ba55f2f5SFrançois Tigeot 		buf->dev_priv_size = dev->driver->dev_priv_size;
8965a3b77d5SFrançois Tigeot 		buf->dev_private = kmalloc(buf->dev_priv_size, M_DRM,
897f8677ba6SMatthew Dillon 					   M_WAITOK | M_NULLOK | M_ZERO);
8987f3c3d6fSHasso Tepper 		if (buf->dev_private == NULL) {
8997f3c3d6fSHasso Tepper 			/* Set count correctly so we free the proper amount. */
9007f3c3d6fSHasso Tepper 			entry->buf_count = count;
9017f3c3d6fSHasso Tepper 			drm_cleanup_buf_error(dev, entry);
902b922632fSImre Vadász 			return -ENOMEM;
9037f3c3d6fSHasso Tepper 		}
9047f3c3d6fSHasso Tepper 
9057f3c3d6fSHasso Tepper 		DRM_DEBUG("buffer %d @ %p\n",
9067f3c3d6fSHasso Tepper 		    entry->buf_count, buf->address);
9077f3c3d6fSHasso Tepper 
9087f3c3d6fSHasso Tepper 		offset += alignment;
9097f3c3d6fSHasso Tepper 		entry->buf_count++;
9107f3c3d6fSHasso Tepper 		byte_count += PAGE_SIZE << page_order;
9117f3c3d6fSHasso Tepper 	}
9127f3c3d6fSHasso Tepper 
9137f3c3d6fSHasso Tepper 	DRM_DEBUG("byte_count: %d\n", byte_count);
9147f3c3d6fSHasso Tepper 
9155718399fSFrançois Tigeot 	temp_buflist = krealloc(dma->buflist,
916b3705d71SHasso Tepper 	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
917f8677ba6SMatthew Dillon 	    M_DRM, M_WAITOK | M_NULLOK);
9187f3c3d6fSHasso Tepper 	if (temp_buflist == NULL) {
9197f3c3d6fSHasso Tepper 		/* Free the entry because it isn't valid */
9207f3c3d6fSHasso Tepper 		drm_cleanup_buf_error(dev, entry);
921b922632fSImre Vadász 		return -ENOMEM;
9227f3c3d6fSHasso Tepper 	}
9237f3c3d6fSHasso Tepper 	dma->buflist = temp_buflist;
9247f3c3d6fSHasso Tepper 
9257f3c3d6fSHasso Tepper 	for (i = 0; i < entry->buf_count; i++) {
9267f3c3d6fSHasso Tepper 		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
9277f3c3d6fSHasso Tepper 	}
9287f3c3d6fSHasso Tepper 
9297f3c3d6fSHasso Tepper 	dma->buf_count += entry->buf_count;
9307f3c3d6fSHasso Tepper 	dma->byte_count += byte_count;
9317f3c3d6fSHasso Tepper 
9327f3c3d6fSHasso Tepper 	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
9337f3c3d6fSHasso Tepper 	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
9347f3c3d6fSHasso Tepper 
9357f3c3d6fSHasso Tepper 	request->count = entry->buf_count;
9367f3c3d6fSHasso Tepper 	request->size = size;
9377f3c3d6fSHasso Tepper 
9387f3c3d6fSHasso Tepper 	dma->flags = _DRM_DMA_USE_SG;
9397f3c3d6fSHasso Tepper 
9407f3c3d6fSHasso Tepper 	return 0;
9417f3c3d6fSHasso Tepper }
9427f3c3d6fSHasso Tepper 
943df4baf3dSFrançois Tigeot /**
944df4baf3dSFrançois Tigeot  * Add AGP buffers for DMA transfers.
945df4baf3dSFrançois Tigeot  *
946df4baf3dSFrançois Tigeot  * \param dev struct drm_device to which the buffers are to be added.
947df4baf3dSFrançois Tigeot  * \param request pointer to a struct drm_buf_desc describing the request.
948df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
949df4baf3dSFrançois Tigeot  *
950df4baf3dSFrançois Tigeot  * After some sanity checks creates a drm_buf structure for each buffer and
951df4baf3dSFrançois Tigeot  * reallocates the buffer list of the same size order to accommodate the new
952df4baf3dSFrançois Tigeot  * buffers.
953df4baf3dSFrançois Tigeot  */
9541b13d190SFrançois Tigeot int drm_legacy_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request)
9557f3c3d6fSHasso Tepper {
9567f3c3d6fSHasso Tepper 	int order, ret;
9577f3c3d6fSHasso Tepper 
9587f3c3d6fSHasso Tepper 	if (request->count < 0 || request->count > 4096)
959b922632fSImre Vadász 		return -EINVAL;
9607f3c3d6fSHasso Tepper 
9614cd92098Szrj 	order = order_base_2(request->size);
9627f3c3d6fSHasso Tepper 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
963b922632fSImre Vadász 		return -EINVAL;
9647f3c3d6fSHasso Tepper 
965b3705d71SHasso Tepper 
9667f3c3d6fSHasso Tepper 	/* No more allocations after first buffer-using ioctl. */
9677f3c3d6fSHasso Tepper 	if (dev->buf_use != 0) {
968b922632fSImre Vadász 		return -EBUSY;
9697f3c3d6fSHasso Tepper 	}
9707f3c3d6fSHasso Tepper 	/* No more than one allocation per order */
9717f3c3d6fSHasso Tepper 	if (dev->dma->bufs[order].buf_count != 0) {
972b922632fSImre Vadász 		return -ENOMEM;
9737f3c3d6fSHasso Tepper 	}
9747f3c3d6fSHasso Tepper 
9757f3c3d6fSHasso Tepper 	ret = drm_do_addbufs_agp(dev, request);
9767f3c3d6fSHasso Tepper 
9777f3c3d6fSHasso Tepper 	return ret;
9787f3c3d6fSHasso Tepper }
9797f3c3d6fSHasso Tepper 
9801b13d190SFrançois Tigeot static int drm_legacy_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request)
9817f3c3d6fSHasso Tepper {
9827f3c3d6fSHasso Tepper 	int order, ret;
9837f3c3d6fSHasso Tepper 
984c6f73aabSFrançois Tigeot 	if (!capable(CAP_SYS_ADMIN))
985b922632fSImre Vadász 		return -EACCES;
9867f3c3d6fSHasso Tepper 
9877f3c3d6fSHasso Tepper 	if (request->count < 0 || request->count > 4096)
988b922632fSImre Vadász 		return -EINVAL;
9897f3c3d6fSHasso Tepper 
9904cd92098Szrj 	order = order_base_2(request->size);
9917f3c3d6fSHasso Tepper 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
992b922632fSImre Vadász 		return -EINVAL;
9937f3c3d6fSHasso Tepper 
994394ce297SFrançois Tigeot 	spin_lock(&dev->buf_lock);
995394ce297SFrançois Tigeot 	if (dev->buf_use) {
996394ce297SFrançois Tigeot 		spin_unlock(&dev->buf_lock);
997b922632fSImre Vadász 		return -EBUSY;
9987f3c3d6fSHasso Tepper 	}
999394ce297SFrançois Tigeot 	atomic_inc(&dev->buf_alloc);
1000394ce297SFrançois Tigeot 	spin_unlock(&dev->buf_lock);
1001394ce297SFrançois Tigeot 
10027f3c3d6fSHasso Tepper 	/* No more than one allocation per order */
10037f3c3d6fSHasso Tepper 	if (dev->dma->bufs[order].buf_count != 0) {
1004b922632fSImre Vadász 		return -ENOMEM;
10057f3c3d6fSHasso Tepper 	}
10067f3c3d6fSHasso Tepper 
10077f3c3d6fSHasso Tepper 	ret = drm_do_addbufs_sg(dev, request);
10087f3c3d6fSHasso Tepper 
10097f3c3d6fSHasso Tepper 	return ret;
10107f3c3d6fSHasso Tepper }
10117f3c3d6fSHasso Tepper 
10121b13d190SFrançois Tigeot int drm_legacy_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request)
10137f3c3d6fSHasso Tepper {
10147f3c3d6fSHasso Tepper 	int order, ret;
10157f3c3d6fSHasso Tepper 
1016c6f73aabSFrançois Tigeot 	if (!capable(CAP_SYS_ADMIN))
1017b922632fSImre Vadász 		return -EACCES;
10187f3c3d6fSHasso Tepper 
10197f3c3d6fSHasso Tepper 	if (request->count < 0 || request->count > 4096)
1020b922632fSImre Vadász 		return -EINVAL;
10217f3c3d6fSHasso Tepper 
10224cd92098Szrj 	order = order_base_2(request->size);
10237f3c3d6fSHasso Tepper 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
1024b922632fSImre Vadász 		return -EINVAL;
10257f3c3d6fSHasso Tepper 
1026394ce297SFrançois Tigeot 	spin_lock(&dev->buf_lock);
1027394ce297SFrançois Tigeot 	if (dev->buf_use) {
1028394ce297SFrançois Tigeot 		spin_unlock(&dev->buf_lock);
1029394ce297SFrançois Tigeot 		return -EBUSY;
1030394ce297SFrançois Tigeot 	}
1031394ce297SFrançois Tigeot 	atomic_inc(&dev->buf_alloc);
1032394ce297SFrançois Tigeot 	spin_unlock(&dev->buf_lock);
1033b3705d71SHasso Tepper 
10347f3c3d6fSHasso Tepper 	/* No more allocations after first buffer-using ioctl. */
10357f3c3d6fSHasso Tepper 	if (dev->buf_use != 0) {
1036b922632fSImre Vadász 		return -EBUSY;
10377f3c3d6fSHasso Tepper 	}
10387f3c3d6fSHasso Tepper 	/* No more than one allocation per order */
10397f3c3d6fSHasso Tepper 	if (dev->dma->bufs[order].buf_count != 0) {
1040b922632fSImre Vadász 		return -ENOMEM;
10417f3c3d6fSHasso Tepper 	}
10427f3c3d6fSHasso Tepper 
10437f3c3d6fSHasso Tepper 	ret = drm_do_addbufs_pci(dev, request);
10447f3c3d6fSHasso Tepper 
10457f3c3d6fSHasso Tepper 	return ret;
10467f3c3d6fSHasso Tepper }
10477f3c3d6fSHasso Tepper 
1048df4baf3dSFrançois Tigeot /**
1049df4baf3dSFrançois Tigeot  * Add buffers for DMA transfers (ioctl).
1050df4baf3dSFrançois Tigeot  *
1051df4baf3dSFrançois Tigeot  * \param inode device inode.
1052df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
1053df4baf3dSFrançois Tigeot  * \param cmd command.
1054df4baf3dSFrançois Tigeot  * \param arg pointer to a struct drm_buf_desc request.
1055df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
1056df4baf3dSFrançois Tigeot  *
1057df4baf3dSFrançois Tigeot  * According with the memory type specified in drm_buf_desc::flags and the
1058df4baf3dSFrançois Tigeot  * build options, it dispatches the call either to addbufs_agp(),
1059df4baf3dSFrançois Tigeot  * addbufs_sg() or addbufs_pci() for AGP, scatter-gather or consistent
1060df4baf3dSFrançois Tigeot  * PCI memory respectively.
1061df4baf3dSFrançois Tigeot  */
10621b13d190SFrançois Tigeot int drm_legacy_addbufs(struct drm_device *dev, void *data,
1063df4baf3dSFrançois Tigeot 		       struct drm_file *file_priv)
10647f3c3d6fSHasso Tepper {
1065b3705d71SHasso Tepper 	struct drm_buf_desc *request = data;
10667f3c3d6fSHasso Tepper 	int err;
10677f3c3d6fSHasso Tepper 
10687f3c3d6fSHasso Tepper 	if (request->flags & _DRM_AGP_BUFFER)
10691b13d190SFrançois Tigeot 		err = drm_legacy_addbufs_agp(dev, request);
10707f3c3d6fSHasso Tepper 	else if (request->flags & _DRM_SG_BUFFER)
10711b13d190SFrançois Tigeot 		err = drm_legacy_addbufs_sg(dev, request);
10727f3c3d6fSHasso Tepper 	else
10731b13d190SFrançois Tigeot 		err = drm_legacy_addbufs_pci(dev, request);
10747f3c3d6fSHasso Tepper 
10757f3c3d6fSHasso Tepper 	return err;
10767f3c3d6fSHasso Tepper }
10777f3c3d6fSHasso Tepper 
1078df4baf3dSFrançois Tigeot /**
1079df4baf3dSFrançois Tigeot  * Get information about the buffer mappings.
1080df4baf3dSFrançois Tigeot  *
1081df4baf3dSFrançois Tigeot  * This was originally mean for debugging purposes, or by a sophisticated
1082df4baf3dSFrançois Tigeot  * client library to determine how best to use the available buffers (e.g.,
1083df4baf3dSFrançois Tigeot  * large buffers can be used for image transfer).
1084df4baf3dSFrançois Tigeot  *
1085df4baf3dSFrançois Tigeot  * \param inode device inode.
1086df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
1087df4baf3dSFrançois Tigeot  * \param cmd command.
1088df4baf3dSFrançois Tigeot  * \param arg pointer to a drm_buf_info structure.
1089df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
1090df4baf3dSFrançois Tigeot  *
109124edb884SFrançois Tigeot  * Increments drm_device::buf_use while holding the drm_device::buf_lock
1092df4baf3dSFrançois Tigeot  * lock, preventing of allocating more buffers after this call. Information
1093df4baf3dSFrançois Tigeot  * about each requested buffer is then copied into user space.
1094df4baf3dSFrançois Tigeot  */
10951b13d190SFrançois Tigeot int drm_legacy_infobufs(struct drm_device *dev, void *data,
1096df4baf3dSFrançois Tigeot 			struct drm_file *file_priv)
10977f3c3d6fSHasso Tepper {
109824edb884SFrançois Tigeot 	struct drm_device_dma *dma = dev->dma;
1099b3705d71SHasso Tepper 	struct drm_buf_info *request = data;
11007f3c3d6fSHasso Tepper 	int i;
11017f3c3d6fSHasso Tepper 	int count;
11027f3c3d6fSHasso Tepper 
110324edb884SFrançois Tigeot 	if (drm_core_check_feature(dev, DRIVER_MODESET))
110424edb884SFrançois Tigeot 		return -EINVAL;
110524edb884SFrançois Tigeot 
110624edb884SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
110724edb884SFrançois Tigeot 		return -EINVAL;
110824edb884SFrançois Tigeot 
110924edb884SFrançois Tigeot 	if (!dma)
111024edb884SFrançois Tigeot 		return -EINVAL;
111124edb884SFrançois Tigeot 
111224edb884SFrançois Tigeot 	spin_lock(&dev->buf_lock);
111324edb884SFrançois Tigeot 	if (atomic_read(&dev->buf_alloc)) {
111424edb884SFrançois Tigeot 		spin_unlock(&dev->buf_lock);
111524edb884SFrançois Tigeot 		return -EBUSY;
111624edb884SFrançois Tigeot 	}
11177f3c3d6fSHasso Tepper 	++dev->buf_use;		/* Can't allocate more after this call */
111824edb884SFrançois Tigeot 	spin_unlock(&dev->buf_lock);
11197f3c3d6fSHasso Tepper 
11207f3c3d6fSHasso Tepper 	for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
1121b3705d71SHasso Tepper 		if (dma->bufs[i].buf_count)
1122b3705d71SHasso Tepper 			++count;
11237f3c3d6fSHasso Tepper 	}
11247f3c3d6fSHasso Tepper 
11257f3c3d6fSHasso Tepper 	DRM_DEBUG("count = %d\n", count);
11267f3c3d6fSHasso Tepper 
11277f3c3d6fSHasso Tepper 	if (request->count >= count) {
11287f3c3d6fSHasso Tepper 		for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
11297f3c3d6fSHasso Tepper 			if (dma->bufs[i].buf_count) {
113024edb884SFrançois Tigeot 				struct drm_buf_desc __user *to =
113124edb884SFrançois Tigeot 				    &request->list[count];
113224edb884SFrançois Tigeot 				struct drm_buf_entry *from = &dma->bufs[i];
113324edb884SFrançois Tigeot 				if (copy_to_user(&to->count,
113424edb884SFrançois Tigeot 						 &from->buf_count,
113524edb884SFrançois Tigeot 						 sizeof(from->buf_count)) ||
113624edb884SFrançois Tigeot 				    copy_to_user(&to->size,
113724edb884SFrançois Tigeot 						 &from->buf_size,
113824edb884SFrançois Tigeot 						 sizeof(from->buf_size)) ||
113924edb884SFrançois Tigeot 				    copy_to_user(&to->low_mark,
114024edb884SFrançois Tigeot 						 &from->low_mark,
114124edb884SFrançois Tigeot 						 sizeof(from->low_mark)) ||
114224edb884SFrançois Tigeot 				    copy_to_user(&to->high_mark,
114324edb884SFrançois Tigeot 						 &from->high_mark,
114424edb884SFrançois Tigeot 						 sizeof(from->high_mark)))
114524edb884SFrançois Tigeot 					return -EFAULT;
11467f3c3d6fSHasso Tepper 
11477f3c3d6fSHasso Tepper 				DRM_DEBUG("%d %d %d %d %d\n",
114824edb884SFrançois Tigeot 					  i,
114924edb884SFrançois Tigeot 					  dma->bufs[i].buf_count,
11507f3c3d6fSHasso Tepper 					  dma->bufs[i].buf_size,
115124edb884SFrançois Tigeot 					  dma->bufs[i].low_mark,
115224edb884SFrançois Tigeot 					  dma->bufs[i].high_mark);
11537f3c3d6fSHasso Tepper 				++count;
11547f3c3d6fSHasso Tepper 			}
11557f3c3d6fSHasso Tepper 		}
11567f3c3d6fSHasso Tepper 	}
11577f3c3d6fSHasso Tepper 	request->count = count;
11587f3c3d6fSHasso Tepper 
115924edb884SFrançois Tigeot 	return 0;
11607f3c3d6fSHasso Tepper }
11617f3c3d6fSHasso Tepper 
1162df4baf3dSFrançois Tigeot /**
1163df4baf3dSFrançois Tigeot  * Specifies a low and high water mark for buffer allocation
1164df4baf3dSFrançois Tigeot  *
1165df4baf3dSFrançois Tigeot  * \param inode device inode.
1166df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
1167df4baf3dSFrançois Tigeot  * \param cmd command.
1168df4baf3dSFrançois Tigeot  * \param arg a pointer to a drm_buf_desc structure.
1169df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
1170df4baf3dSFrançois Tigeot  *
1171df4baf3dSFrançois Tigeot  * Verifies that the size order is bounded between the admissible orders and
1172df4baf3dSFrançois Tigeot  * updates the respective drm_device_dma::bufs entry low and high water mark.
1173df4baf3dSFrançois Tigeot  *
1174df4baf3dSFrançois Tigeot  * \note This ioctl is deprecated and mostly never used.
1175df4baf3dSFrançois Tigeot  */
11761b13d190SFrançois Tigeot int drm_legacy_markbufs(struct drm_device *dev, void *data,
1177df4baf3dSFrançois Tigeot 			struct drm_file *file_priv)
11787f3c3d6fSHasso Tepper {
117924edb884SFrançois Tigeot 	struct drm_device_dma *dma = dev->dma;
1180b3705d71SHasso Tepper 	struct drm_buf_desc *request = data;
11817f3c3d6fSHasso Tepper 	int order;
118224edb884SFrançois Tigeot 	struct drm_buf_entry *entry;
118324edb884SFrançois Tigeot 
118424edb884SFrançois Tigeot 	if (drm_core_check_feature(dev, DRIVER_MODESET))
118524edb884SFrançois Tigeot 		return -EINVAL;
118624edb884SFrançois Tigeot 
118724edb884SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
118824edb884SFrançois Tigeot 		return -EINVAL;
118924edb884SFrançois Tigeot 
119024edb884SFrançois Tigeot 	if (!dma)
119124edb884SFrançois Tigeot 		return -EINVAL;
11927f3c3d6fSHasso Tepper 
11937f3c3d6fSHasso Tepper 	DRM_DEBUG("%d, %d, %d\n",
11947f3c3d6fSHasso Tepper 		  request->size, request->low_mark, request->high_mark);
11954cd92098Szrj 	order = order_base_2(request->size);
119624edb884SFrançois Tigeot 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
119724edb884SFrançois Tigeot 		return -EINVAL;
119824edb884SFrançois Tigeot 	entry = &dma->bufs[order];
11997f3c3d6fSHasso Tepper 
120024edb884SFrançois Tigeot 	if (request->low_mark < 0 || request->low_mark > entry->buf_count)
120124edb884SFrançois Tigeot 		return -EINVAL;
120224edb884SFrançois Tigeot 	if (request->high_mark < 0 || request->high_mark > entry->buf_count)
120324edb884SFrançois Tigeot 		return -EINVAL;
12047f3c3d6fSHasso Tepper 
120524edb884SFrançois Tigeot 	entry->low_mark = request->low_mark;
120624edb884SFrançois Tigeot 	entry->high_mark = request->high_mark;
12077f3c3d6fSHasso Tepper 
12087f3c3d6fSHasso Tepper 	return 0;
12097f3c3d6fSHasso Tepper }
12107f3c3d6fSHasso Tepper 
1211df4baf3dSFrançois Tigeot /**
1212df4baf3dSFrançois Tigeot  * Unreserve the buffers in list, previously reserved using drmDMA.
1213df4baf3dSFrançois Tigeot  *
1214df4baf3dSFrançois Tigeot  * \param inode device inode.
1215df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
1216df4baf3dSFrançois Tigeot  * \param cmd command.
1217df4baf3dSFrançois Tigeot  * \param arg pointer to a drm_buf_free structure.
1218df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
1219df4baf3dSFrançois Tigeot  *
1220df4baf3dSFrançois Tigeot  * Calls free_buffer() for each used buffer.
1221df4baf3dSFrançois Tigeot  * This function is primarily used for debugging.
1222df4baf3dSFrançois Tigeot  */
12231b13d190SFrançois Tigeot int drm_legacy_freebufs(struct drm_device *dev, void *data,
1224df4baf3dSFrançois Tigeot 			struct drm_file *file_priv)
12257f3c3d6fSHasso Tepper {
12264250aa95Szrj 	struct drm_device_dma *dma = dev->dma;
1227b3705d71SHasso Tepper 	struct drm_buf_free *request = data;
12287f3c3d6fSHasso Tepper 	int i;
12297f3c3d6fSHasso Tepper 	int idx;
12304250aa95Szrj 	struct drm_buf *buf;
12317f3c3d6fSHasso Tepper 	int retcode = 0;
12327f3c3d6fSHasso Tepper 
12337f3c3d6fSHasso Tepper 	DRM_DEBUG("%d\n", request->count);
12347f3c3d6fSHasso Tepper 
12357f3c3d6fSHasso Tepper 	for (i = 0; i < request->count; i++) {
1236c6f73aabSFrançois Tigeot 		if (copy_from_user(&idx, &request->list[i], sizeof(idx))) {
1237b922632fSImre Vadász 			retcode = -EFAULT;
12387f3c3d6fSHasso Tepper 			break;
12397f3c3d6fSHasso Tepper 		}
12407f3c3d6fSHasso Tepper 		if (idx < 0 || idx >= dma->buf_count) {
12417f3c3d6fSHasso Tepper 			DRM_ERROR("Index %d (of %d max)\n",
12427f3c3d6fSHasso Tepper 			    idx, dma->buf_count - 1);
1243b922632fSImre Vadász 			retcode = -EINVAL;
12447f3c3d6fSHasso Tepper 			break;
12457f3c3d6fSHasso Tepper 		}
12467f3c3d6fSHasso Tepper 		buf = dma->buflist[idx];
12477f3c3d6fSHasso Tepper 		if (buf->file_priv != file_priv) {
12487f3c3d6fSHasso Tepper 			DRM_ERROR("Process %d freeing buffer not owned\n",
12497f3c3d6fSHasso Tepper 			    DRM_CURRENTPID);
1250b922632fSImre Vadász 			retcode = -EINVAL;
12517f3c3d6fSHasso Tepper 			break;
12527f3c3d6fSHasso Tepper 		}
12531b13d190SFrançois Tigeot 		drm_legacy_free_buffer(dev, buf);
12547f3c3d6fSHasso Tepper 	}
12557f3c3d6fSHasso Tepper 
12567f3c3d6fSHasso Tepper 	return retcode;
12577f3c3d6fSHasso Tepper }
12587f3c3d6fSHasso Tepper 
1259df4baf3dSFrançois Tigeot /**
1260df4baf3dSFrançois Tigeot  * Maps all of the DMA buffers into client-virtual space (ioctl).
1261df4baf3dSFrançois Tigeot  *
1262df4baf3dSFrançois Tigeot  * \param inode device inode.
1263df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
1264df4baf3dSFrançois Tigeot  * \param cmd command.
1265df4baf3dSFrançois Tigeot  * \param arg pointer to a drm_buf_map structure.
1266df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
1267df4baf3dSFrançois Tigeot  *
1268df4baf3dSFrançois Tigeot  * Maps the AGP, SG or PCI buffer region with vm_mmap(), and copies information
1269df4baf3dSFrançois Tigeot  * about each buffer into user space. For PCI buffers, it calls vm_mmap() with
1270df4baf3dSFrançois Tigeot  * offset equal to 0, which drm_mmap() interpretes as PCI buffers and calls
1271df4baf3dSFrançois Tigeot  * drm_mmap_dma().
1272df4baf3dSFrançois Tigeot  */
12731b13d190SFrançois Tigeot int drm_legacy_mapbufs(struct drm_device *dev, void *data,
1274df4baf3dSFrançois Tigeot 		       struct drm_file *file_priv)
12757f3c3d6fSHasso Tepper {
12764250aa95Szrj 	struct drm_device_dma *dma = dev->dma;
12777f3c3d6fSHasso Tepper 	int retcode = 0;
12787f3c3d6fSHasso Tepper 	const int zero = 0;
12797f3c3d6fSHasso Tepper 	vm_offset_t address;
12807f3c3d6fSHasso Tepper 	struct vmspace *vms;
12817f3c3d6fSHasso Tepper 	vm_ooffset_t foff;
12827f3c3d6fSHasso Tepper 	vm_size_t size;
12837f3c3d6fSHasso Tepper 	vm_offset_t vaddr;
1284b3705d71SHasso Tepper 	struct drm_buf_map *request = data;
12857f3c3d6fSHasso Tepper 	int i;
12867f3c3d6fSHasso Tepper 
12877f3c3d6fSHasso Tepper 	vms = DRM_CURPROC->td_proc->p_vmspace;
12887f3c3d6fSHasso Tepper 
1289*88fc0f68SFrançois Tigeot 	if (drm_core_check_feature(dev, DRIVER_MODESET))
1290*88fc0f68SFrançois Tigeot 		return -EINVAL;
1291*88fc0f68SFrançois Tigeot 
1292*88fc0f68SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
1293*88fc0f68SFrançois Tigeot 		return -EINVAL;
1294*88fc0f68SFrançois Tigeot 
1295394ce297SFrançois Tigeot 	if (!dma)
1296394ce297SFrançois Tigeot 		return -EINVAL;
1297394ce297SFrançois Tigeot 
1298394ce297SFrançois Tigeot 	spin_lock(&dev->buf_lock);
1299394ce297SFrançois Tigeot 	if (atomic_read(&dev->buf_alloc)) {
1300394ce297SFrançois Tigeot 		spin_unlock(&dev->buf_lock);
1301394ce297SFrançois Tigeot 		return -EBUSY;
1302394ce297SFrançois Tigeot 	}
13037f3c3d6fSHasso Tepper 	dev->buf_use++;		/* Can't allocate more after this call */
1304394ce297SFrançois Tigeot 	spin_unlock(&dev->buf_lock);
13057f3c3d6fSHasso Tepper 
13067f3c3d6fSHasso Tepper 	if (request->count < dma->buf_count)
13077f3c3d6fSHasso Tepper 		goto done;
13087f3c3d6fSHasso Tepper 
130953e4e524Szrj 	if ((dev->agp && (dma->flags & _DRM_DMA_USE_AGP)) ||
1310b3705d71SHasso Tepper 	    (drm_core_check_feature(dev, DRIVER_SG) &&
1311b3705d71SHasso Tepper 	    (dma->flags & _DRM_DMA_USE_SG))) {
13127f3c3d6fSHasso Tepper 		drm_local_map_t *map = dev->agp_buffer_map;
13137f3c3d6fSHasso Tepper 
13147f3c3d6fSHasso Tepper 		if (map == NULL) {
1315b922632fSImre Vadász 			retcode = -EINVAL;
13167f3c3d6fSHasso Tepper 			goto done;
13177f3c3d6fSHasso Tepper 		}
13187f3c3d6fSHasso Tepper 		size = round_page(map->size);
131999f70504SFrançois Tigeot 		foff = (unsigned long)map->handle;
13207f3c3d6fSHasso Tepper 	} else {
13217f3c3d6fSHasso Tepper 		size = round_page(dma->byte_count),
13227f3c3d6fSHasso Tepper 		foff = 0;
13237f3c3d6fSHasso Tepper 	}
13247f3c3d6fSHasso Tepper 
13257f3c3d6fSHasso Tepper 	vaddr = round_page((vm_offset_t)vms->vm_daddr + MAXDSIZ);
1326b922632fSImre Vadász 	retcode = -vm_mmap(&vms->vm_map, &vaddr, size, PROT_READ | PROT_WRITE,
1327b3705d71SHasso Tepper 	    VM_PROT_ALL, MAP_SHARED | MAP_NOSYNC,
1328b3705d71SHasso Tepper 	    SLIST_FIRST(&dev->devnode->si_hlist), foff);
13297f3c3d6fSHasso Tepper 	if (retcode)
13307f3c3d6fSHasso Tepper 		goto done;
13317f3c3d6fSHasso Tepper 
13327f3c3d6fSHasso Tepper 	request->virtual = (void *)vaddr;
13337f3c3d6fSHasso Tepper 
13347f3c3d6fSHasso Tepper 	for (i = 0; i < dma->buf_count; i++) {
1335c6f73aabSFrançois Tigeot 		if (copy_to_user(&request->list[i].idx,
13367f3c3d6fSHasso Tepper 		    &dma->buflist[i]->idx, sizeof(request->list[0].idx))) {
1337b922632fSImre Vadász 			retcode = -EFAULT;
13387f3c3d6fSHasso Tepper 			goto done;
13397f3c3d6fSHasso Tepper 		}
1340c6f73aabSFrançois Tigeot 		if (copy_to_user(&request->list[i].total,
13417f3c3d6fSHasso Tepper 		    &dma->buflist[i]->total, sizeof(request->list[0].total))) {
1342b922632fSImre Vadász 			retcode = -EFAULT;
13437f3c3d6fSHasso Tepper 			goto done;
13447f3c3d6fSHasso Tepper 		}
1345c6f73aabSFrançois Tigeot 		if (copy_to_user(&request->list[i].used, &zero,
13467f3c3d6fSHasso Tepper 		    sizeof(zero))) {
1347b922632fSImre Vadász 			retcode = -EFAULT;
13487f3c3d6fSHasso Tepper 			goto done;
13497f3c3d6fSHasso Tepper 		}
13507f3c3d6fSHasso Tepper 		address = vaddr + dma->buflist[i]->offset; /* *** */
1351c6f73aabSFrançois Tigeot 		if (copy_to_user(&request->list[i].address, &address,
13527f3c3d6fSHasso Tepper 		    sizeof(address))) {
1353b922632fSImre Vadász 			retcode = -EFAULT;
13547f3c3d6fSHasso Tepper 			goto done;
13557f3c3d6fSHasso Tepper 		}
13567f3c3d6fSHasso Tepper 	}
13577f3c3d6fSHasso Tepper       done:
13587f3c3d6fSHasso Tepper 	request->count = dma->buf_count;
13597f3c3d6fSHasso Tepper 	DRM_DEBUG("%d buffers, retcode = %d\n", request->count, retcode);
13607f3c3d6fSHasso Tepper 
13617f3c3d6fSHasso Tepper 	return retcode;
13627f3c3d6fSHasso Tepper }
13631b13d190SFrançois Tigeot 
13641b13d190SFrançois Tigeot int drm_legacy_dma_ioctl(struct drm_device *dev, void *data,
13651b13d190SFrançois Tigeot 		  struct drm_file *file_priv)
13661b13d190SFrançois Tigeot {
13671b13d190SFrançois Tigeot 	if (drm_core_check_feature(dev, DRIVER_MODESET))
13681b13d190SFrançois Tigeot 		return -EINVAL;
13691b13d190SFrançois Tigeot 
13701b13d190SFrançois Tigeot 	if (dev->driver->dma_ioctl)
13711b13d190SFrançois Tigeot 		return dev->driver->dma_ioctl(dev, data, file_priv);
13721b13d190SFrançois Tigeot 	else
13731b13d190SFrançois Tigeot 		return -EINVAL;
13741b13d190SFrançois Tigeot }
13751b13d190SFrançois Tigeot 
13761b13d190SFrançois Tigeot struct drm_local_map *drm_legacy_getsarea(struct drm_device *dev)
13771b13d190SFrançois Tigeot {
13781b13d190SFrançois Tigeot 	struct drm_map_list *entry;
13791b13d190SFrançois Tigeot 
13801b13d190SFrançois Tigeot 	list_for_each_entry(entry, &dev->maplist, head) {
13811b13d190SFrançois Tigeot 		if (entry->map && entry->map->type == _DRM_SHM &&
13821b13d190SFrançois Tigeot 		    (entry->map->flags & _DRM_CONTAINS_LOCK)) {
13831b13d190SFrançois Tigeot 			return entry->map;
13841b13d190SFrançois Tigeot 		}
13851b13d190SFrançois Tigeot 	}
13861b13d190SFrançois Tigeot 	return NULL;
13871b13d190SFrançois Tigeot }
13881b13d190SFrançois Tigeot EXPORT_SYMBOL(drm_legacy_getsarea);
1389