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