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