1592ffb21SWarner Losh /** 2592ffb21SWarner Losh * \file drm_bufs.c 3592ffb21SWarner Losh * Generic buffer template 4592ffb21SWarner Losh * 5592ffb21SWarner Losh * \author Rickard E. (Rik) Faith <faith@valinux.com> 6592ffb21SWarner Losh * \author Gareth Hughes <gareth@valinux.com> 7592ffb21SWarner Losh */ 8592ffb21SWarner Losh 9592ffb21SWarner Losh /* 10592ffb21SWarner Losh * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com 11592ffb21SWarner Losh * 12592ffb21SWarner Losh * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. 13592ffb21SWarner Losh * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 14592ffb21SWarner Losh * All Rights Reserved. 15592ffb21SWarner Losh * 16592ffb21SWarner Losh * Permission is hereby granted, free of charge, to any person obtaining a 17592ffb21SWarner Losh * copy of this software and associated documentation files (the "Software"), 18592ffb21SWarner Losh * to deal in the Software without restriction, including without limitation 19592ffb21SWarner Losh * the rights to use, copy, modify, merge, publish, distribute, sublicense, 20592ffb21SWarner Losh * and/or sell copies of the Software, and to permit persons to whom the 21592ffb21SWarner Losh * Software is furnished to do so, subject to the following conditions: 22592ffb21SWarner Losh * 23592ffb21SWarner Losh * The above copyright notice and this permission notice (including the next 24592ffb21SWarner Losh * paragraph) shall be included in all copies or substantial portions of the 25592ffb21SWarner Losh * Software. 26592ffb21SWarner Losh * 27592ffb21SWarner Losh * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28592ffb21SWarner Losh * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29592ffb21SWarner Losh * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 30592ffb21SWarner Losh * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 31592ffb21SWarner Losh * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 32592ffb21SWarner Losh * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 33592ffb21SWarner Losh * OTHER DEALINGS IN THE SOFTWARE. 34592ffb21SWarner Losh */ 35592ffb21SWarner Losh 36592ffb21SWarner Losh #include <sys/cdefs.h> 37592ffb21SWarner Losh __FBSDID("$FreeBSD$"); 38592ffb21SWarner Losh 39592ffb21SWarner Losh #include <sys/param.h> 40592ffb21SWarner Losh #include <sys/shm.h> 41592ffb21SWarner Losh 42592ffb21SWarner Losh #include <dev/pci/pcireg.h> 43592ffb21SWarner Losh 44592ffb21SWarner Losh #include <dev/drm2/drmP.h> 45592ffb21SWarner Losh 46592ffb21SWarner Losh /* Allocation of PCI memory resources (framebuffer, registers, etc.) for 47592ffb21SWarner Losh * drm_get_resource_*. Note that they are not RF_ACTIVE, so there's no virtual 48592ffb21SWarner Losh * address for accessing them. Cleaned up at unload. 49592ffb21SWarner Losh */ 50592ffb21SWarner Losh static int drm_alloc_resource(struct drm_device *dev, int resource) 51592ffb21SWarner Losh { 52592ffb21SWarner Losh struct resource *res; 53592ffb21SWarner Losh int rid; 54592ffb21SWarner Losh 55592ffb21SWarner Losh if (resource >= DRM_MAX_PCI_RESOURCE) { 56592ffb21SWarner Losh DRM_ERROR("Resource %d too large\n", resource); 57592ffb21SWarner Losh return 1; 58592ffb21SWarner Losh } 59592ffb21SWarner Losh 60592ffb21SWarner Losh if (dev->pcir[resource] != NULL) { 61592ffb21SWarner Losh return 0; 62592ffb21SWarner Losh } 63592ffb21SWarner Losh 64592ffb21SWarner Losh rid = PCIR_BAR(resource); 65592ffb21SWarner Losh res = bus_alloc_resource_any(dev->dev, SYS_RES_MEMORY, &rid, 66592ffb21SWarner Losh RF_SHAREABLE); 67592ffb21SWarner Losh if (res == NULL) { 68592ffb21SWarner Losh DRM_ERROR("Couldn't find resource 0x%x\n", resource); 69592ffb21SWarner Losh return 1; 70592ffb21SWarner Losh } 71592ffb21SWarner Losh 72592ffb21SWarner Losh if (dev->pcir[resource] == NULL) { 73592ffb21SWarner Losh dev->pcirid[resource] = rid; 74592ffb21SWarner Losh dev->pcir[resource] = res; 75592ffb21SWarner Losh } 76592ffb21SWarner Losh 77592ffb21SWarner Losh return 0; 78592ffb21SWarner Losh } 79592ffb21SWarner Losh 80592ffb21SWarner Losh unsigned long drm_get_resource_start(struct drm_device *dev, 81592ffb21SWarner Losh unsigned int resource) 82592ffb21SWarner Losh { 83592ffb21SWarner Losh unsigned long start; 84592ffb21SWarner Losh 85592ffb21SWarner Losh mtx_lock(&dev->pcir_lock); 86592ffb21SWarner Losh 87592ffb21SWarner Losh if (drm_alloc_resource(dev, resource) != 0) 88592ffb21SWarner Losh return 0; 89592ffb21SWarner Losh 90592ffb21SWarner Losh start = rman_get_start(dev->pcir[resource]); 91592ffb21SWarner Losh 92592ffb21SWarner Losh mtx_unlock(&dev->pcir_lock); 93592ffb21SWarner Losh 94592ffb21SWarner Losh return (start); 95592ffb21SWarner Losh } 96592ffb21SWarner Losh 97592ffb21SWarner Losh unsigned long drm_get_resource_len(struct drm_device *dev, 98592ffb21SWarner Losh unsigned int resource) 99592ffb21SWarner Losh { 100592ffb21SWarner Losh unsigned long len; 101592ffb21SWarner Losh 102592ffb21SWarner Losh mtx_lock(&dev->pcir_lock); 103592ffb21SWarner Losh 104592ffb21SWarner Losh if (drm_alloc_resource(dev, resource) != 0) 105592ffb21SWarner Losh return 0; 106592ffb21SWarner Losh 107592ffb21SWarner Losh len = rman_get_size(dev->pcir[resource]); 108592ffb21SWarner Losh 109592ffb21SWarner Losh mtx_unlock(&dev->pcir_lock); 110592ffb21SWarner Losh 111592ffb21SWarner Losh return (len); 112592ffb21SWarner Losh } 113592ffb21SWarner Losh 114592ffb21SWarner Losh static struct drm_map_list *drm_find_matching_map(struct drm_device *dev, 115592ffb21SWarner Losh struct drm_local_map *map) 116592ffb21SWarner Losh { 117592ffb21SWarner Losh struct drm_map_list *entry; 118592ffb21SWarner Losh list_for_each_entry(entry, &dev->maplist, head) { 119592ffb21SWarner Losh /* 120592ffb21SWarner Losh * Because the kernel-userspace ABI is fixed at a 32-bit offset 121592ffb21SWarner Losh * while PCI resources may live above that, we only compare the 122592ffb21SWarner Losh * lower 32 bits of the map offset for maps of type 123592ffb21SWarner Losh * _DRM_FRAMEBUFFER or _DRM_REGISTERS. 124592ffb21SWarner Losh * It is assumed that if a driver have more than one resource 125592ffb21SWarner Losh * of each type, the lower 32 bits are different. 126592ffb21SWarner Losh */ 127592ffb21SWarner Losh if (!entry->map || 128592ffb21SWarner Losh map->type != entry->map->type || 129592ffb21SWarner Losh entry->master != dev->primary->master) 130592ffb21SWarner Losh continue; 131592ffb21SWarner Losh switch (map->type) { 132592ffb21SWarner Losh case _DRM_SHM: 133592ffb21SWarner Losh if (map->flags != _DRM_CONTAINS_LOCK) 134592ffb21SWarner Losh break; 135592ffb21SWarner Losh return entry; 136592ffb21SWarner Losh case _DRM_REGISTERS: 137592ffb21SWarner Losh case _DRM_FRAME_BUFFER: 138592ffb21SWarner Losh if ((entry->map->offset & 0xffffffff) == 139592ffb21SWarner Losh (map->offset & 0xffffffff)) 140592ffb21SWarner Losh return entry; 141592ffb21SWarner Losh default: /* Make gcc happy */ 142592ffb21SWarner Losh ; 143592ffb21SWarner Losh } 144592ffb21SWarner Losh if (entry->map->offset == map->offset) 145592ffb21SWarner Losh return entry; 146592ffb21SWarner Losh } 147592ffb21SWarner Losh 148592ffb21SWarner Losh return NULL; 149592ffb21SWarner Losh } 150592ffb21SWarner Losh 151592ffb21SWarner Losh static int drm_map_handle(struct drm_device *dev, struct drm_hash_item *hash, 152592ffb21SWarner Losh unsigned long user_token, int hashed_handle, int shm) 153592ffb21SWarner Losh { 154592ffb21SWarner Losh int use_hashed_handle, shift; 155592ffb21SWarner Losh unsigned long add; 156592ffb21SWarner Losh 157592ffb21SWarner Losh #if (BITS_PER_LONG == 64) 158592ffb21SWarner Losh use_hashed_handle = ((user_token & 0xFFFFFFFF00000000UL) || hashed_handle); 159592ffb21SWarner Losh #elif (BITS_PER_LONG == 32) 160592ffb21SWarner Losh use_hashed_handle = hashed_handle; 161592ffb21SWarner Losh #else 162592ffb21SWarner Losh #error Unsupported long size. Neither 64 nor 32 bits. 163592ffb21SWarner Losh #endif 164592ffb21SWarner Losh 165592ffb21SWarner Losh if (!use_hashed_handle) { 166592ffb21SWarner Losh int ret; 167592ffb21SWarner Losh hash->key = user_token >> PAGE_SHIFT; 168592ffb21SWarner Losh ret = drm_ht_insert_item(&dev->map_hash, hash); 169592ffb21SWarner Losh if (ret != -EINVAL) 170592ffb21SWarner Losh return ret; 171592ffb21SWarner Losh } 172592ffb21SWarner Losh 173592ffb21SWarner Losh shift = 0; 174592ffb21SWarner Losh add = DRM_MAP_HASH_OFFSET >> PAGE_SHIFT; 175592ffb21SWarner Losh if (shm && (SHMLBA > PAGE_SIZE)) { 176592ffb21SWarner Losh int bits = ilog2(SHMLBA >> PAGE_SHIFT) + 1; 177592ffb21SWarner Losh 178592ffb21SWarner Losh /* For shared memory, we have to preserve the SHMLBA 179592ffb21SWarner Losh * bits of the eventual vma->vm_pgoff value during 180592ffb21SWarner Losh * mmap(). Otherwise we run into cache aliasing problems 181592ffb21SWarner Losh * on some platforms. On these platforms, the pgoff of 182592ffb21SWarner Losh * a mmap() request is used to pick a suitable virtual 183592ffb21SWarner Losh * address for the mmap() region such that it will not 184592ffb21SWarner Losh * cause cache aliasing problems. 185592ffb21SWarner Losh * 186592ffb21SWarner Losh * Therefore, make sure the SHMLBA relevant bits of the 187592ffb21SWarner Losh * hash value we use are equal to those in the original 188592ffb21SWarner Losh * kernel virtual address. 189592ffb21SWarner Losh */ 190592ffb21SWarner Losh shift = bits; 191592ffb21SWarner Losh add |= ((user_token >> PAGE_SHIFT) & ((1UL << bits) - 1UL)); 192592ffb21SWarner Losh } 193592ffb21SWarner Losh 194592ffb21SWarner Losh return drm_ht_just_insert_please(&dev->map_hash, hash, 195592ffb21SWarner Losh user_token, 32 - PAGE_SHIFT - 3, 196592ffb21SWarner Losh shift, add); 197592ffb21SWarner Losh } 198592ffb21SWarner Losh 199592ffb21SWarner Losh /** 200592ffb21SWarner Losh * Core function to create a range of memory available for mapping by a 201592ffb21SWarner Losh * non-root process. 202592ffb21SWarner Losh * 203592ffb21SWarner Losh * Adjusts the memory offset to its absolute value according to the mapping 204592ffb21SWarner Losh * type. Adds the map to the map list drm_device::maplist. Adds MTRR's where 205592ffb21SWarner Losh * applicable and if supported by the kernel. 206592ffb21SWarner Losh */ 207592ffb21SWarner Losh static int drm_addmap_core(struct drm_device * dev, resource_size_t offset, 208592ffb21SWarner Losh unsigned int size, enum drm_map_type type, 209592ffb21SWarner Losh enum drm_map_flags flags, 210592ffb21SWarner Losh struct drm_map_list ** maplist) 211592ffb21SWarner Losh { 212592ffb21SWarner Losh struct drm_local_map *map; 213592ffb21SWarner Losh struct drm_map_list *list; 214592ffb21SWarner Losh drm_dma_handle_t *dmah; 215592ffb21SWarner Losh unsigned long user_token; 216592ffb21SWarner Losh int ret; 217592ffb21SWarner Losh int align; 218592ffb21SWarner Losh 219592ffb21SWarner Losh map = malloc(sizeof(*map), DRM_MEM_MAPS, M_NOWAIT); 220592ffb21SWarner Losh if (!map) 221592ffb21SWarner Losh return -ENOMEM; 222592ffb21SWarner Losh 223592ffb21SWarner Losh map->offset = offset; 224592ffb21SWarner Losh map->size = size; 225592ffb21SWarner Losh map->flags = flags; 226592ffb21SWarner Losh map->type = type; 227592ffb21SWarner Losh 228592ffb21SWarner Losh /* Only allow shared memory to be removable since we only keep enough 229592ffb21SWarner Losh * book keeping information about shared memory to allow for removal 230592ffb21SWarner Losh * when processes fork. 231592ffb21SWarner Losh */ 232592ffb21SWarner Losh if ((map->flags & _DRM_REMOVABLE) && map->type != _DRM_SHM) { 233592ffb21SWarner Losh free(map, DRM_MEM_MAPS); 234592ffb21SWarner Losh return -EINVAL; 235592ffb21SWarner Losh } 236592ffb21SWarner Losh DRM_DEBUG("offset = 0x%08llx, size = 0x%08lx, type = %d\n", 237592ffb21SWarner Losh (unsigned long long)map->offset, map->size, map->type); 238592ffb21SWarner Losh 239592ffb21SWarner Losh /* page-align _DRM_SHM maps. They are allocated here so there is no security 240592ffb21SWarner Losh * hole created by that and it works around various broken drivers that use 241592ffb21SWarner Losh * a non-aligned quantity to map the SAREA. --BenH 242592ffb21SWarner Losh */ 243592ffb21SWarner Losh if (map->type == _DRM_SHM) 244592ffb21SWarner Losh map->size = PAGE_ALIGN(map->size); 245592ffb21SWarner Losh 246592ffb21SWarner Losh /* 247592ffb21SWarner Losh * FreeBSD port note: FreeBSD's PAGE_MASK is the inverse of 248592ffb21SWarner Losh * Linux's one. That's why the test below doesn't inverse the 249592ffb21SWarner Losh * constant. 250592ffb21SWarner Losh */ 251592ffb21SWarner Losh if ((map->offset & ((resource_size_t)PAGE_MASK)) || (map->size & (PAGE_MASK))) { 252592ffb21SWarner Losh free(map, DRM_MEM_MAPS); 253592ffb21SWarner Losh return -EINVAL; 254592ffb21SWarner Losh } 255592ffb21SWarner Losh map->mtrr = -1; 256592ffb21SWarner Losh map->handle = NULL; 257592ffb21SWarner Losh 258592ffb21SWarner Losh switch (map->type) { 259592ffb21SWarner Losh case _DRM_REGISTERS: 260592ffb21SWarner Losh case _DRM_FRAME_BUFFER: 261592ffb21SWarner Losh #ifdef __linux__ 262592ffb21SWarner Losh #if !defined(__sparc__) && !defined(__alpha__) && !defined(__ia64__) && !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__arm__) 263592ffb21SWarner Losh if (map->offset + (map->size-1) < map->offset || 264592ffb21SWarner Losh map->offset < virt_to_phys(high_memory)) { 265592ffb21SWarner Losh kfree(map); 266592ffb21SWarner Losh return -EINVAL; 267592ffb21SWarner Losh } 268592ffb21SWarner Losh #endif 269592ffb21SWarner Losh #endif 270592ffb21SWarner Losh /* Some drivers preinitialize some maps, without the X Server 271592ffb21SWarner Losh * needing to be aware of it. Therefore, we just return success 272592ffb21SWarner Losh * when the server tries to create a duplicate map. 273592ffb21SWarner Losh */ 274592ffb21SWarner Losh list = drm_find_matching_map(dev, map); 275592ffb21SWarner Losh if (list != NULL) { 276592ffb21SWarner Losh if (list->map->size != map->size) { 277592ffb21SWarner Losh DRM_DEBUG("Matching maps of type %d with " 278592ffb21SWarner Losh "mismatched sizes, (%ld vs %ld)\n", 279592ffb21SWarner Losh map->type, map->size, 280592ffb21SWarner Losh list->map->size); 281592ffb21SWarner Losh list->map->size = map->size; 282592ffb21SWarner Losh } 283592ffb21SWarner Losh 284592ffb21SWarner Losh free(map, DRM_MEM_MAPS); 285592ffb21SWarner Losh *maplist = list; 286592ffb21SWarner Losh return 0; 287592ffb21SWarner Losh } 288592ffb21SWarner Losh 289592ffb21SWarner Losh if (drm_core_has_MTRR(dev)) { 290592ffb21SWarner Losh if (map->type == _DRM_FRAME_BUFFER || 291592ffb21SWarner Losh (map->flags & _DRM_WRITE_COMBINING)) { 292592ffb21SWarner Losh if (drm_mtrr_add( 293592ffb21SWarner Losh map->offset, map->size, 294592ffb21SWarner Losh DRM_MTRR_WC) == 0) 295592ffb21SWarner Losh map->mtrr = 1; 296592ffb21SWarner Losh } 297592ffb21SWarner Losh } 298592ffb21SWarner Losh if (map->type == _DRM_REGISTERS) { 299592ffb21SWarner Losh drm_core_ioremap(map, dev); 300592ffb21SWarner Losh if (!map->handle) { 301592ffb21SWarner Losh free(map, DRM_MEM_MAPS); 302592ffb21SWarner Losh return -ENOMEM; 303592ffb21SWarner Losh } 304592ffb21SWarner Losh } 305592ffb21SWarner Losh 306592ffb21SWarner Losh break; 307592ffb21SWarner Losh case _DRM_SHM: 308592ffb21SWarner Losh list = drm_find_matching_map(dev, map); 309592ffb21SWarner Losh if (list != NULL) { 310592ffb21SWarner Losh if(list->map->size != map->size) { 311592ffb21SWarner Losh DRM_DEBUG("Matching maps of type %d with " 312592ffb21SWarner Losh "mismatched sizes, (%ld vs %ld)\n", 313592ffb21SWarner Losh map->type, map->size, list->map->size); 314592ffb21SWarner Losh list->map->size = map->size; 315592ffb21SWarner Losh } 316592ffb21SWarner Losh 317592ffb21SWarner Losh free(map, DRM_MEM_MAPS); 318592ffb21SWarner Losh *maplist = list; 319592ffb21SWarner Losh return 0; 320592ffb21SWarner Losh } 321592ffb21SWarner Losh map->handle = malloc(map->size, DRM_MEM_MAPS, M_NOWAIT); 322592ffb21SWarner Losh DRM_DEBUG("%lu %d %p\n", 323592ffb21SWarner Losh map->size, drm_order(map->size), map->handle); 324592ffb21SWarner Losh if (!map->handle) { 325592ffb21SWarner Losh free(map, DRM_MEM_MAPS); 326592ffb21SWarner Losh return -ENOMEM; 327592ffb21SWarner Losh } 328592ffb21SWarner Losh map->offset = (unsigned long)map->handle; 329592ffb21SWarner Losh if (map->flags & _DRM_CONTAINS_LOCK) { 330592ffb21SWarner Losh /* Prevent a 2nd X Server from creating a 2nd lock */ 331592ffb21SWarner Losh if (dev->primary->master->lock.hw_lock != NULL) { 332592ffb21SWarner Losh free(map->handle, DRM_MEM_MAPS); 333592ffb21SWarner Losh free(map, DRM_MEM_MAPS); 334592ffb21SWarner Losh return -EBUSY; 335592ffb21SWarner Losh } 336592ffb21SWarner Losh dev->sigdata.lock = dev->primary->master->lock.hw_lock = map->handle; /* Pointer to lock */ 337592ffb21SWarner Losh } 338592ffb21SWarner Losh break; 339592ffb21SWarner Losh case _DRM_AGP: { 340592ffb21SWarner Losh struct drm_agp_mem *entry; 341592ffb21SWarner Losh int valid = 0; 342592ffb21SWarner Losh 343592ffb21SWarner Losh if (!drm_core_has_AGP(dev)) { 344592ffb21SWarner Losh free(map, DRM_MEM_MAPS); 345592ffb21SWarner Losh return -EINVAL; 346592ffb21SWarner Losh } 347592ffb21SWarner Losh #ifdef __linux__ 348592ffb21SWarner Losh #ifdef __alpha__ 349592ffb21SWarner Losh map->offset += dev->hose->mem_space->start; 350592ffb21SWarner Losh #endif 351592ffb21SWarner Losh #endif 352592ffb21SWarner Losh /* In some cases (i810 driver), user space may have already 353592ffb21SWarner Losh * added the AGP base itself, because dev->agp->base previously 354592ffb21SWarner Losh * only got set during AGP enable. So, only add the base 355592ffb21SWarner Losh * address if the map's offset isn't already within the 356592ffb21SWarner Losh * aperture. 357592ffb21SWarner Losh */ 358592ffb21SWarner Losh if (map->offset < dev->agp->base || 359592ffb21SWarner Losh map->offset > dev->agp->base + 360592ffb21SWarner Losh dev->agp->agp_info.ai_aperture_size * 1024 * 1024 - 1) { 361592ffb21SWarner Losh map->offset += dev->agp->base; 362592ffb21SWarner Losh } 363592ffb21SWarner Losh map->mtrr = dev->agp->agp_mtrr; /* for getmap */ 364592ffb21SWarner Losh 365592ffb21SWarner Losh /* This assumes the DRM is in total control of AGP space. 366592ffb21SWarner Losh * It's not always the case as AGP can be in the control 367592ffb21SWarner Losh * of user space (i.e. i810 driver). So this loop will get 368592ffb21SWarner Losh * skipped and we double check that dev->agp->memory is 369592ffb21SWarner Losh * actually set as well as being invalid before EPERM'ing 370592ffb21SWarner Losh */ 371592ffb21SWarner Losh list_for_each_entry(entry, &dev->agp->memory, head) { 372592ffb21SWarner Losh if ((map->offset >= entry->bound) && 373592ffb21SWarner Losh (map->offset + map->size <= entry->bound + entry->pages * PAGE_SIZE)) { 374592ffb21SWarner Losh valid = 1; 375592ffb21SWarner Losh break; 376592ffb21SWarner Losh } 377592ffb21SWarner Losh } 378592ffb21SWarner Losh if (!list_empty(&dev->agp->memory) && !valid) { 379592ffb21SWarner Losh free(map, DRM_MEM_MAPS); 380592ffb21SWarner Losh return -EPERM; 381592ffb21SWarner Losh } 382592ffb21SWarner Losh DRM_DEBUG("AGP offset = 0x%08llx, size = 0x%08lx\n", 383592ffb21SWarner Losh (unsigned long long)map->offset, map->size); 384592ffb21SWarner Losh 385592ffb21SWarner Losh break; 386592ffb21SWarner Losh } 387592ffb21SWarner Losh case _DRM_GEM: 388592ffb21SWarner Losh DRM_ERROR("tried to addmap GEM object\n"); 389592ffb21SWarner Losh break; 390592ffb21SWarner Losh case _DRM_SCATTER_GATHER: 391592ffb21SWarner Losh if (!dev->sg) { 392592ffb21SWarner Losh free(map, DRM_MEM_MAPS); 393592ffb21SWarner Losh return -EINVAL; 394592ffb21SWarner Losh } 395592ffb21SWarner Losh map->handle = (void *)(dev->sg->vaddr + offset); 396592ffb21SWarner Losh map->offset += dev->sg->vaddr; 397592ffb21SWarner Losh break; 398592ffb21SWarner Losh case _DRM_CONSISTENT: 399592ffb21SWarner Losh /* dma_addr_t is 64bit on i386 with CONFIG_HIGHMEM64G, 400592ffb21SWarner Losh * As we're limiting the address to 2^32-1 (or less), 401592ffb21SWarner Losh * casting it down to 32 bits is no problem, but we 402592ffb21SWarner Losh * need to point to a 64bit variable first. */ 403592ffb21SWarner Losh align = map->size; 404592ffb21SWarner Losh if ((align & (align - 1)) != 0) 405592ffb21SWarner Losh align = PAGE_SIZE; 406592ffb21SWarner Losh dmah = drm_pci_alloc(dev, map->size, align, BUS_SPACE_MAXADDR); 407592ffb21SWarner Losh if (!dmah) { 408592ffb21SWarner Losh free(map, DRM_MEM_MAPS); 409592ffb21SWarner Losh return -ENOMEM; 410592ffb21SWarner Losh } 411592ffb21SWarner Losh map->handle = dmah->vaddr; 412592ffb21SWarner Losh map->offset = dmah->busaddr; 413592ffb21SWarner Losh map->dmah = dmah; 414592ffb21SWarner Losh break; 415592ffb21SWarner Losh default: 416592ffb21SWarner Losh free(map, DRM_MEM_MAPS); 417592ffb21SWarner Losh return -EINVAL; 418592ffb21SWarner Losh } 419592ffb21SWarner Losh 420592ffb21SWarner Losh list = malloc(sizeof(*list), DRM_MEM_MAPS, M_ZERO | M_NOWAIT); 421592ffb21SWarner Losh if (!list) { 422592ffb21SWarner Losh if (map->type == _DRM_REGISTERS) 423592ffb21SWarner Losh drm_core_ioremapfree(map, dev); 424592ffb21SWarner Losh free(map, DRM_MEM_MAPS); 425592ffb21SWarner Losh return -EINVAL; 426592ffb21SWarner Losh } 427592ffb21SWarner Losh list->map = map; 428592ffb21SWarner Losh 429592ffb21SWarner Losh DRM_LOCK(dev); 430592ffb21SWarner Losh list_add(&list->head, &dev->maplist); 431592ffb21SWarner Losh 432592ffb21SWarner Losh /* Assign a 32-bit handle */ 433592ffb21SWarner Losh /* We do it here so that dev->struct_mutex protects the increment */ 434592ffb21SWarner Losh user_token = (map->type == _DRM_SHM) ? (unsigned long)map->handle : 435592ffb21SWarner Losh map->offset; 436592ffb21SWarner Losh ret = drm_map_handle(dev, &list->hash, user_token, 0, 437592ffb21SWarner Losh (map->type == _DRM_SHM)); 438592ffb21SWarner Losh if (ret) { 439592ffb21SWarner Losh if (map->type == _DRM_REGISTERS) 440592ffb21SWarner Losh drm_core_ioremapfree(map, dev); 441592ffb21SWarner Losh free(map, DRM_MEM_MAPS); 442592ffb21SWarner Losh free(list, DRM_MEM_MAPS); 443592ffb21SWarner Losh DRM_UNLOCK(dev); 444592ffb21SWarner Losh return ret; 445592ffb21SWarner Losh } 446592ffb21SWarner Losh 447592ffb21SWarner Losh list->user_token = list->hash.key << PAGE_SHIFT; 448592ffb21SWarner Losh DRM_UNLOCK(dev); 449592ffb21SWarner Losh 450592ffb21SWarner Losh if (!(map->flags & _DRM_DRIVER)) 451592ffb21SWarner Losh list->master = dev->primary->master; 452592ffb21SWarner Losh *maplist = list; 453592ffb21SWarner Losh return 0; 454592ffb21SWarner Losh } 455592ffb21SWarner Losh 456592ffb21SWarner Losh int drm_addmap(struct drm_device * dev, resource_size_t offset, 457592ffb21SWarner Losh unsigned int size, enum drm_map_type type, 458592ffb21SWarner Losh enum drm_map_flags flags, struct drm_local_map ** map_ptr) 459592ffb21SWarner Losh { 460592ffb21SWarner Losh struct drm_map_list *list; 461592ffb21SWarner Losh int rc; 462592ffb21SWarner Losh 463592ffb21SWarner Losh rc = drm_addmap_core(dev, offset, size, type, flags, &list); 464592ffb21SWarner Losh if (!rc) 465592ffb21SWarner Losh *map_ptr = list->map; 466592ffb21SWarner Losh return rc; 467592ffb21SWarner Losh } 468592ffb21SWarner Losh 469592ffb21SWarner Losh EXPORT_SYMBOL(drm_addmap); 470592ffb21SWarner Losh 471592ffb21SWarner Losh /** 472592ffb21SWarner Losh * Ioctl to specify a range of memory that is available for mapping by a 473592ffb21SWarner Losh * non-root process. 474592ffb21SWarner Losh * 475592ffb21SWarner Losh * \param inode device inode. 476592ffb21SWarner Losh * \param file_priv DRM file private. 477592ffb21SWarner Losh * \param cmd command. 478592ffb21SWarner Losh * \param arg pointer to a drm_map structure. 479592ffb21SWarner Losh * \return zero on success or a negative value on error. 480592ffb21SWarner Losh * 481592ffb21SWarner Losh */ 482592ffb21SWarner Losh int drm_addmap_ioctl(struct drm_device *dev, void *data, 483592ffb21SWarner Losh struct drm_file *file_priv) 484592ffb21SWarner Losh { 485592ffb21SWarner Losh struct drm_map *map = data; 486592ffb21SWarner Losh struct drm_map_list *maplist; 487592ffb21SWarner Losh int err; 488592ffb21SWarner Losh 489592ffb21SWarner Losh if (!(DRM_SUSER(DRM_CURPROC) || map->type == _DRM_AGP || map->type == _DRM_SHM)) 490592ffb21SWarner Losh return -EPERM; 491592ffb21SWarner Losh 492592ffb21SWarner Losh err = drm_addmap_core(dev, map->offset, map->size, map->type, 493592ffb21SWarner Losh map->flags, &maplist); 494592ffb21SWarner Losh 495592ffb21SWarner Losh if (err) 496592ffb21SWarner Losh return err; 497592ffb21SWarner Losh 498592ffb21SWarner Losh /* avoid a warning on 64-bit, this casting isn't very nice, but the API is set so too late */ 499592ffb21SWarner Losh map->handle = (void *)(unsigned long)maplist->user_token; 500592ffb21SWarner Losh return 0; 501592ffb21SWarner Losh } 502592ffb21SWarner Losh 503592ffb21SWarner Losh /** 504592ffb21SWarner Losh * Remove a map private from list and deallocate resources if the mapping 505592ffb21SWarner Losh * isn't in use. 506592ffb21SWarner Losh * 507592ffb21SWarner Losh * Searches the map on drm_device::maplist, removes it from the list, see if 508592ffb21SWarner Losh * its being used, and free any associate resource (such as MTRR's) if it's not 509592ffb21SWarner Losh * being on use. 510592ffb21SWarner Losh * 511592ffb21SWarner Losh * \sa drm_addmap 512592ffb21SWarner Losh */ 513592ffb21SWarner Losh int drm_rmmap_locked(struct drm_device *dev, struct drm_local_map *map) 514592ffb21SWarner Losh { 515592ffb21SWarner Losh struct drm_map_list *r_list = NULL, *list_t; 516592ffb21SWarner Losh int found = 0; 517592ffb21SWarner Losh struct drm_master *master; 518592ffb21SWarner Losh 519592ffb21SWarner Losh /* Find the list entry for the map and remove it */ 520592ffb21SWarner Losh list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) { 521592ffb21SWarner Losh if (r_list->map == map) { 522592ffb21SWarner Losh master = r_list->master; 523592ffb21SWarner Losh list_del(&r_list->head); 524592ffb21SWarner Losh drm_ht_remove_key(&dev->map_hash, 525592ffb21SWarner Losh r_list->user_token >> PAGE_SHIFT); 526592ffb21SWarner Losh free(r_list, DRM_MEM_MAPS); 527592ffb21SWarner Losh found = 1; 528592ffb21SWarner Losh break; 529592ffb21SWarner Losh } 530592ffb21SWarner Losh } 531592ffb21SWarner Losh 532592ffb21SWarner Losh if (!found) 533592ffb21SWarner Losh return -EINVAL; 534592ffb21SWarner Losh 535592ffb21SWarner Losh switch (map->type) { 536592ffb21SWarner Losh case _DRM_REGISTERS: 537592ffb21SWarner Losh drm_core_ioremapfree(map, dev); 538592ffb21SWarner Losh /* FALLTHROUGH */ 539592ffb21SWarner Losh case _DRM_FRAME_BUFFER: 540592ffb21SWarner Losh if (drm_core_has_MTRR(dev) && map->mtrr >= 0) { 541592ffb21SWarner Losh int retcode; 542592ffb21SWarner Losh retcode = drm_mtrr_del(map->mtrr, map->offset, 543592ffb21SWarner Losh map->size, DRM_MTRR_WC); 544592ffb21SWarner Losh DRM_DEBUG("mtrr_del=%d\n", retcode); 545592ffb21SWarner Losh } 546592ffb21SWarner Losh break; 547592ffb21SWarner Losh case _DRM_SHM: 548592ffb21SWarner Losh free(map->handle, DRM_MEM_MAPS); 549592ffb21SWarner Losh if (master) { 550592ffb21SWarner Losh if (dev->sigdata.lock == master->lock.hw_lock) 551592ffb21SWarner Losh dev->sigdata.lock = NULL; 552592ffb21SWarner Losh master->lock.hw_lock = NULL; /* SHM removed */ 553592ffb21SWarner Losh master->lock.file_priv = NULL; 554592ffb21SWarner Losh DRM_WAKEUP_INT((void *)&master->lock.lock_queue); 555592ffb21SWarner Losh } 556592ffb21SWarner Losh break; 557592ffb21SWarner Losh case _DRM_AGP: 558592ffb21SWarner Losh case _DRM_SCATTER_GATHER: 559592ffb21SWarner Losh break; 560592ffb21SWarner Losh case _DRM_CONSISTENT: 561592ffb21SWarner Losh drm_pci_free(dev, map->dmah); 562592ffb21SWarner Losh break; 563592ffb21SWarner Losh case _DRM_GEM: 564592ffb21SWarner Losh DRM_ERROR("tried to rmmap GEM object\n"); 565592ffb21SWarner Losh break; 566592ffb21SWarner Losh } 567592ffb21SWarner Losh free(map, DRM_MEM_MAPS); 568592ffb21SWarner Losh 569592ffb21SWarner Losh return 0; 570592ffb21SWarner Losh } 571592ffb21SWarner Losh EXPORT_SYMBOL(drm_rmmap_locked); 572592ffb21SWarner Losh 573592ffb21SWarner Losh int drm_rmmap(struct drm_device *dev, struct drm_local_map *map) 574592ffb21SWarner Losh { 575592ffb21SWarner Losh int ret; 576592ffb21SWarner Losh 577592ffb21SWarner Losh DRM_LOCK(dev); 578592ffb21SWarner Losh ret = drm_rmmap_locked(dev, map); 579592ffb21SWarner Losh DRM_UNLOCK(dev); 580592ffb21SWarner Losh 581592ffb21SWarner Losh return ret; 582592ffb21SWarner Losh } 583592ffb21SWarner Losh EXPORT_SYMBOL(drm_rmmap); 584592ffb21SWarner Losh 585592ffb21SWarner Losh /* The rmmap ioctl appears to be unnecessary. All mappings are torn down on 586592ffb21SWarner Losh * the last close of the device, and this is necessary for cleanup when things 587592ffb21SWarner Losh * exit uncleanly. Therefore, having userland manually remove mappings seems 588592ffb21SWarner Losh * like a pointless exercise since they're going away anyway. 589592ffb21SWarner Losh * 590592ffb21SWarner Losh * One use case might be after addmap is allowed for normal users for SHM and 591592ffb21SWarner Losh * gets used by drivers that the server doesn't need to care about. This seems 592592ffb21SWarner Losh * unlikely. 593592ffb21SWarner Losh * 594592ffb21SWarner Losh * \param inode device inode. 595592ffb21SWarner Losh * \param file_priv DRM file private. 596592ffb21SWarner Losh * \param cmd command. 597592ffb21SWarner Losh * \param arg pointer to a struct drm_map structure. 598592ffb21SWarner Losh * \return zero on success or a negative value on error. 599592ffb21SWarner Losh */ 600592ffb21SWarner Losh int drm_rmmap_ioctl(struct drm_device *dev, void *data, 601592ffb21SWarner Losh struct drm_file *file_priv) 602592ffb21SWarner Losh { 603592ffb21SWarner Losh struct drm_map *request = data; 604592ffb21SWarner Losh struct drm_local_map *map = NULL; 605592ffb21SWarner Losh struct drm_map_list *r_list; 606592ffb21SWarner Losh int ret; 607592ffb21SWarner Losh 608592ffb21SWarner Losh DRM_LOCK(dev); 609592ffb21SWarner Losh list_for_each_entry(r_list, &dev->maplist, head) { 610592ffb21SWarner Losh if (r_list->map && 611592ffb21SWarner Losh r_list->user_token == (unsigned long)request->handle && 612592ffb21SWarner Losh r_list->map->flags & _DRM_REMOVABLE) { 613592ffb21SWarner Losh map = r_list->map; 614592ffb21SWarner Losh break; 615592ffb21SWarner Losh } 616592ffb21SWarner Losh } 617592ffb21SWarner Losh 618592ffb21SWarner Losh /* List has wrapped around to the head pointer, or its empty we didn't 619592ffb21SWarner Losh * find anything. 620592ffb21SWarner Losh */ 621592ffb21SWarner Losh if (list_empty(&dev->maplist) || !map) { 622592ffb21SWarner Losh DRM_UNLOCK(dev); 623592ffb21SWarner Losh return -EINVAL; 624592ffb21SWarner Losh } 625592ffb21SWarner Losh 626592ffb21SWarner Losh /* Register and framebuffer maps are permanent */ 627592ffb21SWarner Losh if ((map->type == _DRM_REGISTERS) || (map->type == _DRM_FRAME_BUFFER)) { 628592ffb21SWarner Losh DRM_UNLOCK(dev); 629592ffb21SWarner Losh return 0; 630592ffb21SWarner Losh } 631592ffb21SWarner Losh 632592ffb21SWarner Losh ret = drm_rmmap_locked(dev, map); 633592ffb21SWarner Losh 634592ffb21SWarner Losh DRM_UNLOCK(dev); 635592ffb21SWarner Losh 636592ffb21SWarner Losh return ret; 637592ffb21SWarner Losh } 638592ffb21SWarner Losh 639592ffb21SWarner Losh /** 640592ffb21SWarner Losh * Cleanup after an error on one of the addbufs() functions. 641592ffb21SWarner Losh * 642592ffb21SWarner Losh * \param dev DRM device. 643592ffb21SWarner Losh * \param entry buffer entry where the error occurred. 644592ffb21SWarner Losh * 645592ffb21SWarner Losh * Frees any pages and buffers associated with the given entry. 646592ffb21SWarner Losh */ 647592ffb21SWarner Losh static void drm_cleanup_buf_error(struct drm_device * dev, 648592ffb21SWarner Losh struct drm_buf_entry * entry) 649592ffb21SWarner Losh { 650592ffb21SWarner Losh int i; 651592ffb21SWarner Losh 652592ffb21SWarner Losh if (entry->seg_count) { 653592ffb21SWarner Losh for (i = 0; i < entry->seg_count; i++) { 654592ffb21SWarner Losh if (entry->seglist[i]) { 655592ffb21SWarner Losh drm_pci_free(dev, entry->seglist[i]); 656592ffb21SWarner Losh } 657592ffb21SWarner Losh } 658592ffb21SWarner Losh free(entry->seglist, DRM_MEM_SEGS); 659592ffb21SWarner Losh 660592ffb21SWarner Losh entry->seg_count = 0; 661592ffb21SWarner Losh } 662592ffb21SWarner Losh 663592ffb21SWarner Losh if (entry->buf_count) { 664592ffb21SWarner Losh for (i = 0; i < entry->buf_count; i++) { 665592ffb21SWarner Losh free(entry->buflist[i].dev_private, DRM_MEM_BUFS); 666592ffb21SWarner Losh } 667592ffb21SWarner Losh free(entry->buflist, DRM_MEM_BUFS); 668592ffb21SWarner Losh 669592ffb21SWarner Losh entry->buf_count = 0; 670592ffb21SWarner Losh } 671592ffb21SWarner Losh } 672592ffb21SWarner Losh 673592ffb21SWarner Losh #if __OS_HAS_AGP 674592ffb21SWarner Losh /** 675592ffb21SWarner Losh * Add AGP buffers for DMA transfers. 676592ffb21SWarner Losh * 677592ffb21SWarner Losh * \param dev struct drm_device to which the buffers are to be added. 678592ffb21SWarner Losh * \param request pointer to a struct drm_buf_desc describing the request. 679592ffb21SWarner Losh * \return zero on success or a negative number on failure. 680592ffb21SWarner Losh * 681592ffb21SWarner Losh * After some sanity checks creates a drm_buf structure for each buffer and 682592ffb21SWarner Losh * reallocates the buffer list of the same size order to accommodate the new 683592ffb21SWarner Losh * buffers. 684592ffb21SWarner Losh */ 685592ffb21SWarner Losh int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request) 686592ffb21SWarner Losh { 687592ffb21SWarner Losh struct drm_device_dma *dma = dev->dma; 688592ffb21SWarner Losh struct drm_buf_entry *entry; 689592ffb21SWarner Losh struct drm_agp_mem *agp_entry; 690592ffb21SWarner Losh struct drm_buf *buf; 691592ffb21SWarner Losh unsigned long offset; 692592ffb21SWarner Losh unsigned long agp_offset; 693592ffb21SWarner Losh int count; 694592ffb21SWarner Losh int order; 695592ffb21SWarner Losh int size; 696592ffb21SWarner Losh int alignment; 697592ffb21SWarner Losh int page_order; 698592ffb21SWarner Losh int total; 699592ffb21SWarner Losh int byte_count; 700592ffb21SWarner Losh int i, valid; 701592ffb21SWarner Losh struct drm_buf **temp_buflist; 702592ffb21SWarner Losh 703592ffb21SWarner Losh if (!dma) 704592ffb21SWarner Losh return -EINVAL; 705592ffb21SWarner Losh 706592ffb21SWarner Losh count = request->count; 707592ffb21SWarner Losh order = drm_order(request->size); 708592ffb21SWarner Losh size = 1 << order; 709592ffb21SWarner Losh 710592ffb21SWarner Losh alignment = (request->flags & _DRM_PAGE_ALIGN) 711592ffb21SWarner Losh ? PAGE_ALIGN(size) : size; 712592ffb21SWarner Losh page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; 713592ffb21SWarner Losh total = PAGE_SIZE << page_order; 714592ffb21SWarner Losh 715592ffb21SWarner Losh byte_count = 0; 716592ffb21SWarner Losh agp_offset = dev->agp->base + request->agp_start; 717592ffb21SWarner Losh 718592ffb21SWarner Losh DRM_DEBUG("count: %d\n", count); 719592ffb21SWarner Losh DRM_DEBUG("order: %d\n", order); 720592ffb21SWarner Losh DRM_DEBUG("size: %d\n", size); 721592ffb21SWarner Losh DRM_DEBUG("agp_offset: %lx\n", agp_offset); 722592ffb21SWarner Losh DRM_DEBUG("alignment: %d\n", alignment); 723592ffb21SWarner Losh DRM_DEBUG("page_order: %d\n", page_order); 724592ffb21SWarner Losh DRM_DEBUG("total: %d\n", total); 725592ffb21SWarner Losh 726592ffb21SWarner Losh if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) 727592ffb21SWarner Losh return -EINVAL; 728592ffb21SWarner Losh 729592ffb21SWarner Losh /* Make sure buffers are located in AGP memory that we own */ 730592ffb21SWarner Losh valid = 0; 731592ffb21SWarner Losh list_for_each_entry(agp_entry, &dev->agp->memory, head) { 732592ffb21SWarner Losh if ((agp_offset >= agp_entry->bound) && 733592ffb21SWarner Losh (agp_offset + total * count <= agp_entry->bound + agp_entry->pages * PAGE_SIZE)) { 734592ffb21SWarner Losh valid = 1; 735592ffb21SWarner Losh break; 736592ffb21SWarner Losh } 737592ffb21SWarner Losh } 738592ffb21SWarner Losh if (!list_empty(&dev->agp->memory) && !valid) { 739592ffb21SWarner Losh DRM_DEBUG("zone invalid\n"); 740592ffb21SWarner Losh return -EINVAL; 741592ffb21SWarner Losh } 742592ffb21SWarner Losh mtx_lock(&dev->count_lock); 743592ffb21SWarner Losh if (dev->buf_use) { 744592ffb21SWarner Losh mtx_unlock(&dev->count_lock); 745592ffb21SWarner Losh return -EBUSY; 746592ffb21SWarner Losh } 747592ffb21SWarner Losh atomic_inc(&dev->buf_alloc); 748592ffb21SWarner Losh mtx_unlock(&dev->count_lock); 749592ffb21SWarner Losh 750592ffb21SWarner Losh DRM_LOCK(dev); 751592ffb21SWarner Losh entry = &dma->bufs[order]; 752592ffb21SWarner Losh if (entry->buf_count) { 753592ffb21SWarner Losh DRM_UNLOCK(dev); 754592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 755592ffb21SWarner Losh return -ENOMEM; /* May only call once for each order */ 756592ffb21SWarner Losh } 757592ffb21SWarner Losh 758592ffb21SWarner Losh if (count < 0 || count > 4096) { 759592ffb21SWarner Losh DRM_UNLOCK(dev); 760592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 761592ffb21SWarner Losh return -EINVAL; 762592ffb21SWarner Losh } 763592ffb21SWarner Losh 764592ffb21SWarner Losh entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS, 765592ffb21SWarner Losh M_NOWAIT | M_ZERO); 766592ffb21SWarner Losh if (!entry->buflist) { 767592ffb21SWarner Losh DRM_UNLOCK(dev); 768592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 769592ffb21SWarner Losh return -ENOMEM; 770592ffb21SWarner Losh } 771592ffb21SWarner Losh 772592ffb21SWarner Losh entry->buf_size = size; 773592ffb21SWarner Losh entry->page_order = page_order; 774592ffb21SWarner Losh 775592ffb21SWarner Losh offset = 0; 776592ffb21SWarner Losh 777592ffb21SWarner Losh while (entry->buf_count < count) { 778592ffb21SWarner Losh buf = &entry->buflist[entry->buf_count]; 779592ffb21SWarner Losh buf->idx = dma->buf_count + entry->buf_count; 780592ffb21SWarner Losh buf->total = alignment; 781592ffb21SWarner Losh buf->order = order; 782592ffb21SWarner Losh buf->used = 0; 783592ffb21SWarner Losh 784592ffb21SWarner Losh buf->offset = (dma->byte_count + offset); 785592ffb21SWarner Losh buf->bus_address = agp_offset + offset; 786592ffb21SWarner Losh buf->address = (void *)(agp_offset + offset); 787592ffb21SWarner Losh buf->next = NULL; 788592ffb21SWarner Losh buf->waiting = 0; 789592ffb21SWarner Losh buf->pending = 0; 790592ffb21SWarner Losh buf->file_priv = NULL; 791592ffb21SWarner Losh 792592ffb21SWarner Losh buf->dev_priv_size = dev->driver->dev_priv_size; 793592ffb21SWarner Losh buf->dev_private = malloc(buf->dev_priv_size, DRM_MEM_BUFS, 794592ffb21SWarner Losh M_NOWAIT | M_ZERO); 795592ffb21SWarner Losh if (!buf->dev_private) { 796592ffb21SWarner Losh /* Set count correctly so we free the proper amount. */ 797592ffb21SWarner Losh entry->buf_count = count; 798592ffb21SWarner Losh drm_cleanup_buf_error(dev, entry); 799592ffb21SWarner Losh DRM_UNLOCK(dev); 800592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 801592ffb21SWarner Losh return -ENOMEM; 802592ffb21SWarner Losh } 803592ffb21SWarner Losh 804592ffb21SWarner Losh DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address); 805592ffb21SWarner Losh 806592ffb21SWarner Losh offset += alignment; 807592ffb21SWarner Losh entry->buf_count++; 808592ffb21SWarner Losh byte_count += PAGE_SIZE << page_order; 809592ffb21SWarner Losh } 810592ffb21SWarner Losh 811592ffb21SWarner Losh DRM_DEBUG("byte_count: %d\n", byte_count); 812592ffb21SWarner Losh 813592ffb21SWarner Losh temp_buflist = realloc(dma->buflist, 814592ffb21SWarner Losh (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), 815592ffb21SWarner Losh DRM_MEM_BUFS, M_NOWAIT); 816592ffb21SWarner Losh if (!temp_buflist) { 817592ffb21SWarner Losh /* Free the entry because it isn't valid */ 818592ffb21SWarner Losh drm_cleanup_buf_error(dev, entry); 819592ffb21SWarner Losh DRM_UNLOCK(dev); 820592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 821592ffb21SWarner Losh return -ENOMEM; 822592ffb21SWarner Losh } 823592ffb21SWarner Losh dma->buflist = temp_buflist; 824592ffb21SWarner Losh 825592ffb21SWarner Losh for (i = 0; i < entry->buf_count; i++) { 826592ffb21SWarner Losh dma->buflist[i + dma->buf_count] = &entry->buflist[i]; 827592ffb21SWarner Losh } 828592ffb21SWarner Losh 829592ffb21SWarner Losh dma->buf_count += entry->buf_count; 830592ffb21SWarner Losh dma->seg_count += entry->seg_count; 831592ffb21SWarner Losh dma->page_count += byte_count >> PAGE_SHIFT; 832592ffb21SWarner Losh dma->byte_count += byte_count; 833592ffb21SWarner Losh 834592ffb21SWarner Losh DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count); 835592ffb21SWarner Losh DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count); 836592ffb21SWarner Losh 837592ffb21SWarner Losh DRM_UNLOCK(dev); 838592ffb21SWarner Losh 839592ffb21SWarner Losh request->count = entry->buf_count; 840592ffb21SWarner Losh request->size = size; 841592ffb21SWarner Losh 842592ffb21SWarner Losh dma->flags = _DRM_DMA_USE_AGP; 843592ffb21SWarner Losh 844592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 845592ffb21SWarner Losh return 0; 846592ffb21SWarner Losh } 847592ffb21SWarner Losh EXPORT_SYMBOL(drm_addbufs_agp); 848592ffb21SWarner Losh #endif /* __OS_HAS_AGP */ 849592ffb21SWarner Losh 850592ffb21SWarner Losh int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request) 851592ffb21SWarner Losh { 852592ffb21SWarner Losh struct drm_device_dma *dma = dev->dma; 853592ffb21SWarner Losh int count; 854592ffb21SWarner Losh int order; 855592ffb21SWarner Losh int size; 856592ffb21SWarner Losh int total; 857592ffb21SWarner Losh int page_order; 858592ffb21SWarner Losh struct drm_buf_entry *entry; 859592ffb21SWarner Losh drm_dma_handle_t *dmah; 860592ffb21SWarner Losh struct drm_buf *buf; 861592ffb21SWarner Losh int alignment; 862592ffb21SWarner Losh unsigned long offset; 863592ffb21SWarner Losh int i; 864592ffb21SWarner Losh int byte_count; 865592ffb21SWarner Losh int page_count; 866592ffb21SWarner Losh unsigned long *temp_pagelist; 867592ffb21SWarner Losh struct drm_buf **temp_buflist; 868592ffb21SWarner Losh 869592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_PCI_DMA)) 870592ffb21SWarner Losh return -EINVAL; 871592ffb21SWarner Losh 872592ffb21SWarner Losh if (!dma) 873592ffb21SWarner Losh return -EINVAL; 874592ffb21SWarner Losh 875592ffb21SWarner Losh if (!DRM_SUSER(DRM_CURPROC)) 876592ffb21SWarner Losh return -EPERM; 877592ffb21SWarner Losh 878592ffb21SWarner Losh count = request->count; 879592ffb21SWarner Losh order = drm_order(request->size); 880592ffb21SWarner Losh size = 1 << order; 881592ffb21SWarner Losh 882592ffb21SWarner Losh DRM_DEBUG("count=%d, size=%d (%d), order=%d\n", 883592ffb21SWarner Losh request->count, request->size, size, order); 884592ffb21SWarner Losh 885592ffb21SWarner Losh if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) 886592ffb21SWarner Losh return -EINVAL; 887592ffb21SWarner Losh 888592ffb21SWarner Losh alignment = (request->flags & _DRM_PAGE_ALIGN) 889592ffb21SWarner Losh ? PAGE_ALIGN(size) : size; 890592ffb21SWarner Losh page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; 891592ffb21SWarner Losh total = PAGE_SIZE << page_order; 892592ffb21SWarner Losh 893592ffb21SWarner Losh mtx_lock(&dev->count_lock); 894592ffb21SWarner Losh if (dev->buf_use) { 895592ffb21SWarner Losh mtx_unlock(&dev->count_lock); 896592ffb21SWarner Losh return -EBUSY; 897592ffb21SWarner Losh } 898592ffb21SWarner Losh atomic_inc(&dev->buf_alloc); 899592ffb21SWarner Losh mtx_unlock(&dev->count_lock); 900592ffb21SWarner Losh 901592ffb21SWarner Losh DRM_LOCK(dev); 902592ffb21SWarner Losh entry = &dma->bufs[order]; 903592ffb21SWarner Losh if (entry->buf_count) { 904592ffb21SWarner Losh DRM_UNLOCK(dev); 905592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 906592ffb21SWarner Losh return -ENOMEM; /* May only call once for each order */ 907592ffb21SWarner Losh } 908592ffb21SWarner Losh 909592ffb21SWarner Losh if (count < 0 || count > 4096) { 910592ffb21SWarner Losh DRM_UNLOCK(dev); 911592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 912592ffb21SWarner Losh return -EINVAL; 913592ffb21SWarner Losh } 914592ffb21SWarner Losh 915592ffb21SWarner Losh entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS, 916592ffb21SWarner Losh M_NOWAIT | M_ZERO); 917592ffb21SWarner Losh if (!entry->buflist) { 918592ffb21SWarner Losh DRM_UNLOCK(dev); 919592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 920592ffb21SWarner Losh return -ENOMEM; 921592ffb21SWarner Losh } 922592ffb21SWarner Losh 923592ffb21SWarner Losh entry->seglist = malloc(count * sizeof(*entry->seglist), DRM_MEM_SEGS, 924592ffb21SWarner Losh M_NOWAIT | M_ZERO); 925592ffb21SWarner Losh if (!entry->seglist) { 926592ffb21SWarner Losh free(entry->buflist, DRM_MEM_BUFS); 927592ffb21SWarner Losh DRM_UNLOCK(dev); 928592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 929592ffb21SWarner Losh return -ENOMEM; 930592ffb21SWarner Losh } 931592ffb21SWarner Losh 932592ffb21SWarner Losh /* Keep the original pagelist until we know all the allocations 933592ffb21SWarner Losh * have succeeded 934592ffb21SWarner Losh */ 935592ffb21SWarner Losh temp_pagelist = malloc((dma->page_count + (count << page_order)) * 936592ffb21SWarner Losh sizeof(*dma->pagelist), DRM_MEM_PAGES, M_NOWAIT); 937592ffb21SWarner Losh if (!temp_pagelist) { 938592ffb21SWarner Losh free(entry->buflist, DRM_MEM_BUFS); 939592ffb21SWarner Losh free(entry->seglist, DRM_MEM_SEGS); 940592ffb21SWarner Losh DRM_UNLOCK(dev); 941592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 942592ffb21SWarner Losh return -ENOMEM; 943592ffb21SWarner Losh } 944592ffb21SWarner Losh memcpy(temp_pagelist, 945592ffb21SWarner Losh dma->pagelist, dma->page_count * sizeof(*dma->pagelist)); 946592ffb21SWarner Losh DRM_DEBUG("pagelist: %d entries\n", 947592ffb21SWarner Losh dma->page_count + (count << page_order)); 948592ffb21SWarner Losh 949592ffb21SWarner Losh entry->buf_size = size; 950592ffb21SWarner Losh entry->page_order = page_order; 951592ffb21SWarner Losh byte_count = 0; 952592ffb21SWarner Losh page_count = 0; 953592ffb21SWarner Losh 954592ffb21SWarner Losh while (entry->buf_count < count) { 955592ffb21SWarner Losh 956592ffb21SWarner Losh dmah = drm_pci_alloc(dev, PAGE_SIZE << page_order, 0x1000, BUS_SPACE_MAXADDR); 957592ffb21SWarner Losh 958592ffb21SWarner Losh if (!dmah) { 959592ffb21SWarner Losh /* Set count correctly so we free the proper amount. */ 960592ffb21SWarner Losh entry->buf_count = count; 961592ffb21SWarner Losh entry->seg_count = count; 962592ffb21SWarner Losh drm_cleanup_buf_error(dev, entry); 963592ffb21SWarner Losh free(temp_pagelist, DRM_MEM_PAGES); 964592ffb21SWarner Losh DRM_UNLOCK(dev); 965592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 966592ffb21SWarner Losh return -ENOMEM; 967592ffb21SWarner Losh } 968592ffb21SWarner Losh entry->seglist[entry->seg_count++] = dmah; 969592ffb21SWarner Losh for (i = 0; i < (1 << page_order); i++) { 970592ffb21SWarner Losh DRM_DEBUG("page %d @ 0x%08lx\n", 971592ffb21SWarner Losh dma->page_count + page_count, 972592ffb21SWarner Losh (unsigned long)dmah->vaddr + PAGE_SIZE * i); 973592ffb21SWarner Losh temp_pagelist[dma->page_count + page_count++] 974592ffb21SWarner Losh = (unsigned long)dmah->vaddr + PAGE_SIZE * i; 975592ffb21SWarner Losh } 976592ffb21SWarner Losh for (offset = 0; 977592ffb21SWarner Losh offset + size <= total && entry->buf_count < count; 978592ffb21SWarner Losh offset += alignment, ++entry->buf_count) { 979592ffb21SWarner Losh buf = &entry->buflist[entry->buf_count]; 980592ffb21SWarner Losh buf->idx = dma->buf_count + entry->buf_count; 981592ffb21SWarner Losh buf->total = alignment; 982592ffb21SWarner Losh buf->order = order; 983592ffb21SWarner Losh buf->used = 0; 984592ffb21SWarner Losh buf->offset = (dma->byte_count + byte_count + offset); 985592ffb21SWarner Losh buf->address = (void *)((char *)dmah->vaddr + offset); 986592ffb21SWarner Losh buf->bus_address = dmah->busaddr + offset; 987592ffb21SWarner Losh buf->next = NULL; 988592ffb21SWarner Losh buf->waiting = 0; 989592ffb21SWarner Losh buf->pending = 0; 990592ffb21SWarner Losh buf->file_priv = NULL; 991592ffb21SWarner Losh 992592ffb21SWarner Losh buf->dev_priv_size = dev->driver->dev_priv_size; 993592ffb21SWarner Losh buf->dev_private = malloc(buf->dev_priv_size, 994592ffb21SWarner Losh DRM_MEM_BUFS, M_NOWAIT | M_ZERO); 995592ffb21SWarner Losh if (!buf->dev_private) { 996592ffb21SWarner Losh /* Set count correctly so we free the proper amount. */ 997592ffb21SWarner Losh entry->buf_count = count; 998592ffb21SWarner Losh entry->seg_count = count; 999592ffb21SWarner Losh drm_cleanup_buf_error(dev, entry); 1000592ffb21SWarner Losh free(temp_pagelist, DRM_MEM_PAGES); 1001592ffb21SWarner Losh DRM_UNLOCK(dev); 1002592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 1003592ffb21SWarner Losh return -ENOMEM; 1004592ffb21SWarner Losh } 1005592ffb21SWarner Losh 1006592ffb21SWarner Losh DRM_DEBUG("buffer %d @ %p\n", 1007592ffb21SWarner Losh entry->buf_count, buf->address); 1008592ffb21SWarner Losh } 1009592ffb21SWarner Losh byte_count += PAGE_SIZE << page_order; 1010592ffb21SWarner Losh } 1011592ffb21SWarner Losh 1012592ffb21SWarner Losh temp_buflist = realloc(dma->buflist, 1013592ffb21SWarner Losh (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), 1014592ffb21SWarner Losh DRM_MEM_BUFS, M_NOWAIT); 1015592ffb21SWarner Losh if (!temp_buflist) { 1016592ffb21SWarner Losh /* Free the entry because it isn't valid */ 1017592ffb21SWarner Losh drm_cleanup_buf_error(dev, entry); 1018592ffb21SWarner Losh free(temp_pagelist, DRM_MEM_PAGES); 1019592ffb21SWarner Losh DRM_UNLOCK(dev); 1020592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 1021592ffb21SWarner Losh return -ENOMEM; 1022592ffb21SWarner Losh } 1023592ffb21SWarner Losh dma->buflist = temp_buflist; 1024592ffb21SWarner Losh 1025592ffb21SWarner Losh for (i = 0; i < entry->buf_count; i++) { 1026592ffb21SWarner Losh dma->buflist[i + dma->buf_count] = &entry->buflist[i]; 1027592ffb21SWarner Losh } 1028592ffb21SWarner Losh 1029592ffb21SWarner Losh /* No allocations failed, so now we can replace the original pagelist 1030592ffb21SWarner Losh * with the new one. 1031592ffb21SWarner Losh */ 1032592ffb21SWarner Losh if (dma->page_count) { 1033592ffb21SWarner Losh free(dma->pagelist, DRM_MEM_PAGES); 1034592ffb21SWarner Losh } 1035592ffb21SWarner Losh dma->pagelist = temp_pagelist; 1036592ffb21SWarner Losh 1037592ffb21SWarner Losh dma->buf_count += entry->buf_count; 1038592ffb21SWarner Losh dma->seg_count += entry->seg_count; 1039592ffb21SWarner Losh dma->page_count += entry->seg_count << page_order; 1040592ffb21SWarner Losh dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order); 1041592ffb21SWarner Losh 1042592ffb21SWarner Losh DRM_UNLOCK(dev); 1043592ffb21SWarner Losh 1044592ffb21SWarner Losh request->count = entry->buf_count; 1045592ffb21SWarner Losh request->size = size; 1046592ffb21SWarner Losh 1047592ffb21SWarner Losh if (request->flags & _DRM_PCI_BUFFER_RO) 1048592ffb21SWarner Losh dma->flags = _DRM_DMA_USE_PCI_RO; 1049592ffb21SWarner Losh 1050592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 1051592ffb21SWarner Losh return 0; 1052592ffb21SWarner Losh 1053592ffb21SWarner Losh } 1054592ffb21SWarner Losh EXPORT_SYMBOL(drm_addbufs_pci); 1055592ffb21SWarner Losh 1056592ffb21SWarner Losh static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request) 1057592ffb21SWarner Losh { 1058592ffb21SWarner Losh struct drm_device_dma *dma = dev->dma; 1059592ffb21SWarner Losh struct drm_buf_entry *entry; 1060592ffb21SWarner Losh struct drm_buf *buf; 1061592ffb21SWarner Losh unsigned long offset; 1062592ffb21SWarner Losh unsigned long agp_offset; 1063592ffb21SWarner Losh int count; 1064592ffb21SWarner Losh int order; 1065592ffb21SWarner Losh int size; 1066592ffb21SWarner Losh int alignment; 1067592ffb21SWarner Losh int page_order; 1068592ffb21SWarner Losh int total; 1069592ffb21SWarner Losh int byte_count; 1070592ffb21SWarner Losh int i; 1071592ffb21SWarner Losh struct drm_buf **temp_buflist; 1072592ffb21SWarner Losh 1073592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_SG)) 1074592ffb21SWarner Losh return -EINVAL; 1075592ffb21SWarner Losh 1076592ffb21SWarner Losh if (!dma) 1077592ffb21SWarner Losh return -EINVAL; 1078592ffb21SWarner Losh 1079592ffb21SWarner Losh if (!DRM_SUSER(DRM_CURPROC)) 1080592ffb21SWarner Losh return -EPERM; 1081592ffb21SWarner Losh 1082592ffb21SWarner Losh count = request->count; 1083592ffb21SWarner Losh order = drm_order(request->size); 1084592ffb21SWarner Losh size = 1 << order; 1085592ffb21SWarner Losh 1086592ffb21SWarner Losh alignment = (request->flags & _DRM_PAGE_ALIGN) 1087592ffb21SWarner Losh ? PAGE_ALIGN(size) : size; 1088592ffb21SWarner Losh page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; 1089592ffb21SWarner Losh total = PAGE_SIZE << page_order; 1090592ffb21SWarner Losh 1091592ffb21SWarner Losh byte_count = 0; 1092592ffb21SWarner Losh agp_offset = request->agp_start; 1093592ffb21SWarner Losh 1094592ffb21SWarner Losh DRM_DEBUG("count: %d\n", count); 1095592ffb21SWarner Losh DRM_DEBUG("order: %d\n", order); 1096592ffb21SWarner Losh DRM_DEBUG("size: %d\n", size); 1097592ffb21SWarner Losh DRM_DEBUG("agp_offset: %lu\n", agp_offset); 1098592ffb21SWarner Losh DRM_DEBUG("alignment: %d\n", alignment); 1099592ffb21SWarner Losh DRM_DEBUG("page_order: %d\n", page_order); 1100592ffb21SWarner Losh DRM_DEBUG("total: %d\n", total); 1101592ffb21SWarner Losh 1102592ffb21SWarner Losh if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) 1103592ffb21SWarner Losh return -EINVAL; 1104592ffb21SWarner Losh 1105592ffb21SWarner Losh mtx_lock(&dev->count_lock); 1106592ffb21SWarner Losh if (dev->buf_use) { 1107592ffb21SWarner Losh mtx_unlock(&dev->count_lock); 1108592ffb21SWarner Losh return -EBUSY; 1109592ffb21SWarner Losh } 1110592ffb21SWarner Losh atomic_inc(&dev->buf_alloc); 1111592ffb21SWarner Losh mtx_unlock(&dev->count_lock); 1112592ffb21SWarner Losh 1113592ffb21SWarner Losh DRM_LOCK(dev); 1114592ffb21SWarner Losh entry = &dma->bufs[order]; 1115592ffb21SWarner Losh if (entry->buf_count) { 1116592ffb21SWarner Losh DRM_UNLOCK(dev); 1117592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 1118592ffb21SWarner Losh return -ENOMEM; /* May only call once for each order */ 1119592ffb21SWarner Losh } 1120592ffb21SWarner Losh 1121592ffb21SWarner Losh if (count < 0 || count > 4096) { 1122592ffb21SWarner Losh DRM_UNLOCK(dev); 1123592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 1124592ffb21SWarner Losh return -EINVAL; 1125592ffb21SWarner Losh } 1126592ffb21SWarner Losh 1127592ffb21SWarner Losh entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS, 1128592ffb21SWarner Losh M_NOWAIT | M_ZERO); 1129592ffb21SWarner Losh if (!entry->buflist) { 1130592ffb21SWarner Losh DRM_UNLOCK(dev); 1131592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 1132592ffb21SWarner Losh return -ENOMEM; 1133592ffb21SWarner Losh } 1134592ffb21SWarner Losh 1135592ffb21SWarner Losh entry->buf_size = size; 1136592ffb21SWarner Losh entry->page_order = page_order; 1137592ffb21SWarner Losh 1138592ffb21SWarner Losh offset = 0; 1139592ffb21SWarner Losh 1140592ffb21SWarner Losh while (entry->buf_count < count) { 1141592ffb21SWarner Losh buf = &entry->buflist[entry->buf_count]; 1142592ffb21SWarner Losh buf->idx = dma->buf_count + entry->buf_count; 1143592ffb21SWarner Losh buf->total = alignment; 1144592ffb21SWarner Losh buf->order = order; 1145592ffb21SWarner Losh buf->used = 0; 1146592ffb21SWarner Losh 1147592ffb21SWarner Losh buf->offset = (dma->byte_count + offset); 1148592ffb21SWarner Losh buf->bus_address = agp_offset + offset; 1149592ffb21SWarner Losh buf->address = (void *)(agp_offset + offset 1150592ffb21SWarner Losh + (unsigned long)dev->sg->vaddr); 1151592ffb21SWarner Losh buf->next = NULL; 1152592ffb21SWarner Losh buf->waiting = 0; 1153592ffb21SWarner Losh buf->pending = 0; 1154592ffb21SWarner Losh buf->file_priv = NULL; 1155592ffb21SWarner Losh 1156592ffb21SWarner Losh buf->dev_priv_size = dev->driver->dev_priv_size; 1157592ffb21SWarner Losh buf->dev_private = malloc(buf->dev_priv_size, DRM_MEM_BUFS, 1158592ffb21SWarner Losh M_NOWAIT | M_ZERO); 1159592ffb21SWarner Losh if (!buf->dev_private) { 1160592ffb21SWarner Losh /* Set count correctly so we free the proper amount. */ 1161592ffb21SWarner Losh entry->buf_count = count; 1162592ffb21SWarner Losh drm_cleanup_buf_error(dev, entry); 1163592ffb21SWarner Losh DRM_UNLOCK(dev); 1164592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 1165592ffb21SWarner Losh return -ENOMEM; 1166592ffb21SWarner Losh } 1167592ffb21SWarner Losh 1168592ffb21SWarner Losh DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address); 1169592ffb21SWarner Losh 1170592ffb21SWarner Losh offset += alignment; 1171592ffb21SWarner Losh entry->buf_count++; 1172592ffb21SWarner Losh byte_count += PAGE_SIZE << page_order; 1173592ffb21SWarner Losh } 1174592ffb21SWarner Losh 1175592ffb21SWarner Losh DRM_DEBUG("byte_count: %d\n", byte_count); 1176592ffb21SWarner Losh 1177592ffb21SWarner Losh temp_buflist = realloc(dma->buflist, 1178592ffb21SWarner Losh (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), 1179592ffb21SWarner Losh DRM_MEM_BUFS, M_NOWAIT); 1180592ffb21SWarner Losh if (!temp_buflist) { 1181592ffb21SWarner Losh /* Free the entry because it isn't valid */ 1182592ffb21SWarner Losh drm_cleanup_buf_error(dev, entry); 1183592ffb21SWarner Losh DRM_UNLOCK(dev); 1184592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 1185592ffb21SWarner Losh return -ENOMEM; 1186592ffb21SWarner Losh } 1187592ffb21SWarner Losh dma->buflist = temp_buflist; 1188592ffb21SWarner Losh 1189592ffb21SWarner Losh for (i = 0; i < entry->buf_count; i++) { 1190592ffb21SWarner Losh dma->buflist[i + dma->buf_count] = &entry->buflist[i]; 1191592ffb21SWarner Losh } 1192592ffb21SWarner Losh 1193592ffb21SWarner Losh dma->buf_count += entry->buf_count; 1194592ffb21SWarner Losh dma->seg_count += entry->seg_count; 1195592ffb21SWarner Losh dma->page_count += byte_count >> PAGE_SHIFT; 1196592ffb21SWarner Losh dma->byte_count += byte_count; 1197592ffb21SWarner Losh 1198592ffb21SWarner Losh DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count); 1199592ffb21SWarner Losh DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count); 1200592ffb21SWarner Losh 1201592ffb21SWarner Losh DRM_UNLOCK(dev); 1202592ffb21SWarner Losh 1203592ffb21SWarner Losh request->count = entry->buf_count; 1204592ffb21SWarner Losh request->size = size; 1205592ffb21SWarner Losh 1206592ffb21SWarner Losh dma->flags = _DRM_DMA_USE_SG; 1207592ffb21SWarner Losh 1208592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 1209592ffb21SWarner Losh return 0; 1210592ffb21SWarner Losh } 1211592ffb21SWarner Losh 1212592ffb21SWarner Losh static int drm_addbufs_fb(struct drm_device * dev, struct drm_buf_desc * request) 1213592ffb21SWarner Losh { 1214592ffb21SWarner Losh struct drm_device_dma *dma = dev->dma; 1215592ffb21SWarner Losh struct drm_buf_entry *entry; 1216592ffb21SWarner Losh struct drm_buf *buf; 1217592ffb21SWarner Losh unsigned long offset; 1218592ffb21SWarner Losh unsigned long agp_offset; 1219592ffb21SWarner Losh int count; 1220592ffb21SWarner Losh int order; 1221592ffb21SWarner Losh int size; 1222592ffb21SWarner Losh int alignment; 1223592ffb21SWarner Losh int page_order; 1224592ffb21SWarner Losh int total; 1225592ffb21SWarner Losh int byte_count; 1226592ffb21SWarner Losh int i; 1227592ffb21SWarner Losh struct drm_buf **temp_buflist; 1228592ffb21SWarner Losh 1229592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_FB_DMA)) 1230592ffb21SWarner Losh return -EINVAL; 1231592ffb21SWarner Losh 1232592ffb21SWarner Losh if (!dma) 1233592ffb21SWarner Losh return -EINVAL; 1234592ffb21SWarner Losh 1235592ffb21SWarner Losh if (!DRM_SUSER(DRM_CURPROC)) 1236592ffb21SWarner Losh return -EPERM; 1237592ffb21SWarner Losh 1238592ffb21SWarner Losh count = request->count; 1239592ffb21SWarner Losh order = drm_order(request->size); 1240592ffb21SWarner Losh size = 1 << order; 1241592ffb21SWarner Losh 1242592ffb21SWarner Losh alignment = (request->flags & _DRM_PAGE_ALIGN) 1243592ffb21SWarner Losh ? PAGE_ALIGN(size) : size; 1244592ffb21SWarner Losh page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; 1245592ffb21SWarner Losh total = PAGE_SIZE << page_order; 1246592ffb21SWarner Losh 1247592ffb21SWarner Losh byte_count = 0; 1248592ffb21SWarner Losh agp_offset = request->agp_start; 1249592ffb21SWarner Losh 1250592ffb21SWarner Losh DRM_DEBUG("count: %d\n", count); 1251592ffb21SWarner Losh DRM_DEBUG("order: %d\n", order); 1252592ffb21SWarner Losh DRM_DEBUG("size: %d\n", size); 1253592ffb21SWarner Losh DRM_DEBUG("agp_offset: %lu\n", agp_offset); 1254592ffb21SWarner Losh DRM_DEBUG("alignment: %d\n", alignment); 1255592ffb21SWarner Losh DRM_DEBUG("page_order: %d\n", page_order); 1256592ffb21SWarner Losh DRM_DEBUG("total: %d\n", total); 1257592ffb21SWarner Losh 1258592ffb21SWarner Losh if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) 1259592ffb21SWarner Losh return -EINVAL; 1260592ffb21SWarner Losh 1261592ffb21SWarner Losh mtx_lock(&dev->count_lock); 1262592ffb21SWarner Losh if (dev->buf_use) { 1263592ffb21SWarner Losh mtx_unlock(&dev->count_lock); 1264592ffb21SWarner Losh return -EBUSY; 1265592ffb21SWarner Losh } 1266592ffb21SWarner Losh atomic_inc(&dev->buf_alloc); 1267592ffb21SWarner Losh mtx_unlock(&dev->count_lock); 1268592ffb21SWarner Losh 1269592ffb21SWarner Losh DRM_LOCK(dev); 1270592ffb21SWarner Losh entry = &dma->bufs[order]; 1271592ffb21SWarner Losh if (entry->buf_count) { 1272592ffb21SWarner Losh DRM_UNLOCK(dev); 1273592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 1274592ffb21SWarner Losh return -ENOMEM; /* May only call once for each order */ 1275592ffb21SWarner Losh } 1276592ffb21SWarner Losh 1277592ffb21SWarner Losh if (count < 0 || count > 4096) { 1278592ffb21SWarner Losh DRM_UNLOCK(dev); 1279592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 1280592ffb21SWarner Losh return -EINVAL; 1281592ffb21SWarner Losh } 1282592ffb21SWarner Losh 1283592ffb21SWarner Losh entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS, 1284592ffb21SWarner Losh M_NOWAIT | M_ZERO); 1285592ffb21SWarner Losh if (!entry->buflist) { 1286592ffb21SWarner Losh DRM_UNLOCK(dev); 1287592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 1288592ffb21SWarner Losh return -ENOMEM; 1289592ffb21SWarner Losh } 1290592ffb21SWarner Losh 1291592ffb21SWarner Losh entry->buf_size = size; 1292592ffb21SWarner Losh entry->page_order = page_order; 1293592ffb21SWarner Losh 1294592ffb21SWarner Losh offset = 0; 1295592ffb21SWarner Losh 1296592ffb21SWarner Losh while (entry->buf_count < count) { 1297592ffb21SWarner Losh buf = &entry->buflist[entry->buf_count]; 1298592ffb21SWarner Losh buf->idx = dma->buf_count + entry->buf_count; 1299592ffb21SWarner Losh buf->total = alignment; 1300592ffb21SWarner Losh buf->order = order; 1301592ffb21SWarner Losh buf->used = 0; 1302592ffb21SWarner Losh 1303592ffb21SWarner Losh buf->offset = (dma->byte_count + offset); 1304592ffb21SWarner Losh buf->bus_address = agp_offset + offset; 1305592ffb21SWarner Losh buf->address = (void *)(agp_offset + offset); 1306592ffb21SWarner Losh buf->next = NULL; 1307592ffb21SWarner Losh buf->waiting = 0; 1308592ffb21SWarner Losh buf->pending = 0; 1309592ffb21SWarner Losh buf->file_priv = NULL; 1310592ffb21SWarner Losh 1311592ffb21SWarner Losh buf->dev_priv_size = dev->driver->dev_priv_size; 1312592ffb21SWarner Losh buf->dev_private = malloc(buf->dev_priv_size, DRM_MEM_BUFS, 1313592ffb21SWarner Losh M_NOWAIT | M_ZERO); 1314592ffb21SWarner Losh if (!buf->dev_private) { 1315592ffb21SWarner Losh /* Set count correctly so we free the proper amount. */ 1316592ffb21SWarner Losh entry->buf_count = count; 1317592ffb21SWarner Losh drm_cleanup_buf_error(dev, entry); 1318592ffb21SWarner Losh DRM_UNLOCK(dev); 1319592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 1320592ffb21SWarner Losh return -ENOMEM; 1321592ffb21SWarner Losh } 1322592ffb21SWarner Losh 1323592ffb21SWarner Losh DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address); 1324592ffb21SWarner Losh 1325592ffb21SWarner Losh offset += alignment; 1326592ffb21SWarner Losh entry->buf_count++; 1327592ffb21SWarner Losh byte_count += PAGE_SIZE << page_order; 1328592ffb21SWarner Losh } 1329592ffb21SWarner Losh 1330592ffb21SWarner Losh DRM_DEBUG("byte_count: %d\n", byte_count); 1331592ffb21SWarner Losh 1332592ffb21SWarner Losh temp_buflist = realloc(dma->buflist, 1333592ffb21SWarner Losh (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist), 1334592ffb21SWarner Losh DRM_MEM_BUFS, M_NOWAIT); 1335592ffb21SWarner Losh if (!temp_buflist) { 1336592ffb21SWarner Losh /* Free the entry because it isn't valid */ 1337592ffb21SWarner Losh drm_cleanup_buf_error(dev, entry); 1338592ffb21SWarner Losh DRM_UNLOCK(dev); 1339592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 1340592ffb21SWarner Losh return -ENOMEM; 1341592ffb21SWarner Losh } 1342592ffb21SWarner Losh dma->buflist = temp_buflist; 1343592ffb21SWarner Losh 1344592ffb21SWarner Losh for (i = 0; i < entry->buf_count; i++) { 1345592ffb21SWarner Losh dma->buflist[i + dma->buf_count] = &entry->buflist[i]; 1346592ffb21SWarner Losh } 1347592ffb21SWarner Losh 1348592ffb21SWarner Losh dma->buf_count += entry->buf_count; 1349592ffb21SWarner Losh dma->seg_count += entry->seg_count; 1350592ffb21SWarner Losh dma->page_count += byte_count >> PAGE_SHIFT; 1351592ffb21SWarner Losh dma->byte_count += byte_count; 1352592ffb21SWarner Losh 1353592ffb21SWarner Losh DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count); 1354592ffb21SWarner Losh DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count); 1355592ffb21SWarner Losh 1356592ffb21SWarner Losh DRM_UNLOCK(dev); 1357592ffb21SWarner Losh 1358592ffb21SWarner Losh request->count = entry->buf_count; 1359592ffb21SWarner Losh request->size = size; 1360592ffb21SWarner Losh 1361592ffb21SWarner Losh dma->flags = _DRM_DMA_USE_FB; 1362592ffb21SWarner Losh 1363592ffb21SWarner Losh atomic_dec(&dev->buf_alloc); 1364592ffb21SWarner Losh return 0; 1365592ffb21SWarner Losh } 1366592ffb21SWarner Losh 1367592ffb21SWarner Losh 1368592ffb21SWarner Losh /** 1369592ffb21SWarner Losh * Add buffers for DMA transfers (ioctl). 1370592ffb21SWarner Losh * 1371592ffb21SWarner Losh * \param inode device inode. 1372592ffb21SWarner Losh * \param file_priv DRM file private. 1373592ffb21SWarner Losh * \param cmd command. 1374592ffb21SWarner Losh * \param arg pointer to a struct drm_buf_desc request. 1375592ffb21SWarner Losh * \return zero on success or a negative number on failure. 1376592ffb21SWarner Losh * 1377592ffb21SWarner Losh * According with the memory type specified in drm_buf_desc::flags and the 1378592ffb21SWarner Losh * build options, it dispatches the call either to addbufs_agp(), 1379592ffb21SWarner Losh * addbufs_sg() or addbufs_pci() for AGP, scatter-gather or consistent 1380592ffb21SWarner Losh * PCI memory respectively. 1381592ffb21SWarner Losh */ 1382592ffb21SWarner Losh int drm_addbufs(struct drm_device *dev, void *data, 1383592ffb21SWarner Losh struct drm_file *file_priv) 1384592ffb21SWarner Losh { 1385592ffb21SWarner Losh struct drm_buf_desc *request = data; 1386592ffb21SWarner Losh int ret; 1387592ffb21SWarner Losh 1388592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) 1389592ffb21SWarner Losh return -EINVAL; 1390592ffb21SWarner Losh 1391592ffb21SWarner Losh #if __OS_HAS_AGP 1392592ffb21SWarner Losh if (request->flags & _DRM_AGP_BUFFER) 1393592ffb21SWarner Losh ret = drm_addbufs_agp(dev, request); 1394592ffb21SWarner Losh else 1395592ffb21SWarner Losh #endif 1396592ffb21SWarner Losh if (request->flags & _DRM_SG_BUFFER) 1397592ffb21SWarner Losh ret = drm_addbufs_sg(dev, request); 1398592ffb21SWarner Losh else if (request->flags & _DRM_FB_BUFFER) 1399592ffb21SWarner Losh ret = drm_addbufs_fb(dev, request); 1400592ffb21SWarner Losh else 1401592ffb21SWarner Losh ret = drm_addbufs_pci(dev, request); 1402592ffb21SWarner Losh 1403592ffb21SWarner Losh return ret; 1404592ffb21SWarner Losh } 1405592ffb21SWarner Losh 1406592ffb21SWarner Losh /** 1407592ffb21SWarner Losh * Get information about the buffer mappings. 1408592ffb21SWarner Losh * 1409592ffb21SWarner Losh * This was originally mean for debugging purposes, or by a sophisticated 1410592ffb21SWarner Losh * client library to determine how best to use the available buffers (e.g., 1411592ffb21SWarner Losh * large buffers can be used for image transfer). 1412592ffb21SWarner Losh * 1413592ffb21SWarner Losh * \param inode device inode. 1414592ffb21SWarner Losh * \param file_priv DRM file private. 1415592ffb21SWarner Losh * \param cmd command. 1416592ffb21SWarner Losh * \param arg pointer to a drm_buf_info structure. 1417592ffb21SWarner Losh * \return zero on success or a negative number on failure. 1418592ffb21SWarner Losh * 1419592ffb21SWarner Losh * Increments drm_device::buf_use while holding the drm_device::count_lock 1420592ffb21SWarner Losh * lock, preventing of allocating more buffers after this call. Information 1421592ffb21SWarner Losh * about each requested buffer is then copied into user space. 1422592ffb21SWarner Losh */ 1423592ffb21SWarner Losh int drm_infobufs(struct drm_device *dev, void *data, 1424592ffb21SWarner Losh struct drm_file *file_priv) 1425592ffb21SWarner Losh { 1426592ffb21SWarner Losh struct drm_device_dma *dma = dev->dma; 1427592ffb21SWarner Losh struct drm_buf_info *request = data; 1428592ffb21SWarner Losh int i; 1429592ffb21SWarner Losh int count; 1430592ffb21SWarner Losh 1431592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) 1432592ffb21SWarner Losh return -EINVAL; 1433592ffb21SWarner Losh 1434592ffb21SWarner Losh if (!dma) 1435592ffb21SWarner Losh return -EINVAL; 1436592ffb21SWarner Losh 1437592ffb21SWarner Losh mtx_lock(&dev->count_lock); 1438592ffb21SWarner Losh if (atomic_read(&dev->buf_alloc)) { 1439592ffb21SWarner Losh mtx_unlock(&dev->count_lock); 1440592ffb21SWarner Losh return -EBUSY; 1441592ffb21SWarner Losh } 1442592ffb21SWarner Losh ++dev->buf_use; /* Can't allocate more after this call */ 1443592ffb21SWarner Losh mtx_unlock(&dev->count_lock); 1444592ffb21SWarner Losh 1445592ffb21SWarner Losh for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) { 1446592ffb21SWarner Losh if (dma->bufs[i].buf_count) 1447592ffb21SWarner Losh ++count; 1448592ffb21SWarner Losh } 1449592ffb21SWarner Losh 1450592ffb21SWarner Losh DRM_DEBUG("count = %d\n", count); 1451592ffb21SWarner Losh 1452592ffb21SWarner Losh if (request->count >= count) { 1453592ffb21SWarner Losh for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) { 1454592ffb21SWarner Losh if (dma->bufs[i].buf_count) { 1455592ffb21SWarner Losh struct drm_buf_desc __user *to = 1456592ffb21SWarner Losh &request->list[count]; 1457592ffb21SWarner Losh struct drm_buf_entry *from = &dma->bufs[i]; 1458592ffb21SWarner Losh struct drm_freelist *list = &dma->bufs[i].freelist; 1459592ffb21SWarner Losh if (copy_to_user(&to->count, 1460592ffb21SWarner Losh &from->buf_count, 1461592ffb21SWarner Losh sizeof(from->buf_count)) || 1462592ffb21SWarner Losh copy_to_user(&to->size, 1463592ffb21SWarner Losh &from->buf_size, 1464592ffb21SWarner Losh sizeof(from->buf_size)) || 1465592ffb21SWarner Losh copy_to_user(&to->low_mark, 1466592ffb21SWarner Losh &list->low_mark, 1467592ffb21SWarner Losh sizeof(list->low_mark)) || 1468592ffb21SWarner Losh copy_to_user(&to->high_mark, 1469592ffb21SWarner Losh &list->high_mark, 1470592ffb21SWarner Losh sizeof(list->high_mark))) 1471592ffb21SWarner Losh return -EFAULT; 1472592ffb21SWarner Losh 1473592ffb21SWarner Losh DRM_DEBUG("%d %d %d %d %d\n", 1474592ffb21SWarner Losh i, 1475592ffb21SWarner Losh dma->bufs[i].buf_count, 1476592ffb21SWarner Losh dma->bufs[i].buf_size, 1477592ffb21SWarner Losh dma->bufs[i].freelist.low_mark, 1478592ffb21SWarner Losh dma->bufs[i].freelist.high_mark); 1479592ffb21SWarner Losh ++count; 1480592ffb21SWarner Losh } 1481592ffb21SWarner Losh } 1482592ffb21SWarner Losh } 1483592ffb21SWarner Losh request->count = count; 1484592ffb21SWarner Losh 1485592ffb21SWarner Losh return 0; 1486592ffb21SWarner Losh } 1487592ffb21SWarner Losh 1488592ffb21SWarner Losh /** 1489592ffb21SWarner Losh * Specifies a low and high water mark for buffer allocation 1490592ffb21SWarner Losh * 1491592ffb21SWarner Losh * \param inode device inode. 1492592ffb21SWarner Losh * \param file_priv DRM file private. 1493592ffb21SWarner Losh * \param cmd command. 1494592ffb21SWarner Losh * \param arg a pointer to a drm_buf_desc structure. 1495592ffb21SWarner Losh * \return zero on success or a negative number on failure. 1496592ffb21SWarner Losh * 1497592ffb21SWarner Losh * Verifies that the size order is bounded between the admissible orders and 1498592ffb21SWarner Losh * updates the respective drm_device_dma::bufs entry low and high water mark. 1499592ffb21SWarner Losh * 1500592ffb21SWarner Losh * \note This ioctl is deprecated and mostly never used. 1501592ffb21SWarner Losh */ 1502592ffb21SWarner Losh int drm_markbufs(struct drm_device *dev, void *data, 1503592ffb21SWarner Losh struct drm_file *file_priv) 1504592ffb21SWarner Losh { 1505592ffb21SWarner Losh struct drm_device_dma *dma = dev->dma; 1506592ffb21SWarner Losh struct drm_buf_desc *request = data; 1507592ffb21SWarner Losh int order; 1508592ffb21SWarner Losh struct drm_buf_entry *entry; 1509592ffb21SWarner Losh 1510592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) 1511592ffb21SWarner Losh return -EINVAL; 1512592ffb21SWarner Losh 1513592ffb21SWarner Losh if (!dma) 1514592ffb21SWarner Losh return -EINVAL; 1515592ffb21SWarner Losh 1516592ffb21SWarner Losh DRM_DEBUG("%d, %d, %d\n", 1517592ffb21SWarner Losh request->size, request->low_mark, request->high_mark); 1518592ffb21SWarner Losh order = drm_order(request->size); 1519592ffb21SWarner Losh if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) 1520592ffb21SWarner Losh return -EINVAL; 1521592ffb21SWarner Losh entry = &dma->bufs[order]; 1522592ffb21SWarner Losh 1523592ffb21SWarner Losh if (request->low_mark < 0 || request->low_mark > entry->buf_count) 1524592ffb21SWarner Losh return -EINVAL; 1525592ffb21SWarner Losh if (request->high_mark < 0 || request->high_mark > entry->buf_count) 1526592ffb21SWarner Losh return -EINVAL; 1527592ffb21SWarner Losh 1528592ffb21SWarner Losh entry->freelist.low_mark = request->low_mark; 1529592ffb21SWarner Losh entry->freelist.high_mark = request->high_mark; 1530592ffb21SWarner Losh 1531592ffb21SWarner Losh return 0; 1532592ffb21SWarner Losh } 1533592ffb21SWarner Losh 1534592ffb21SWarner Losh /** 1535592ffb21SWarner Losh * Unreserve the buffers in list, previously reserved using drmDMA. 1536592ffb21SWarner Losh * 1537592ffb21SWarner Losh * \param inode device inode. 1538592ffb21SWarner Losh * \param file_priv DRM file private. 1539592ffb21SWarner Losh * \param cmd command. 1540592ffb21SWarner Losh * \param arg pointer to a drm_buf_free structure. 1541592ffb21SWarner Losh * \return zero on success or a negative number on failure. 1542592ffb21SWarner Losh * 1543592ffb21SWarner Losh * Calls free_buffer() for each used buffer. 1544592ffb21SWarner Losh * This function is primarily used for debugging. 1545592ffb21SWarner Losh */ 1546592ffb21SWarner Losh int drm_freebufs(struct drm_device *dev, void *data, 1547592ffb21SWarner Losh struct drm_file *file_priv) 1548592ffb21SWarner Losh { 1549592ffb21SWarner Losh struct drm_device_dma *dma = dev->dma; 1550592ffb21SWarner Losh struct drm_buf_free *request = data; 1551592ffb21SWarner Losh int i; 1552592ffb21SWarner Losh int idx; 1553592ffb21SWarner Losh struct drm_buf *buf; 1554592ffb21SWarner Losh 1555592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) 1556592ffb21SWarner Losh return -EINVAL; 1557592ffb21SWarner Losh 1558592ffb21SWarner Losh if (!dma) 1559592ffb21SWarner Losh return -EINVAL; 1560592ffb21SWarner Losh 1561592ffb21SWarner Losh DRM_DEBUG("%d\n", request->count); 1562592ffb21SWarner Losh for (i = 0; i < request->count; i++) { 1563592ffb21SWarner Losh if (copy_from_user(&idx, &request->list[i], sizeof(idx))) 1564592ffb21SWarner Losh return -EFAULT; 1565592ffb21SWarner Losh if (idx < 0 || idx >= dma->buf_count) { 1566592ffb21SWarner Losh DRM_ERROR("Index %d (of %d max)\n", 1567592ffb21SWarner Losh idx, dma->buf_count - 1); 1568592ffb21SWarner Losh return -EINVAL; 1569592ffb21SWarner Losh } 1570592ffb21SWarner Losh buf = dma->buflist[idx]; 1571592ffb21SWarner Losh if (buf->file_priv != file_priv) { 1572592ffb21SWarner Losh DRM_ERROR("Process %d freeing buffer not owned\n", 1573592ffb21SWarner Losh DRM_CURRENTPID); 1574592ffb21SWarner Losh return -EINVAL; 1575592ffb21SWarner Losh } 1576592ffb21SWarner Losh drm_free_buffer(dev, buf); 1577592ffb21SWarner Losh } 1578592ffb21SWarner Losh 1579592ffb21SWarner Losh return 0; 1580592ffb21SWarner Losh } 1581592ffb21SWarner Losh 1582592ffb21SWarner Losh /** 1583592ffb21SWarner Losh * Maps all of the DMA buffers into client-virtual space (ioctl). 1584592ffb21SWarner Losh * 1585592ffb21SWarner Losh * \param inode device inode. 1586592ffb21SWarner Losh * \param file_priv DRM file private. 1587592ffb21SWarner Losh * \param cmd command. 1588592ffb21SWarner Losh * \param arg pointer to a drm_buf_map structure. 1589592ffb21SWarner Losh * \return zero on success or a negative number on failure. 1590592ffb21SWarner Losh * 1591592ffb21SWarner Losh * Maps the AGP, SG or PCI buffer region with vm_mmap(), and copies information 1592592ffb21SWarner Losh * about each buffer into user space. For PCI buffers, it calls vm_mmap() with 1593592ffb21SWarner Losh * offset equal to 0, which drm_mmap() interpretes as PCI buffers and calls 1594592ffb21SWarner Losh * drm_mmap_dma(). 1595592ffb21SWarner Losh */ 1596592ffb21SWarner Losh int drm_mapbufs(struct drm_device *dev, void *data, 1597592ffb21SWarner Losh struct drm_file *file_priv) 1598592ffb21SWarner Losh { 1599592ffb21SWarner Losh struct drm_device_dma *dma = dev->dma; 1600592ffb21SWarner Losh int retcode = 0; 1601592ffb21SWarner Losh const int zero = 0; 1602592ffb21SWarner Losh vm_offset_t virtual; 1603592ffb21SWarner Losh vm_offset_t address; 1604592ffb21SWarner Losh struct vmspace *vms; 1605592ffb21SWarner Losh struct drm_buf_map *request = data; 1606592ffb21SWarner Losh int i; 1607592ffb21SWarner Losh 1608592ffb21SWarner Losh if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA)) 1609592ffb21SWarner Losh return -EINVAL; 1610592ffb21SWarner Losh 1611592ffb21SWarner Losh if (!dma) 1612592ffb21SWarner Losh return -EINVAL; 1613592ffb21SWarner Losh 1614592ffb21SWarner Losh mtx_lock(&dev->count_lock); 1615592ffb21SWarner Losh if (atomic_read(&dev->buf_alloc)) { 1616592ffb21SWarner Losh mtx_unlock(&dev->count_lock); 1617592ffb21SWarner Losh return -EBUSY; 1618592ffb21SWarner Losh } 1619592ffb21SWarner Losh dev->buf_use++; /* Can't allocate more after this call */ 1620592ffb21SWarner Losh mtx_unlock(&dev->count_lock); 1621592ffb21SWarner Losh 1622592ffb21SWarner Losh vms = DRM_CURPROC->td_proc->p_vmspace; 1623592ffb21SWarner Losh 1624592ffb21SWarner Losh if (request->count >= dma->buf_count) { 1625592ffb21SWarner Losh if ((drm_core_has_AGP(dev) && (dma->flags & _DRM_DMA_USE_AGP)) 1626592ffb21SWarner Losh || (drm_core_check_feature(dev, DRIVER_SG) 1627592ffb21SWarner Losh && (dma->flags & _DRM_DMA_USE_SG)) 1628592ffb21SWarner Losh || (drm_core_check_feature(dev, DRIVER_FB_DMA) 1629592ffb21SWarner Losh && (dma->flags & _DRM_DMA_USE_FB))) { 1630592ffb21SWarner Losh struct drm_local_map *map = dev->agp_buffer_map; 1631592ffb21SWarner Losh vm_ooffset_t token = dev->agp_buffer_token; 1632592ffb21SWarner Losh 1633592ffb21SWarner Losh if (!map) { 1634592ffb21SWarner Losh retcode = -EINVAL; 1635592ffb21SWarner Losh goto done; 1636592ffb21SWarner Losh } 1637592ffb21SWarner Losh retcode = vm_mmap(&vms->vm_map, &virtual, map->size, 1638*f34702b7SJohn Baldwin VM_PROT_RW, VM_PROT_RW, MAP_SHARED | MAP_NOSYNC, 1639*f34702b7SJohn Baldwin OBJT_DEVICE, file_priv->minor->device, token); 1640592ffb21SWarner Losh } else { 1641592ffb21SWarner Losh retcode = vm_mmap(&vms->vm_map, &virtual, dma->byte_count, 1642*f34702b7SJohn Baldwin VM_PROT_RW, VM_PROT_RW, MAP_SHARED | MAP_NOSYNC, 1643*f34702b7SJohn Baldwin OBJT_DEVICE, file_priv->minor->device, 0); 1644592ffb21SWarner Losh } 1645592ffb21SWarner Losh if (retcode) { 1646592ffb21SWarner Losh /* Real error */ 1647592ffb21SWarner Losh retcode = -retcode; 1648592ffb21SWarner Losh goto done; 1649592ffb21SWarner Losh } 1650592ffb21SWarner Losh request->virtual = (void __user *)virtual; 1651592ffb21SWarner Losh 1652592ffb21SWarner Losh for (i = 0; i < dma->buf_count; i++) { 1653592ffb21SWarner Losh if (copy_to_user(&request->list[i].idx, 1654592ffb21SWarner Losh &dma->buflist[i]->idx, 1655592ffb21SWarner Losh sizeof(request->list[0].idx))) { 1656592ffb21SWarner Losh retcode = -EFAULT; 1657592ffb21SWarner Losh goto done; 1658592ffb21SWarner Losh } 1659592ffb21SWarner Losh if (copy_to_user(&request->list[i].total, 1660592ffb21SWarner Losh &dma->buflist[i]->total, 1661592ffb21SWarner Losh sizeof(request->list[0].total))) { 1662592ffb21SWarner Losh retcode = -EFAULT; 1663592ffb21SWarner Losh goto done; 1664592ffb21SWarner Losh } 1665592ffb21SWarner Losh if (copy_to_user(&request->list[i].used, 1666592ffb21SWarner Losh &zero, sizeof(zero))) { 1667592ffb21SWarner Losh retcode = -EFAULT; 1668592ffb21SWarner Losh goto done; 1669592ffb21SWarner Losh } 1670592ffb21SWarner Losh address = virtual + dma->buflist[i]->offset; /* *** */ 1671592ffb21SWarner Losh if (copy_to_user(&request->list[i].address, 1672592ffb21SWarner Losh &address, sizeof(address))) { 1673592ffb21SWarner Losh retcode = -EFAULT; 1674592ffb21SWarner Losh goto done; 1675592ffb21SWarner Losh } 1676592ffb21SWarner Losh } 1677592ffb21SWarner Losh } 1678592ffb21SWarner Losh done: 1679592ffb21SWarner Losh request->count = dma->buf_count; 1680592ffb21SWarner Losh DRM_DEBUG("%d buffers, retcode = %d\n", request->count, retcode); 1681592ffb21SWarner Losh 1682592ffb21SWarner Losh return retcode; 1683592ffb21SWarner Losh } 1684592ffb21SWarner Losh 1685592ffb21SWarner Losh /** 1686592ffb21SWarner Losh * Compute size order. Returns the exponent of the smaller power of two which 1687592ffb21SWarner Losh * is greater or equal to given number. 1688592ffb21SWarner Losh * 1689592ffb21SWarner Losh * \param size size. 1690592ffb21SWarner Losh * \return order. 1691592ffb21SWarner Losh * 1692592ffb21SWarner Losh * \todo Can be made faster. 1693592ffb21SWarner Losh */ 1694592ffb21SWarner Losh int drm_order(unsigned long size) 1695592ffb21SWarner Losh { 1696592ffb21SWarner Losh int order; 1697592ffb21SWarner Losh unsigned long tmp; 1698592ffb21SWarner Losh 1699592ffb21SWarner Losh for (order = 0, tmp = size >> 1; tmp; tmp >>= 1, order++) ; 1700592ffb21SWarner Losh 1701592ffb21SWarner Losh if (size & (size - 1)) 1702592ffb21SWarner Losh ++order; 1703592ffb21SWarner Losh 1704592ffb21SWarner Losh return order; 1705592ffb21SWarner Losh } 1706592ffb21SWarner Losh EXPORT_SYMBOL(drm_order); 1707