162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Legacy: Generic DRM Buffer Management
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
562306a36Sopenharmony_ci * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
662306a36Sopenharmony_ci * All Rights Reserved.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Author: Rickard E. (Rik) Faith <faith@valinux.com>
962306a36Sopenharmony_ci * Author: Gareth Hughes <gareth@valinux.com>
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
1262306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
1362306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
1462306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1562306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
1662306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the next
1962306a36Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
2062306a36Sopenharmony_ci * Software.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2362306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2462306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2562306a36Sopenharmony_ci * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
2662306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2762306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2862306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <linux/export.h>
3262306a36Sopenharmony_ci#include <linux/log2.h>
3362306a36Sopenharmony_ci#include <linux/mm.h>
3462306a36Sopenharmony_ci#include <linux/mman.h>
3562306a36Sopenharmony_ci#include <linux/nospec.h>
3662306a36Sopenharmony_ci#include <linux/pci.h>
3762306a36Sopenharmony_ci#include <linux/slab.h>
3862306a36Sopenharmony_ci#include <linux/uaccess.h>
3962306a36Sopenharmony_ci#include <linux/vmalloc.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#include <asm/shmparam.h>
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#include <drm/drm_device.h>
4462306a36Sopenharmony_ci#include <drm/drm_drv.h>
4562306a36Sopenharmony_ci#include <drm/drm_file.h>
4662306a36Sopenharmony_ci#include <drm/drm_print.h>
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#include "drm_legacy.h"
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic struct drm_map_list *drm_find_matching_map(struct drm_device *dev,
5262306a36Sopenharmony_ci						  struct drm_local_map *map)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct drm_map_list *entry;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	list_for_each_entry(entry, &dev->maplist, head) {
5762306a36Sopenharmony_ci		/*
5862306a36Sopenharmony_ci		 * Because the kernel-userspace ABI is fixed at a 32-bit offset
5962306a36Sopenharmony_ci		 * while PCI resources may live above that, we only compare the
6062306a36Sopenharmony_ci		 * lower 32 bits of the map offset for maps of type
6162306a36Sopenharmony_ci		 * _DRM_FRAMEBUFFER or _DRM_REGISTERS.
6262306a36Sopenharmony_ci		 * It is assumed that if a driver have more than one resource
6362306a36Sopenharmony_ci		 * of each type, the lower 32 bits are different.
6462306a36Sopenharmony_ci		 */
6562306a36Sopenharmony_ci		if (!entry->map ||
6662306a36Sopenharmony_ci		    map->type != entry->map->type ||
6762306a36Sopenharmony_ci		    entry->master != dev->master)
6862306a36Sopenharmony_ci			continue;
6962306a36Sopenharmony_ci		switch (map->type) {
7062306a36Sopenharmony_ci		case _DRM_SHM:
7162306a36Sopenharmony_ci			if (map->flags != _DRM_CONTAINS_LOCK)
7262306a36Sopenharmony_ci				break;
7362306a36Sopenharmony_ci			return entry;
7462306a36Sopenharmony_ci		case _DRM_REGISTERS:
7562306a36Sopenharmony_ci		case _DRM_FRAME_BUFFER:
7662306a36Sopenharmony_ci			if ((entry->map->offset & 0xffffffff) ==
7762306a36Sopenharmony_ci			    (map->offset & 0xffffffff))
7862306a36Sopenharmony_ci				return entry;
7962306a36Sopenharmony_ci			break;
8062306a36Sopenharmony_ci		default: /* Make gcc happy */
8162306a36Sopenharmony_ci			break;
8262306a36Sopenharmony_ci		}
8362306a36Sopenharmony_ci		if (entry->map->offset == map->offset)
8462306a36Sopenharmony_ci			return entry;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return NULL;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic int drm_map_handle(struct drm_device *dev, struct drm_hash_item *hash,
9162306a36Sopenharmony_ci			  unsigned long user_token, int hashed_handle, int shm)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	int use_hashed_handle, shift;
9462306a36Sopenharmony_ci	unsigned long add;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#if (BITS_PER_LONG == 64)
9762306a36Sopenharmony_ci	use_hashed_handle = ((user_token & 0xFFFFFFFF00000000UL) || hashed_handle);
9862306a36Sopenharmony_ci#elif (BITS_PER_LONG == 32)
9962306a36Sopenharmony_ci	use_hashed_handle = hashed_handle;
10062306a36Sopenharmony_ci#else
10162306a36Sopenharmony_ci#error Unsupported long size. Neither 64 nor 32 bits.
10262306a36Sopenharmony_ci#endif
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (!use_hashed_handle) {
10562306a36Sopenharmony_ci		int ret;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		hash->key = user_token >> PAGE_SHIFT;
10862306a36Sopenharmony_ci		ret = drm_ht_insert_item(&dev->map_hash, hash);
10962306a36Sopenharmony_ci		if (ret != -EINVAL)
11062306a36Sopenharmony_ci			return ret;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	shift = 0;
11462306a36Sopenharmony_ci	add = DRM_MAP_HASH_OFFSET >> PAGE_SHIFT;
11562306a36Sopenharmony_ci	if (shm && (SHMLBA > PAGE_SIZE)) {
11662306a36Sopenharmony_ci		int bits = ilog2(SHMLBA >> PAGE_SHIFT) + 1;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci		/* For shared memory, we have to preserve the SHMLBA
11962306a36Sopenharmony_ci		 * bits of the eventual vma->vm_pgoff value during
12062306a36Sopenharmony_ci		 * mmap().  Otherwise we run into cache aliasing problems
12162306a36Sopenharmony_ci		 * on some platforms.  On these platforms, the pgoff of
12262306a36Sopenharmony_ci		 * a mmap() request is used to pick a suitable virtual
12362306a36Sopenharmony_ci		 * address for the mmap() region such that it will not
12462306a36Sopenharmony_ci		 * cause cache aliasing problems.
12562306a36Sopenharmony_ci		 *
12662306a36Sopenharmony_ci		 * Therefore, make sure the SHMLBA relevant bits of the
12762306a36Sopenharmony_ci		 * hash value we use are equal to those in the original
12862306a36Sopenharmony_ci		 * kernel virtual address.
12962306a36Sopenharmony_ci		 */
13062306a36Sopenharmony_ci		shift = bits;
13162306a36Sopenharmony_ci		add |= ((user_token >> PAGE_SHIFT) & ((1UL << bits) - 1UL));
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	return drm_ht_just_insert_please(&dev->map_hash, hash,
13562306a36Sopenharmony_ci					 user_token, 32 - PAGE_SHIFT - 3,
13662306a36Sopenharmony_ci					 shift, add);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/*
14062306a36Sopenharmony_ci * Core function to create a range of memory available for mapping by a
14162306a36Sopenharmony_ci * non-root process.
14262306a36Sopenharmony_ci *
14362306a36Sopenharmony_ci * Adjusts the memory offset to its absolute value according to the mapping
14462306a36Sopenharmony_ci * type.  Adds the map to the map list drm_device::maplist. Adds MTRR's where
14562306a36Sopenharmony_ci * applicable and if supported by the kernel.
14662306a36Sopenharmony_ci */
14762306a36Sopenharmony_cistatic int drm_addmap_core(struct drm_device *dev, resource_size_t offset,
14862306a36Sopenharmony_ci			   unsigned int size, enum drm_map_type type,
14962306a36Sopenharmony_ci			   enum drm_map_flags flags,
15062306a36Sopenharmony_ci			   struct drm_map_list **maplist)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	struct drm_local_map *map;
15362306a36Sopenharmony_ci	struct drm_map_list *list;
15462306a36Sopenharmony_ci	unsigned long user_token;
15562306a36Sopenharmony_ci	int ret;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	map = kmalloc(sizeof(*map), GFP_KERNEL);
15862306a36Sopenharmony_ci	if (!map)
15962306a36Sopenharmony_ci		return -ENOMEM;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	map->offset = offset;
16262306a36Sopenharmony_ci	map->size = size;
16362306a36Sopenharmony_ci	map->flags = flags;
16462306a36Sopenharmony_ci	map->type = type;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* Only allow shared memory to be removable since we only keep enough
16762306a36Sopenharmony_ci	 * book keeping information about shared memory to allow for removal
16862306a36Sopenharmony_ci	 * when processes fork.
16962306a36Sopenharmony_ci	 */
17062306a36Sopenharmony_ci	if ((map->flags & _DRM_REMOVABLE) && map->type != _DRM_SHM) {
17162306a36Sopenharmony_ci		kfree(map);
17262306a36Sopenharmony_ci		return -EINVAL;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci	DRM_DEBUG("offset = 0x%08llx, size = 0x%08lx, type = %d\n",
17562306a36Sopenharmony_ci		  (unsigned long long)map->offset, map->size, map->type);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/* page-align _DRM_SHM maps. They are allocated here so there is no security
17862306a36Sopenharmony_ci	 * hole created by that and it works around various broken drivers that use
17962306a36Sopenharmony_ci	 * a non-aligned quantity to map the SAREA. --BenH
18062306a36Sopenharmony_ci	 */
18162306a36Sopenharmony_ci	if (map->type == _DRM_SHM)
18262306a36Sopenharmony_ci		map->size = PAGE_ALIGN(map->size);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if ((map->offset & (~(resource_size_t)PAGE_MASK)) || (map->size & (~PAGE_MASK))) {
18562306a36Sopenharmony_ci		kfree(map);
18662306a36Sopenharmony_ci		return -EINVAL;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci	map->mtrr = -1;
18962306a36Sopenharmony_ci	map->handle = NULL;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	switch (map->type) {
19262306a36Sopenharmony_ci	case _DRM_REGISTERS:
19362306a36Sopenharmony_ci	case _DRM_FRAME_BUFFER:
19462306a36Sopenharmony_ci#if !defined(__sparc__) && !defined(__alpha__) && !defined(__ia64__) && !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__arm__)
19562306a36Sopenharmony_ci		if (map->offset + (map->size-1) < map->offset ||
19662306a36Sopenharmony_ci		    map->offset < virt_to_phys(high_memory)) {
19762306a36Sopenharmony_ci			kfree(map);
19862306a36Sopenharmony_ci			return -EINVAL;
19962306a36Sopenharmony_ci		}
20062306a36Sopenharmony_ci#endif
20162306a36Sopenharmony_ci		/* Some drivers preinitialize some maps, without the X Server
20262306a36Sopenharmony_ci		 * needing to be aware of it.  Therefore, we just return success
20362306a36Sopenharmony_ci		 * when the server tries to create a duplicate map.
20462306a36Sopenharmony_ci		 */
20562306a36Sopenharmony_ci		list = drm_find_matching_map(dev, map);
20662306a36Sopenharmony_ci		if (list != NULL) {
20762306a36Sopenharmony_ci			if (list->map->size != map->size) {
20862306a36Sopenharmony_ci				DRM_DEBUG("Matching maps of type %d with "
20962306a36Sopenharmony_ci					  "mismatched sizes, (%ld vs %ld)\n",
21062306a36Sopenharmony_ci					  map->type, map->size,
21162306a36Sopenharmony_ci					  list->map->size);
21262306a36Sopenharmony_ci				list->map->size = map->size;
21362306a36Sopenharmony_ci			}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci			kfree(map);
21662306a36Sopenharmony_ci			*maplist = list;
21762306a36Sopenharmony_ci			return 0;
21862306a36Sopenharmony_ci		}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		if (map->type == _DRM_FRAME_BUFFER ||
22162306a36Sopenharmony_ci		    (map->flags & _DRM_WRITE_COMBINING)) {
22262306a36Sopenharmony_ci			map->mtrr =
22362306a36Sopenharmony_ci				arch_phys_wc_add(map->offset, map->size);
22462306a36Sopenharmony_ci		}
22562306a36Sopenharmony_ci		if (map->type == _DRM_REGISTERS) {
22662306a36Sopenharmony_ci			if (map->flags & _DRM_WRITE_COMBINING)
22762306a36Sopenharmony_ci				map->handle = ioremap_wc(map->offset,
22862306a36Sopenharmony_ci							 map->size);
22962306a36Sopenharmony_ci			else
23062306a36Sopenharmony_ci				map->handle = ioremap(map->offset, map->size);
23162306a36Sopenharmony_ci			if (!map->handle) {
23262306a36Sopenharmony_ci				kfree(map);
23362306a36Sopenharmony_ci				return -ENOMEM;
23462306a36Sopenharmony_ci			}
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		break;
23862306a36Sopenharmony_ci	case _DRM_SHM:
23962306a36Sopenharmony_ci		list = drm_find_matching_map(dev, map);
24062306a36Sopenharmony_ci		if (list != NULL) {
24162306a36Sopenharmony_ci			if (list->map->size != map->size) {
24262306a36Sopenharmony_ci				DRM_DEBUG("Matching maps of type %d with "
24362306a36Sopenharmony_ci					  "mismatched sizes, (%ld vs %ld)\n",
24462306a36Sopenharmony_ci					  map->type, map->size, list->map->size);
24562306a36Sopenharmony_ci				list->map->size = map->size;
24662306a36Sopenharmony_ci			}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci			kfree(map);
24962306a36Sopenharmony_ci			*maplist = list;
25062306a36Sopenharmony_ci			return 0;
25162306a36Sopenharmony_ci		}
25262306a36Sopenharmony_ci		map->handle = vmalloc_user(map->size);
25362306a36Sopenharmony_ci		DRM_DEBUG("%lu %d %p\n",
25462306a36Sopenharmony_ci			  map->size, order_base_2(map->size), map->handle);
25562306a36Sopenharmony_ci		if (!map->handle) {
25662306a36Sopenharmony_ci			kfree(map);
25762306a36Sopenharmony_ci			return -ENOMEM;
25862306a36Sopenharmony_ci		}
25962306a36Sopenharmony_ci		map->offset = (unsigned long)map->handle;
26062306a36Sopenharmony_ci		if (map->flags & _DRM_CONTAINS_LOCK) {
26162306a36Sopenharmony_ci			/* Prevent a 2nd X Server from creating a 2nd lock */
26262306a36Sopenharmony_ci			if (dev->master->lock.hw_lock != NULL) {
26362306a36Sopenharmony_ci				vfree(map->handle);
26462306a36Sopenharmony_ci				kfree(map);
26562306a36Sopenharmony_ci				return -EBUSY;
26662306a36Sopenharmony_ci			}
26762306a36Sopenharmony_ci			dev->sigdata.lock = dev->master->lock.hw_lock = map->handle;	/* Pointer to lock */
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci		break;
27062306a36Sopenharmony_ci	case _DRM_AGP: {
27162306a36Sopenharmony_ci		struct drm_agp_mem *entry;
27262306a36Sopenharmony_ci		int valid = 0;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci		if (!dev->agp) {
27562306a36Sopenharmony_ci			kfree(map);
27662306a36Sopenharmony_ci			return -EINVAL;
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci#ifdef __alpha__
27962306a36Sopenharmony_ci		map->offset += dev->hose->mem_space->start;
28062306a36Sopenharmony_ci#endif
28162306a36Sopenharmony_ci		/* In some cases (i810 driver), user space may have already
28262306a36Sopenharmony_ci		 * added the AGP base itself, because dev->agp->base previously
28362306a36Sopenharmony_ci		 * only got set during AGP enable.  So, only add the base
28462306a36Sopenharmony_ci		 * address if the map's offset isn't already within the
28562306a36Sopenharmony_ci		 * aperture.
28662306a36Sopenharmony_ci		 */
28762306a36Sopenharmony_ci		if (map->offset < dev->agp->base ||
28862306a36Sopenharmony_ci		    map->offset > dev->agp->base +
28962306a36Sopenharmony_ci		    dev->agp->agp_info.aper_size * 1024 * 1024 - 1) {
29062306a36Sopenharmony_ci			map->offset += dev->agp->base;
29162306a36Sopenharmony_ci		}
29262306a36Sopenharmony_ci		map->mtrr = dev->agp->agp_mtrr;	/* for getmap */
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		/* This assumes the DRM is in total control of AGP space.
29562306a36Sopenharmony_ci		 * It's not always the case as AGP can be in the control
29662306a36Sopenharmony_ci		 * of user space (i.e. i810 driver). So this loop will get
29762306a36Sopenharmony_ci		 * skipped and we double check that dev->agp->memory is
29862306a36Sopenharmony_ci		 * actually set as well as being invalid before EPERM'ing
29962306a36Sopenharmony_ci		 */
30062306a36Sopenharmony_ci		list_for_each_entry(entry, &dev->agp->memory, head) {
30162306a36Sopenharmony_ci			if ((map->offset >= entry->bound) &&
30262306a36Sopenharmony_ci			    (map->offset + map->size <= entry->bound + entry->pages * PAGE_SIZE)) {
30362306a36Sopenharmony_ci				valid = 1;
30462306a36Sopenharmony_ci				break;
30562306a36Sopenharmony_ci			}
30662306a36Sopenharmony_ci		}
30762306a36Sopenharmony_ci		if (!list_empty(&dev->agp->memory) && !valid) {
30862306a36Sopenharmony_ci			kfree(map);
30962306a36Sopenharmony_ci			return -EPERM;
31062306a36Sopenharmony_ci		}
31162306a36Sopenharmony_ci		DRM_DEBUG("AGP offset = 0x%08llx, size = 0x%08lx\n",
31262306a36Sopenharmony_ci			  (unsigned long long)map->offset, map->size);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci		break;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci	case _DRM_SCATTER_GATHER:
31762306a36Sopenharmony_ci		if (!dev->sg) {
31862306a36Sopenharmony_ci			kfree(map);
31962306a36Sopenharmony_ci			return -EINVAL;
32062306a36Sopenharmony_ci		}
32162306a36Sopenharmony_ci		map->offset += (unsigned long)dev->sg->virtual;
32262306a36Sopenharmony_ci		break;
32362306a36Sopenharmony_ci	case _DRM_CONSISTENT:
32462306a36Sopenharmony_ci		/* dma_addr_t is 64bit on i386 with CONFIG_HIGHMEM64G,
32562306a36Sopenharmony_ci		 * As we're limiting the address to 2^32-1 (or less),
32662306a36Sopenharmony_ci		 * casting it down to 32 bits is no problem, but we
32762306a36Sopenharmony_ci		 * need to point to a 64bit variable first.
32862306a36Sopenharmony_ci		 */
32962306a36Sopenharmony_ci		map->handle = dma_alloc_coherent(dev->dev,
33062306a36Sopenharmony_ci						 map->size,
33162306a36Sopenharmony_ci						 &map->offset,
33262306a36Sopenharmony_ci						 GFP_KERNEL);
33362306a36Sopenharmony_ci		if (!map->handle) {
33462306a36Sopenharmony_ci			kfree(map);
33562306a36Sopenharmony_ci			return -ENOMEM;
33662306a36Sopenharmony_ci		}
33762306a36Sopenharmony_ci		break;
33862306a36Sopenharmony_ci	default:
33962306a36Sopenharmony_ci		kfree(map);
34062306a36Sopenharmony_ci		return -EINVAL;
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	list = kzalloc(sizeof(*list), GFP_KERNEL);
34462306a36Sopenharmony_ci	if (!list) {
34562306a36Sopenharmony_ci		if (map->type == _DRM_REGISTERS)
34662306a36Sopenharmony_ci			iounmap(map->handle);
34762306a36Sopenharmony_ci		kfree(map);
34862306a36Sopenharmony_ci		return -EINVAL;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci	list->map = map;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	mutex_lock(&dev->struct_mutex);
35362306a36Sopenharmony_ci	list_add(&list->head, &dev->maplist);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/* Assign a 32-bit handle */
35662306a36Sopenharmony_ci	/* We do it here so that dev->struct_mutex protects the increment */
35762306a36Sopenharmony_ci	user_token = (map->type == _DRM_SHM) ? (unsigned long)map->handle :
35862306a36Sopenharmony_ci		map->offset;
35962306a36Sopenharmony_ci	ret = drm_map_handle(dev, &list->hash, user_token, 0,
36062306a36Sopenharmony_ci			     (map->type == _DRM_SHM));
36162306a36Sopenharmony_ci	if (ret) {
36262306a36Sopenharmony_ci		if (map->type == _DRM_REGISTERS)
36362306a36Sopenharmony_ci			iounmap(map->handle);
36462306a36Sopenharmony_ci		kfree(map);
36562306a36Sopenharmony_ci		kfree(list);
36662306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
36762306a36Sopenharmony_ci		return ret;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	list->user_token = list->hash.key << PAGE_SHIFT;
37162306a36Sopenharmony_ci	mutex_unlock(&dev->struct_mutex);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (!(map->flags & _DRM_DRIVER))
37462306a36Sopenharmony_ci		list->master = dev->master;
37562306a36Sopenharmony_ci	*maplist = list;
37662306a36Sopenharmony_ci	return 0;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ciint drm_legacy_addmap(struct drm_device *dev, resource_size_t offset,
38062306a36Sopenharmony_ci		      unsigned int size, enum drm_map_type type,
38162306a36Sopenharmony_ci		      enum drm_map_flags flags, struct drm_local_map **map_ptr)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct drm_map_list *list;
38462306a36Sopenharmony_ci	int rc;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	rc = drm_addmap_core(dev, offset, size, type, flags, &list);
38762306a36Sopenharmony_ci	if (!rc)
38862306a36Sopenharmony_ci		*map_ptr = list->map;
38962306a36Sopenharmony_ci	return rc;
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_legacy_addmap);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistruct drm_local_map *drm_legacy_findmap(struct drm_device *dev,
39462306a36Sopenharmony_ci					 unsigned int token)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	struct drm_map_list *_entry;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	list_for_each_entry(_entry, &dev->maplist, head)
39962306a36Sopenharmony_ci		if (_entry->user_token == token)
40062306a36Sopenharmony_ci			return _entry->map;
40162306a36Sopenharmony_ci	return NULL;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_legacy_findmap);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci/*
40662306a36Sopenharmony_ci * Ioctl to specify a range of memory that is available for mapping by a
40762306a36Sopenharmony_ci * non-root process.
40862306a36Sopenharmony_ci *
40962306a36Sopenharmony_ci * \param inode device inode.
41062306a36Sopenharmony_ci * \param file_priv DRM file private.
41162306a36Sopenharmony_ci * \param cmd command.
41262306a36Sopenharmony_ci * \param arg pointer to a drm_map structure.
41362306a36Sopenharmony_ci * \return zero on success or a negative value on error.
41462306a36Sopenharmony_ci *
41562306a36Sopenharmony_ci */
41662306a36Sopenharmony_ciint drm_legacy_addmap_ioctl(struct drm_device *dev, void *data,
41762306a36Sopenharmony_ci			    struct drm_file *file_priv)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	struct drm_map *map = data;
42062306a36Sopenharmony_ci	struct drm_map_list *maplist;
42162306a36Sopenharmony_ci	int err;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (!(capable(CAP_SYS_ADMIN) || map->type == _DRM_AGP || map->type == _DRM_SHM))
42462306a36Sopenharmony_ci		return -EPERM;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
42762306a36Sopenharmony_ci		return -EOPNOTSUPP;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	err = drm_addmap_core(dev, map->offset, map->size, map->type,
43062306a36Sopenharmony_ci			      map->flags, &maplist);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (err)
43362306a36Sopenharmony_ci		return err;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	/* avoid a warning on 64-bit, this casting isn't very nice, but the API is set so too late */
43662306a36Sopenharmony_ci	map->handle = (void *)(unsigned long)maplist->user_token;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	/*
43962306a36Sopenharmony_ci	 * It appears that there are no users of this value whatsoever --
44062306a36Sopenharmony_ci	 * drmAddMap just discards it.  Let's not encourage its use.
44162306a36Sopenharmony_ci	 * (Keeping drm_addmap_core's returned mtrr value would be wrong --
44262306a36Sopenharmony_ci	 *  it's not a real mtrr index anymore.)
44362306a36Sopenharmony_ci	 */
44462306a36Sopenharmony_ci	map->mtrr = -1;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	return 0;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci/*
45062306a36Sopenharmony_ci * Get a mapping information.
45162306a36Sopenharmony_ci *
45262306a36Sopenharmony_ci * \param inode device inode.
45362306a36Sopenharmony_ci * \param file_priv DRM file private.
45462306a36Sopenharmony_ci * \param cmd command.
45562306a36Sopenharmony_ci * \param arg user argument, pointing to a drm_map structure.
45662306a36Sopenharmony_ci *
45762306a36Sopenharmony_ci * \return zero on success or a negative number on failure.
45862306a36Sopenharmony_ci *
45962306a36Sopenharmony_ci * Searches for the mapping with the specified offset and copies its information
46062306a36Sopenharmony_ci * into userspace
46162306a36Sopenharmony_ci */
46262306a36Sopenharmony_ciint drm_legacy_getmap_ioctl(struct drm_device *dev, void *data,
46362306a36Sopenharmony_ci			    struct drm_file *file_priv)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	struct drm_map *map = data;
46662306a36Sopenharmony_ci	struct drm_map_list *r_list = NULL;
46762306a36Sopenharmony_ci	struct list_head *list;
46862306a36Sopenharmony_ci	int idx;
46962306a36Sopenharmony_ci	int i;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
47262306a36Sopenharmony_ci		return -EOPNOTSUPP;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	idx = map->offset;
47562306a36Sopenharmony_ci	if (idx < 0)
47662306a36Sopenharmony_ci		return -EINVAL;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	i = 0;
47962306a36Sopenharmony_ci	mutex_lock(&dev->struct_mutex);
48062306a36Sopenharmony_ci	list_for_each(list, &dev->maplist) {
48162306a36Sopenharmony_ci		if (i == idx) {
48262306a36Sopenharmony_ci			r_list = list_entry(list, struct drm_map_list, head);
48362306a36Sopenharmony_ci			break;
48462306a36Sopenharmony_ci		}
48562306a36Sopenharmony_ci		i++;
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci	if (!r_list || !r_list->map) {
48862306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
48962306a36Sopenharmony_ci		return -EINVAL;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	map->offset = r_list->map->offset;
49362306a36Sopenharmony_ci	map->size = r_list->map->size;
49462306a36Sopenharmony_ci	map->type = r_list->map->type;
49562306a36Sopenharmony_ci	map->flags = r_list->map->flags;
49662306a36Sopenharmony_ci	map->handle = (void *)(unsigned long) r_list->user_token;
49762306a36Sopenharmony_ci	map->mtrr = arch_phys_wc_index(r_list->map->mtrr);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	mutex_unlock(&dev->struct_mutex);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	return 0;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci/*
50562306a36Sopenharmony_ci * Remove a map private from list and deallocate resources if the mapping
50662306a36Sopenharmony_ci * isn't in use.
50762306a36Sopenharmony_ci *
50862306a36Sopenharmony_ci * Searches the map on drm_device::maplist, removes it from the list, see if
50962306a36Sopenharmony_ci * it's being used, and free any associated resource (such as MTRR's) if it's not
51062306a36Sopenharmony_ci * being on use.
51162306a36Sopenharmony_ci *
51262306a36Sopenharmony_ci * \sa drm_legacy_addmap
51362306a36Sopenharmony_ci */
51462306a36Sopenharmony_ciint drm_legacy_rmmap_locked(struct drm_device *dev, struct drm_local_map *map)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	struct drm_map_list *r_list = NULL, *list_t;
51762306a36Sopenharmony_ci	int found = 0;
51862306a36Sopenharmony_ci	struct drm_master *master;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	/* Find the list entry for the map and remove it */
52162306a36Sopenharmony_ci	list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) {
52262306a36Sopenharmony_ci		if (r_list->map == map) {
52362306a36Sopenharmony_ci			master = r_list->master;
52462306a36Sopenharmony_ci			list_del(&r_list->head);
52562306a36Sopenharmony_ci			drm_ht_remove_key(&dev->map_hash,
52662306a36Sopenharmony_ci					  r_list->user_token >> PAGE_SHIFT);
52762306a36Sopenharmony_ci			kfree(r_list);
52862306a36Sopenharmony_ci			found = 1;
52962306a36Sopenharmony_ci			break;
53062306a36Sopenharmony_ci		}
53162306a36Sopenharmony_ci	}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (!found)
53462306a36Sopenharmony_ci		return -EINVAL;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	switch (map->type) {
53762306a36Sopenharmony_ci	case _DRM_REGISTERS:
53862306a36Sopenharmony_ci		iounmap(map->handle);
53962306a36Sopenharmony_ci		fallthrough;
54062306a36Sopenharmony_ci	case _DRM_FRAME_BUFFER:
54162306a36Sopenharmony_ci		arch_phys_wc_del(map->mtrr);
54262306a36Sopenharmony_ci		break;
54362306a36Sopenharmony_ci	case _DRM_SHM:
54462306a36Sopenharmony_ci		vfree(map->handle);
54562306a36Sopenharmony_ci		if (master) {
54662306a36Sopenharmony_ci			if (dev->sigdata.lock == master->lock.hw_lock)
54762306a36Sopenharmony_ci				dev->sigdata.lock = NULL;
54862306a36Sopenharmony_ci			master->lock.hw_lock = NULL;   /* SHM removed */
54962306a36Sopenharmony_ci			master->lock.file_priv = NULL;
55062306a36Sopenharmony_ci			wake_up_interruptible_all(&master->lock.lock_queue);
55162306a36Sopenharmony_ci		}
55262306a36Sopenharmony_ci		break;
55362306a36Sopenharmony_ci	case _DRM_AGP:
55462306a36Sopenharmony_ci	case _DRM_SCATTER_GATHER:
55562306a36Sopenharmony_ci		break;
55662306a36Sopenharmony_ci	case _DRM_CONSISTENT:
55762306a36Sopenharmony_ci		dma_free_coherent(dev->dev,
55862306a36Sopenharmony_ci				  map->size,
55962306a36Sopenharmony_ci				  map->handle,
56062306a36Sopenharmony_ci				  map->offset);
56162306a36Sopenharmony_ci		break;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci	kfree(map);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	return 0;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_legacy_rmmap_locked);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_civoid drm_legacy_rmmap(struct drm_device *dev, struct drm_local_map *map)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
57262306a36Sopenharmony_ci		return;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	mutex_lock(&dev->struct_mutex);
57562306a36Sopenharmony_ci	drm_legacy_rmmap_locked(dev, map);
57662306a36Sopenharmony_ci	mutex_unlock(&dev->struct_mutex);
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_legacy_rmmap);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_civoid drm_legacy_master_rmmaps(struct drm_device *dev, struct drm_master *master)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct drm_map_list *r_list, *list_temp;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
58562306a36Sopenharmony_ci		return;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	mutex_lock(&dev->struct_mutex);
58862306a36Sopenharmony_ci	list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) {
58962306a36Sopenharmony_ci		if (r_list->master == master) {
59062306a36Sopenharmony_ci			drm_legacy_rmmap_locked(dev, r_list->map);
59162306a36Sopenharmony_ci			r_list = NULL;
59262306a36Sopenharmony_ci		}
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci	mutex_unlock(&dev->struct_mutex);
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_civoid drm_legacy_rmmaps(struct drm_device *dev)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	struct drm_map_list *r_list, *list_temp;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
60262306a36Sopenharmony_ci		drm_legacy_rmmap(dev, r_list->map);
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci/* The rmmap ioctl appears to be unnecessary.  All mappings are torn down on
60662306a36Sopenharmony_ci * the last close of the device, and this is necessary for cleanup when things
60762306a36Sopenharmony_ci * exit uncleanly.  Therefore, having userland manually remove mappings seems
60862306a36Sopenharmony_ci * like a pointless exercise since they're going away anyway.
60962306a36Sopenharmony_ci *
61062306a36Sopenharmony_ci * One use case might be after addmap is allowed for normal users for SHM and
61162306a36Sopenharmony_ci * gets used by drivers that the server doesn't need to care about.  This seems
61262306a36Sopenharmony_ci * unlikely.
61362306a36Sopenharmony_ci *
61462306a36Sopenharmony_ci * \param inode device inode.
61562306a36Sopenharmony_ci * \param file_priv DRM file private.
61662306a36Sopenharmony_ci * \param cmd command.
61762306a36Sopenharmony_ci * \param arg pointer to a struct drm_map structure.
61862306a36Sopenharmony_ci * \return zero on success or a negative value on error.
61962306a36Sopenharmony_ci */
62062306a36Sopenharmony_ciint drm_legacy_rmmap_ioctl(struct drm_device *dev, void *data,
62162306a36Sopenharmony_ci			   struct drm_file *file_priv)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	struct drm_map *request = data;
62462306a36Sopenharmony_ci	struct drm_local_map *map = NULL;
62562306a36Sopenharmony_ci	struct drm_map_list *r_list;
62662306a36Sopenharmony_ci	int ret;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
62962306a36Sopenharmony_ci		return -EOPNOTSUPP;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	mutex_lock(&dev->struct_mutex);
63262306a36Sopenharmony_ci	list_for_each_entry(r_list, &dev->maplist, head) {
63362306a36Sopenharmony_ci		if (r_list->map &&
63462306a36Sopenharmony_ci		    r_list->user_token == (unsigned long)request->handle &&
63562306a36Sopenharmony_ci		    r_list->map->flags & _DRM_REMOVABLE) {
63662306a36Sopenharmony_ci			map = r_list->map;
63762306a36Sopenharmony_ci			break;
63862306a36Sopenharmony_ci		}
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	/* List has wrapped around to the head pointer, or it's empty we didn't
64262306a36Sopenharmony_ci	 * find anything.
64362306a36Sopenharmony_ci	 */
64462306a36Sopenharmony_ci	if (list_empty(&dev->maplist) || !map) {
64562306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
64662306a36Sopenharmony_ci		return -EINVAL;
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	/* Register and framebuffer maps are permanent */
65062306a36Sopenharmony_ci	if ((map->type == _DRM_REGISTERS) || (map->type == _DRM_FRAME_BUFFER)) {
65162306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
65262306a36Sopenharmony_ci		return 0;
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	ret = drm_legacy_rmmap_locked(dev, map);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	mutex_unlock(&dev->struct_mutex);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	return ret;
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci/*
66362306a36Sopenharmony_ci * Cleanup after an error on one of the addbufs() functions.
66462306a36Sopenharmony_ci *
66562306a36Sopenharmony_ci * \param dev DRM device.
66662306a36Sopenharmony_ci * \param entry buffer entry where the error occurred.
66762306a36Sopenharmony_ci *
66862306a36Sopenharmony_ci * Frees any pages and buffers associated with the given entry.
66962306a36Sopenharmony_ci */
67062306a36Sopenharmony_cistatic void drm_cleanup_buf_error(struct drm_device *dev,
67162306a36Sopenharmony_ci				  struct drm_buf_entry *entry)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	drm_dma_handle_t *dmah;
67462306a36Sopenharmony_ci	int i;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	if (entry->seg_count) {
67762306a36Sopenharmony_ci		for (i = 0; i < entry->seg_count; i++) {
67862306a36Sopenharmony_ci			if (entry->seglist[i]) {
67962306a36Sopenharmony_ci				dmah = entry->seglist[i];
68062306a36Sopenharmony_ci				dma_free_coherent(dev->dev,
68162306a36Sopenharmony_ci						  dmah->size,
68262306a36Sopenharmony_ci						  dmah->vaddr,
68362306a36Sopenharmony_ci						  dmah->busaddr);
68462306a36Sopenharmony_ci				kfree(dmah);
68562306a36Sopenharmony_ci			}
68662306a36Sopenharmony_ci		}
68762306a36Sopenharmony_ci		kfree(entry->seglist);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci		entry->seg_count = 0;
69062306a36Sopenharmony_ci	}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	if (entry->buf_count) {
69362306a36Sopenharmony_ci		for (i = 0; i < entry->buf_count; i++) {
69462306a36Sopenharmony_ci			kfree(entry->buflist[i].dev_private);
69562306a36Sopenharmony_ci		}
69662306a36Sopenharmony_ci		kfree(entry->buflist);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci		entry->buf_count = 0;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_AGP)
70362306a36Sopenharmony_ci/*
70462306a36Sopenharmony_ci * Add AGP buffers for DMA transfers.
70562306a36Sopenharmony_ci *
70662306a36Sopenharmony_ci * \param dev struct drm_device to which the buffers are to be added.
70762306a36Sopenharmony_ci * \param request pointer to a struct drm_buf_desc describing the request.
70862306a36Sopenharmony_ci * \return zero on success or a negative number on failure.
70962306a36Sopenharmony_ci *
71062306a36Sopenharmony_ci * After some sanity checks creates a drm_buf structure for each buffer and
71162306a36Sopenharmony_ci * reallocates the buffer list of the same size order to accommodate the new
71262306a36Sopenharmony_ci * buffers.
71362306a36Sopenharmony_ci */
71462306a36Sopenharmony_ciint drm_legacy_addbufs_agp(struct drm_device *dev,
71562306a36Sopenharmony_ci			   struct drm_buf_desc *request)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	struct drm_device_dma *dma = dev->dma;
71862306a36Sopenharmony_ci	struct drm_buf_entry *entry;
71962306a36Sopenharmony_ci	struct drm_agp_mem *agp_entry;
72062306a36Sopenharmony_ci	struct drm_buf *buf;
72162306a36Sopenharmony_ci	unsigned long offset;
72262306a36Sopenharmony_ci	unsigned long agp_offset;
72362306a36Sopenharmony_ci	int count;
72462306a36Sopenharmony_ci	int order;
72562306a36Sopenharmony_ci	int size;
72662306a36Sopenharmony_ci	int alignment;
72762306a36Sopenharmony_ci	int page_order;
72862306a36Sopenharmony_ci	int total;
72962306a36Sopenharmony_ci	int byte_count;
73062306a36Sopenharmony_ci	int i, valid;
73162306a36Sopenharmony_ci	struct drm_buf **temp_buflist;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	if (!dma)
73462306a36Sopenharmony_ci		return -EINVAL;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	count = request->count;
73762306a36Sopenharmony_ci	order = order_base_2(request->size);
73862306a36Sopenharmony_ci	size = 1 << order;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	alignment = (request->flags & _DRM_PAGE_ALIGN)
74162306a36Sopenharmony_ci	    ? PAGE_ALIGN(size) : size;
74262306a36Sopenharmony_ci	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
74362306a36Sopenharmony_ci	total = PAGE_SIZE << page_order;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	byte_count = 0;
74662306a36Sopenharmony_ci	agp_offset = dev->agp->base + request->agp_start;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	DRM_DEBUG("count:      %d\n", count);
74962306a36Sopenharmony_ci	DRM_DEBUG("order:      %d\n", order);
75062306a36Sopenharmony_ci	DRM_DEBUG("size:       %d\n", size);
75162306a36Sopenharmony_ci	DRM_DEBUG("agp_offset: %lx\n", agp_offset);
75262306a36Sopenharmony_ci	DRM_DEBUG("alignment:  %d\n", alignment);
75362306a36Sopenharmony_ci	DRM_DEBUG("page_order: %d\n", page_order);
75462306a36Sopenharmony_ci	DRM_DEBUG("total:      %d\n", total);
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
75762306a36Sopenharmony_ci		return -EINVAL;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	/* Make sure buffers are located in AGP memory that we own */
76062306a36Sopenharmony_ci	valid = 0;
76162306a36Sopenharmony_ci	list_for_each_entry(agp_entry, &dev->agp->memory, head) {
76262306a36Sopenharmony_ci		if ((agp_offset >= agp_entry->bound) &&
76362306a36Sopenharmony_ci		    (agp_offset + total * count <= agp_entry->bound + agp_entry->pages * PAGE_SIZE)) {
76462306a36Sopenharmony_ci			valid = 1;
76562306a36Sopenharmony_ci			break;
76662306a36Sopenharmony_ci		}
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci	if (!list_empty(&dev->agp->memory) && !valid) {
76962306a36Sopenharmony_ci		DRM_DEBUG("zone invalid\n");
77062306a36Sopenharmony_ci		return -EINVAL;
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci	spin_lock(&dev->buf_lock);
77362306a36Sopenharmony_ci	if (dev->buf_use) {
77462306a36Sopenharmony_ci		spin_unlock(&dev->buf_lock);
77562306a36Sopenharmony_ci		return -EBUSY;
77662306a36Sopenharmony_ci	}
77762306a36Sopenharmony_ci	atomic_inc(&dev->buf_alloc);
77862306a36Sopenharmony_ci	spin_unlock(&dev->buf_lock);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	mutex_lock(&dev->struct_mutex);
78162306a36Sopenharmony_ci	entry = &dma->bufs[order];
78262306a36Sopenharmony_ci	if (entry->buf_count) {
78362306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
78462306a36Sopenharmony_ci		atomic_dec(&dev->buf_alloc);
78562306a36Sopenharmony_ci		return -ENOMEM;	/* May only call once for each order */
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	if (count < 0 || count > 4096) {
78962306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
79062306a36Sopenharmony_ci		atomic_dec(&dev->buf_alloc);
79162306a36Sopenharmony_ci		return -EINVAL;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	entry->buflist = kcalloc(count, sizeof(*entry->buflist), GFP_KERNEL);
79562306a36Sopenharmony_ci	if (!entry->buflist) {
79662306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
79762306a36Sopenharmony_ci		atomic_dec(&dev->buf_alloc);
79862306a36Sopenharmony_ci		return -ENOMEM;
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	entry->buf_size = size;
80262306a36Sopenharmony_ci	entry->page_order = page_order;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	offset = 0;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	while (entry->buf_count < count) {
80762306a36Sopenharmony_ci		buf = &entry->buflist[entry->buf_count];
80862306a36Sopenharmony_ci		buf->idx = dma->buf_count + entry->buf_count;
80962306a36Sopenharmony_ci		buf->total = alignment;
81062306a36Sopenharmony_ci		buf->order = order;
81162306a36Sopenharmony_ci		buf->used = 0;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci		buf->offset = (dma->byte_count + offset);
81462306a36Sopenharmony_ci		buf->bus_address = agp_offset + offset;
81562306a36Sopenharmony_ci		buf->address = (void *)(agp_offset + offset);
81662306a36Sopenharmony_ci		buf->next = NULL;
81762306a36Sopenharmony_ci		buf->waiting = 0;
81862306a36Sopenharmony_ci		buf->pending = 0;
81962306a36Sopenharmony_ci		buf->file_priv = NULL;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci		buf->dev_priv_size = dev->driver->dev_priv_size;
82262306a36Sopenharmony_ci		buf->dev_private = kzalloc(buf->dev_priv_size, GFP_KERNEL);
82362306a36Sopenharmony_ci		if (!buf->dev_private) {
82462306a36Sopenharmony_ci			/* Set count correctly so we free the proper amount. */
82562306a36Sopenharmony_ci			entry->buf_count = count;
82662306a36Sopenharmony_ci			drm_cleanup_buf_error(dev, entry);
82762306a36Sopenharmony_ci			mutex_unlock(&dev->struct_mutex);
82862306a36Sopenharmony_ci			atomic_dec(&dev->buf_alloc);
82962306a36Sopenharmony_ci			return -ENOMEM;
83062306a36Sopenharmony_ci		}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci		DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci		offset += alignment;
83562306a36Sopenharmony_ci		entry->buf_count++;
83662306a36Sopenharmony_ci		byte_count += PAGE_SIZE << page_order;
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	DRM_DEBUG("byte_count: %d\n", byte_count);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	temp_buflist = krealloc(dma->buflist,
84262306a36Sopenharmony_ci				(dma->buf_count + entry->buf_count) *
84362306a36Sopenharmony_ci				sizeof(*dma->buflist), GFP_KERNEL);
84462306a36Sopenharmony_ci	if (!temp_buflist) {
84562306a36Sopenharmony_ci		/* Free the entry because it isn't valid */
84662306a36Sopenharmony_ci		drm_cleanup_buf_error(dev, entry);
84762306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
84862306a36Sopenharmony_ci		atomic_dec(&dev->buf_alloc);
84962306a36Sopenharmony_ci		return -ENOMEM;
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci	dma->buflist = temp_buflist;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	for (i = 0; i < entry->buf_count; i++) {
85462306a36Sopenharmony_ci		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
85562306a36Sopenharmony_ci	}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	dma->buf_count += entry->buf_count;
85862306a36Sopenharmony_ci	dma->seg_count += entry->seg_count;
85962306a36Sopenharmony_ci	dma->page_count += byte_count >> PAGE_SHIFT;
86062306a36Sopenharmony_ci	dma->byte_count += byte_count;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
86362306a36Sopenharmony_ci	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	mutex_unlock(&dev->struct_mutex);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	request->count = entry->buf_count;
86862306a36Sopenharmony_ci	request->size = size;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	dma->flags = _DRM_DMA_USE_AGP;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	atomic_dec(&dev->buf_alloc);
87362306a36Sopenharmony_ci	return 0;
87462306a36Sopenharmony_ci}
87562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_legacy_addbufs_agp);
87662306a36Sopenharmony_ci#endif /* CONFIG_AGP */
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ciint drm_legacy_addbufs_pci(struct drm_device *dev,
87962306a36Sopenharmony_ci			   struct drm_buf_desc *request)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	struct drm_device_dma *dma = dev->dma;
88262306a36Sopenharmony_ci	int count;
88362306a36Sopenharmony_ci	int order;
88462306a36Sopenharmony_ci	int size;
88562306a36Sopenharmony_ci	int total;
88662306a36Sopenharmony_ci	int page_order;
88762306a36Sopenharmony_ci	struct drm_buf_entry *entry;
88862306a36Sopenharmony_ci	drm_dma_handle_t *dmah;
88962306a36Sopenharmony_ci	struct drm_buf *buf;
89062306a36Sopenharmony_ci	int alignment;
89162306a36Sopenharmony_ci	unsigned long offset;
89262306a36Sopenharmony_ci	int i;
89362306a36Sopenharmony_ci	int byte_count;
89462306a36Sopenharmony_ci	int page_count;
89562306a36Sopenharmony_ci	unsigned long *temp_pagelist;
89662306a36Sopenharmony_ci	struct drm_buf **temp_buflist;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_PCI_DMA))
89962306a36Sopenharmony_ci		return -EOPNOTSUPP;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	if (!dma)
90262306a36Sopenharmony_ci		return -EINVAL;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
90562306a36Sopenharmony_ci		return -EPERM;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	count = request->count;
90862306a36Sopenharmony_ci	order = order_base_2(request->size);
90962306a36Sopenharmony_ci	size = 1 << order;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	DRM_DEBUG("count=%d, size=%d (%d), order=%d\n",
91262306a36Sopenharmony_ci		  request->count, request->size, size, order);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
91562306a36Sopenharmony_ci		return -EINVAL;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	alignment = (request->flags & _DRM_PAGE_ALIGN)
91862306a36Sopenharmony_ci	    ? PAGE_ALIGN(size) : size;
91962306a36Sopenharmony_ci	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
92062306a36Sopenharmony_ci	total = PAGE_SIZE << page_order;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	spin_lock(&dev->buf_lock);
92362306a36Sopenharmony_ci	if (dev->buf_use) {
92462306a36Sopenharmony_ci		spin_unlock(&dev->buf_lock);
92562306a36Sopenharmony_ci		return -EBUSY;
92662306a36Sopenharmony_ci	}
92762306a36Sopenharmony_ci	atomic_inc(&dev->buf_alloc);
92862306a36Sopenharmony_ci	spin_unlock(&dev->buf_lock);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	mutex_lock(&dev->struct_mutex);
93162306a36Sopenharmony_ci	entry = &dma->bufs[order];
93262306a36Sopenharmony_ci	if (entry->buf_count) {
93362306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
93462306a36Sopenharmony_ci		atomic_dec(&dev->buf_alloc);
93562306a36Sopenharmony_ci		return -ENOMEM;	/* May only call once for each order */
93662306a36Sopenharmony_ci	}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	if (count < 0 || count > 4096) {
93962306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
94062306a36Sopenharmony_ci		atomic_dec(&dev->buf_alloc);
94162306a36Sopenharmony_ci		return -EINVAL;
94262306a36Sopenharmony_ci	}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	entry->buflist = kcalloc(count, sizeof(*entry->buflist), GFP_KERNEL);
94562306a36Sopenharmony_ci	if (!entry->buflist) {
94662306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
94762306a36Sopenharmony_ci		atomic_dec(&dev->buf_alloc);
94862306a36Sopenharmony_ci		return -ENOMEM;
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	entry->seglist = kcalloc(count, sizeof(*entry->seglist), GFP_KERNEL);
95262306a36Sopenharmony_ci	if (!entry->seglist) {
95362306a36Sopenharmony_ci		kfree(entry->buflist);
95462306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
95562306a36Sopenharmony_ci		atomic_dec(&dev->buf_alloc);
95662306a36Sopenharmony_ci		return -ENOMEM;
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	/* Keep the original pagelist until we know all the allocations
96062306a36Sopenharmony_ci	 * have succeeded
96162306a36Sopenharmony_ci	 */
96262306a36Sopenharmony_ci	temp_pagelist = kmalloc_array(dma->page_count + (count << page_order),
96362306a36Sopenharmony_ci				      sizeof(*dma->pagelist),
96462306a36Sopenharmony_ci				      GFP_KERNEL);
96562306a36Sopenharmony_ci	if (!temp_pagelist) {
96662306a36Sopenharmony_ci		kfree(entry->buflist);
96762306a36Sopenharmony_ci		kfree(entry->seglist);
96862306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
96962306a36Sopenharmony_ci		atomic_dec(&dev->buf_alloc);
97062306a36Sopenharmony_ci		return -ENOMEM;
97162306a36Sopenharmony_ci	}
97262306a36Sopenharmony_ci	memcpy(temp_pagelist,
97362306a36Sopenharmony_ci	       dma->pagelist, dma->page_count * sizeof(*dma->pagelist));
97462306a36Sopenharmony_ci	DRM_DEBUG("pagelist: %d entries\n",
97562306a36Sopenharmony_ci		  dma->page_count + (count << page_order));
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	entry->buf_size = size;
97862306a36Sopenharmony_ci	entry->page_order = page_order;
97962306a36Sopenharmony_ci	byte_count = 0;
98062306a36Sopenharmony_ci	page_count = 0;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	while (entry->buf_count < count) {
98362306a36Sopenharmony_ci		dmah = kmalloc(sizeof(drm_dma_handle_t), GFP_KERNEL);
98462306a36Sopenharmony_ci		if (!dmah) {
98562306a36Sopenharmony_ci			/* Set count correctly so we free the proper amount. */
98662306a36Sopenharmony_ci			entry->buf_count = count;
98762306a36Sopenharmony_ci			entry->seg_count = count;
98862306a36Sopenharmony_ci			drm_cleanup_buf_error(dev, entry);
98962306a36Sopenharmony_ci			kfree(temp_pagelist);
99062306a36Sopenharmony_ci			mutex_unlock(&dev->struct_mutex);
99162306a36Sopenharmony_ci			atomic_dec(&dev->buf_alloc);
99262306a36Sopenharmony_ci			return -ENOMEM;
99362306a36Sopenharmony_ci		}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci		dmah->size = total;
99662306a36Sopenharmony_ci		dmah->vaddr = dma_alloc_coherent(dev->dev,
99762306a36Sopenharmony_ci						 dmah->size,
99862306a36Sopenharmony_ci						 &dmah->busaddr,
99962306a36Sopenharmony_ci						 GFP_KERNEL);
100062306a36Sopenharmony_ci		if (!dmah->vaddr) {
100162306a36Sopenharmony_ci			kfree(dmah);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci			/* Set count correctly so we free the proper amount. */
100462306a36Sopenharmony_ci			entry->buf_count = count;
100562306a36Sopenharmony_ci			entry->seg_count = count;
100662306a36Sopenharmony_ci			drm_cleanup_buf_error(dev, entry);
100762306a36Sopenharmony_ci			kfree(temp_pagelist);
100862306a36Sopenharmony_ci			mutex_unlock(&dev->struct_mutex);
100962306a36Sopenharmony_ci			atomic_dec(&dev->buf_alloc);
101062306a36Sopenharmony_ci			return -ENOMEM;
101162306a36Sopenharmony_ci		}
101262306a36Sopenharmony_ci		entry->seglist[entry->seg_count++] = dmah;
101362306a36Sopenharmony_ci		for (i = 0; i < (1 << page_order); i++) {
101462306a36Sopenharmony_ci			DRM_DEBUG("page %d @ 0x%08lx\n",
101562306a36Sopenharmony_ci				  dma->page_count + page_count,
101662306a36Sopenharmony_ci				  (unsigned long)dmah->vaddr + PAGE_SIZE * i);
101762306a36Sopenharmony_ci			temp_pagelist[dma->page_count + page_count++]
101862306a36Sopenharmony_ci				= (unsigned long)dmah->vaddr + PAGE_SIZE * i;
101962306a36Sopenharmony_ci		}
102062306a36Sopenharmony_ci		for (offset = 0;
102162306a36Sopenharmony_ci		     offset + size <= total && entry->buf_count < count;
102262306a36Sopenharmony_ci		     offset += alignment, ++entry->buf_count) {
102362306a36Sopenharmony_ci			buf = &entry->buflist[entry->buf_count];
102462306a36Sopenharmony_ci			buf->idx = dma->buf_count + entry->buf_count;
102562306a36Sopenharmony_ci			buf->total = alignment;
102662306a36Sopenharmony_ci			buf->order = order;
102762306a36Sopenharmony_ci			buf->used = 0;
102862306a36Sopenharmony_ci			buf->offset = (dma->byte_count + byte_count + offset);
102962306a36Sopenharmony_ci			buf->address = (void *)(dmah->vaddr + offset);
103062306a36Sopenharmony_ci			buf->bus_address = dmah->busaddr + offset;
103162306a36Sopenharmony_ci			buf->next = NULL;
103262306a36Sopenharmony_ci			buf->waiting = 0;
103362306a36Sopenharmony_ci			buf->pending = 0;
103462306a36Sopenharmony_ci			buf->file_priv = NULL;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci			buf->dev_priv_size = dev->driver->dev_priv_size;
103762306a36Sopenharmony_ci			buf->dev_private = kzalloc(buf->dev_priv_size,
103862306a36Sopenharmony_ci						GFP_KERNEL);
103962306a36Sopenharmony_ci			if (!buf->dev_private) {
104062306a36Sopenharmony_ci				/* Set count correctly so we free the proper amount. */
104162306a36Sopenharmony_ci				entry->buf_count = count;
104262306a36Sopenharmony_ci				entry->seg_count = count;
104362306a36Sopenharmony_ci				drm_cleanup_buf_error(dev, entry);
104462306a36Sopenharmony_ci				kfree(temp_pagelist);
104562306a36Sopenharmony_ci				mutex_unlock(&dev->struct_mutex);
104662306a36Sopenharmony_ci				atomic_dec(&dev->buf_alloc);
104762306a36Sopenharmony_ci				return -ENOMEM;
104862306a36Sopenharmony_ci			}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci			DRM_DEBUG("buffer %d @ %p\n",
105162306a36Sopenharmony_ci				  entry->buf_count, buf->address);
105262306a36Sopenharmony_ci		}
105362306a36Sopenharmony_ci		byte_count += PAGE_SIZE << page_order;
105462306a36Sopenharmony_ci	}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	temp_buflist = krealloc(dma->buflist,
105762306a36Sopenharmony_ci				(dma->buf_count + entry->buf_count) *
105862306a36Sopenharmony_ci				sizeof(*dma->buflist), GFP_KERNEL);
105962306a36Sopenharmony_ci	if (!temp_buflist) {
106062306a36Sopenharmony_ci		/* Free the entry because it isn't valid */
106162306a36Sopenharmony_ci		drm_cleanup_buf_error(dev, entry);
106262306a36Sopenharmony_ci		kfree(temp_pagelist);
106362306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
106462306a36Sopenharmony_ci		atomic_dec(&dev->buf_alloc);
106562306a36Sopenharmony_ci		return -ENOMEM;
106662306a36Sopenharmony_ci	}
106762306a36Sopenharmony_ci	dma->buflist = temp_buflist;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	for (i = 0; i < entry->buf_count; i++) {
107062306a36Sopenharmony_ci		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
107162306a36Sopenharmony_ci	}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	/* No allocations failed, so now we can replace the original pagelist
107462306a36Sopenharmony_ci	 * with the new one.
107562306a36Sopenharmony_ci	 */
107662306a36Sopenharmony_ci	if (dma->page_count) {
107762306a36Sopenharmony_ci		kfree(dma->pagelist);
107862306a36Sopenharmony_ci	}
107962306a36Sopenharmony_ci	dma->pagelist = temp_pagelist;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	dma->buf_count += entry->buf_count;
108262306a36Sopenharmony_ci	dma->seg_count += entry->seg_count;
108362306a36Sopenharmony_ci	dma->page_count += entry->seg_count << page_order;
108462306a36Sopenharmony_ci	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	mutex_unlock(&dev->struct_mutex);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	request->count = entry->buf_count;
108962306a36Sopenharmony_ci	request->size = size;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	if (request->flags & _DRM_PCI_BUFFER_RO)
109262306a36Sopenharmony_ci		dma->flags = _DRM_DMA_USE_PCI_RO;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	atomic_dec(&dev->buf_alloc);
109562306a36Sopenharmony_ci	return 0;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci}
109862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_legacy_addbufs_pci);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_cistatic int drm_legacy_addbufs_sg(struct drm_device *dev,
110162306a36Sopenharmony_ci				 struct drm_buf_desc *request)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	struct drm_device_dma *dma = dev->dma;
110462306a36Sopenharmony_ci	struct drm_buf_entry *entry;
110562306a36Sopenharmony_ci	struct drm_buf *buf;
110662306a36Sopenharmony_ci	unsigned long offset;
110762306a36Sopenharmony_ci	unsigned long agp_offset;
110862306a36Sopenharmony_ci	int count;
110962306a36Sopenharmony_ci	int order;
111062306a36Sopenharmony_ci	int size;
111162306a36Sopenharmony_ci	int alignment;
111262306a36Sopenharmony_ci	int page_order;
111362306a36Sopenharmony_ci	int total;
111462306a36Sopenharmony_ci	int byte_count;
111562306a36Sopenharmony_ci	int i;
111662306a36Sopenharmony_ci	struct drm_buf **temp_buflist;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_SG))
111962306a36Sopenharmony_ci		return -EOPNOTSUPP;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	if (!dma)
112262306a36Sopenharmony_ci		return -EINVAL;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
112562306a36Sopenharmony_ci		return -EPERM;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	count = request->count;
112862306a36Sopenharmony_ci	order = order_base_2(request->size);
112962306a36Sopenharmony_ci	size = 1 << order;
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	alignment = (request->flags & _DRM_PAGE_ALIGN)
113262306a36Sopenharmony_ci	    ? PAGE_ALIGN(size) : size;
113362306a36Sopenharmony_ci	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
113462306a36Sopenharmony_ci	total = PAGE_SIZE << page_order;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	byte_count = 0;
113762306a36Sopenharmony_ci	agp_offset = request->agp_start;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	DRM_DEBUG("count:      %d\n", count);
114062306a36Sopenharmony_ci	DRM_DEBUG("order:      %d\n", order);
114162306a36Sopenharmony_ci	DRM_DEBUG("size:       %d\n", size);
114262306a36Sopenharmony_ci	DRM_DEBUG("agp_offset: %lu\n", agp_offset);
114362306a36Sopenharmony_ci	DRM_DEBUG("alignment:  %d\n", alignment);
114462306a36Sopenharmony_ci	DRM_DEBUG("page_order: %d\n", page_order);
114562306a36Sopenharmony_ci	DRM_DEBUG("total:      %d\n", total);
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
114862306a36Sopenharmony_ci		return -EINVAL;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	spin_lock(&dev->buf_lock);
115162306a36Sopenharmony_ci	if (dev->buf_use) {
115262306a36Sopenharmony_ci		spin_unlock(&dev->buf_lock);
115362306a36Sopenharmony_ci		return -EBUSY;
115462306a36Sopenharmony_ci	}
115562306a36Sopenharmony_ci	atomic_inc(&dev->buf_alloc);
115662306a36Sopenharmony_ci	spin_unlock(&dev->buf_lock);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	mutex_lock(&dev->struct_mutex);
115962306a36Sopenharmony_ci	entry = &dma->bufs[order];
116062306a36Sopenharmony_ci	if (entry->buf_count) {
116162306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
116262306a36Sopenharmony_ci		atomic_dec(&dev->buf_alloc);
116362306a36Sopenharmony_ci		return -ENOMEM;	/* May only call once for each order */
116462306a36Sopenharmony_ci	}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	if (count < 0 || count > 4096) {
116762306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
116862306a36Sopenharmony_ci		atomic_dec(&dev->buf_alloc);
116962306a36Sopenharmony_ci		return -EINVAL;
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	entry->buflist = kcalloc(count, sizeof(*entry->buflist), GFP_KERNEL);
117362306a36Sopenharmony_ci	if (!entry->buflist) {
117462306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
117562306a36Sopenharmony_ci		atomic_dec(&dev->buf_alloc);
117662306a36Sopenharmony_ci		return -ENOMEM;
117762306a36Sopenharmony_ci	}
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	entry->buf_size = size;
118062306a36Sopenharmony_ci	entry->page_order = page_order;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	offset = 0;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	while (entry->buf_count < count) {
118562306a36Sopenharmony_ci		buf = &entry->buflist[entry->buf_count];
118662306a36Sopenharmony_ci		buf->idx = dma->buf_count + entry->buf_count;
118762306a36Sopenharmony_ci		buf->total = alignment;
118862306a36Sopenharmony_ci		buf->order = order;
118962306a36Sopenharmony_ci		buf->used = 0;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci		buf->offset = (dma->byte_count + offset);
119262306a36Sopenharmony_ci		buf->bus_address = agp_offset + offset;
119362306a36Sopenharmony_ci		buf->address = (void *)(agp_offset + offset
119462306a36Sopenharmony_ci					+ (unsigned long)dev->sg->virtual);
119562306a36Sopenharmony_ci		buf->next = NULL;
119662306a36Sopenharmony_ci		buf->waiting = 0;
119762306a36Sopenharmony_ci		buf->pending = 0;
119862306a36Sopenharmony_ci		buf->file_priv = NULL;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci		buf->dev_priv_size = dev->driver->dev_priv_size;
120162306a36Sopenharmony_ci		buf->dev_private = kzalloc(buf->dev_priv_size, GFP_KERNEL);
120262306a36Sopenharmony_ci		if (!buf->dev_private) {
120362306a36Sopenharmony_ci			/* Set count correctly so we free the proper amount. */
120462306a36Sopenharmony_ci			entry->buf_count = count;
120562306a36Sopenharmony_ci			drm_cleanup_buf_error(dev, entry);
120662306a36Sopenharmony_ci			mutex_unlock(&dev->struct_mutex);
120762306a36Sopenharmony_ci			atomic_dec(&dev->buf_alloc);
120862306a36Sopenharmony_ci			return -ENOMEM;
120962306a36Sopenharmony_ci		}
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci		DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci		offset += alignment;
121462306a36Sopenharmony_ci		entry->buf_count++;
121562306a36Sopenharmony_ci		byte_count += PAGE_SIZE << page_order;
121662306a36Sopenharmony_ci	}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	DRM_DEBUG("byte_count: %d\n", byte_count);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	temp_buflist = krealloc(dma->buflist,
122162306a36Sopenharmony_ci				(dma->buf_count + entry->buf_count) *
122262306a36Sopenharmony_ci				sizeof(*dma->buflist), GFP_KERNEL);
122362306a36Sopenharmony_ci	if (!temp_buflist) {
122462306a36Sopenharmony_ci		/* Free the entry because it isn't valid */
122562306a36Sopenharmony_ci		drm_cleanup_buf_error(dev, entry);
122662306a36Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
122762306a36Sopenharmony_ci		atomic_dec(&dev->buf_alloc);
122862306a36Sopenharmony_ci		return -ENOMEM;
122962306a36Sopenharmony_ci	}
123062306a36Sopenharmony_ci	dma->buflist = temp_buflist;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	for (i = 0; i < entry->buf_count; i++) {
123362306a36Sopenharmony_ci		dma->buflist[i + dma->buf_count] = &entry->buflist[i];
123462306a36Sopenharmony_ci	}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	dma->buf_count += entry->buf_count;
123762306a36Sopenharmony_ci	dma->seg_count += entry->seg_count;
123862306a36Sopenharmony_ci	dma->page_count += byte_count >> PAGE_SHIFT;
123962306a36Sopenharmony_ci	dma->byte_count += byte_count;
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
124262306a36Sopenharmony_ci	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	mutex_unlock(&dev->struct_mutex);
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	request->count = entry->buf_count;
124762306a36Sopenharmony_ci	request->size = size;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	dma->flags = _DRM_DMA_USE_SG;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	atomic_dec(&dev->buf_alloc);
125262306a36Sopenharmony_ci	return 0;
125362306a36Sopenharmony_ci}
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci/*
125662306a36Sopenharmony_ci * Add buffers for DMA transfers (ioctl).
125762306a36Sopenharmony_ci *
125862306a36Sopenharmony_ci * \param inode device inode.
125962306a36Sopenharmony_ci * \param file_priv DRM file private.
126062306a36Sopenharmony_ci * \param cmd command.
126162306a36Sopenharmony_ci * \param arg pointer to a struct drm_buf_desc request.
126262306a36Sopenharmony_ci * \return zero on success or a negative number on failure.
126362306a36Sopenharmony_ci *
126462306a36Sopenharmony_ci * According with the memory type specified in drm_buf_desc::flags and the
126562306a36Sopenharmony_ci * build options, it dispatches the call either to addbufs_agp(),
126662306a36Sopenharmony_ci * addbufs_sg() or addbufs_pci() for AGP, scatter-gather or consistent
126762306a36Sopenharmony_ci * PCI memory respectively.
126862306a36Sopenharmony_ci */
126962306a36Sopenharmony_ciint drm_legacy_addbufs(struct drm_device *dev, void *data,
127062306a36Sopenharmony_ci		       struct drm_file *file_priv)
127162306a36Sopenharmony_ci{
127262306a36Sopenharmony_ci	struct drm_buf_desc *request = data;
127362306a36Sopenharmony_ci	int ret;
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
127662306a36Sopenharmony_ci		return -EOPNOTSUPP;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
127962306a36Sopenharmony_ci		return -EOPNOTSUPP;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_AGP)
128262306a36Sopenharmony_ci	if (request->flags & _DRM_AGP_BUFFER)
128362306a36Sopenharmony_ci		ret = drm_legacy_addbufs_agp(dev, request);
128462306a36Sopenharmony_ci	else
128562306a36Sopenharmony_ci#endif
128662306a36Sopenharmony_ci	if (request->flags & _DRM_SG_BUFFER)
128762306a36Sopenharmony_ci		ret = drm_legacy_addbufs_sg(dev, request);
128862306a36Sopenharmony_ci	else if (request->flags & _DRM_FB_BUFFER)
128962306a36Sopenharmony_ci		ret = -EINVAL;
129062306a36Sopenharmony_ci	else
129162306a36Sopenharmony_ci		ret = drm_legacy_addbufs_pci(dev, request);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	return ret;
129462306a36Sopenharmony_ci}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci/*
129762306a36Sopenharmony_ci * Get information about the buffer mappings.
129862306a36Sopenharmony_ci *
129962306a36Sopenharmony_ci * This was originally mean for debugging purposes, or by a sophisticated
130062306a36Sopenharmony_ci * client library to determine how best to use the available buffers (e.g.,
130162306a36Sopenharmony_ci * large buffers can be used for image transfer).
130262306a36Sopenharmony_ci *
130362306a36Sopenharmony_ci * \param inode device inode.
130462306a36Sopenharmony_ci * \param file_priv DRM file private.
130562306a36Sopenharmony_ci * \param cmd command.
130662306a36Sopenharmony_ci * \param arg pointer to a drm_buf_info structure.
130762306a36Sopenharmony_ci * \return zero on success or a negative number on failure.
130862306a36Sopenharmony_ci *
130962306a36Sopenharmony_ci * Increments drm_device::buf_use while holding the drm_device::buf_lock
131062306a36Sopenharmony_ci * lock, preventing of allocating more buffers after this call. Information
131162306a36Sopenharmony_ci * about each requested buffer is then copied into user space.
131262306a36Sopenharmony_ci */
131362306a36Sopenharmony_ciint __drm_legacy_infobufs(struct drm_device *dev,
131462306a36Sopenharmony_ci			void *data, int *p,
131562306a36Sopenharmony_ci			int (*f)(void *, int, struct drm_buf_entry *))
131662306a36Sopenharmony_ci{
131762306a36Sopenharmony_ci	struct drm_device_dma *dma = dev->dma;
131862306a36Sopenharmony_ci	int i;
131962306a36Sopenharmony_ci	int count;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
132262306a36Sopenharmony_ci		return -EOPNOTSUPP;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
132562306a36Sopenharmony_ci		return -EOPNOTSUPP;
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	if (!dma)
132862306a36Sopenharmony_ci		return -EINVAL;
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	spin_lock(&dev->buf_lock);
133162306a36Sopenharmony_ci	if (atomic_read(&dev->buf_alloc)) {
133262306a36Sopenharmony_ci		spin_unlock(&dev->buf_lock);
133362306a36Sopenharmony_ci		return -EBUSY;
133462306a36Sopenharmony_ci	}
133562306a36Sopenharmony_ci	++dev->buf_use;		/* Can't allocate more after this call */
133662306a36Sopenharmony_ci	spin_unlock(&dev->buf_lock);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
133962306a36Sopenharmony_ci		if (dma->bufs[i].buf_count)
134062306a36Sopenharmony_ci			++count;
134162306a36Sopenharmony_ci	}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	DRM_DEBUG("count = %d\n", count);
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	if (*p >= count) {
134662306a36Sopenharmony_ci		for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
134762306a36Sopenharmony_ci			struct drm_buf_entry *from = &dma->bufs[i];
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci			if (from->buf_count) {
135062306a36Sopenharmony_ci				if (f(data, count, from) < 0)
135162306a36Sopenharmony_ci					return -EFAULT;
135262306a36Sopenharmony_ci				DRM_DEBUG("%d %d %d %d %d\n",
135362306a36Sopenharmony_ci					  i,
135462306a36Sopenharmony_ci					  dma->bufs[i].buf_count,
135562306a36Sopenharmony_ci					  dma->bufs[i].buf_size,
135662306a36Sopenharmony_ci					  dma->bufs[i].low_mark,
135762306a36Sopenharmony_ci					  dma->bufs[i].high_mark);
135862306a36Sopenharmony_ci				++count;
135962306a36Sopenharmony_ci			}
136062306a36Sopenharmony_ci		}
136162306a36Sopenharmony_ci	}
136262306a36Sopenharmony_ci	*p = count;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	return 0;
136562306a36Sopenharmony_ci}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_cistatic int copy_one_buf(void *data, int count, struct drm_buf_entry *from)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	struct drm_buf_info *request = data;
137062306a36Sopenharmony_ci	struct drm_buf_desc __user *to = &request->list[count];
137162306a36Sopenharmony_ci	struct drm_buf_desc v = {.count = from->buf_count,
137262306a36Sopenharmony_ci				 .size = from->buf_size,
137362306a36Sopenharmony_ci				 .low_mark = from->low_mark,
137462306a36Sopenharmony_ci				 .high_mark = from->high_mark};
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	if (copy_to_user(to, &v, offsetof(struct drm_buf_desc, flags)))
137762306a36Sopenharmony_ci		return -EFAULT;
137862306a36Sopenharmony_ci	return 0;
137962306a36Sopenharmony_ci}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ciint drm_legacy_infobufs(struct drm_device *dev, void *data,
138262306a36Sopenharmony_ci			struct drm_file *file_priv)
138362306a36Sopenharmony_ci{
138462306a36Sopenharmony_ci	struct drm_buf_info *request = data;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	return __drm_legacy_infobufs(dev, data, &request->count, copy_one_buf);
138762306a36Sopenharmony_ci}
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci/*
139062306a36Sopenharmony_ci * Specifies a low and high water mark for buffer allocation
139162306a36Sopenharmony_ci *
139262306a36Sopenharmony_ci * \param inode device inode.
139362306a36Sopenharmony_ci * \param file_priv DRM file private.
139462306a36Sopenharmony_ci * \param cmd command.
139562306a36Sopenharmony_ci * \param arg a pointer to a drm_buf_desc structure.
139662306a36Sopenharmony_ci * \return zero on success or a negative number on failure.
139762306a36Sopenharmony_ci *
139862306a36Sopenharmony_ci * Verifies that the size order is bounded between the admissible orders and
139962306a36Sopenharmony_ci * updates the respective drm_device_dma::bufs entry low and high water mark.
140062306a36Sopenharmony_ci *
140162306a36Sopenharmony_ci * \note This ioctl is deprecated and mostly never used.
140262306a36Sopenharmony_ci */
140362306a36Sopenharmony_ciint drm_legacy_markbufs(struct drm_device *dev, void *data,
140462306a36Sopenharmony_ci			struct drm_file *file_priv)
140562306a36Sopenharmony_ci{
140662306a36Sopenharmony_ci	struct drm_device_dma *dma = dev->dma;
140762306a36Sopenharmony_ci	struct drm_buf_desc *request = data;
140862306a36Sopenharmony_ci	int order;
140962306a36Sopenharmony_ci	struct drm_buf_entry *entry;
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
141262306a36Sopenharmony_ci		return -EOPNOTSUPP;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
141562306a36Sopenharmony_ci		return -EOPNOTSUPP;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	if (!dma)
141862306a36Sopenharmony_ci		return -EINVAL;
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	DRM_DEBUG("%d, %d, %d\n",
142162306a36Sopenharmony_ci		  request->size, request->low_mark, request->high_mark);
142262306a36Sopenharmony_ci	order = order_base_2(request->size);
142362306a36Sopenharmony_ci	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
142462306a36Sopenharmony_ci		return -EINVAL;
142562306a36Sopenharmony_ci	entry = &dma->bufs[order];
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	if (request->low_mark < 0 || request->low_mark > entry->buf_count)
142862306a36Sopenharmony_ci		return -EINVAL;
142962306a36Sopenharmony_ci	if (request->high_mark < 0 || request->high_mark > entry->buf_count)
143062306a36Sopenharmony_ci		return -EINVAL;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	entry->low_mark = request->low_mark;
143362306a36Sopenharmony_ci	entry->high_mark = request->high_mark;
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	return 0;
143662306a36Sopenharmony_ci}
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci/*
143962306a36Sopenharmony_ci * Unreserve the buffers in list, previously reserved using drmDMA.
144062306a36Sopenharmony_ci *
144162306a36Sopenharmony_ci * \param inode device inode.
144262306a36Sopenharmony_ci * \param file_priv DRM file private.
144362306a36Sopenharmony_ci * \param cmd command.
144462306a36Sopenharmony_ci * \param arg pointer to a drm_buf_free structure.
144562306a36Sopenharmony_ci * \return zero on success or a negative number on failure.
144662306a36Sopenharmony_ci *
144762306a36Sopenharmony_ci * Calls free_buffer() for each used buffer.
144862306a36Sopenharmony_ci * This function is primarily used for debugging.
144962306a36Sopenharmony_ci */
145062306a36Sopenharmony_ciint drm_legacy_freebufs(struct drm_device *dev, void *data,
145162306a36Sopenharmony_ci			struct drm_file *file_priv)
145262306a36Sopenharmony_ci{
145362306a36Sopenharmony_ci	struct drm_device_dma *dma = dev->dma;
145462306a36Sopenharmony_ci	struct drm_buf_free *request = data;
145562306a36Sopenharmony_ci	int i;
145662306a36Sopenharmony_ci	int idx;
145762306a36Sopenharmony_ci	struct drm_buf *buf;
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
146062306a36Sopenharmony_ci		return -EOPNOTSUPP;
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
146362306a36Sopenharmony_ci		return -EOPNOTSUPP;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	if (!dma)
146662306a36Sopenharmony_ci		return -EINVAL;
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	DRM_DEBUG("%d\n", request->count);
146962306a36Sopenharmony_ci	for (i = 0; i < request->count; i++) {
147062306a36Sopenharmony_ci		if (copy_from_user(&idx, &request->list[i], sizeof(idx)))
147162306a36Sopenharmony_ci			return -EFAULT;
147262306a36Sopenharmony_ci		if (idx < 0 || idx >= dma->buf_count) {
147362306a36Sopenharmony_ci			DRM_ERROR("Index %d (of %d max)\n",
147462306a36Sopenharmony_ci				  idx, dma->buf_count - 1);
147562306a36Sopenharmony_ci			return -EINVAL;
147662306a36Sopenharmony_ci		}
147762306a36Sopenharmony_ci		idx = array_index_nospec(idx, dma->buf_count);
147862306a36Sopenharmony_ci		buf = dma->buflist[idx];
147962306a36Sopenharmony_ci		if (buf->file_priv != file_priv) {
148062306a36Sopenharmony_ci			DRM_ERROR("Process %d freeing buffer not owned\n",
148162306a36Sopenharmony_ci				  task_pid_nr(current));
148262306a36Sopenharmony_ci			return -EINVAL;
148362306a36Sopenharmony_ci		}
148462306a36Sopenharmony_ci		drm_legacy_free_buffer(dev, buf);
148562306a36Sopenharmony_ci	}
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	return 0;
148862306a36Sopenharmony_ci}
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci/*
149162306a36Sopenharmony_ci * Maps all of the DMA buffers into client-virtual space (ioctl).
149262306a36Sopenharmony_ci *
149362306a36Sopenharmony_ci * \param inode device inode.
149462306a36Sopenharmony_ci * \param file_priv DRM file private.
149562306a36Sopenharmony_ci * \param cmd command.
149662306a36Sopenharmony_ci * \param arg pointer to a drm_buf_map structure.
149762306a36Sopenharmony_ci * \return zero on success or a negative number on failure.
149862306a36Sopenharmony_ci *
149962306a36Sopenharmony_ci * Maps the AGP, SG or PCI buffer region with vm_mmap(), and copies information
150062306a36Sopenharmony_ci * about each buffer into user space. For PCI buffers, it calls vm_mmap() with
150162306a36Sopenharmony_ci * offset equal to 0, which drm_mmap() interprets as PCI buffers and calls
150262306a36Sopenharmony_ci * drm_mmap_dma().
150362306a36Sopenharmony_ci */
150462306a36Sopenharmony_ciint __drm_legacy_mapbufs(struct drm_device *dev, void *data, int *p,
150562306a36Sopenharmony_ci			 void __user **v,
150662306a36Sopenharmony_ci			 int (*f)(void *, int, unsigned long,
150762306a36Sopenharmony_ci				 struct drm_buf *),
150862306a36Sopenharmony_ci				 struct drm_file *file_priv)
150962306a36Sopenharmony_ci{
151062306a36Sopenharmony_ci	struct drm_device_dma *dma = dev->dma;
151162306a36Sopenharmony_ci	int retcode = 0;
151262306a36Sopenharmony_ci	unsigned long virtual;
151362306a36Sopenharmony_ci	int i;
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
151662306a36Sopenharmony_ci		return -EOPNOTSUPP;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
151962306a36Sopenharmony_ci		return -EOPNOTSUPP;
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	if (!dma)
152262306a36Sopenharmony_ci		return -EINVAL;
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	spin_lock(&dev->buf_lock);
152562306a36Sopenharmony_ci	if (atomic_read(&dev->buf_alloc)) {
152662306a36Sopenharmony_ci		spin_unlock(&dev->buf_lock);
152762306a36Sopenharmony_ci		return -EBUSY;
152862306a36Sopenharmony_ci	}
152962306a36Sopenharmony_ci	dev->buf_use++;		/* Can't allocate more after this call */
153062306a36Sopenharmony_ci	spin_unlock(&dev->buf_lock);
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	if (*p >= dma->buf_count) {
153362306a36Sopenharmony_ci		if ((dev->agp && (dma->flags & _DRM_DMA_USE_AGP))
153462306a36Sopenharmony_ci		    || (drm_core_check_feature(dev, DRIVER_SG)
153562306a36Sopenharmony_ci			&& (dma->flags & _DRM_DMA_USE_SG))) {
153662306a36Sopenharmony_ci			struct drm_local_map *map = dev->agp_buffer_map;
153762306a36Sopenharmony_ci			unsigned long token = dev->agp_buffer_token;
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci			if (!map) {
154062306a36Sopenharmony_ci				retcode = -EINVAL;
154162306a36Sopenharmony_ci				goto done;
154262306a36Sopenharmony_ci			}
154362306a36Sopenharmony_ci			virtual = vm_mmap(file_priv->filp, 0, map->size,
154462306a36Sopenharmony_ci					  PROT_READ | PROT_WRITE,
154562306a36Sopenharmony_ci					  MAP_SHARED,
154662306a36Sopenharmony_ci					  token);
154762306a36Sopenharmony_ci		} else {
154862306a36Sopenharmony_ci			virtual = vm_mmap(file_priv->filp, 0, dma->byte_count,
154962306a36Sopenharmony_ci					  PROT_READ | PROT_WRITE,
155062306a36Sopenharmony_ci					  MAP_SHARED, 0);
155162306a36Sopenharmony_ci		}
155262306a36Sopenharmony_ci		if (virtual > -1024UL) {
155362306a36Sopenharmony_ci			/* Real error */
155462306a36Sopenharmony_ci			retcode = (signed long)virtual;
155562306a36Sopenharmony_ci			goto done;
155662306a36Sopenharmony_ci		}
155762306a36Sopenharmony_ci		*v = (void __user *)virtual;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci		for (i = 0; i < dma->buf_count; i++) {
156062306a36Sopenharmony_ci			if (f(data, i, virtual, dma->buflist[i]) < 0) {
156162306a36Sopenharmony_ci				retcode = -EFAULT;
156262306a36Sopenharmony_ci				goto done;
156362306a36Sopenharmony_ci			}
156462306a36Sopenharmony_ci		}
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci      done:
156762306a36Sopenharmony_ci	*p = dma->buf_count;
156862306a36Sopenharmony_ci	DRM_DEBUG("%d buffers, retcode = %d\n", *p, retcode);
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	return retcode;
157162306a36Sopenharmony_ci}
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_cistatic int map_one_buf(void *data, int idx, unsigned long virtual,
157462306a36Sopenharmony_ci			struct drm_buf *buf)
157562306a36Sopenharmony_ci{
157662306a36Sopenharmony_ci	struct drm_buf_map *request = data;
157762306a36Sopenharmony_ci	unsigned long address = virtual + buf->offset;	/* *** */
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	if (copy_to_user(&request->list[idx].idx, &buf->idx,
158062306a36Sopenharmony_ci			 sizeof(request->list[0].idx)))
158162306a36Sopenharmony_ci		return -EFAULT;
158262306a36Sopenharmony_ci	if (copy_to_user(&request->list[idx].total, &buf->total,
158362306a36Sopenharmony_ci			 sizeof(request->list[0].total)))
158462306a36Sopenharmony_ci		return -EFAULT;
158562306a36Sopenharmony_ci	if (clear_user(&request->list[idx].used, sizeof(int)))
158662306a36Sopenharmony_ci		return -EFAULT;
158762306a36Sopenharmony_ci	if (copy_to_user(&request->list[idx].address, &address,
158862306a36Sopenharmony_ci			 sizeof(address)))
158962306a36Sopenharmony_ci		return -EFAULT;
159062306a36Sopenharmony_ci	return 0;
159162306a36Sopenharmony_ci}
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ciint drm_legacy_mapbufs(struct drm_device *dev, void *data,
159462306a36Sopenharmony_ci		       struct drm_file *file_priv)
159562306a36Sopenharmony_ci{
159662306a36Sopenharmony_ci	struct drm_buf_map *request = data;
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	return __drm_legacy_mapbufs(dev, data, &request->count,
159962306a36Sopenharmony_ci				    &request->virtual, map_one_buf,
160062306a36Sopenharmony_ci				    file_priv);
160162306a36Sopenharmony_ci}
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ciint drm_legacy_dma_ioctl(struct drm_device *dev, void *data,
160462306a36Sopenharmony_ci		  struct drm_file *file_priv)
160562306a36Sopenharmony_ci{
160662306a36Sopenharmony_ci	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
160762306a36Sopenharmony_ci		return -EOPNOTSUPP;
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	if (dev->driver->dma_ioctl)
161062306a36Sopenharmony_ci		return dev->driver->dma_ioctl(dev, data, file_priv);
161162306a36Sopenharmony_ci	else
161262306a36Sopenharmony_ci		return -EINVAL;
161362306a36Sopenharmony_ci}
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_cistruct drm_local_map *drm_legacy_getsarea(struct drm_device *dev)
161662306a36Sopenharmony_ci{
161762306a36Sopenharmony_ci	struct drm_map_list *entry;
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	list_for_each_entry(entry, &dev->maplist, head) {
162062306a36Sopenharmony_ci		if (entry->map && entry->map->type == _DRM_SHM &&
162162306a36Sopenharmony_ci		    (entry->map->flags & _DRM_CONTAINS_LOCK)) {
162262306a36Sopenharmony_ci			return entry->map;
162362306a36Sopenharmony_ci		}
162462306a36Sopenharmony_ci	}
162562306a36Sopenharmony_ci	return NULL;
162662306a36Sopenharmony_ci}
162762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_legacy_getsarea);
1628