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