xref: /dflybsd-src/sys/dev/drm/drm_bufs.c (revision 53e4e5245e1754d21b860e70ba7b35f9e471995c)
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 
315718399fSFrançois Tigeot #include <sys/conf.h>
325718399fSFrançois Tigeot #include <bus/pci/pcireg.h>
33df4baf3dSFrançois Tigeot #include <linux/types.h>
34df4baf3dSFrançois Tigeot #include <linux/export.h>
3518e26a6dSFrançois Tigeot #include <drm/drmP.h>
361b13d190SFrançois Tigeot #include "drm_legacy.h"
377f3c3d6fSHasso Tepper 
381b13d190SFrançois Tigeot int drm_legacy_addmap(struct drm_device * dev, resource_size_t offset,
39df4baf3dSFrançois Tigeot 		      unsigned int size, enum drm_map_type type,
40df4baf3dSFrançois Tigeot 		      enum drm_map_flags flags, struct drm_local_map **map_ptr)
417f3c3d6fSHasso Tepper {
42f599cd46SFrançois Tigeot 	struct drm_local_map *map;
4379d1f0c0SFrançois Tigeot 	struct drm_map_list *entry = NULL;
445c002123SFrançois Tigeot 	drm_dma_handle_t *dmah;
4579d1f0c0SFrançois Tigeot 
4679d1f0c0SFrançois Tigeot 	/* Allocate a new map structure, fill it in, and do any type-specific
4779d1f0c0SFrançois Tigeot 	 * initialization necessary.
4879d1f0c0SFrançois Tigeot 	 */
49f8677ba6SMatthew Dillon 	map = kmalloc(sizeof(*map), M_DRM, M_ZERO | M_WAITOK | M_NULLOK);
5079d1f0c0SFrançois Tigeot 	if (!map) {
51b922632fSImre Vadász 		return -ENOMEM;
5279d1f0c0SFrançois Tigeot 	}
5379d1f0c0SFrançois Tigeot 
5479d1f0c0SFrançois Tigeot 	map->offset = offset;
5579d1f0c0SFrançois Tigeot 	map->size = size;
5679d1f0c0SFrançois Tigeot 	map->type = type;
5779d1f0c0SFrançois Tigeot 	map->flags = flags;
587f3c3d6fSHasso Tepper 
597f3c3d6fSHasso Tepper 	/* Only allow shared memory to be removable since we only keep enough
607f3c3d6fSHasso Tepper 	 * book keeping information about shared memory to allow for removal
617f3c3d6fSHasso Tepper 	 * when processes fork.
627f3c3d6fSHasso Tepper 	 */
637f3c3d6fSHasso Tepper 	if ((flags & _DRM_REMOVABLE) && type != _DRM_SHM) {
647f3c3d6fSHasso Tepper 		DRM_ERROR("Requested removable map for non-DRM_SHM\n");
655a3b77d5SFrançois Tigeot 		drm_free(map, M_DRM);
66b922632fSImre Vadász 		return -EINVAL;
677f3c3d6fSHasso Tepper 	}
687f3c3d6fSHasso Tepper 	if ((offset & PAGE_MASK) || (size & PAGE_MASK)) {
692ffc4fa7SSascha Wildner 		DRM_ERROR("offset/size not page aligned: 0x%jx/0x%04x\n",
702ffc4fa7SSascha Wildner 		    (uintmax_t)offset, size);
715a3b77d5SFrançois Tigeot 		drm_free(map, M_DRM);
72b922632fSImre Vadász 		return -EINVAL;
737f3c3d6fSHasso Tepper 	}
747f3c3d6fSHasso Tepper 	if (offset + size < offset) {
752ffc4fa7SSascha Wildner 		DRM_ERROR("offset and size wrap around: 0x%jx/0x%04x\n",
762ffc4fa7SSascha Wildner 		    (uintmax_t)offset, size);
775a3b77d5SFrançois Tigeot 		drm_free(map, M_DRM);
78b922632fSImre Vadász 		return -EINVAL;
797f3c3d6fSHasso Tepper 	}
807f3c3d6fSHasso Tepper 
81df4baf3dSFrançois Tigeot 	DRM_DEBUG("offset = 0x%08llx, size = 0x%08lx, type = %d\n",
82df4baf3dSFrançois Tigeot 		  (unsigned long long)map->offset, map->size, map->type);
837f3c3d6fSHasso Tepper 
847f3c3d6fSHasso Tepper 	/* Check if this is just another version of a kernel-allocated map, and
857f3c3d6fSHasso Tepper 	 * just hand that back if so.
867f3c3d6fSHasso Tepper 	 */
877f3c3d6fSHasso Tepper 	if (type == _DRM_REGISTERS || type == _DRM_FRAME_BUFFER ||
887f3c3d6fSHasso Tepper 	    type == _DRM_SHM) {
89f599cd46SFrançois Tigeot 		list_for_each_entry(entry, &dev->maplist, head) {
90f599cd46SFrançois Tigeot 			if (entry->map->type == type && (entry->map->offset == offset ||
91f599cd46SFrançois Tigeot 			    (entry->map->type == _DRM_SHM &&
92f599cd46SFrançois Tigeot 			    entry->map->flags == _DRM_CONTAINS_LOCK))) {
93f599cd46SFrançois Tigeot 				entry->map->size = size;
947f3c3d6fSHasso Tepper 				DRM_DEBUG("Found kernel map %d\n", type);
957f3c3d6fSHasso Tepper 				goto done;
967f3c3d6fSHasso Tepper 			}
977f3c3d6fSHasso Tepper 		}
987f3c3d6fSHasso Tepper 	}
997f3c3d6fSHasso Tepper 
1007f3c3d6fSHasso Tepper 	switch (map->type) {
1017f3c3d6fSHasso Tepper 	case _DRM_REGISTERS:
1027f3c3d6fSHasso Tepper 	case _DRM_FRAME_BUFFER:
1036431cd91SFrançois Tigeot 
1046431cd91SFrançois Tigeot 		if (map->type == _DRM_FRAME_BUFFER ||
1056431cd91SFrançois Tigeot 		    (map->flags & _DRM_WRITE_COMBINING)) {
1066431cd91SFrançois Tigeot 			map->mtrr =
1076431cd91SFrançois Tigeot 				arch_phys_wc_add(map->offset, map->size);
1086431cd91SFrançois Tigeot 		}
1096431cd91SFrançois Tigeot 		if (map->type == _DRM_REGISTERS) {
1106431cd91SFrançois Tigeot 			if (map->flags & _DRM_WRITE_COMBINING)
1116431cd91SFrançois Tigeot 				map->handle = ioremap_wc(map->offset,
1126431cd91SFrançois Tigeot 							 map->size);
1136431cd91SFrançois Tigeot 			else
1146431cd91SFrançois Tigeot 				map->handle = ioremap(map->offset, map->size);
1156431cd91SFrançois Tigeot 			if (!map->handle) {
1166431cd91SFrançois Tigeot 				kfree(map);
1176431cd91SFrançois Tigeot 				return -ENOMEM;
1186431cd91SFrançois Tigeot 			}
1196431cd91SFrançois Tigeot 		}
1206431cd91SFrançois Tigeot 
1217f3c3d6fSHasso Tepper 		break;
1227f3c3d6fSHasso Tepper 	case _DRM_SHM:
123f8677ba6SMatthew Dillon 		map->handle = kmalloc(map->size, M_DRM, M_WAITOK | M_NULLOK);
1247f3c3d6fSHasso Tepper 		DRM_DEBUG("%lu %d %p\n",
1254cd92098Szrj 			  map->size, order_base_2(map->size), map->handle);
1265c002123SFrançois Tigeot 		if (!map->handle) {
1275a3b77d5SFrançois Tigeot 			drm_free(map, M_DRM);
128b922632fSImre Vadász 			return -ENOMEM;
1297f3c3d6fSHasso Tepper 		}
1305c002123SFrançois Tigeot 		map->offset = (unsigned long)map->handle;
1317f3c3d6fSHasso Tepper 		if (map->flags & _DRM_CONTAINS_LOCK) {
1327f3c3d6fSHasso Tepper 			/* Prevent a 2nd X Server from creating a 2nd lock */
13379f713b0SFrançois Tigeot 			DRM_LOCK(dev);
13479f713b0SFrançois Tigeot 			if (dev->lock.hw_lock != NULL) {
13579f713b0SFrançois Tigeot 				DRM_UNLOCK(dev);
1365c002123SFrançois Tigeot 				drm_free(map->handle, M_DRM);
1375a3b77d5SFrançois Tigeot 				drm_free(map, M_DRM);
138b922632fSImre Vadász 				return -EBUSY;
1397f3c3d6fSHasso Tepper 			}
14079f713b0SFrançois Tigeot 			dev->lock.hw_lock = map->handle; /* Pointer to lock */
14179f713b0SFrançois Tigeot 			DRM_UNLOCK(dev);
1427f3c3d6fSHasso Tepper 		}
1437f3c3d6fSHasso Tepper 		break;
1447f3c3d6fSHasso Tepper 	case _DRM_AGP:
145*53e4e524Szrj 
146*53e4e524Szrj 		if (!dev->agp) {
147*53e4e524Szrj 			kfree(map);
148*53e4e524Szrj 			return -EINVAL;
149*53e4e524Szrj 		}
1507f3c3d6fSHasso Tepper 		/*valid = 0;*/
1517f3c3d6fSHasso Tepper 		/* In some cases (i810 driver), user space may have already
1527f3c3d6fSHasso Tepper 		 * added the AGP base itself, because dev->agp->base previously
1537f3c3d6fSHasso Tepper 		 * only got set during AGP enable.  So, only add the base
1547f3c3d6fSHasso Tepper 		 * address if the map's offset isn't already within the
1557f3c3d6fSHasso Tepper 		 * aperture.
1567f3c3d6fSHasso Tepper 		 */
1577f3c3d6fSHasso Tepper 		if (map->offset < dev->agp->base ||
1587f3c3d6fSHasso Tepper 		    map->offset > dev->agp->base +
1599d567857SJean-Sébastien Pédron 		    dev->agp->agp_info.ai_aperture_size - 1) {
1607f3c3d6fSHasso Tepper 			map->offset += dev->agp->base;
1617f3c3d6fSHasso Tepper 		}
1629d567857SJean-Sébastien Pédron 		map->mtrr   = dev->agp->agp_mtrr; /* for getmap */
1637f3c3d6fSHasso Tepper 		/*for (entry = dev->agp->memory; entry; entry = entry->next) {
1647f3c3d6fSHasso Tepper 			if ((map->offset >= entry->bound) &&
1657f3c3d6fSHasso Tepper 			    (map->offset + map->size <=
1667f3c3d6fSHasso Tepper 			    entry->bound + entry->pages * PAGE_SIZE)) {
1677f3c3d6fSHasso Tepper 				valid = 1;
1687f3c3d6fSHasso Tepper 				break;
1697f3c3d6fSHasso Tepper 			}
1707f3c3d6fSHasso Tepper 		}
1717f3c3d6fSHasso Tepper 		if (!valid) {
1725a3b77d5SFrançois Tigeot 			drm_free(map, M_DRM);
173b922632fSImre Vadász 			return -EACCES;
1747f3c3d6fSHasso Tepper 		}*/
1757f3c3d6fSHasso Tepper 		break;
1767f3c3d6fSHasso Tepper 	case _DRM_SCATTER_GATHER:
1777f3c3d6fSHasso Tepper 		if (!dev->sg) {
1785a3b77d5SFrançois Tigeot 			drm_free(map, M_DRM);
179b922632fSImre Vadász 			return -EINVAL;
1807f3c3d6fSHasso Tepper 		}
1815c002123SFrançois Tigeot 		map->handle = (void *)(uintptr_t)(dev->sg->vaddr + offset);
18299f70504SFrançois Tigeot 		map->offset = dev->sg->vaddr + offset;
1837f3c3d6fSHasso Tepper 		break;
1847f3c3d6fSHasso Tepper 	case _DRM_CONSISTENT:
185b31e9d59SFrançois Tigeot 		/* dma_addr_t is 64bit on i386 with CONFIG_HIGHMEM64G,
186b31e9d59SFrançois Tigeot 		 * As we're limiting the address to 2^32-1 (or less),
187b31e9d59SFrançois Tigeot 		 * casting it down to 32 bits is no problem, but we
188b31e9d59SFrançois Tigeot 		 * need to point to a 64bit variable first. */
189b31e9d59SFrançois Tigeot 		dmah = drm_pci_alloc(dev, map->size, map->size);
1905c002123SFrançois Tigeot 		if (!dmah) {
191158486a6SFrançois Tigeot 			kfree(map);
192b31e9d59SFrançois Tigeot 			return -ENOMEM;
1937f3c3d6fSHasso Tepper 		}
1945c002123SFrançois Tigeot 		map->handle = dmah->vaddr;
195b31e9d59SFrançois Tigeot 		map->offset = dmah->busaddr;
1967f3c3d6fSHasso Tepper 		break;
1977f3c3d6fSHasso Tepper 	default:
1987f3c3d6fSHasso Tepper 		DRM_ERROR("Bad map type %d\n", map->type);
1995a3b77d5SFrançois Tigeot 		drm_free(map, M_DRM);
200b922632fSImre Vadász 		return -EINVAL;
2017f3c3d6fSHasso Tepper 	}
2027f3c3d6fSHasso Tepper 
203f599cd46SFrançois Tigeot 	list_add(&entry->head, &dev->maplist);
2047f3c3d6fSHasso Tepper 
2057f3c3d6fSHasso Tepper done:
2067f3c3d6fSHasso Tepper 	/* Jumped to, with lock held, when a kernel map is found. */
2077f3c3d6fSHasso Tepper 
2087f3c3d6fSHasso Tepper 	DRM_DEBUG("Added map %d 0x%lx/0x%lx\n", map->type, map->offset,
2097f3c3d6fSHasso Tepper 	    map->size);
2107f3c3d6fSHasso Tepper 
2117f3c3d6fSHasso Tepper 	*map_ptr = map;
2127f3c3d6fSHasso Tepper 
2137f3c3d6fSHasso Tepper 	return 0;
2147f3c3d6fSHasso Tepper }
2157f3c3d6fSHasso Tepper 
216df4baf3dSFrançois Tigeot /**
217df4baf3dSFrançois Tigeot  * Ioctl to specify a range of memory that is available for mapping by a
218df4baf3dSFrançois Tigeot  * non-root process.
219df4baf3dSFrançois Tigeot  *
220df4baf3dSFrançois Tigeot  * \param inode device inode.
221df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
222df4baf3dSFrançois Tigeot  * \param cmd command.
223df4baf3dSFrançois Tigeot  * \param arg pointer to a drm_map structure.
224df4baf3dSFrançois Tigeot  * \return zero on success or a negative value on error.
225df4baf3dSFrançois Tigeot  *
226df4baf3dSFrançois Tigeot  */
2271b13d190SFrançois Tigeot int drm_legacy_addmap_ioctl(struct drm_device *dev, void *data,
228b3705d71SHasso Tepper 			    struct drm_file *file_priv)
2297f3c3d6fSHasso Tepper {
230b3705d71SHasso Tepper 	struct drm_map *request = data;
2317f3c3d6fSHasso Tepper 	drm_local_map_t *map;
2327f3c3d6fSHasso Tepper 	int err;
2337f3c3d6fSHasso Tepper 
2347f3c3d6fSHasso Tepper 	if (!(dev->flags & (FREAD|FWRITE)))
235b922632fSImre Vadász 		return -EACCES; /* Require read/write */
2367f3c3d6fSHasso Tepper 
237c6f73aabSFrançois Tigeot 	if (!capable(CAP_SYS_ADMIN) && request->type != _DRM_AGP)
238b922632fSImre Vadász 		return -EACCES;
2397f3c3d6fSHasso Tepper 
24079f713b0SFrançois Tigeot 	DRM_LOCK(dev);
2411b13d190SFrançois Tigeot 	err = drm_legacy_addmap(dev, request->offset, request->size, request->type,
2427f3c3d6fSHasso Tepper 	    request->flags, &map);
24379f713b0SFrançois Tigeot 	DRM_UNLOCK(dev);
24479f713b0SFrançois Tigeot 	if (err != 0)
2457f3c3d6fSHasso Tepper 		return err;
2467f3c3d6fSHasso Tepper 
2477f3c3d6fSHasso Tepper 	request->offset = map->offset;
2487f3c3d6fSHasso Tepper 	request->size = map->size;
2497f3c3d6fSHasso Tepper 	request->type = map->type;
2507f3c3d6fSHasso Tepper 	request->flags = map->flags;
2517f3c3d6fSHasso Tepper 	request->mtrr   = map->mtrr;
25299f70504SFrançois Tigeot 	request->handle = (void *)map->handle;
2537f3c3d6fSHasso Tepper 
2547f3c3d6fSHasso Tepper 	return 0;
2557f3c3d6fSHasso Tepper }
2567f3c3d6fSHasso Tepper 
2571b13d190SFrançois Tigeot /**
2581b13d190SFrançois Tigeot  * Remove a map private from list and deallocate resources if the mapping
2591b13d190SFrançois Tigeot  * isn't in use.
2601b13d190SFrançois Tigeot  *
2611b13d190SFrançois Tigeot  * Searches the map on drm_device::maplist, removes it from the list, see if
2621b13d190SFrançois Tigeot  * its being used, and free any associate resource (such as MTRR's) if it's not
2631b13d190SFrançois Tigeot  * being on use.
2641b13d190SFrançois Tigeot  *
2651b13d190SFrançois Tigeot  * \sa drm_legacy_addmap
2661b13d190SFrançois Tigeot  */
2671b13d190SFrançois Tigeot int drm_legacy_rmmap_locked(struct drm_device *dev, struct drm_local_map *map)
2687f3c3d6fSHasso Tepper {
269f599cd46SFrançois Tigeot 	struct drm_map_list *r_list = NULL, *list_t;
2705c002123SFrançois Tigeot 	drm_dma_handle_t dmah;
271f599cd46SFrançois Tigeot 	int found = 0;
27279f713b0SFrançois Tigeot 
273f599cd46SFrançois Tigeot 	/* Find the list entry for the map and remove it */
274f599cd46SFrançois Tigeot 	list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) {
275f599cd46SFrançois Tigeot 		if (r_list->map == map) {
276f599cd46SFrançois Tigeot 			list_del(&r_list->head);
2771b13d190SFrançois Tigeot 			kfree(r_list);
278f599cd46SFrançois Tigeot 			found = 1;
279f599cd46SFrançois Tigeot 			break;
280f599cd46SFrançois Tigeot 		}
281f599cd46SFrançois Tigeot 	}
282f599cd46SFrançois Tigeot 
283f599cd46SFrançois Tigeot 	if (!found)
2841b13d190SFrançois Tigeot 		return -EINVAL;
2857f3c3d6fSHasso Tepper 
2867f3c3d6fSHasso Tepper 	switch (map->type) {
2877f3c3d6fSHasso Tepper 	case _DRM_REGISTERS:
2886431cd91SFrançois Tigeot 		drm_legacy_ioremapfree(map, dev);
2897f3c3d6fSHasso Tepper 		/* FALLTHROUGH */
2907f3c3d6fSHasso Tepper 	case _DRM_FRAME_BUFFER:
2916431cd91SFrançois Tigeot 		arch_phys_wc_del(map->mtrr);
2927f3c3d6fSHasso Tepper 		break;
2937f3c3d6fSHasso Tepper 	case _DRM_SHM:
2945c002123SFrançois Tigeot 		drm_free(map->handle, M_DRM);
2957f3c3d6fSHasso Tepper 		break;
2967f3c3d6fSHasso Tepper 	case _DRM_AGP:
2977f3c3d6fSHasso Tepper 	case _DRM_SCATTER_GATHER:
2987f3c3d6fSHasso Tepper 		break;
2997f3c3d6fSHasso Tepper 	case _DRM_CONSISTENT:
3005c002123SFrançois Tigeot 		dmah.vaddr = map->handle;
3015c002123SFrançois Tigeot 		dmah.busaddr = map->offset;
3021b13d190SFrançois Tigeot 		dmah.size = map->size;
3031b13d190SFrançois Tigeot 		__drm_legacy_pci_free(dev, &dmah);
3047f3c3d6fSHasso Tepper 		break;
3051b13d190SFrançois Tigeot 	}
3061b13d190SFrançois Tigeot 	kfree(map);
3071b13d190SFrançois Tigeot 
3081b13d190SFrançois Tigeot 	return 0;
3097f3c3d6fSHasso Tepper }
31079f713b0SFrançois Tigeot 
3111b13d190SFrançois Tigeot int drm_legacy_rmmap(struct drm_device *dev, struct drm_local_map *map)
3121b13d190SFrançois Tigeot {
3131b13d190SFrançois Tigeot 	int ret;
3141b13d190SFrançois Tigeot 
3151b13d190SFrançois Tigeot 	mutex_lock(&dev->struct_mutex);
3161b13d190SFrançois Tigeot 	ret = drm_legacy_rmmap_locked(dev, map);
3171b13d190SFrançois Tigeot 	mutex_unlock(&dev->struct_mutex);
3181b13d190SFrançois Tigeot 
3191b13d190SFrançois Tigeot 	return ret;
3207f3c3d6fSHasso Tepper }
3211b13d190SFrançois Tigeot EXPORT_SYMBOL(drm_legacy_rmmap);
3227f3c3d6fSHasso Tepper 
323f599cd46SFrançois Tigeot /* The rmmap ioctl appears to be unnecessary.  All mappings are torn down on
324f599cd46SFrançois Tigeot  * the last close of the device, and this is necessary for cleanup when things
325f599cd46SFrançois Tigeot  * exit uncleanly.  Therefore, having userland manually remove mappings seems
326f599cd46SFrançois Tigeot  * like a pointless exercise since they're going away anyway.
327f599cd46SFrançois Tigeot  *
328f599cd46SFrançois Tigeot  * One use case might be after addmap is allowed for normal users for SHM and
329f599cd46SFrançois Tigeot  * gets used by drivers that the server doesn't need to care about.  This seems
330f599cd46SFrançois Tigeot  * unlikely.
331f599cd46SFrançois Tigeot  *
332f599cd46SFrançois Tigeot  * \param inode device inode.
333f599cd46SFrançois Tigeot  * \param file_priv DRM file private.
334f599cd46SFrançois Tigeot  * \param cmd command.
335f599cd46SFrançois Tigeot  * \param arg pointer to a struct drm_map structure.
336f599cd46SFrançois Tigeot  * \return zero on success or a negative value on error.
3377f3c3d6fSHasso Tepper  */
3381b13d190SFrançois Tigeot int drm_legacy_rmmap_ioctl(struct drm_device *dev, void *data,
339b3705d71SHasso Tepper 			   struct drm_file *file_priv)
3407f3c3d6fSHasso Tepper {
341b3705d71SHasso Tepper 	struct drm_map *request = data;
342f599cd46SFrançois Tigeot 	struct drm_local_map *map = NULL;
343f599cd46SFrançois Tigeot 	struct drm_map_list *r_list;
3447f3c3d6fSHasso Tepper 
3455718399fSFrançois Tigeot 	DRM_LOCK(dev);
346f599cd46SFrançois Tigeot 	list_for_each_entry(r_list, &dev->maplist, head) {
347f599cd46SFrançois Tigeot 		if (r_list->map &&
348f599cd46SFrançois Tigeot 		    r_list->user_token == (unsigned long)request->handle &&
349f599cd46SFrançois Tigeot 		    r_list->map->flags & _DRM_REMOVABLE) {
350f599cd46SFrançois Tigeot 			map = r_list->map;
3517f3c3d6fSHasso Tepper 			break;
3527f3c3d6fSHasso Tepper 		}
353f599cd46SFrançois Tigeot 	}
3547f3c3d6fSHasso Tepper 
355f599cd46SFrançois Tigeot 	/* List has wrapped around to the head pointer, or its empty we didn't
356f599cd46SFrançois Tigeot 	 * find anything.
357f599cd46SFrançois Tigeot 	 */
358f599cd46SFrançois Tigeot 	if (list_empty(&dev->maplist) || !map) {
3595718399fSFrançois Tigeot 		DRM_UNLOCK(dev);
360f599cd46SFrançois Tigeot 		return -EINVAL;
361f599cd46SFrançois Tigeot 	}
362f599cd46SFrançois Tigeot 
363f599cd46SFrançois Tigeot 	/* Register and framebuffer maps are permanent */
364f599cd46SFrançois Tigeot 	if ((map->type == _DRM_REGISTERS) || (map->type == _DRM_FRAME_BUFFER)) {
365f599cd46SFrançois Tigeot 		DRM_UNLOCK(dev);
366f599cd46SFrançois Tigeot 		return 0;
3677f3c3d6fSHasso Tepper 	}
3687f3c3d6fSHasso Tepper 
3691b13d190SFrançois Tigeot 	drm_legacy_rmmap(dev, map);
3707f3c3d6fSHasso Tepper 
3715718399fSFrançois Tigeot 	DRM_UNLOCK(dev);
3727f3c3d6fSHasso Tepper 
3737f3c3d6fSHasso Tepper 	return 0;
3747f3c3d6fSHasso Tepper }
3757f3c3d6fSHasso Tepper 
376df4baf3dSFrançois Tigeot /**
377df4baf3dSFrançois Tigeot  * Cleanup after an error on one of the addbufs() functions.
378df4baf3dSFrançois Tigeot  *
379df4baf3dSFrançois Tigeot  * \param dev DRM device.
380df4baf3dSFrançois Tigeot  * \param entry buffer entry where the error occurred.
381df4baf3dSFrançois Tigeot  *
382df4baf3dSFrançois Tigeot  * Frees any pages and buffers associated with the given entry.
383df4baf3dSFrançois Tigeot  */
384b3705d71SHasso Tepper static void drm_cleanup_buf_error(struct drm_device * dev,
385df4baf3dSFrançois Tigeot 				  struct drm_buf_entry * entry)
3867f3c3d6fSHasso Tepper {
3877f3c3d6fSHasso Tepper 	int i;
3887f3c3d6fSHasso Tepper 
3897f3c3d6fSHasso Tepper 	if (entry->seg_count) {
3907f3c3d6fSHasso Tepper 		for (i = 0; i < entry->seg_count; i++) {
3917f3c3d6fSHasso Tepper 			drm_pci_free(dev, entry->seglist[i]);
3927f3c3d6fSHasso Tepper 		}
3935a3b77d5SFrançois Tigeot 		drm_free(entry->seglist, M_DRM);
3947f3c3d6fSHasso Tepper 
3957f3c3d6fSHasso Tepper 		entry->seg_count = 0;
3967f3c3d6fSHasso Tepper 	}
3977f3c3d6fSHasso Tepper 
3987f3c3d6fSHasso Tepper    	if (entry->buf_count) {
3997f3c3d6fSHasso Tepper 	   	for (i = 0; i < entry->buf_count; i++) {
4005a3b77d5SFrançois Tigeot 			drm_free(entry->buflist[i].dev_private, M_DRM);
4017f3c3d6fSHasso Tepper 		}
4025a3b77d5SFrançois Tigeot 		drm_free(entry->buflist, M_DRM);
4037f3c3d6fSHasso Tepper 
4047f3c3d6fSHasso Tepper 		entry->buf_count = 0;
4057f3c3d6fSHasso Tepper 	}
4067f3c3d6fSHasso Tepper }
4077f3c3d6fSHasso Tepper 
408b3705d71SHasso Tepper static int drm_do_addbufs_agp(struct drm_device *dev, struct drm_buf_desc *request)
4097f3c3d6fSHasso Tepper {
4104250aa95Szrj 	struct drm_device_dma *dma = dev->dma;
4114250aa95Szrj 	struct drm_buf_entry *entry;
4124250aa95Szrj 	/* struct drm_agp_mem *agp_entry; */
4134250aa95Szrj 	/* int valid */
4144250aa95Szrj 	struct drm_buf *buf;
4157f3c3d6fSHasso Tepper 	unsigned long offset;
4167f3c3d6fSHasso Tepper 	unsigned long agp_offset;
4177f3c3d6fSHasso Tepper 	int count;
4187f3c3d6fSHasso Tepper 	int order;
4197f3c3d6fSHasso Tepper 	int size;
4207f3c3d6fSHasso Tepper 	int alignment;
4217f3c3d6fSHasso Tepper 	int page_order;
4227f3c3d6fSHasso Tepper 	int total;
4237f3c3d6fSHasso Tepper 	int byte_count;
4247f3c3d6fSHasso Tepper 	int i;
4254250aa95Szrj 	struct drm_buf **temp_buflist;
4267f3c3d6fSHasso Tepper 
4277f3c3d6fSHasso Tepper 	count = request->count;
4284cd92098Szrj 	order = order_base_2(request->size);
4297f3c3d6fSHasso Tepper 	size = 1 << order;
4307f3c3d6fSHasso Tepper 
4317f3c3d6fSHasso Tepper 	alignment  = (request->flags & _DRM_PAGE_ALIGN)
4327f3c3d6fSHasso Tepper 	    ? round_page(size) : size;
4337f3c3d6fSHasso Tepper 	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
4347f3c3d6fSHasso Tepper 	total = PAGE_SIZE << page_order;
4357f3c3d6fSHasso Tepper 
4367f3c3d6fSHasso Tepper 	byte_count = 0;
4377f3c3d6fSHasso Tepper 	agp_offset = dev->agp->base + request->agp_start;
4387f3c3d6fSHasso Tepper 
4397f3c3d6fSHasso Tepper 	DRM_DEBUG("count:      %d\n",  count);
4407f3c3d6fSHasso Tepper 	DRM_DEBUG("order:      %d\n",  order);
4417f3c3d6fSHasso Tepper 	DRM_DEBUG("size:       %d\n",  size);
4427f3c3d6fSHasso Tepper 	DRM_DEBUG("agp_offset: 0x%lx\n", agp_offset);
4437f3c3d6fSHasso Tepper 	DRM_DEBUG("alignment:  %d\n",  alignment);
4447f3c3d6fSHasso Tepper 	DRM_DEBUG("page_order: %d\n",  page_order);
4457f3c3d6fSHasso Tepper 	DRM_DEBUG("total:      %d\n",  total);
4467f3c3d6fSHasso Tepper 
4477f3c3d6fSHasso Tepper 	/* Make sure buffers are located in AGP memory that we own */
4487f3c3d6fSHasso Tepper 	/* Breaks MGA due to drm_alloc_agp not setting up entries for the
4497f3c3d6fSHasso Tepper 	 * memory.  Safe to ignore for now because these ioctls are still
4507f3c3d6fSHasso Tepper 	 * root-only.
4517f3c3d6fSHasso Tepper 	 */
4527f3c3d6fSHasso Tepper 	/*valid = 0;
4537f3c3d6fSHasso Tepper 	for (agp_entry = dev->agp->memory; agp_entry;
4547f3c3d6fSHasso Tepper 	    agp_entry = agp_entry->next) {
4557f3c3d6fSHasso Tepper 		if ((agp_offset >= agp_entry->bound) &&
4567f3c3d6fSHasso Tepper 		    (agp_offset + total * count <=
4577f3c3d6fSHasso Tepper 		    agp_entry->bound + agp_entry->pages * PAGE_SIZE)) {
4587f3c3d6fSHasso Tepper 			valid = 1;
4597f3c3d6fSHasso Tepper 			break;
4607f3c3d6fSHasso Tepper 		}
4617f3c3d6fSHasso Tepper 	}
4627f3c3d6fSHasso Tepper 	if (!valid) {
4637f3c3d6fSHasso Tepper 		DRM_DEBUG("zone invalid\n");
464b922632fSImre Vadász 		return -EINVAL;
4657f3c3d6fSHasso Tepper 	}*/
4667f3c3d6fSHasso Tepper 
4677f3c3d6fSHasso Tepper 	entry = &dma->bufs[order];
4687f3c3d6fSHasso Tepper 
4695a3b77d5SFrançois Tigeot 	entry->buflist = kmalloc(count * sizeof(*entry->buflist), M_DRM,
470f8677ba6SMatthew Dillon 				 M_WAITOK | M_NULLOK | M_ZERO);
4717f3c3d6fSHasso Tepper 	if (!entry->buflist) {
472b922632fSImre Vadász 		return -ENOMEM;
4737f3c3d6fSHasso Tepper 	}
4747f3c3d6fSHasso Tepper 
4757f3c3d6fSHasso Tepper 	entry->buf_size = size;
4767f3c3d6fSHasso Tepper 	entry->page_order = page_order;
4777f3c3d6fSHasso Tepper 
4787f3c3d6fSHasso Tepper 	offset = 0;
4797f3c3d6fSHasso Tepper 
4807f3c3d6fSHasso Tepper 	while (entry->buf_count < count) {
4817f3c3d6fSHasso Tepper 		buf          = &entry->buflist[entry->buf_count];
4827f3c3d6fSHasso Tepper 		buf->idx     = dma->buf_count + entry->buf_count;
4837f3c3d6fSHasso Tepper 		buf->total   = alignment;
4847f3c3d6fSHasso Tepper 		buf->order   = order;
4857f3c3d6fSHasso Tepper 		buf->used    = 0;
4867f3c3d6fSHasso Tepper 
4877f3c3d6fSHasso Tepper 		buf->offset  = (dma->byte_count + offset);
4887f3c3d6fSHasso Tepper 		buf->bus_address = agp_offset + offset;
4897f3c3d6fSHasso Tepper 		buf->address = (void *)(agp_offset + offset);
4907f3c3d6fSHasso Tepper 		buf->next    = NULL;
4917f3c3d6fSHasso Tepper 		buf->pending = 0;
4927f3c3d6fSHasso Tepper 		buf->file_priv = NULL;
4937f3c3d6fSHasso Tepper 
494ba55f2f5SFrançois Tigeot 		buf->dev_priv_size = dev->driver->dev_priv_size;
4955a3b77d5SFrançois Tigeot 		buf->dev_private = kmalloc(buf->dev_priv_size, M_DRM,
496f8677ba6SMatthew Dillon 					   M_WAITOK | M_NULLOK | M_ZERO);
4977f3c3d6fSHasso Tepper 		if (buf->dev_private == NULL) {
4987f3c3d6fSHasso Tepper 			/* Set count correctly so we free the proper amount. */
4997f3c3d6fSHasso Tepper 			entry->buf_count = count;
5007f3c3d6fSHasso Tepper 			drm_cleanup_buf_error(dev, entry);
501b922632fSImre Vadász 			return -ENOMEM;
5027f3c3d6fSHasso Tepper 		}
5037f3c3d6fSHasso Tepper 
5047f3c3d6fSHasso Tepper 		offset += alignment;
5057f3c3d6fSHasso Tepper 		entry->buf_count++;
5067f3c3d6fSHasso Tepper 		byte_count += PAGE_SIZE << page_order;
5077f3c3d6fSHasso Tepper 	}
5087f3c3d6fSHasso Tepper 
5097f3c3d6fSHasso Tepper 	DRM_DEBUG("byte_count: %d\n", byte_count);
5107f3c3d6fSHasso Tepper 
5115718399fSFrançois Tigeot 	temp_buflist = krealloc(dma->buflist,
512b3705d71SHasso Tepper 	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
513f8677ba6SMatthew Dillon 	    M_DRM, M_WAITOK | M_NULLOK);
5147f3c3d6fSHasso Tepper 	if (temp_buflist == NULL) {
5157f3c3d6fSHasso Tepper 		/* Free the entry because it isn't valid */
5167f3c3d6fSHasso Tepper 		drm_cleanup_buf_error(dev, entry);
517b922632fSImre Vadász 		return -ENOMEM;
5187f3c3d6fSHasso Tepper 	}
5197f3c3d6fSHasso Tepper 	dma->buflist = temp_buflist;
5207f3c3d6fSHasso Tepper 
5217f3c3d6fSHasso Tepper 	for (i = 0; i < entry->buf_count; i++) {
5227f3c3d6fSHasso Tepper 		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
5237f3c3d6fSHasso Tepper 	}
5247f3c3d6fSHasso Tepper 
5257f3c3d6fSHasso Tepper 	dma->buf_count += entry->buf_count;
5267f3c3d6fSHasso Tepper 	dma->byte_count += byte_count;
5277f3c3d6fSHasso Tepper 
5287f3c3d6fSHasso Tepper 	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
5297f3c3d6fSHasso Tepper 	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
5307f3c3d6fSHasso Tepper 
5317f3c3d6fSHasso Tepper 	request->count = entry->buf_count;
5327f3c3d6fSHasso Tepper 	request->size = size;
5337f3c3d6fSHasso Tepper 
5347f3c3d6fSHasso Tepper 	dma->flags = _DRM_DMA_USE_AGP;
5357f3c3d6fSHasso Tepper 
5367f3c3d6fSHasso Tepper 	return 0;
5377f3c3d6fSHasso Tepper }
5387f3c3d6fSHasso Tepper 
539b3705d71SHasso Tepper static int drm_do_addbufs_pci(struct drm_device *dev, struct drm_buf_desc *request)
5407f3c3d6fSHasso Tepper {
5414250aa95Szrj 	struct drm_device_dma *dma = dev->dma;
5427f3c3d6fSHasso Tepper 	int count;
5437f3c3d6fSHasso Tepper 	int order;
5447f3c3d6fSHasso Tepper 	int size;
5457f3c3d6fSHasso Tepper 	int total;
5467f3c3d6fSHasso Tepper 	int page_order;
5474250aa95Szrj 	struct drm_buf_entry *entry;
548b31e9d59SFrançois Tigeot 	drm_dma_handle_t *dmah;
5494250aa95Szrj 	struct drm_buf *buf;
5507f3c3d6fSHasso Tepper 	int alignment;
5517f3c3d6fSHasso Tepper 	unsigned long offset;
5527f3c3d6fSHasso Tepper 	int i;
5537f3c3d6fSHasso Tepper 	int byte_count;
5547f3c3d6fSHasso Tepper 	int page_count;
5557f3c3d6fSHasso Tepper 	unsigned long *temp_pagelist;
5564250aa95Szrj 	struct drm_buf **temp_buflist;
5577f3c3d6fSHasso Tepper 
5587f3c3d6fSHasso Tepper 	count = request->count;
5594cd92098Szrj 	order = order_base_2(request->size);
5607f3c3d6fSHasso Tepper 	size = 1 << order;
5617f3c3d6fSHasso Tepper 
5627f3c3d6fSHasso Tepper 	DRM_DEBUG("count=%d, size=%d (%d), order=%d\n",
5637f3c3d6fSHasso Tepper 	    request->count, request->size, size, order);
5647f3c3d6fSHasso Tepper 
5657f3c3d6fSHasso Tepper 	alignment = (request->flags & _DRM_PAGE_ALIGN)
5667f3c3d6fSHasso Tepper 	    ? round_page(size) : size;
5677f3c3d6fSHasso Tepper 	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
5687f3c3d6fSHasso Tepper 	total = PAGE_SIZE << page_order;
5697f3c3d6fSHasso Tepper 
5707f3c3d6fSHasso Tepper 	entry = &dma->bufs[order];
5717f3c3d6fSHasso Tepper 
5725a3b77d5SFrançois Tigeot 	entry->buflist = kmalloc(count * sizeof(*entry->buflist), M_DRM,
573f8677ba6SMatthew Dillon 				 M_WAITOK | M_NULLOK | M_ZERO);
5745a3b77d5SFrançois Tigeot 	entry->seglist = kmalloc(count * sizeof(*entry->seglist), M_DRM,
575f8677ba6SMatthew Dillon 				 M_WAITOK | M_NULLOK | M_ZERO);
5767f3c3d6fSHasso Tepper 
5777f3c3d6fSHasso Tepper 	/* Keep the original pagelist until we know all the allocations
5787f3c3d6fSHasso Tepper 	 * have succeeded
5797f3c3d6fSHasso Tepper 	 */
5805718399fSFrançois Tigeot 	temp_pagelist = kmalloc((dma->page_count + (count << page_order)) *
581f8677ba6SMatthew Dillon 				sizeof(*dma->pagelist),
582f8677ba6SMatthew Dillon 				M_DRM, M_WAITOK | M_NULLOK);
5837f3c3d6fSHasso Tepper 
5847f3c3d6fSHasso Tepper 	if (entry->buflist == NULL || entry->seglist == NULL ||
5857f3c3d6fSHasso Tepper 	    temp_pagelist == NULL) {
5865a3b77d5SFrançois Tigeot 		drm_free(temp_pagelist, M_DRM);
5875a3b77d5SFrançois Tigeot 		drm_free(entry->seglist, M_DRM);
5885a3b77d5SFrançois Tigeot 		drm_free(entry->buflist, M_DRM);
589b922632fSImre Vadász 		return -ENOMEM;
5907f3c3d6fSHasso Tepper 	}
5917f3c3d6fSHasso Tepper 
5927f3c3d6fSHasso Tepper 	memcpy(temp_pagelist, dma->pagelist, dma->page_count *
5937f3c3d6fSHasso Tepper 	    sizeof(*dma->pagelist));
5947f3c3d6fSHasso Tepper 
5957f3c3d6fSHasso Tepper 	DRM_DEBUG("pagelist: %d entries\n",
5967f3c3d6fSHasso Tepper 	    dma->page_count + (count << page_order));
5977f3c3d6fSHasso Tepper 
5987f3c3d6fSHasso Tepper 	entry->buf_size	= size;
5997f3c3d6fSHasso Tepper 	entry->page_order = page_order;
6007f3c3d6fSHasso Tepper 	byte_count = 0;
6017f3c3d6fSHasso Tepper 	page_count = 0;
6027f3c3d6fSHasso Tepper 
6037f3c3d6fSHasso Tepper 	while (entry->buf_count < count) {
6045718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
605b31e9d59SFrançois Tigeot 		dmah = drm_pci_alloc(dev, PAGE_SIZE << page_order, 0x1000);
6065718399fSFrançois Tigeot 		spin_lock(&dev->dma_lock);
607b31e9d59SFrançois Tigeot 
608b31e9d59SFrançois Tigeot 		if (!dmah) {
6097f3c3d6fSHasso Tepper 			/* Set count correctly so we free the proper amount. */
6107f3c3d6fSHasso Tepper 			entry->buf_count = count;
6117f3c3d6fSHasso Tepper 			entry->seg_count = count;
6127f3c3d6fSHasso Tepper 			drm_cleanup_buf_error(dev, entry);
6135a3b77d5SFrançois Tigeot 			drm_free(temp_pagelist, M_DRM);
614b31e9d59SFrançois Tigeot 			return -ENOMEM;
6157f3c3d6fSHasso Tepper 		}
6167f3c3d6fSHasso Tepper 
6177f3c3d6fSHasso Tepper 		entry->seglist[entry->seg_count++] = dmah;
6187f3c3d6fSHasso Tepper 		for (i = 0; i < (1 << page_order); i++) {
619b31e9d59SFrançois Tigeot 			DRM_DEBUG("page %d @ 0x%08lx\n",
6207f3c3d6fSHasso Tepper 				  dma->page_count + page_count,
621b31e9d59SFrançois Tigeot 				  (unsigned long)dmah->vaddr + PAGE_SIZE * i);
622b31e9d59SFrançois Tigeot 			temp_pagelist[dma->page_count + page_count++]
623b31e9d59SFrançois Tigeot 				= (unsigned long)dmah->vaddr + PAGE_SIZE * i;
6247f3c3d6fSHasso Tepper 		}
6257f3c3d6fSHasso Tepper 		for (offset = 0;
6267f3c3d6fSHasso Tepper 		    offset + size <= total && entry->buf_count < count;
6277f3c3d6fSHasso Tepper 		    offset += alignment, ++entry->buf_count) {
6287f3c3d6fSHasso Tepper 			buf	     = &entry->buflist[entry->buf_count];
6297f3c3d6fSHasso Tepper 			buf->idx     = dma->buf_count + entry->buf_count;
6307f3c3d6fSHasso Tepper 			buf->total   = alignment;
6317f3c3d6fSHasso Tepper 			buf->order   = order;
6327f3c3d6fSHasso Tepper 			buf->used    = 0;
6337f3c3d6fSHasso Tepper 			buf->offset  = (dma->byte_count + byte_count + offset);
6347f3c3d6fSHasso Tepper 			buf->address = ((char *)dmah->vaddr + offset);
6357f3c3d6fSHasso Tepper 			buf->bus_address = dmah->busaddr + offset;
6367f3c3d6fSHasso Tepper 			buf->next    = NULL;
6377f3c3d6fSHasso Tepper 			buf->pending = 0;
6387f3c3d6fSHasso Tepper 			buf->file_priv = NULL;
6397f3c3d6fSHasso Tepper 
640ba55f2f5SFrançois Tigeot 			buf->dev_priv_size = dev->driver->dev_priv_size;
6415718399fSFrançois Tigeot 			buf->dev_private = kmalloc(buf->dev_priv_size,
642f8677ba6SMatthew Dillon 						   M_DRM,
643f8677ba6SMatthew Dillon 						   M_WAITOK | M_NULLOK |
644f8677ba6SMatthew Dillon 						    M_ZERO);
6457f3c3d6fSHasso Tepper 			if (buf->dev_private == NULL) {
6467f3c3d6fSHasso Tepper 				/* Set count correctly so we free the proper amount. */
6477f3c3d6fSHasso Tepper 				entry->buf_count = count;
6487f3c3d6fSHasso Tepper 				entry->seg_count = count;
6497f3c3d6fSHasso Tepper 				drm_cleanup_buf_error(dev, entry);
6505a3b77d5SFrançois Tigeot 				drm_free(temp_pagelist, M_DRM);
651b922632fSImre Vadász 				return -ENOMEM;
6527f3c3d6fSHasso Tepper 			}
6537f3c3d6fSHasso Tepper 
6547f3c3d6fSHasso Tepper 			DRM_DEBUG("buffer %d @ %p\n",
6557f3c3d6fSHasso Tepper 			    entry->buf_count, buf->address);
6567f3c3d6fSHasso Tepper 		}
6577f3c3d6fSHasso Tepper 		byte_count += PAGE_SIZE << page_order;
6587f3c3d6fSHasso Tepper 	}
6597f3c3d6fSHasso Tepper 
6605718399fSFrançois Tigeot 	temp_buflist = krealloc(dma->buflist,
661b3705d71SHasso Tepper 	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
662f8677ba6SMatthew Dillon 	    M_DRM, M_WAITOK | M_NULLOK);
6637f3c3d6fSHasso Tepper 	if (temp_buflist == NULL) {
6647f3c3d6fSHasso Tepper 		/* Free the entry because it isn't valid */
6657f3c3d6fSHasso Tepper 		drm_cleanup_buf_error(dev, entry);
6665a3b77d5SFrançois Tigeot 		drm_free(temp_pagelist, M_DRM);
667b922632fSImre Vadász 		return -ENOMEM;
6687f3c3d6fSHasso Tepper 	}
6697f3c3d6fSHasso Tepper 	dma->buflist = temp_buflist;
6707f3c3d6fSHasso Tepper 
6717f3c3d6fSHasso Tepper 	for (i = 0; i < entry->buf_count; i++) {
6727f3c3d6fSHasso Tepper 		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
6737f3c3d6fSHasso Tepper 	}
6747f3c3d6fSHasso Tepper 
6757f3c3d6fSHasso Tepper 	/* No allocations failed, so now we can replace the orginal pagelist
6767f3c3d6fSHasso Tepper 	 * with the new one.
6777f3c3d6fSHasso Tepper 	 */
6785a3b77d5SFrançois Tigeot 	drm_free(dma->pagelist, M_DRM);
6797f3c3d6fSHasso Tepper 	dma->pagelist = temp_pagelist;
6807f3c3d6fSHasso Tepper 
6817f3c3d6fSHasso Tepper 	dma->buf_count += entry->buf_count;
6827f3c3d6fSHasso Tepper 	dma->seg_count += entry->seg_count;
6837f3c3d6fSHasso Tepper 	dma->page_count += entry->seg_count << page_order;
6847f3c3d6fSHasso Tepper 	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
6857f3c3d6fSHasso Tepper 
6867f3c3d6fSHasso Tepper 	request->count = entry->buf_count;
6877f3c3d6fSHasso Tepper 	request->size = size;
6887f3c3d6fSHasso Tepper 
6897f3c3d6fSHasso Tepper 	return 0;
6907f3c3d6fSHasso Tepper 
6917f3c3d6fSHasso Tepper }
6927f3c3d6fSHasso Tepper 
693b3705d71SHasso Tepper static int drm_do_addbufs_sg(struct drm_device *dev, struct drm_buf_desc *request)
6947f3c3d6fSHasso Tepper {
6954250aa95Szrj 	struct drm_device_dma *dma = dev->dma;
6964250aa95Szrj 	struct drm_buf_entry *entry;
6974250aa95Szrj 	struct drm_buf *buf;
6987f3c3d6fSHasso Tepper 	unsigned long offset;
6997f3c3d6fSHasso Tepper 	unsigned long agp_offset;
7007f3c3d6fSHasso Tepper 	int count;
7017f3c3d6fSHasso Tepper 	int order;
7027f3c3d6fSHasso Tepper 	int size;
7037f3c3d6fSHasso Tepper 	int alignment;
7047f3c3d6fSHasso Tepper 	int page_order;
7057f3c3d6fSHasso Tepper 	int total;
7067f3c3d6fSHasso Tepper 	int byte_count;
7077f3c3d6fSHasso Tepper 	int i;
7084250aa95Szrj 	struct drm_buf **temp_buflist;
7097f3c3d6fSHasso Tepper 
7107f3c3d6fSHasso Tepper 	count = request->count;
7114cd92098Szrj 	order = order_base_2(request->size);
7127f3c3d6fSHasso Tepper 	size = 1 << order;
7137f3c3d6fSHasso Tepper 
7147f3c3d6fSHasso Tepper 	alignment  = (request->flags & _DRM_PAGE_ALIGN)
7157f3c3d6fSHasso Tepper 	    ? round_page(size) : size;
7167f3c3d6fSHasso Tepper 	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
7177f3c3d6fSHasso Tepper 	total = PAGE_SIZE << page_order;
7187f3c3d6fSHasso Tepper 
7197f3c3d6fSHasso Tepper 	byte_count = 0;
7207f3c3d6fSHasso Tepper 	agp_offset = request->agp_start;
7217f3c3d6fSHasso Tepper 
7227f3c3d6fSHasso Tepper 	DRM_DEBUG("count:      %d\n",  count);
7237f3c3d6fSHasso Tepper 	DRM_DEBUG("order:      %d\n",  order);
7247f3c3d6fSHasso Tepper 	DRM_DEBUG("size:       %d\n",  size);
7257f3c3d6fSHasso Tepper 	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
7267f3c3d6fSHasso Tepper 	DRM_DEBUG("alignment:  %d\n",  alignment);
7277f3c3d6fSHasso Tepper 	DRM_DEBUG("page_order: %d\n",  page_order);
7287f3c3d6fSHasso Tepper 	DRM_DEBUG("total:      %d\n",  total);
7297f3c3d6fSHasso Tepper 
7307f3c3d6fSHasso Tepper 	entry = &dma->bufs[order];
7317f3c3d6fSHasso Tepper 
7325a3b77d5SFrançois Tigeot 	entry->buflist = kmalloc(count * sizeof(*entry->buflist), M_DRM,
733f8677ba6SMatthew Dillon 				 M_WAITOK | M_NULLOK | M_ZERO);
7347f3c3d6fSHasso Tepper 	if (entry->buflist == NULL)
735b922632fSImre Vadász 		return -ENOMEM;
7367f3c3d6fSHasso Tepper 
7377f3c3d6fSHasso Tepper 	entry->buf_size = size;
7387f3c3d6fSHasso Tepper 	entry->page_order = page_order;
7397f3c3d6fSHasso Tepper 
7407f3c3d6fSHasso Tepper 	offset = 0;
7417f3c3d6fSHasso Tepper 
7427f3c3d6fSHasso Tepper 	while (entry->buf_count < count) {
7437f3c3d6fSHasso Tepper 		buf          = &entry->buflist[entry->buf_count];
7447f3c3d6fSHasso Tepper 		buf->idx     = dma->buf_count + entry->buf_count;
7457f3c3d6fSHasso Tepper 		buf->total   = alignment;
7467f3c3d6fSHasso Tepper 		buf->order   = order;
7477f3c3d6fSHasso Tepper 		buf->used    = 0;
7487f3c3d6fSHasso Tepper 
7497f3c3d6fSHasso Tepper 		buf->offset  = (dma->byte_count + offset);
7507f3c3d6fSHasso Tepper 		buf->bus_address = agp_offset + offset;
75199f70504SFrançois Tigeot 		buf->address = (void *)(agp_offset + offset + dev->sg->vaddr);
7527f3c3d6fSHasso Tepper 		buf->next    = NULL;
7537f3c3d6fSHasso Tepper 		buf->pending = 0;
7547f3c3d6fSHasso Tepper 		buf->file_priv = NULL;
7557f3c3d6fSHasso Tepper 
756ba55f2f5SFrançois Tigeot 		buf->dev_priv_size = dev->driver->dev_priv_size;
7575a3b77d5SFrançois Tigeot 		buf->dev_private = kmalloc(buf->dev_priv_size, M_DRM,
758f8677ba6SMatthew Dillon 					   M_WAITOK | M_NULLOK | M_ZERO);
7597f3c3d6fSHasso Tepper 		if (buf->dev_private == NULL) {
7607f3c3d6fSHasso Tepper 			/* Set count correctly so we free the proper amount. */
7617f3c3d6fSHasso Tepper 			entry->buf_count = count;
7627f3c3d6fSHasso Tepper 			drm_cleanup_buf_error(dev, entry);
763b922632fSImre Vadász 			return -ENOMEM;
7647f3c3d6fSHasso Tepper 		}
7657f3c3d6fSHasso Tepper 
7667f3c3d6fSHasso Tepper 		DRM_DEBUG("buffer %d @ %p\n",
7677f3c3d6fSHasso Tepper 		    entry->buf_count, buf->address);
7687f3c3d6fSHasso Tepper 
7697f3c3d6fSHasso Tepper 		offset += alignment;
7707f3c3d6fSHasso Tepper 		entry->buf_count++;
7717f3c3d6fSHasso Tepper 		byte_count += PAGE_SIZE << page_order;
7727f3c3d6fSHasso Tepper 	}
7737f3c3d6fSHasso Tepper 
7747f3c3d6fSHasso Tepper 	DRM_DEBUG("byte_count: %d\n", byte_count);
7757f3c3d6fSHasso Tepper 
7765718399fSFrançois Tigeot 	temp_buflist = krealloc(dma->buflist,
777b3705d71SHasso Tepper 	    (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
778f8677ba6SMatthew Dillon 	    M_DRM, M_WAITOK | M_NULLOK);
7797f3c3d6fSHasso Tepper 	if (temp_buflist == NULL) {
7807f3c3d6fSHasso Tepper 		/* Free the entry because it isn't valid */
7817f3c3d6fSHasso Tepper 		drm_cleanup_buf_error(dev, entry);
782b922632fSImre Vadász 		return -ENOMEM;
7837f3c3d6fSHasso Tepper 	}
7847f3c3d6fSHasso Tepper 	dma->buflist = temp_buflist;
7857f3c3d6fSHasso Tepper 
7867f3c3d6fSHasso Tepper 	for (i = 0; i < entry->buf_count; i++) {
7877f3c3d6fSHasso Tepper 		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
7887f3c3d6fSHasso Tepper 	}
7897f3c3d6fSHasso Tepper 
7907f3c3d6fSHasso Tepper 	dma->buf_count += entry->buf_count;
7917f3c3d6fSHasso Tepper 	dma->byte_count += byte_count;
7927f3c3d6fSHasso Tepper 
7937f3c3d6fSHasso Tepper 	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
7947f3c3d6fSHasso Tepper 	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
7957f3c3d6fSHasso Tepper 
7967f3c3d6fSHasso Tepper 	request->count = entry->buf_count;
7977f3c3d6fSHasso Tepper 	request->size = size;
7987f3c3d6fSHasso Tepper 
7997f3c3d6fSHasso Tepper 	dma->flags = _DRM_DMA_USE_SG;
8007f3c3d6fSHasso Tepper 
8017f3c3d6fSHasso Tepper 	return 0;
8027f3c3d6fSHasso Tepper }
8037f3c3d6fSHasso Tepper 
804df4baf3dSFrançois Tigeot /**
805df4baf3dSFrançois Tigeot  * Add AGP buffers for DMA transfers.
806df4baf3dSFrançois Tigeot  *
807df4baf3dSFrançois Tigeot  * \param dev struct drm_device to which the buffers are to be added.
808df4baf3dSFrançois Tigeot  * \param request pointer to a struct drm_buf_desc describing the request.
809df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
810df4baf3dSFrançois Tigeot  *
811df4baf3dSFrançois Tigeot  * After some sanity checks creates a drm_buf structure for each buffer and
812df4baf3dSFrançois Tigeot  * reallocates the buffer list of the same size order to accommodate the new
813df4baf3dSFrançois Tigeot  * buffers.
814df4baf3dSFrançois Tigeot  */
8151b13d190SFrançois Tigeot int drm_legacy_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request)
8167f3c3d6fSHasso Tepper {
8177f3c3d6fSHasso Tepper 	int order, ret;
8187f3c3d6fSHasso Tepper 
8197f3c3d6fSHasso Tepper 	if (request->count < 0 || request->count > 4096)
820b922632fSImre Vadász 		return -EINVAL;
8217f3c3d6fSHasso Tepper 
8224cd92098Szrj 	order = order_base_2(request->size);
8237f3c3d6fSHasso Tepper 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
824b922632fSImre Vadász 		return -EINVAL;
8257f3c3d6fSHasso Tepper 
8265718399fSFrançois Tigeot 	spin_lock(&dev->dma_lock);
827b3705d71SHasso Tepper 
8287f3c3d6fSHasso Tepper 	/* No more allocations after first buffer-using ioctl. */
8297f3c3d6fSHasso Tepper 	if (dev->buf_use != 0) {
8305718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
831b922632fSImre Vadász 		return -EBUSY;
8327f3c3d6fSHasso Tepper 	}
8337f3c3d6fSHasso Tepper 	/* No more than one allocation per order */
8347f3c3d6fSHasso Tepper 	if (dev->dma->bufs[order].buf_count != 0) {
8355718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
836b922632fSImre Vadász 		return -ENOMEM;
8377f3c3d6fSHasso Tepper 	}
8387f3c3d6fSHasso Tepper 
8397f3c3d6fSHasso Tepper 	ret = drm_do_addbufs_agp(dev, request);
8407f3c3d6fSHasso Tepper 
8415718399fSFrançois Tigeot 	spin_unlock(&dev->dma_lock);
8427f3c3d6fSHasso Tepper 
8437f3c3d6fSHasso Tepper 	return ret;
8447f3c3d6fSHasso Tepper }
8457f3c3d6fSHasso Tepper 
8461b13d190SFrançois Tigeot static int drm_legacy_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request)
8477f3c3d6fSHasso Tepper {
8487f3c3d6fSHasso Tepper 	int order, ret;
8497f3c3d6fSHasso Tepper 
850c6f73aabSFrançois Tigeot 	if (!capable(CAP_SYS_ADMIN))
851b922632fSImre Vadász 		return -EACCES;
8527f3c3d6fSHasso Tepper 
8537f3c3d6fSHasso Tepper 	if (request->count < 0 || request->count > 4096)
854b922632fSImre Vadász 		return -EINVAL;
8557f3c3d6fSHasso Tepper 
8564cd92098Szrj 	order = order_base_2(request->size);
8577f3c3d6fSHasso Tepper 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
858b922632fSImre Vadász 		return -EINVAL;
8597f3c3d6fSHasso Tepper 
8605718399fSFrançois Tigeot 	spin_lock(&dev->dma_lock);
861b3705d71SHasso Tepper 
8627f3c3d6fSHasso Tepper 	/* No more allocations after first buffer-using ioctl. */
8637f3c3d6fSHasso Tepper 	if (dev->buf_use != 0) {
8645718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
865b922632fSImre Vadász 		return -EBUSY;
8667f3c3d6fSHasso Tepper 	}
8677f3c3d6fSHasso Tepper 	/* No more than one allocation per order */
8687f3c3d6fSHasso Tepper 	if (dev->dma->bufs[order].buf_count != 0) {
8695718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
870b922632fSImre Vadász 		return -ENOMEM;
8717f3c3d6fSHasso Tepper 	}
8727f3c3d6fSHasso Tepper 
8737f3c3d6fSHasso Tepper 	ret = drm_do_addbufs_sg(dev, request);
8747f3c3d6fSHasso Tepper 
8755718399fSFrançois Tigeot 	spin_unlock(&dev->dma_lock);
8767f3c3d6fSHasso Tepper 
8777f3c3d6fSHasso Tepper 	return ret;
8787f3c3d6fSHasso Tepper }
8797f3c3d6fSHasso Tepper 
8801b13d190SFrançois Tigeot int drm_legacy_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request)
8817f3c3d6fSHasso Tepper {
8827f3c3d6fSHasso Tepper 	int order, ret;
8837f3c3d6fSHasso Tepper 
884c6f73aabSFrançois Tigeot 	if (!capable(CAP_SYS_ADMIN))
885b922632fSImre Vadász 		return -EACCES;
8867f3c3d6fSHasso Tepper 
8877f3c3d6fSHasso Tepper 	if (request->count < 0 || request->count > 4096)
888b922632fSImre Vadász 		return -EINVAL;
8897f3c3d6fSHasso Tepper 
8904cd92098Szrj 	order = order_base_2(request->size);
8917f3c3d6fSHasso Tepper 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
892b922632fSImre Vadász 		return -EINVAL;
8937f3c3d6fSHasso Tepper 
8945718399fSFrançois Tigeot 	spin_lock(&dev->dma_lock);
895b3705d71SHasso Tepper 
8967f3c3d6fSHasso Tepper 	/* No more allocations after first buffer-using ioctl. */
8977f3c3d6fSHasso Tepper 	if (dev->buf_use != 0) {
8985718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
899b922632fSImre Vadász 		return -EBUSY;
9007f3c3d6fSHasso Tepper 	}
9017f3c3d6fSHasso Tepper 	/* No more than one allocation per order */
9027f3c3d6fSHasso Tepper 	if (dev->dma->bufs[order].buf_count != 0) {
9035718399fSFrançois Tigeot 		spin_unlock(&dev->dma_lock);
904b922632fSImre Vadász 		return -ENOMEM;
9057f3c3d6fSHasso Tepper 	}
9067f3c3d6fSHasso Tepper 
9077f3c3d6fSHasso Tepper 	ret = drm_do_addbufs_pci(dev, request);
9087f3c3d6fSHasso Tepper 
9095718399fSFrançois Tigeot 	spin_unlock(&dev->dma_lock);
9107f3c3d6fSHasso Tepper 
9117f3c3d6fSHasso Tepper 	return ret;
9127f3c3d6fSHasso Tepper }
9137f3c3d6fSHasso Tepper 
914df4baf3dSFrançois Tigeot /**
915df4baf3dSFrançois Tigeot  * Add buffers for DMA transfers (ioctl).
916df4baf3dSFrançois Tigeot  *
917df4baf3dSFrançois Tigeot  * \param inode device inode.
918df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
919df4baf3dSFrançois Tigeot  * \param cmd command.
920df4baf3dSFrançois Tigeot  * \param arg pointer to a struct drm_buf_desc request.
921df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
922df4baf3dSFrançois Tigeot  *
923df4baf3dSFrançois Tigeot  * According with the memory type specified in drm_buf_desc::flags and the
924df4baf3dSFrançois Tigeot  * build options, it dispatches the call either to addbufs_agp(),
925df4baf3dSFrançois Tigeot  * addbufs_sg() or addbufs_pci() for AGP, scatter-gather or consistent
926df4baf3dSFrançois Tigeot  * PCI memory respectively.
927df4baf3dSFrançois Tigeot  */
9281b13d190SFrançois Tigeot int drm_legacy_addbufs(struct drm_device *dev, void *data,
929df4baf3dSFrançois Tigeot 		       struct drm_file *file_priv)
9307f3c3d6fSHasso Tepper {
931b3705d71SHasso Tepper 	struct drm_buf_desc *request = data;
9327f3c3d6fSHasso Tepper 	int err;
9337f3c3d6fSHasso Tepper 
9347f3c3d6fSHasso Tepper 	if (request->flags & _DRM_AGP_BUFFER)
9351b13d190SFrançois Tigeot 		err = drm_legacy_addbufs_agp(dev, request);
9367f3c3d6fSHasso Tepper 	else if (request->flags & _DRM_SG_BUFFER)
9371b13d190SFrançois Tigeot 		err = drm_legacy_addbufs_sg(dev, request);
9387f3c3d6fSHasso Tepper 	else
9391b13d190SFrançois Tigeot 		err = drm_legacy_addbufs_pci(dev, request);
9407f3c3d6fSHasso Tepper 
9417f3c3d6fSHasso Tepper 	return err;
9427f3c3d6fSHasso Tepper }
9437f3c3d6fSHasso Tepper 
944df4baf3dSFrançois Tigeot /**
945df4baf3dSFrançois Tigeot  * Get information about the buffer mappings.
946df4baf3dSFrançois Tigeot  *
947df4baf3dSFrançois Tigeot  * This was originally mean for debugging purposes, or by a sophisticated
948df4baf3dSFrançois Tigeot  * client library to determine how best to use the available buffers (e.g.,
949df4baf3dSFrançois Tigeot  * large buffers can be used for image transfer).
950df4baf3dSFrançois Tigeot  *
951df4baf3dSFrançois Tigeot  * \param inode device inode.
952df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
953df4baf3dSFrançois Tigeot  * \param cmd command.
954df4baf3dSFrançois Tigeot  * \param arg pointer to a drm_buf_info structure.
955df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
956df4baf3dSFrançois Tigeot  *
95724edb884SFrançois Tigeot  * Increments drm_device::buf_use while holding the drm_device::buf_lock
958df4baf3dSFrançois Tigeot  * lock, preventing of allocating more buffers after this call. Information
959df4baf3dSFrançois Tigeot  * about each requested buffer is then copied into user space.
960df4baf3dSFrançois Tigeot  */
9611b13d190SFrançois Tigeot int drm_legacy_infobufs(struct drm_device *dev, void *data,
962df4baf3dSFrançois Tigeot 			struct drm_file *file_priv)
9637f3c3d6fSHasso Tepper {
96424edb884SFrançois Tigeot 	struct drm_device_dma *dma = dev->dma;
965b3705d71SHasso Tepper 	struct drm_buf_info *request = data;
9667f3c3d6fSHasso Tepper 	int i;
9677f3c3d6fSHasso Tepper 	int count;
9687f3c3d6fSHasso Tepper 
96924edb884SFrançois Tigeot 	if (drm_core_check_feature(dev, DRIVER_MODESET))
97024edb884SFrançois Tigeot 		return -EINVAL;
97124edb884SFrançois Tigeot 
97224edb884SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
97324edb884SFrançois Tigeot 		return -EINVAL;
97424edb884SFrançois Tigeot 
97524edb884SFrançois Tigeot 	if (!dma)
97624edb884SFrançois Tigeot 		return -EINVAL;
97724edb884SFrançois Tigeot 
97824edb884SFrançois Tigeot 	spin_lock(&dev->buf_lock);
97924edb884SFrançois Tigeot 	if (atomic_read(&dev->buf_alloc)) {
98024edb884SFrançois Tigeot 		spin_unlock(&dev->buf_lock);
98124edb884SFrançois Tigeot 		return -EBUSY;
98224edb884SFrançois Tigeot 	}
9837f3c3d6fSHasso Tepper 	++dev->buf_use;		/* Can't allocate more after this call */
98424edb884SFrançois Tigeot 	spin_unlock(&dev->buf_lock);
9857f3c3d6fSHasso Tepper 
9867f3c3d6fSHasso Tepper 	for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
987b3705d71SHasso Tepper 		if (dma->bufs[i].buf_count)
988b3705d71SHasso Tepper 			++count;
9897f3c3d6fSHasso Tepper 	}
9907f3c3d6fSHasso Tepper 
9917f3c3d6fSHasso Tepper 	DRM_DEBUG("count = %d\n", count);
9927f3c3d6fSHasso Tepper 
9937f3c3d6fSHasso Tepper 	if (request->count >= count) {
9947f3c3d6fSHasso Tepper 		for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
9957f3c3d6fSHasso Tepper 			if (dma->bufs[i].buf_count) {
99624edb884SFrançois Tigeot 				struct drm_buf_desc __user *to =
99724edb884SFrançois Tigeot 				    &request->list[count];
99824edb884SFrançois Tigeot 				struct drm_buf_entry *from = &dma->bufs[i];
99924edb884SFrançois Tigeot 				if (copy_to_user(&to->count,
100024edb884SFrançois Tigeot 						 &from->buf_count,
100124edb884SFrançois Tigeot 						 sizeof(from->buf_count)) ||
100224edb884SFrançois Tigeot 				    copy_to_user(&to->size,
100324edb884SFrançois Tigeot 						 &from->buf_size,
100424edb884SFrançois Tigeot 						 sizeof(from->buf_size)) ||
100524edb884SFrançois Tigeot 				    copy_to_user(&to->low_mark,
100624edb884SFrançois Tigeot 						 &from->low_mark,
100724edb884SFrançois Tigeot 						 sizeof(from->low_mark)) ||
100824edb884SFrançois Tigeot 				    copy_to_user(&to->high_mark,
100924edb884SFrançois Tigeot 						 &from->high_mark,
101024edb884SFrançois Tigeot 						 sizeof(from->high_mark)))
101124edb884SFrançois Tigeot 					return -EFAULT;
10127f3c3d6fSHasso Tepper 
10137f3c3d6fSHasso Tepper 				DRM_DEBUG("%d %d %d %d %d\n",
101424edb884SFrançois Tigeot 					  i,
101524edb884SFrançois Tigeot 					  dma->bufs[i].buf_count,
10167f3c3d6fSHasso Tepper 					  dma->bufs[i].buf_size,
101724edb884SFrançois Tigeot 					  dma->bufs[i].low_mark,
101824edb884SFrançois Tigeot 					  dma->bufs[i].high_mark);
10197f3c3d6fSHasso Tepper 				++count;
10207f3c3d6fSHasso Tepper 			}
10217f3c3d6fSHasso Tepper 		}
10227f3c3d6fSHasso Tepper 	}
10237f3c3d6fSHasso Tepper 	request->count = count;
10247f3c3d6fSHasso Tepper 
102524edb884SFrançois Tigeot 	return 0;
10267f3c3d6fSHasso Tepper }
10277f3c3d6fSHasso Tepper 
1028df4baf3dSFrançois Tigeot /**
1029df4baf3dSFrançois Tigeot  * Specifies a low and high water mark for buffer allocation
1030df4baf3dSFrançois Tigeot  *
1031df4baf3dSFrançois Tigeot  * \param inode device inode.
1032df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
1033df4baf3dSFrançois Tigeot  * \param cmd command.
1034df4baf3dSFrançois Tigeot  * \param arg a pointer to a drm_buf_desc structure.
1035df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
1036df4baf3dSFrançois Tigeot  *
1037df4baf3dSFrançois Tigeot  * Verifies that the size order is bounded between the admissible orders and
1038df4baf3dSFrançois Tigeot  * updates the respective drm_device_dma::bufs entry low and high water mark.
1039df4baf3dSFrançois Tigeot  *
1040df4baf3dSFrançois Tigeot  * \note This ioctl is deprecated and mostly never used.
1041df4baf3dSFrançois Tigeot  */
10421b13d190SFrançois Tigeot int drm_legacy_markbufs(struct drm_device *dev, void *data,
1043df4baf3dSFrançois Tigeot 			struct drm_file *file_priv)
10447f3c3d6fSHasso Tepper {
104524edb884SFrançois Tigeot 	struct drm_device_dma *dma = dev->dma;
1046b3705d71SHasso Tepper 	struct drm_buf_desc *request = data;
10477f3c3d6fSHasso Tepper 	int order;
104824edb884SFrançois Tigeot 	struct drm_buf_entry *entry;
104924edb884SFrançois Tigeot 
105024edb884SFrançois Tigeot 	if (drm_core_check_feature(dev, DRIVER_MODESET))
105124edb884SFrançois Tigeot 		return -EINVAL;
105224edb884SFrançois Tigeot 
105324edb884SFrançois Tigeot 	if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
105424edb884SFrançois Tigeot 		return -EINVAL;
105524edb884SFrançois Tigeot 
105624edb884SFrançois Tigeot 	if (!dma)
105724edb884SFrançois Tigeot 		return -EINVAL;
10587f3c3d6fSHasso Tepper 
10597f3c3d6fSHasso Tepper 	DRM_DEBUG("%d, %d, %d\n",
10607f3c3d6fSHasso Tepper 		  request->size, request->low_mark, request->high_mark);
10614cd92098Szrj 	order = order_base_2(request->size);
106224edb884SFrançois Tigeot 	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
106324edb884SFrançois Tigeot 		return -EINVAL;
106424edb884SFrançois Tigeot 	entry = &dma->bufs[order];
10657f3c3d6fSHasso Tepper 
106624edb884SFrançois Tigeot 	if (request->low_mark < 0 || request->low_mark > entry->buf_count)
106724edb884SFrançois Tigeot 		return -EINVAL;
106824edb884SFrançois Tigeot 	if (request->high_mark < 0 || request->high_mark > entry->buf_count)
106924edb884SFrançois Tigeot 		return -EINVAL;
10707f3c3d6fSHasso Tepper 
107124edb884SFrançois Tigeot 	entry->low_mark = request->low_mark;
107224edb884SFrançois Tigeot 	entry->high_mark = request->high_mark;
10737f3c3d6fSHasso Tepper 
10747f3c3d6fSHasso Tepper 	return 0;
10757f3c3d6fSHasso Tepper }
10767f3c3d6fSHasso Tepper 
1077df4baf3dSFrançois Tigeot /**
1078df4baf3dSFrançois Tigeot  * Unreserve the buffers in list, previously reserved using drmDMA.
1079df4baf3dSFrançois Tigeot  *
1080df4baf3dSFrançois Tigeot  * \param inode device inode.
1081df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
1082df4baf3dSFrançois Tigeot  * \param cmd command.
1083df4baf3dSFrançois Tigeot  * \param arg pointer to a drm_buf_free structure.
1084df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
1085df4baf3dSFrançois Tigeot  *
1086df4baf3dSFrançois Tigeot  * Calls free_buffer() for each used buffer.
1087df4baf3dSFrançois Tigeot  * This function is primarily used for debugging.
1088df4baf3dSFrançois Tigeot  */
10891b13d190SFrançois Tigeot int drm_legacy_freebufs(struct drm_device *dev, void *data,
1090df4baf3dSFrançois Tigeot 			struct drm_file *file_priv)
10917f3c3d6fSHasso Tepper {
10924250aa95Szrj 	struct drm_device_dma *dma = dev->dma;
1093b3705d71SHasso Tepper 	struct drm_buf_free *request = data;
10947f3c3d6fSHasso Tepper 	int i;
10957f3c3d6fSHasso Tepper 	int idx;
10964250aa95Szrj 	struct drm_buf *buf;
10977f3c3d6fSHasso Tepper 	int retcode = 0;
10987f3c3d6fSHasso Tepper 
10997f3c3d6fSHasso Tepper 	DRM_DEBUG("%d\n", request->count);
11007f3c3d6fSHasso Tepper 
11015718399fSFrançois Tigeot 	spin_lock(&dev->dma_lock);
11027f3c3d6fSHasso Tepper 	for (i = 0; i < request->count; i++) {
1103c6f73aabSFrançois Tigeot 		if (copy_from_user(&idx, &request->list[i], sizeof(idx))) {
1104b922632fSImre Vadász 			retcode = -EFAULT;
11057f3c3d6fSHasso Tepper 			break;
11067f3c3d6fSHasso Tepper 		}
11077f3c3d6fSHasso Tepper 		if (idx < 0 || idx >= dma->buf_count) {
11087f3c3d6fSHasso Tepper 			DRM_ERROR("Index %d (of %d max)\n",
11097f3c3d6fSHasso Tepper 			    idx, dma->buf_count - 1);
1110b922632fSImre Vadász 			retcode = -EINVAL;
11117f3c3d6fSHasso Tepper 			break;
11127f3c3d6fSHasso Tepper 		}
11137f3c3d6fSHasso Tepper 		buf = dma->buflist[idx];
11147f3c3d6fSHasso Tepper 		if (buf->file_priv != file_priv) {
11157f3c3d6fSHasso Tepper 			DRM_ERROR("Process %d freeing buffer not owned\n",
11167f3c3d6fSHasso Tepper 			    DRM_CURRENTPID);
1117b922632fSImre Vadász 			retcode = -EINVAL;
11187f3c3d6fSHasso Tepper 			break;
11197f3c3d6fSHasso Tepper 		}
11201b13d190SFrançois Tigeot 		drm_legacy_free_buffer(dev, buf);
11217f3c3d6fSHasso Tepper 	}
11225718399fSFrançois Tigeot 	spin_unlock(&dev->dma_lock);
11237f3c3d6fSHasso Tepper 
11247f3c3d6fSHasso Tepper 	return retcode;
11257f3c3d6fSHasso Tepper }
11267f3c3d6fSHasso Tepper 
1127df4baf3dSFrançois Tigeot /**
1128df4baf3dSFrançois Tigeot  * Maps all of the DMA buffers into client-virtual space (ioctl).
1129df4baf3dSFrançois Tigeot  *
1130df4baf3dSFrançois Tigeot  * \param inode device inode.
1131df4baf3dSFrançois Tigeot  * \param file_priv DRM file private.
1132df4baf3dSFrançois Tigeot  * \param cmd command.
1133df4baf3dSFrançois Tigeot  * \param arg pointer to a drm_buf_map structure.
1134df4baf3dSFrançois Tigeot  * \return zero on success or a negative number on failure.
1135df4baf3dSFrançois Tigeot  *
1136df4baf3dSFrançois Tigeot  * Maps the AGP, SG or PCI buffer region with vm_mmap(), and copies information
1137df4baf3dSFrançois Tigeot  * about each buffer into user space. For PCI buffers, it calls vm_mmap() with
1138df4baf3dSFrançois Tigeot  * offset equal to 0, which drm_mmap() interpretes as PCI buffers and calls
1139df4baf3dSFrançois Tigeot  * drm_mmap_dma().
1140df4baf3dSFrançois Tigeot  */
11411b13d190SFrançois Tigeot int drm_legacy_mapbufs(struct drm_device *dev, void *data,
1142df4baf3dSFrançois Tigeot 		       struct drm_file *file_priv)
11437f3c3d6fSHasso Tepper {
11444250aa95Szrj 	struct drm_device_dma *dma = dev->dma;
11457f3c3d6fSHasso Tepper 	int retcode = 0;
11467f3c3d6fSHasso Tepper 	const int zero = 0;
11477f3c3d6fSHasso Tepper 	vm_offset_t address;
11487f3c3d6fSHasso Tepper 	struct vmspace *vms;
11497f3c3d6fSHasso Tepper 	vm_ooffset_t foff;
11507f3c3d6fSHasso Tepper 	vm_size_t size;
11517f3c3d6fSHasso Tepper 	vm_offset_t vaddr;
1152b3705d71SHasso Tepper 	struct drm_buf_map *request = data;
11537f3c3d6fSHasso Tepper 	int i;
11547f3c3d6fSHasso Tepper 
11557f3c3d6fSHasso Tepper 	vms = DRM_CURPROC->td_proc->p_vmspace;
11567f3c3d6fSHasso Tepper 
11575718399fSFrançois Tigeot 	spin_lock(&dev->dma_lock);
11587f3c3d6fSHasso Tepper 	dev->buf_use++;		/* Can't allocate more after this call */
11595718399fSFrançois Tigeot 	spin_unlock(&dev->dma_lock);
11607f3c3d6fSHasso Tepper 
11617f3c3d6fSHasso Tepper 	if (request->count < dma->buf_count)
11627f3c3d6fSHasso Tepper 		goto done;
11637f3c3d6fSHasso Tepper 
1164*53e4e524Szrj 	if ((dev->agp && (dma->flags & _DRM_DMA_USE_AGP)) ||
1165b3705d71SHasso Tepper 	    (drm_core_check_feature(dev, DRIVER_SG) &&
1166b3705d71SHasso Tepper 	    (dma->flags & _DRM_DMA_USE_SG))) {
11677f3c3d6fSHasso Tepper 		drm_local_map_t *map = dev->agp_buffer_map;
11687f3c3d6fSHasso Tepper 
11697f3c3d6fSHasso Tepper 		if (map == NULL) {
1170b922632fSImre Vadász 			retcode = -EINVAL;
11717f3c3d6fSHasso Tepper 			goto done;
11727f3c3d6fSHasso Tepper 		}
11737f3c3d6fSHasso Tepper 		size = round_page(map->size);
117499f70504SFrançois Tigeot 		foff = (unsigned long)map->handle;
11757f3c3d6fSHasso Tepper 	} else {
11767f3c3d6fSHasso Tepper 		size = round_page(dma->byte_count),
11777f3c3d6fSHasso Tepper 		foff = 0;
11787f3c3d6fSHasso Tepper 	}
11797f3c3d6fSHasso Tepper 
11807f3c3d6fSHasso Tepper 	vaddr = round_page((vm_offset_t)vms->vm_daddr + MAXDSIZ);
1181b922632fSImre Vadász 	retcode = -vm_mmap(&vms->vm_map, &vaddr, size, PROT_READ | PROT_WRITE,
1182b3705d71SHasso Tepper 	    VM_PROT_ALL, MAP_SHARED | MAP_NOSYNC,
1183b3705d71SHasso Tepper 	    SLIST_FIRST(&dev->devnode->si_hlist), foff);
11847f3c3d6fSHasso Tepper 	if (retcode)
11857f3c3d6fSHasso Tepper 		goto done;
11867f3c3d6fSHasso Tepper 
11877f3c3d6fSHasso Tepper 	request->virtual = (void *)vaddr;
11887f3c3d6fSHasso Tepper 
11897f3c3d6fSHasso Tepper 	for (i = 0; i < dma->buf_count; i++) {
1190c6f73aabSFrançois Tigeot 		if (copy_to_user(&request->list[i].idx,
11917f3c3d6fSHasso Tepper 		    &dma->buflist[i]->idx, sizeof(request->list[0].idx))) {
1192b922632fSImre Vadász 			retcode = -EFAULT;
11937f3c3d6fSHasso Tepper 			goto done;
11947f3c3d6fSHasso Tepper 		}
1195c6f73aabSFrançois Tigeot 		if (copy_to_user(&request->list[i].total,
11967f3c3d6fSHasso Tepper 		    &dma->buflist[i]->total, sizeof(request->list[0].total))) {
1197b922632fSImre Vadász 			retcode = -EFAULT;
11987f3c3d6fSHasso Tepper 			goto done;
11997f3c3d6fSHasso Tepper 		}
1200c6f73aabSFrançois Tigeot 		if (copy_to_user(&request->list[i].used, &zero,
12017f3c3d6fSHasso Tepper 		    sizeof(zero))) {
1202b922632fSImre Vadász 			retcode = -EFAULT;
12037f3c3d6fSHasso Tepper 			goto done;
12047f3c3d6fSHasso Tepper 		}
12057f3c3d6fSHasso Tepper 		address = vaddr + dma->buflist[i]->offset; /* *** */
1206c6f73aabSFrançois Tigeot 		if (copy_to_user(&request->list[i].address, &address,
12077f3c3d6fSHasso Tepper 		    sizeof(address))) {
1208b922632fSImre Vadász 			retcode = -EFAULT;
12097f3c3d6fSHasso Tepper 			goto done;
12107f3c3d6fSHasso Tepper 		}
12117f3c3d6fSHasso Tepper 	}
12127f3c3d6fSHasso Tepper       done:
12137f3c3d6fSHasso Tepper 	request->count = dma->buf_count;
12147f3c3d6fSHasso Tepper 	DRM_DEBUG("%d buffers, retcode = %d\n", request->count, retcode);
12157f3c3d6fSHasso Tepper 
12167f3c3d6fSHasso Tepper 	return retcode;
12177f3c3d6fSHasso Tepper }
12181b13d190SFrançois Tigeot 
12191b13d190SFrançois Tigeot int drm_legacy_dma_ioctl(struct drm_device *dev, void *data,
12201b13d190SFrançois Tigeot 		  struct drm_file *file_priv)
12211b13d190SFrançois Tigeot {
12221b13d190SFrançois Tigeot 	if (drm_core_check_feature(dev, DRIVER_MODESET))
12231b13d190SFrançois Tigeot 		return -EINVAL;
12241b13d190SFrançois Tigeot 
12251b13d190SFrançois Tigeot 	if (dev->driver->dma_ioctl)
12261b13d190SFrançois Tigeot 		return dev->driver->dma_ioctl(dev, data, file_priv);
12271b13d190SFrançois Tigeot 	else
12281b13d190SFrançois Tigeot 		return -EINVAL;
12291b13d190SFrançois Tigeot }
12301b13d190SFrançois Tigeot 
12311b13d190SFrançois Tigeot struct drm_local_map *drm_legacy_getsarea(struct drm_device *dev)
12321b13d190SFrançois Tigeot {
12331b13d190SFrançois Tigeot 	struct drm_map_list *entry;
12341b13d190SFrançois Tigeot 
12351b13d190SFrançois Tigeot 	list_for_each_entry(entry, &dev->maplist, head) {
12361b13d190SFrançois Tigeot 		if (entry->map && entry->map->type == _DRM_SHM &&
12371b13d190SFrançois Tigeot 		    (entry->map->flags & _DRM_CONTAINS_LOCK)) {
12381b13d190SFrançois Tigeot 			return entry->map;
12391b13d190SFrançois Tigeot 		}
12401b13d190SFrançois Tigeot 	}
12411b13d190SFrançois Tigeot 	return NULL;
12421b13d190SFrançois Tigeot }
12431b13d190SFrançois Tigeot EXPORT_SYMBOL(drm_legacy_getsarea);
1244