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