The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/drm/drm_bufs.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
    3  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
    4  * All Rights Reserved.
    5  *
    6  * Permission is hereby granted, free of charge, to any person obtaining a
    7  * copy of this software and associated documentation files (the "Software"),
    8  * to deal in the Software without restriction, including without limitation
    9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   10  * and/or sell copies of the Software, and to permit persons to whom the
   11  * Software is furnished to do so, subject to the following conditions:
   12  *
   13  * The above copyright notice and this permission notice (including the next
   14  * paragraph) shall be included in all copies or substantial portions of the
   15  * Software.
   16  *
   17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   20  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
   21  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
   22  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
   23  * OTHER DEALINGS IN THE SOFTWARE.
   24  *
   25  * Authors:
   26  *    Rickard E. (Rik) Faith <faith@valinux.com>
   27  *    Gareth Hughes <gareth@valinux.com>
   28  *
   29  */
   30 
   31 #include <sys/cdefs.h>
   32 __FBSDID("$FreeBSD$");
   33 
   34 /** @file drm_bufs.c
   35  * Implementation of the ioctls for setup of DRM mappings and DMA buffers.
   36  */
   37 
   38 #include "dev/pci/pcireg.h"
   39 
   40 #include "dev/drm/drmP.h"
   41 
   42 /* Allocation of PCI memory resources (framebuffer, registers, etc.) for
   43  * drm_get_resource_*.  Note that they are not RF_ACTIVE, so there's no virtual
   44  * address for accessing them.  Cleaned up at unload.
   45  */
   46 static int drm_alloc_resource(struct drm_device *dev, int resource)
   47 {
   48         struct resource *res;
   49         int rid;
   50 
   51         DRM_SPINLOCK_ASSERT(&dev->dev_lock);
   52 
   53         if (resource >= DRM_MAX_PCI_RESOURCE) {
   54                 DRM_ERROR("Resource %d too large\n", resource);
   55                 return 1;
   56         }
   57 
   58         if (dev->pcir[resource] != NULL) {
   59                 return 0;
   60         }
   61 
   62         DRM_UNLOCK();
   63         rid = PCIR_BAR(resource);
   64         res = bus_alloc_resource_any(dev->device, SYS_RES_MEMORY, &rid,
   65             RF_SHAREABLE);
   66         DRM_LOCK();
   67         if (res == NULL) {
   68                 DRM_ERROR("Couldn't find resource 0x%x\n", resource);
   69                 return 1;
   70         }
   71 
   72         if (dev->pcir[resource] == NULL) {
   73                 dev->pcirid[resource] = rid;
   74                 dev->pcir[resource] = res;
   75         }
   76 
   77         return 0;
   78 }
   79 
   80 unsigned long drm_get_resource_start(struct drm_device *dev,
   81                                      unsigned int resource)
   82 {
   83         if (drm_alloc_resource(dev, resource) != 0)
   84                 return 0;
   85 
   86         return rman_get_start(dev->pcir[resource]);
   87 }
   88 
   89 unsigned long drm_get_resource_len(struct drm_device *dev,
   90                                    unsigned int resource)
   91 {
   92         if (drm_alloc_resource(dev, resource) != 0)
   93                 return 0;
   94 
   95         return rman_get_size(dev->pcir[resource]);
   96 }
   97 
   98 int drm_addmap(struct drm_device * dev, unsigned long offset,
   99                unsigned long size,
  100     enum drm_map_type type, enum drm_map_flags flags, drm_local_map_t **map_ptr)
  101 {
  102         drm_local_map_t *map;
  103         int align;
  104         /*drm_agp_mem_t *entry;
  105         int valid;*/
  106 
  107         /* Only allow shared memory to be removable since we only keep enough
  108          * book keeping information about shared memory to allow for removal
  109          * when processes fork.
  110          */
  111         if ((flags & _DRM_REMOVABLE) && type != _DRM_SHM) {
  112                 DRM_ERROR("Requested removable map for non-DRM_SHM\n");
  113                 return EINVAL;
  114         }
  115         if ((offset & PAGE_MASK) || (size & PAGE_MASK)) {
  116                 DRM_ERROR("offset/size not page aligned: 0x%lx/0x%lx\n",
  117                     offset, size);
  118                 return EINVAL;
  119         }
  120         if (offset + size < offset) {
  121                 DRM_ERROR("offset and size wrap around: 0x%lx/0x%lx\n",
  122                     offset, size);
  123                 return EINVAL;
  124         }
  125 
  126         DRM_DEBUG("offset = 0x%08lx, size = 0x%08lx, type = %d\n", offset,
  127             size, type);
  128 
  129         /* Check if this is just another version of a kernel-allocated map, and
  130          * just hand that back if so.
  131          */
  132         if (type == _DRM_REGISTERS || type == _DRM_FRAME_BUFFER ||
  133             type == _DRM_SHM) {
  134                 TAILQ_FOREACH(map, &dev->maplist, link) {
  135                         if (map->type == type && (map->offset == offset ||
  136                             (map->type == _DRM_SHM &&
  137                             map->flags == _DRM_CONTAINS_LOCK))) {
  138                                 map->size = size;
  139                                 DRM_DEBUG("Found kernel map %d\n", type);
  140                                 goto done;
  141                         }
  142                 }
  143         }
  144         DRM_UNLOCK();
  145 
  146         /* Allocate a new map structure, fill it in, and do any type-specific
  147          * initialization necessary.
  148          */
  149         map = malloc(sizeof(*map), DRM_MEM_MAPS, M_ZERO | M_NOWAIT);
  150         if (!map) {
  151                 DRM_LOCK();
  152                 return ENOMEM;
  153         }
  154 
  155         map->offset = offset;
  156         map->size = size;
  157         map->type = type;
  158         map->flags = flags;
  159         map->handle = (void *)((unsigned long)alloc_unr(dev->map_unrhdr) <<
  160             DRM_MAP_HANDLE_SHIFT);
  161 
  162         switch (map->type) {
  163         case _DRM_REGISTERS:
  164                 map->virtual = drm_ioremap(dev, map);
  165                 if (!(map->flags & _DRM_WRITE_COMBINING))
  166                         break;
  167                 /* FALLTHROUGH */
  168         case _DRM_FRAME_BUFFER:
  169                 if (drm_mtrr_add(map->offset, map->size, DRM_MTRR_WC) == 0)
  170                         map->mtrr = 1;
  171                 break;
  172         case _DRM_SHM:
  173                 map->virtual = malloc(map->size, DRM_MEM_MAPS, M_NOWAIT);
  174                 DRM_DEBUG("%lu %d %p\n",
  175                     map->size, drm_order(map->size), map->virtual);
  176                 if (!map->virtual) {
  177                         free(map, DRM_MEM_MAPS);
  178                         DRM_LOCK();
  179                         return ENOMEM;
  180                 }
  181                 map->offset = (unsigned long)map->virtual;
  182                 if (map->flags & _DRM_CONTAINS_LOCK) {
  183                         /* Prevent a 2nd X Server from creating a 2nd lock */
  184                         DRM_LOCK();
  185                         if (dev->lock.hw_lock != NULL) {
  186                                 DRM_UNLOCK();
  187                                 free(map->virtual, DRM_MEM_MAPS);
  188                                 free(map, DRM_MEM_MAPS);
  189                                 return EBUSY;
  190                         }
  191                         dev->lock.hw_lock = map->virtual; /* Pointer to lock */
  192                         DRM_UNLOCK();
  193                 }
  194                 break;
  195         case _DRM_AGP:
  196                 /*valid = 0;*/
  197                 /* In some cases (i810 driver), user space may have already
  198                  * added the AGP base itself, because dev->agp->base previously
  199                  * only got set during AGP enable.  So, only add the base
  200                  * address if the map's offset isn't already within the
  201                  * aperture.
  202                  */
  203                 if (map->offset < dev->agp->base ||
  204                     map->offset > dev->agp->base +
  205                     dev->agp->info.ai_aperture_size - 1) {
  206                         map->offset += dev->agp->base;
  207                 }
  208                 map->mtrr   = dev->agp->mtrr; /* for getmap */
  209                 /*for (entry = dev->agp->memory; entry; entry = entry->next) {
  210                         if ((map->offset >= entry->bound) &&
  211                             (map->offset + map->size <=
  212                             entry->bound + entry->pages * PAGE_SIZE)) {
  213                                 valid = 1;
  214                                 break;
  215                         }
  216                 }
  217                 if (!valid) {
  218                         free(map, DRM_MEM_MAPS);
  219                         DRM_LOCK();
  220                         return EACCES;
  221                 }*/
  222                 break;
  223         case _DRM_SCATTER_GATHER:
  224                 if (!dev->sg) {
  225                         free(map, DRM_MEM_MAPS);
  226                         DRM_LOCK();
  227                         return EINVAL;
  228                 }
  229                 map->virtual = (void *)(dev->sg->vaddr + offset);
  230                 map->offset = dev->sg->vaddr + offset;
  231                 break;
  232         case _DRM_CONSISTENT:
  233                 /* Unfortunately, we don't get any alignment specification from
  234                  * the caller, so we have to guess.  drm_pci_alloc requires
  235                  * a power-of-two alignment, so try to align the bus address of
  236                  * the map to it size if possible, otherwise just assume
  237                  * PAGE_SIZE alignment.
  238                  */
  239                 align = map->size;
  240                 if ((align & (align - 1)) != 0)
  241                         align = PAGE_SIZE;
  242                 map->dmah = drm_pci_alloc(dev, map->size, align, 0xfffffffful);
  243                 if (map->dmah == NULL) {
  244                         free(map, DRM_MEM_MAPS);
  245                         DRM_LOCK();
  246                         return ENOMEM;
  247                 }
  248                 map->virtual = map->dmah->vaddr;
  249                 map->offset = map->dmah->busaddr;
  250                 break;
  251         default:
  252                 DRM_ERROR("Bad map type %d\n", map->type);
  253                 free(map, DRM_MEM_MAPS);
  254                 DRM_LOCK();
  255                 return EINVAL;
  256         }
  257 
  258         DRM_LOCK();
  259         TAILQ_INSERT_TAIL(&dev->maplist, map, link);
  260 
  261 done:
  262         /* Jumped to, with lock held, when a kernel map is found. */
  263 
  264         DRM_DEBUG("Added map %d 0x%lx/0x%lx\n", map->type, map->offset,
  265             map->size);
  266 
  267         *map_ptr = map;
  268 
  269         return 0;
  270 }
  271 
  272 int drm_addmap_ioctl(struct drm_device *dev, void *data,
  273                      struct drm_file *file_priv)
  274 {
  275         struct drm_map *request = data;
  276         drm_local_map_t *map;
  277         int err;
  278 
  279         if (!(dev->flags & (FREAD|FWRITE)))
  280                 return EACCES; /* Require read/write */
  281 
  282         if (!DRM_SUSER(DRM_CURPROC) && request->type != _DRM_AGP)
  283                 return EACCES;
  284 
  285         DRM_LOCK();
  286         err = drm_addmap(dev, request->offset, request->size, request->type,
  287             request->flags, &map);
  288         DRM_UNLOCK();
  289         if (err != 0)
  290                 return err;
  291 
  292         request->offset = map->offset;
  293         request->size = map->size;
  294         request->type = map->type;
  295         request->flags = map->flags;
  296         request->mtrr   = map->mtrr;
  297         request->handle = (void *)map->handle;
  298 
  299         return 0;
  300 }
  301 
  302 void drm_rmmap(struct drm_device *dev, drm_local_map_t *map)
  303 {
  304         DRM_SPINLOCK_ASSERT(&dev->dev_lock);
  305 
  306         if (map == NULL)
  307                 return;
  308 
  309         TAILQ_REMOVE(&dev->maplist, map, link);
  310 
  311         switch (map->type) {
  312         case _DRM_REGISTERS:
  313                 if (map->bsr == NULL)
  314                         drm_ioremapfree(map);
  315                 /* FALLTHROUGH */
  316         case _DRM_FRAME_BUFFER:
  317                 if (map->mtrr) {
  318                         int __unused retcode;
  319                         
  320                         retcode = drm_mtrr_del(0, map->offset, map->size,
  321                             DRM_MTRR_WC);
  322                         DRM_DEBUG("mtrr_del = %d\n", retcode);
  323                 }
  324                 break;
  325         case _DRM_SHM:
  326                 free(map->virtual, DRM_MEM_MAPS);
  327                 break;
  328         case _DRM_AGP:
  329         case _DRM_SCATTER_GATHER:
  330                 break;
  331         case _DRM_CONSISTENT:
  332                 drm_pci_free(dev, map->dmah);
  333                 break;
  334         default:
  335                 DRM_ERROR("Bad map type %d\n", map->type);
  336                 break;
  337         }
  338 
  339         if (map->bsr != NULL) {
  340                 bus_release_resource(dev->device, SYS_RES_MEMORY, map->rid,
  341                     map->bsr);
  342         }
  343 
  344         DRM_UNLOCK();
  345         if (map->handle)
  346                 free_unr(dev->map_unrhdr, (unsigned long)map->handle >>
  347                     DRM_MAP_HANDLE_SHIFT);
  348         DRM_LOCK();
  349 
  350         free(map, DRM_MEM_MAPS);
  351 }
  352 
  353 /* Remove a map private from list and deallocate resources if the mapping
  354  * isn't in use.
  355  */
  356 
  357 int drm_rmmap_ioctl(struct drm_device *dev, void *data,
  358                     struct drm_file *file_priv)
  359 {
  360         drm_local_map_t *map;
  361         struct drm_map *request = data;
  362 
  363         DRM_LOCK();
  364         TAILQ_FOREACH(map, &dev->maplist, link) {
  365                 if (map->handle == request->handle &&
  366                     map->flags & _DRM_REMOVABLE)
  367                         break;
  368         }
  369 
  370         /* No match found. */
  371         if (map == NULL) {
  372                 DRM_UNLOCK();
  373                 return EINVAL;
  374         }
  375 
  376         drm_rmmap(dev, map);
  377 
  378         DRM_UNLOCK();
  379 
  380         return 0;
  381 }
  382 
  383 
  384 static void drm_cleanup_buf_error(struct drm_device *dev,
  385                                   drm_buf_entry_t *entry)
  386 {
  387         int i;
  388 
  389         if (entry->seg_count) {
  390                 for (i = 0; i < entry->seg_count; i++) {
  391                         drm_pci_free(dev, entry->seglist[i]);
  392                 }
  393                 free(entry->seglist, DRM_MEM_SEGS);
  394 
  395                 entry->seg_count = 0;
  396         }
  397 
  398         if (entry->buf_count) {
  399                 for (i = 0; i < entry->buf_count; i++) {
  400                         free(entry->buflist[i].dev_private, DRM_MEM_BUFS);
  401                 }
  402                 free(entry->buflist, DRM_MEM_BUFS);
  403 
  404                 entry->buf_count = 0;
  405         }
  406 }
  407 
  408 static int drm_do_addbufs_agp(struct drm_device *dev, struct drm_buf_desc *request)
  409 {
  410         drm_device_dma_t *dma = dev->dma;
  411         drm_buf_entry_t *entry;
  412         /*drm_agp_mem_t *agp_entry;
  413         int valid*/
  414         drm_buf_t *buf;
  415         unsigned long offset;
  416         unsigned long agp_offset;
  417         int count;
  418         int order;
  419         int size;
  420         int alignment;
  421         int page_order;
  422         int total;
  423         int byte_count;
  424         int i;
  425         drm_buf_t **temp_buflist;
  426 
  427         count = request->count;
  428         order = drm_order(request->size);
  429         size = 1 << order;
  430 
  431         alignment  = (request->flags & _DRM_PAGE_ALIGN)
  432             ? round_page(size) : size;
  433         page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
  434         total = PAGE_SIZE << page_order;
  435 
  436         byte_count = 0;
  437         agp_offset = dev->agp->base + request->agp_start;
  438 
  439         DRM_DEBUG("count:      %d\n",  count);
  440         DRM_DEBUG("order:      %d\n",  order);
  441         DRM_DEBUG("size:       %d\n",  size);
  442         DRM_DEBUG("agp_offset: 0x%lx\n", agp_offset);
  443         DRM_DEBUG("alignment:  %d\n",  alignment);
  444         DRM_DEBUG("page_order: %d\n",  page_order);
  445         DRM_DEBUG("total:      %d\n",  total);
  446 
  447         /* Make sure buffers are located in AGP memory that we own */
  448         /* Breaks MGA due to drm_alloc_agp not setting up entries for the
  449          * memory.  Safe to ignore for now because these ioctls are still
  450          * root-only.
  451          */
  452         /*valid = 0;
  453         for (agp_entry = dev->agp->memory; agp_entry;
  454             agp_entry = agp_entry->next) {
  455                 if ((agp_offset >= agp_entry->bound) &&
  456                     (agp_offset + total * count <=
  457                     agp_entry->bound + agp_entry->pages * PAGE_SIZE)) {
  458                         valid = 1;
  459                         break;
  460                 }
  461         }
  462         if (!valid) {
  463                 DRM_DEBUG("zone invalid\n");
  464                 return EINVAL;
  465         }*/
  466 
  467         entry = &dma->bufs[order];
  468 
  469         entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS,
  470             M_NOWAIT | M_ZERO);
  471         if (!entry->buflist) {
  472                 return ENOMEM;
  473         }
  474 
  475         entry->buf_size = size;
  476         entry->page_order = page_order;
  477 
  478         offset = 0;
  479 
  480         while (entry->buf_count < count) {
  481                 buf          = &entry->buflist[entry->buf_count];
  482                 buf->idx     = dma->buf_count + entry->buf_count;
  483                 buf->total   = alignment;
  484                 buf->order   = order;
  485                 buf->used    = 0;
  486 
  487                 buf->offset  = (dma->byte_count + offset);
  488                 buf->bus_address = agp_offset + offset;
  489                 buf->address = (void *)(agp_offset + offset);
  490                 buf->next    = NULL;
  491                 buf->pending = 0;
  492                 buf->file_priv = NULL;
  493 
  494                 buf->dev_priv_size = dev->driver->buf_priv_size;
  495                 buf->dev_private = malloc(buf->dev_priv_size, DRM_MEM_BUFS,
  496                     M_NOWAIT | M_ZERO);
  497                 if (buf->dev_private == NULL) {
  498                         /* Set count correctly so we free the proper amount. */
  499                         entry->buf_count = count;
  500                         drm_cleanup_buf_error(dev, entry);
  501                         return ENOMEM;
  502                 }
  503 
  504                 offset += alignment;
  505                 entry->buf_count++;
  506                 byte_count += PAGE_SIZE << page_order;
  507         }
  508 
  509         DRM_DEBUG("byte_count: %d\n", byte_count);
  510 
  511         temp_buflist = realloc(dma->buflist,
  512             (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
  513             DRM_MEM_BUFS, M_NOWAIT);
  514         if (temp_buflist == NULL) {
  515                 /* Free the entry because it isn't valid */
  516                 drm_cleanup_buf_error(dev, entry);
  517                 return ENOMEM;
  518         }
  519         dma->buflist = temp_buflist;
  520 
  521         for (i = 0; i < entry->buf_count; i++) {
  522                 dma->buflist[i + dma->buf_count] = &entry->buflist[i];
  523         }
  524 
  525         dma->buf_count += entry->buf_count;
  526         dma->byte_count += byte_count;
  527 
  528         DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
  529         DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
  530 
  531         request->count = entry->buf_count;
  532         request->size = size;
  533 
  534         dma->flags = _DRM_DMA_USE_AGP;
  535 
  536         return 0;
  537 }
  538 
  539 static int drm_do_addbufs_pci(struct drm_device *dev, struct drm_buf_desc *request)
  540 {
  541         drm_device_dma_t *dma = dev->dma;
  542         int count;
  543         int order;
  544         int size;
  545         int total;
  546         int page_order;
  547         drm_buf_entry_t *entry;
  548         drm_buf_t *buf;
  549         int alignment;
  550         unsigned long offset;
  551         int i;
  552         int byte_count;
  553         int page_count;
  554         unsigned long *temp_pagelist;
  555         drm_buf_t **temp_buflist;
  556 
  557         count = request->count;
  558         order = drm_order(request->size);
  559         size = 1 << order;
  560 
  561         DRM_DEBUG("count=%d, size=%d (%d), order=%d\n",
  562             request->count, request->size, size, order);
  563 
  564         alignment = (request->flags & _DRM_PAGE_ALIGN)
  565             ? round_page(size) : size;
  566         page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
  567         total = PAGE_SIZE << page_order;
  568 
  569         entry = &dma->bufs[order];
  570 
  571         entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS,
  572             M_NOWAIT | M_ZERO);
  573         entry->seglist = malloc(count * sizeof(*entry->seglist), DRM_MEM_SEGS,
  574             M_NOWAIT | M_ZERO);
  575 
  576         /* Keep the original pagelist until we know all the allocations
  577          * have succeeded
  578          */
  579         temp_pagelist = malloc((dma->page_count + (count << page_order)) *
  580             sizeof(*dma->pagelist), DRM_MEM_PAGES, M_NOWAIT);
  581 
  582         if (entry->buflist == NULL || entry->seglist == NULL || 
  583             temp_pagelist == NULL) {
  584                 free(temp_pagelist, DRM_MEM_PAGES);
  585                 free(entry->seglist, DRM_MEM_SEGS);
  586                 free(entry->buflist, DRM_MEM_BUFS);
  587                 return ENOMEM;
  588         }
  589 
  590         memcpy(temp_pagelist, dma->pagelist, dma->page_count * 
  591             sizeof(*dma->pagelist));
  592 
  593         DRM_DEBUG("pagelist: %d entries\n",
  594             dma->page_count + (count << page_order));
  595 
  596         entry->buf_size = size;
  597         entry->page_order = page_order;
  598         byte_count = 0;
  599         page_count = 0;
  600 
  601         while (entry->buf_count < count) {
  602                 DRM_SPINUNLOCK(&dev->dma_lock);
  603                 drm_dma_handle_t *dmah = drm_pci_alloc(dev, size, alignment,
  604                     0xfffffffful);
  605                 DRM_SPINLOCK(&dev->dma_lock);
  606                 if (dmah == NULL) {
  607                         /* Set count correctly so we free the proper amount. */
  608                         entry->buf_count = count;
  609                         entry->seg_count = count;
  610                         drm_cleanup_buf_error(dev, entry);
  611                         free(temp_pagelist, DRM_MEM_PAGES);
  612                         return ENOMEM;
  613                 }
  614 
  615                 entry->seglist[entry->seg_count++] = dmah;
  616                 for (i = 0; i < (1 << page_order); i++) {
  617                         DRM_DEBUG("page %d @ %p\n",
  618                             dma->page_count + page_count,
  619                             (char *)dmah->vaddr + PAGE_SIZE * i);
  620                         temp_pagelist[dma->page_count + page_count++] = 
  621                             (long)dmah->vaddr + PAGE_SIZE * i;
  622                 }
  623                 for (offset = 0;
  624                     offset + size <= total && entry->buf_count < count;
  625                     offset += alignment, ++entry->buf_count) {
  626                         buf          = &entry->buflist[entry->buf_count];
  627                         buf->idx     = dma->buf_count + entry->buf_count;
  628                         buf->total   = alignment;
  629                         buf->order   = order;
  630                         buf->used    = 0;
  631                         buf->offset  = (dma->byte_count + byte_count + offset);
  632                         buf->address = ((char *)dmah->vaddr + offset);
  633                         buf->bus_address = dmah->busaddr + offset;
  634                         buf->next    = NULL;
  635                         buf->pending = 0;
  636                         buf->file_priv = NULL;
  637 
  638                         buf->dev_priv_size = dev->driver->buf_priv_size;
  639                         buf->dev_private = malloc(buf->dev_priv_size,
  640                             DRM_MEM_BUFS, M_NOWAIT | M_ZERO);
  641                         if (buf->dev_private == NULL) {
  642                                 /* Set count correctly so we free the proper amount. */
  643                                 entry->buf_count = count;
  644                                 entry->seg_count = count;
  645                                 drm_cleanup_buf_error(dev, entry);
  646                                 free(temp_pagelist, DRM_MEM_PAGES);
  647                                 return ENOMEM;
  648                         }
  649 
  650                         DRM_DEBUG("buffer %d @ %p\n",
  651                             entry->buf_count, buf->address);
  652                 }
  653                 byte_count += PAGE_SIZE << page_order;
  654         }
  655 
  656         temp_buflist = realloc(dma->buflist,
  657             (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
  658             DRM_MEM_BUFS, M_NOWAIT);
  659         if (temp_buflist == NULL) {
  660                 /* Free the entry because it isn't valid */
  661                 drm_cleanup_buf_error(dev, entry);
  662                 free(temp_pagelist, DRM_MEM_PAGES);
  663                 return ENOMEM;
  664         }
  665         dma->buflist = temp_buflist;
  666 
  667         for (i = 0; i < entry->buf_count; i++) {
  668                 dma->buflist[i + dma->buf_count] = &entry->buflist[i];
  669         }
  670 
  671         /* No allocations failed, so now we can replace the orginal pagelist
  672          * with the new one.
  673          */
  674         free(dma->pagelist, DRM_MEM_PAGES);
  675         dma->pagelist = temp_pagelist;
  676 
  677         dma->buf_count += entry->buf_count;
  678         dma->seg_count += entry->seg_count;
  679         dma->page_count += entry->seg_count << page_order;
  680         dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
  681 
  682         request->count = entry->buf_count;
  683         request->size = size;
  684 
  685         return 0;
  686 
  687 }
  688 
  689 static int drm_do_addbufs_sg(struct drm_device *dev, struct drm_buf_desc *request)
  690 {
  691         drm_device_dma_t *dma = dev->dma;
  692         drm_buf_entry_t *entry;
  693         drm_buf_t *buf;
  694         unsigned long offset;
  695         unsigned long agp_offset;
  696         int count;
  697         int order;
  698         int size;
  699         int alignment;
  700         int page_order;
  701         int total;
  702         int byte_count;
  703         int i;
  704         drm_buf_t **temp_buflist;
  705 
  706         count = request->count;
  707         order = drm_order(request->size);
  708         size = 1 << order;
  709 
  710         alignment  = (request->flags & _DRM_PAGE_ALIGN)
  711             ? round_page(size) : size;
  712         page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
  713         total = PAGE_SIZE << page_order;
  714 
  715         byte_count = 0;
  716         agp_offset = request->agp_start;
  717 
  718         DRM_DEBUG("count:      %d\n",  count);
  719         DRM_DEBUG("order:      %d\n",  order);
  720         DRM_DEBUG("size:       %d\n",  size);
  721         DRM_DEBUG("agp_offset: %ld\n", agp_offset);
  722         DRM_DEBUG("alignment:  %d\n",  alignment);
  723         DRM_DEBUG("page_order: %d\n",  page_order);
  724         DRM_DEBUG("total:      %d\n",  total);
  725 
  726         entry = &dma->bufs[order];
  727 
  728         entry->buflist = malloc(count * sizeof(*entry->buflist), DRM_MEM_BUFS,
  729             M_NOWAIT | M_ZERO);
  730         if (entry->buflist == NULL)
  731                 return ENOMEM;
  732 
  733         entry->buf_size = size;
  734         entry->page_order = page_order;
  735 
  736         offset = 0;
  737 
  738         while (entry->buf_count < count) {
  739                 buf          = &entry->buflist[entry->buf_count];
  740                 buf->idx     = dma->buf_count + entry->buf_count;
  741                 buf->total   = alignment;
  742                 buf->order   = order;
  743                 buf->used    = 0;
  744 
  745                 buf->offset  = (dma->byte_count + offset);
  746                 buf->bus_address = agp_offset + offset;
  747                 buf->address = (void *)(agp_offset + offset + dev->sg->vaddr);
  748                 buf->next    = NULL;
  749                 buf->pending = 0;
  750                 buf->file_priv = NULL;
  751 
  752                 buf->dev_priv_size = dev->driver->buf_priv_size;
  753                 buf->dev_private = malloc(buf->dev_priv_size, DRM_MEM_BUFS,
  754                     M_NOWAIT | M_ZERO);
  755                 if (buf->dev_private == NULL) {
  756                         /* Set count correctly so we free the proper amount. */
  757                         entry->buf_count = count;
  758                         drm_cleanup_buf_error(dev, entry);
  759                         return ENOMEM;
  760                 }
  761 
  762                 DRM_DEBUG("buffer %d @ %p\n",
  763                     entry->buf_count, buf->address);
  764 
  765                 offset += alignment;
  766                 entry->buf_count++;
  767                 byte_count += PAGE_SIZE << page_order;
  768         }
  769 
  770         DRM_DEBUG("byte_count: %d\n", byte_count);
  771 
  772         temp_buflist = realloc(dma->buflist,
  773             (dma->buf_count + entry->buf_count) * sizeof(*dma->buflist),
  774             DRM_MEM_BUFS, M_NOWAIT);
  775         if (temp_buflist == NULL) {
  776                 /* Free the entry because it isn't valid */
  777                 drm_cleanup_buf_error(dev, entry);
  778                 return ENOMEM;
  779         }
  780         dma->buflist = temp_buflist;
  781 
  782         for (i = 0; i < entry->buf_count; i++) {
  783                 dma->buflist[i + dma->buf_count] = &entry->buflist[i];
  784         }
  785 
  786         dma->buf_count += entry->buf_count;
  787         dma->byte_count += byte_count;
  788 
  789         DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
  790         DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
  791 
  792         request->count = entry->buf_count;
  793         request->size = size;
  794 
  795         dma->flags = _DRM_DMA_USE_SG;
  796 
  797         return 0;
  798 }
  799 
  800 int drm_addbufs_agp(struct drm_device *dev, struct drm_buf_desc *request)
  801 {
  802         int order, ret;
  803 
  804         if (request->count < 0 || request->count > 4096)
  805                 return EINVAL;
  806         
  807         order = drm_order(request->size);
  808         if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
  809                 return EINVAL;
  810 
  811         DRM_SPINLOCK(&dev->dma_lock);
  812 
  813         /* No more allocations after first buffer-using ioctl. */
  814         if (dev->buf_use != 0) {
  815                 DRM_SPINUNLOCK(&dev->dma_lock);
  816                 return EBUSY;
  817         }
  818         /* No more than one allocation per order */
  819         if (dev->dma->bufs[order].buf_count != 0) {
  820                 DRM_SPINUNLOCK(&dev->dma_lock);
  821                 return ENOMEM;
  822         }
  823 
  824         ret = drm_do_addbufs_agp(dev, request);
  825 
  826         DRM_SPINUNLOCK(&dev->dma_lock);
  827 
  828         return ret;
  829 }
  830 
  831 int drm_addbufs_sg(struct drm_device *dev, struct drm_buf_desc *request)
  832 {
  833         int order, ret;
  834 
  835         if (!DRM_SUSER(DRM_CURPROC))
  836                 return EACCES;
  837 
  838         if (request->count < 0 || request->count > 4096)
  839                 return EINVAL;
  840 
  841         order = drm_order(request->size);
  842         if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
  843                 return EINVAL;
  844 
  845         DRM_SPINLOCK(&dev->dma_lock);
  846 
  847         /* No more allocations after first buffer-using ioctl. */
  848         if (dev->buf_use != 0) {
  849                 DRM_SPINUNLOCK(&dev->dma_lock);
  850                 return EBUSY;
  851         }
  852         /* No more than one allocation per order */
  853         if (dev->dma->bufs[order].buf_count != 0) {
  854                 DRM_SPINUNLOCK(&dev->dma_lock);
  855                 return ENOMEM;
  856         }
  857 
  858         ret = drm_do_addbufs_sg(dev, request);
  859 
  860         DRM_SPINUNLOCK(&dev->dma_lock);
  861 
  862         return ret;
  863 }
  864 
  865 int drm_addbufs_pci(struct drm_device *dev, struct drm_buf_desc *request)
  866 {
  867         int order, ret;
  868 
  869         if (!DRM_SUSER(DRM_CURPROC))
  870                 return EACCES;
  871 
  872         if (request->count < 0 || request->count > 4096)
  873                 return EINVAL;
  874 
  875         order = drm_order(request->size);
  876         if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
  877                 return EINVAL;
  878 
  879         DRM_SPINLOCK(&dev->dma_lock);
  880 
  881         /* No more allocations after first buffer-using ioctl. */
  882         if (dev->buf_use != 0) {
  883                 DRM_SPINUNLOCK(&dev->dma_lock);
  884                 return EBUSY;
  885         }
  886         /* No more than one allocation per order */
  887         if (dev->dma->bufs[order].buf_count != 0) {
  888                 DRM_SPINUNLOCK(&dev->dma_lock);
  889                 return ENOMEM;
  890         }
  891 
  892         ret = drm_do_addbufs_pci(dev, request);
  893 
  894         DRM_SPINUNLOCK(&dev->dma_lock);
  895 
  896         return ret;
  897 }
  898 
  899 int drm_addbufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
  900 {
  901         struct drm_buf_desc *request = data;
  902         int err;
  903 
  904         if (request->flags & _DRM_AGP_BUFFER)
  905                 err = drm_addbufs_agp(dev, request);
  906         else if (request->flags & _DRM_SG_BUFFER)
  907                 err = drm_addbufs_sg(dev, request);
  908         else
  909                 err = drm_addbufs_pci(dev, request);
  910 
  911         return err;
  912 }
  913 
  914 int drm_infobufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
  915 {
  916         drm_device_dma_t *dma = dev->dma;
  917         struct drm_buf_info *request = data;
  918         int i;
  919         int count;
  920         int retcode = 0;
  921 
  922         DRM_SPINLOCK(&dev->dma_lock);
  923         ++dev->buf_use;         /* Can't allocate more after this call */
  924         DRM_SPINUNLOCK(&dev->dma_lock);
  925 
  926         for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
  927                 if (dma->bufs[i].buf_count)
  928                         ++count;
  929         }
  930 
  931         DRM_DEBUG("count = %d\n", count);
  932 
  933         if (request->count >= count) {
  934                 for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
  935                         if (dma->bufs[i].buf_count) {
  936                                 struct drm_buf_desc from;
  937 
  938                                 from.count = dma->bufs[i].buf_count;
  939                                 from.size = dma->bufs[i].buf_size;
  940                                 from.low_mark = dma->bufs[i].freelist.low_mark;
  941                                 from.high_mark = dma->bufs[i].freelist.high_mark;
  942 
  943                                 if (DRM_COPY_TO_USER(&request->list[count], &from,
  944                                     sizeof(struct drm_buf_desc)) != 0) {
  945                                         retcode = EFAULT;
  946                                         break;
  947                                 }
  948 
  949                                 DRM_DEBUG("%d %d %d %d %d\n",
  950                                     i, dma->bufs[i].buf_count,
  951                                     dma->bufs[i].buf_size,
  952                                     dma->bufs[i].freelist.low_mark,
  953                                     dma->bufs[i].freelist.high_mark);
  954                                 ++count;
  955                         }
  956                 }
  957         }
  958         request->count = count;
  959 
  960         return retcode;
  961 }
  962 
  963 int drm_markbufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
  964 {
  965         drm_device_dma_t *dma = dev->dma;
  966         struct drm_buf_desc *request = data;
  967         int order;
  968 
  969         DRM_DEBUG("%d, %d, %d\n",
  970                   request->size, request->low_mark, request->high_mark);
  971         
  972 
  973         order = drm_order(request->size);       
  974         if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ||
  975             request->low_mark < 0 || request->high_mark < 0) {
  976                 return EINVAL;
  977         }
  978 
  979         DRM_SPINLOCK(&dev->dma_lock);
  980         if (request->low_mark > dma->bufs[order].buf_count ||
  981             request->high_mark > dma->bufs[order].buf_count) {
  982                 DRM_SPINUNLOCK(&dev->dma_lock);
  983                 return EINVAL;
  984         }
  985 
  986         dma->bufs[order].freelist.low_mark  = request->low_mark;
  987         dma->bufs[order].freelist.high_mark = request->high_mark;
  988         DRM_SPINUNLOCK(&dev->dma_lock);
  989 
  990         return 0;
  991 }
  992 
  993 int drm_freebufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
  994 {
  995         drm_device_dma_t *dma = dev->dma;
  996         struct drm_buf_free *request = data;
  997         int i;
  998         int idx;
  999         drm_buf_t *buf;
 1000         int retcode = 0;
 1001 
 1002         DRM_DEBUG("%d\n", request->count);
 1003         
 1004         DRM_SPINLOCK(&dev->dma_lock);
 1005         for (i = 0; i < request->count; i++) {
 1006                 if (DRM_COPY_FROM_USER(&idx, &request->list[i], sizeof(idx))) {
 1007                         retcode = EFAULT;
 1008                         break;
 1009                 }
 1010                 if (idx < 0 || idx >= dma->buf_count) {
 1011                         DRM_ERROR("Index %d (of %d max)\n",
 1012                             idx, dma->buf_count - 1);
 1013                         retcode = EINVAL;
 1014                         break;
 1015                 }
 1016                 buf = dma->buflist[idx];
 1017                 if (buf->file_priv != file_priv) {
 1018                         DRM_ERROR("Process %d freeing buffer not owned\n",
 1019                             DRM_CURRENTPID);
 1020                         retcode = EINVAL;
 1021                         break;
 1022                 }
 1023                 drm_free_buffer(dev, buf);
 1024         }
 1025         DRM_SPINUNLOCK(&dev->dma_lock);
 1026 
 1027         return retcode;
 1028 }
 1029 
 1030 int drm_mapbufs(struct drm_device *dev, void *data, struct drm_file *file_priv)
 1031 {
 1032         drm_device_dma_t *dma = dev->dma;
 1033         int retcode = 0;
 1034         const int zero = 0;
 1035         vm_offset_t address;
 1036         struct vmspace *vms;
 1037         vm_ooffset_t foff;
 1038         vm_size_t size;
 1039         vm_offset_t vaddr;
 1040         struct drm_buf_map *request = data;
 1041         int i;
 1042 
 1043         vms = DRM_CURPROC->td_proc->p_vmspace;
 1044 
 1045         DRM_SPINLOCK(&dev->dma_lock);
 1046         dev->buf_use++;         /* Can't allocate more after this call */
 1047         DRM_SPINUNLOCK(&dev->dma_lock);
 1048 
 1049         if (request->count < dma->buf_count)
 1050                 goto done;
 1051 
 1052         if ((drm_core_has_AGP(dev) && (dma->flags & _DRM_DMA_USE_AGP)) ||
 1053             (drm_core_check_feature(dev, DRIVER_SG) &&
 1054             (dma->flags & _DRM_DMA_USE_SG))) {
 1055                 drm_local_map_t *map = dev->agp_buffer_map;
 1056 
 1057                 if (map == NULL) {
 1058                         retcode = EINVAL;
 1059                         goto done;
 1060                 }
 1061                 size = round_page(map->size);
 1062                 foff = (unsigned long)map->handle;
 1063         } else {
 1064                 size = round_page(dma->byte_count),
 1065                 foff = 0;
 1066         }
 1067 
 1068         vaddr = round_page((vm_offset_t)vms->vm_daddr + MAXDSIZ);
 1069 #if __FreeBSD_version >= 600023
 1070         retcode = vm_mmap(&vms->vm_map, &vaddr, size, PROT_READ | PROT_WRITE,
 1071             VM_PROT_ALL, MAP_SHARED | MAP_NOSYNC, OBJT_DEVICE,
 1072             dev->devnode, foff);
 1073 #else
 1074         retcode = vm_mmap(&vms->vm_map, &vaddr, size, PROT_READ | PROT_WRITE,
 1075             VM_PROT_ALL, MAP_SHARED | MAP_NOSYNC,
 1076             SLIST_FIRST(&dev->devnode->si_hlist), foff);
 1077 #endif
 1078         if (retcode)
 1079                 goto done;
 1080 
 1081         request->virtual = (void *)vaddr;
 1082 
 1083         for (i = 0; i < dma->buf_count; i++) {
 1084                 if (DRM_COPY_TO_USER(&request->list[i].idx,
 1085                     &dma->buflist[i]->idx, sizeof(request->list[0].idx))) {
 1086                         retcode = EFAULT;
 1087                         goto done;
 1088                 }
 1089                 if (DRM_COPY_TO_USER(&request->list[i].total,
 1090                     &dma->buflist[i]->total, sizeof(request->list[0].total))) {
 1091                         retcode = EFAULT;
 1092                         goto done;
 1093                 }
 1094                 if (DRM_COPY_TO_USER(&request->list[i].used, &zero,
 1095                     sizeof(zero))) {
 1096                         retcode = EFAULT;
 1097                         goto done;
 1098                 }
 1099                 address = vaddr + dma->buflist[i]->offset; /* *** */
 1100                 if (DRM_COPY_TO_USER(&request->list[i].address, &address,
 1101                     sizeof(address))) {
 1102                         retcode = EFAULT;
 1103                         goto done;
 1104                 }
 1105         }
 1106 
 1107  done:
 1108         request->count = dma->buf_count;
 1109 
 1110         DRM_DEBUG("%d buffers, retcode = %d\n", request->count, retcode);
 1111 
 1112         return retcode;
 1113 }
 1114 
 1115 /*
 1116  * Compute order.  Can be made faster.
 1117  */
 1118 int drm_order(unsigned long size)
 1119 {
 1120         int order;
 1121 
 1122         if (size == 0)
 1123                 return 0;
 1124 
 1125         order = flsl(size) - 1;
 1126         if (size & ~(1ul << order))
 1127                 ++order;
 1128 
 1129         return order;
 1130 }

Cache object: 6b5995e1e05e7edd341335a01146a03d


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.