File: /usr/src/linux/drivers/char/drm/drm_vm.h

1     /* drm_vm.h -- Memory mapping for DRM -*- linux-c -*-
2      * Created: Mon Jan  4 08:58:31 1999 by faith@valinux.com
3      *
4      * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
5      * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
6      * All Rights Reserved.
7      *
8      * Permission is hereby granted, free of charge, to any person obtaining a
9      * copy of this software and associated documentation files (the "Software"),
10      * to deal in the Software without restriction, including without limitation
11      * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12      * and/or sell copies of the Software, and to permit persons to whom the
13      * Software is furnished to do so, subject to the following conditions:
14      *
15      * The above copyright notice and this permission notice (including the next
16      * paragraph) shall be included in all copies or substantial portions of the
17      * Software.
18      *
19      * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20      * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21      * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
22      * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23      * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24      * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25      * OTHER DEALINGS IN THE SOFTWARE.
26      *
27      * Authors:
28      *    Rickard E. (Rik) Faith <faith@valinux.com>
29      *    Gareth Hughes <gareth@valinux.com>
30      */
31     
32     #define __NO_VERSION__
33     #include "drmP.h"
34     
35     struct vm_operations_struct   DRM(vm_ops) = {
36     	nopage:	 DRM(vm_nopage),
37     	open:	 DRM(vm_open),
38     	close:	 DRM(vm_close),
39     };
40     
41     struct vm_operations_struct   DRM(vm_shm_ops) = {
42     	nopage:	 DRM(vm_shm_nopage),
43     	open:	 DRM(vm_open),
44     	close:	 DRM(vm_shm_close),
45     };
46     
47     struct vm_operations_struct   DRM(vm_dma_ops) = {
48     	nopage:	 DRM(vm_dma_nopage),
49     	open:	 DRM(vm_open),
50     	close:	 DRM(vm_close),
51     };
52     
53     struct vm_operations_struct   DRM(vm_sg_ops) = {
54     	nopage:  DRM(vm_sg_nopage),
55     	open:    DRM(vm_open),
56     	close:   DRM(vm_close),
57     };
58     
59     #if LINUX_VERSION_CODE < 0x020317
60     unsigned long DRM(vm_nopage)(struct vm_area_struct *vma,
61     			     unsigned long address,
62     			     int write_access)
63     #else
64     				/* Return type changed in 2.3.23 */
65     struct page *DRM(vm_nopage)(struct vm_area_struct *vma,
66     			    unsigned long address,
67     			    int write_access)
68     #endif
69     {
70     #if __REALLY_HAVE_AGP
71     	drm_file_t *priv  = vma->vm_file->private_data;
72     	drm_device_t *dev = priv->dev;
73     	drm_map_t *map    = NULL;
74     	drm_map_list_t  *r_list;
75     	struct list_head *list;
76     
77     	/*
78              * Find the right map
79              */
80     
81     	if(!dev->agp->cant_use_aperture) goto vm_nopage_error;
82     
83     	list_for_each(list, &dev->maplist->head) {
84     		r_list = (drm_map_list_t *)list;
85     		map = r_list->map;
86     		if (!map) continue;
87     		if (map->offset == VM_OFFSET(vma)) break;
88     	}
89     
90     	if (map && map->type == _DRM_AGP) {
91     		unsigned long offset = address - vma->vm_start;
92     		unsigned long baddr = VM_OFFSET(vma) + offset;
93     		struct drm_agp_mem *agpmem;
94     		struct page *page;
95     
96     #if __alpha__
97     		/*
98                      * Adjust to a bus-relative address
99                      */
100     		baddr -= dev->hose->mem_space->start;
101     #endif
102     
103     		/*
104                      * It's AGP memory - find the real physical page to map
105                      */
106     		for(agpmem = dev->agp->memory; agpmem; agpmem = agpmem->next) {
107     			if (agpmem->bound <= baddr &&
108     			    agpmem->bound + agpmem->pages * PAGE_SIZE > baddr) 
109     				break;
110     		}
111     
112     		if (!agpmem) goto vm_nopage_error;
113     
114     		/*
115                      * Get the page, inc the use count, and return it
116                      */
117     		offset = (baddr - agpmem->bound) >> PAGE_SHIFT;
118     		agpmem->memory->memory[offset] &= dev->agp->page_mask;
119     		page = virt_to_page(__va(agpmem->memory->memory[offset]));
120     		get_page(page);
121     
122     		DRM_DEBUG("baddr = 0x%lx page = 0x%p, offset = 0x%lx\n",
123     			  baddr, __va(agpmem->memory->memory[offset]), offset);
124     
125     #if LINUX_VERSION_CODE < 0x020317
126     		return page_address(page);
127     #else
128     		return page;
129     #endif
130             }
131     vm_nopage_error:
132     #endif /* __REALLY_HAVE_AGP */
133     
134     	return NOPAGE_SIGBUS;		/* Disallow mremap */
135     }
136     
137     #if LINUX_VERSION_CODE < 0x020317
138     unsigned long DRM(vm_shm_nopage)(struct vm_area_struct *vma,
139     				 unsigned long address,
140     				 int write_access)
141     #else
142     				/* Return type changed in 2.3.23 */
143     struct page *DRM(vm_shm_nopage)(struct vm_area_struct *vma,
144     				unsigned long address,
145     				int write_access)
146     #endif
147     {
148     #if LINUX_VERSION_CODE >= 0x020300
149     	drm_map_t	 *map	 = (drm_map_t *)vma->vm_private_data;
150     #else
151     	drm_map_t	 *map	 = (drm_map_t *)vma->vm_pte;
152     #endif
153     	unsigned long	 offset;
154     	unsigned long	 i;
155     	pgd_t		 *pgd;
156     	pmd_t		 *pmd;
157     	pte_t		 *pte;
158     	struct page	 *page;
159     
160     	if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
161     	if (!map)    		   return NOPAGE_OOM;  /* Nothing allocated */
162     
163     	offset	 = address - vma->vm_start;
164     	i = (unsigned long)map->handle + offset;
165     	/* We have to walk page tables here because we need large SAREA's, and
166     	 * they need to be virtually contiguous in kernel space.
167     	 */
168     	pgd = pgd_offset_k( i );
169     	if( !pgd_present( *pgd ) ) return NOPAGE_OOM;
170     	pmd = pmd_offset( pgd, i );
171     	if( !pmd_present( *pmd ) ) return NOPAGE_OOM;
172     	pte = pte_offset( pmd, i );
173     	if( !pte_present( *pte ) ) return NOPAGE_OOM;
174     
175     	page = pte_page(*pte);
176     	get_page(page);
177     
178     	DRM_DEBUG("0x%08lx => 0x%08x\n", address, page_to_bus(page));
179     #if LINUX_VERSION_CODE < 0x020317
180     	return page_address(page);
181     #else
182     	return page;
183     #endif
184     }
185     
186     /* Special close routine which deletes map information if we are the last
187      * person to close a mapping and its not in the global maplist.
188      */
189     
190     void DRM(vm_shm_close)(struct vm_area_struct *vma)
191     {
192     	drm_file_t	*priv	= vma->vm_file->private_data;
193     	drm_device_t	*dev	= priv->dev;
194     	drm_vma_entry_t *pt, *prev, *next;
195     	drm_map_t *map;
196     	drm_map_list_t *r_list;
197     	struct list_head *list;
198     	int found_maps = 0;
199     
200     	DRM_DEBUG("0x%08lx,0x%08lx\n",
201     		  vma->vm_start, vma->vm_end - vma->vm_start);
202     #if LINUX_VERSION_CODE < 0x020333
203     	MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
204     #endif
205     	atomic_dec(&dev->vma_count);
206     
207     #if LINUX_VERSION_CODE >= 0x020300
208     	map = vma->vm_private_data;
209     #else
210     	map = vma->vm_pte;
211     #endif
212     
213     	down(&dev->struct_sem);
214     	for (pt = dev->vmalist, prev = NULL; pt; pt = next) {
215     		next = pt->next;
216     #if LINUX_VERSION_CODE >= 0x020300
217     		if (pt->vma->vm_private_data == map) found_maps++;
218     #else
219     		if (pt->vma->vm_pte == map) found_maps++;
220     #endif
221     		if (pt->vma == vma) {
222     			if (prev) {
223     				prev->next = pt->next;
224     			} else {
225     				dev->vmalist = pt->next;
226     			}
227     			DRM(free)(pt, sizeof(*pt), DRM_MEM_VMAS);
228     		} else {
229     			prev = pt;
230     		}
231     	}
232     	/* We were the only map that was found */
233     	if(found_maps == 1 &&
234     	   map->flags & _DRM_REMOVABLE) {
235     		/* Check to see if we are in the maplist, if we are not, then
236     		 * we delete this mappings information.
237     		 */
238     		found_maps = 0;
239     		list = &dev->maplist->head;
240     		list_for_each(list, &dev->maplist->head) {
241     			r_list = (drm_map_list_t *) list;
242     			if (r_list->map == map) found_maps++;
243     		}
244     
245     		if(!found_maps) {
246     			switch (map->type) {
247     			case _DRM_REGISTERS:
248     			case _DRM_FRAME_BUFFER:
249     #if __REALLY_HAVE_MTRR
250     				if (map->mtrr >= 0) {
251     					int retcode;
252     					retcode = mtrr_del(map->mtrr,
253     							   map->offset,
254     							   map->size);
255     					DRM_DEBUG("mtrr_del = %d\n", retcode);
256     				}
257     #endif
258     				DRM(ioremapfree)(map->handle, map->size);
259     				break;
260     			case _DRM_SHM:
261     				vfree(map->handle);
262     				break;
263     			case _DRM_AGP:
264     			case _DRM_SCATTER_GATHER:
265     				break;
266     			}
267     			DRM(free)(map, sizeof(*map), DRM_MEM_MAPS);
268     		}
269     	}
270     	up(&dev->struct_sem);
271     }
272     
273     #if LINUX_VERSION_CODE < 0x020317
274     unsigned long DRM(vm_dma_nopage)(struct vm_area_struct *vma,
275     				 unsigned long address,
276     				 int write_access)
277     #else
278     				/* Return type changed in 2.3.23 */
279     struct page *DRM(vm_dma_nopage)(struct vm_area_struct *vma,
280     				unsigned long address,
281     				int write_access)
282     #endif
283     {
284     	drm_file_t	 *priv	 = vma->vm_file->private_data;
285     	drm_device_t	 *dev	 = priv->dev;
286     	drm_device_dma_t *dma	 = dev->dma;
287     	unsigned long	 offset;
288     	unsigned long	 page_nr;
289     	struct page	 *page;
290     
291     	if (!dma)		   return NOPAGE_SIGBUS; /* Error */
292     	if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
293     	if (!dma->pagelist)	   return NOPAGE_OOM ; /* Nothing allocated */
294     
295     	offset	 = address - vma->vm_start; /* vm_[pg]off[set] should be 0 */
296     	page_nr  = offset >> PAGE_SHIFT;
297     	page = virt_to_page((dma->pagelist[page_nr] + 
298     			     (offset & (~PAGE_MASK))));
299     
300     	get_page(page);
301     
302     	DRM_DEBUG("0x%08lx (page %lu) => 0x%08x\n", address, page_nr, 
303     		  page_to_bus(page));
304     #if LINUX_VERSION_CODE < 0x020317
305     	return page_address(page);
306     #else
307     	return page;
308     #endif
309     }
310     
311     #if LINUX_VERSION_CODE < 0x020317
312     unsigned long DRM(vm_sg_nopage)(struct vm_area_struct *vma,
313     				unsigned long address,
314     				int write_access)
315     #else
316     				/* Return type changed in 2.3.23 */
317     struct page *DRM(vm_sg_nopage)(struct vm_area_struct *vma,
318     			       unsigned long address,
319     			       int write_access)
320     #endif
321     {
322     #if LINUX_VERSION_CODE >= 0x020300
323     	drm_map_t        *map    = (drm_map_t *)vma->vm_private_data;
324     #else
325     	drm_map_t        *map    = (drm_map_t *)vma->vm_pte;
326     #endif
327     	drm_file_t *priv = vma->vm_file->private_data;
328     	drm_device_t *dev = priv->dev;
329     	drm_sg_mem_t *entry = dev->sg;
330     	unsigned long offset;
331     	unsigned long map_offset;
332     	unsigned long page_offset;
333     	struct page *page;
334     
335     	if (!entry)                return NOPAGE_SIGBUS; /* Error */
336     	if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
337     	if (!entry->pagelist)      return NOPAGE_OOM ;  /* Nothing allocated */
338     
339     
340     	offset = address - vma->vm_start;
341     	map_offset = map->offset - dev->sg->handle;
342     	page_offset = (offset >> PAGE_SHIFT) + (map_offset >> PAGE_SHIFT);
343     	page = entry->pagelist[page_offset];
344     	get_page(page);
345     
346     #if LINUX_VERSION_CODE < 0x020317
347     	return page_address(page);
348     #else
349     	return page;
350     #endif
351     }
352     
353     void DRM(vm_open)(struct vm_area_struct *vma)
354     {
355     	drm_file_t	*priv	= vma->vm_file->private_data;
356     	drm_device_t	*dev	= priv->dev;
357     	drm_vma_entry_t *vma_entry;
358     
359     	DRM_DEBUG("0x%08lx,0x%08lx\n",
360     		  vma->vm_start, vma->vm_end - vma->vm_start);
361     	atomic_inc(&dev->vma_count);
362     #if LINUX_VERSION_CODE < 0x020333
363     				/* The map can exist after the fd is closed. */
364     	MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
365     #endif
366     
367     	vma_entry = DRM(alloc)(sizeof(*vma_entry), DRM_MEM_VMAS);
368     	if (vma_entry) {
369     		down(&dev->struct_sem);
370     		vma_entry->vma	= vma;
371     		vma_entry->next = dev->vmalist;
372     		vma_entry->pid	= current->pid;
373     		dev->vmalist	= vma_entry;
374     		up(&dev->struct_sem);
375     	}
376     }
377     
378     void DRM(vm_close)(struct vm_area_struct *vma)
379     {
380     	drm_file_t	*priv	= vma->vm_file->private_data;
381     	drm_device_t	*dev	= priv->dev;
382     	drm_vma_entry_t *pt, *prev;
383     
384     	DRM_DEBUG("0x%08lx,0x%08lx\n",
385     		  vma->vm_start, vma->vm_end - vma->vm_start);
386     #if LINUX_VERSION_CODE < 0x020333
387     	MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
388     #endif
389     	atomic_dec(&dev->vma_count);
390     
391     	down(&dev->struct_sem);
392     	for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) {
393     		if (pt->vma == vma) {
394     			if (prev) {
395     				prev->next = pt->next;
396     			} else {
397     				dev->vmalist = pt->next;
398     			}
399     			DRM(free)(pt, sizeof(*pt), DRM_MEM_VMAS);
400     			break;
401     		}
402     	}
403     	up(&dev->struct_sem);
404     }
405     
406     int DRM(mmap_dma)(struct file *filp, struct vm_area_struct *vma)
407     {
408     	drm_file_t	 *priv	 = filp->private_data;
409     	drm_device_t	 *dev;
410     	drm_device_dma_t *dma;
411     	unsigned long	 length	 = vma->vm_end - vma->vm_start;
412     
413     	lock_kernel();
414     	dev	 = priv->dev;
415     	dma	 = dev->dma;
416     	DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
417     		  vma->vm_start, vma->vm_end, VM_OFFSET(vma));
418     
419     				/* Length must match exact page count */
420     	if (!dma || (length >> PAGE_SHIFT) != dma->page_count) {
421     		unlock_kernel();
422     		return -EINVAL;
423     	}
424     	unlock_kernel();
425     
426     	vma->vm_ops   = &DRM(vm_dma_ops);
427     	vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */
428     
429     #if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */
430     				/* In Linux 2.2.3 and above, this is
431     				   handled in do_mmap() in mm/mmap.c. */
432     	++filp->f_count;
433     #endif
434     	vma->vm_file  =	 filp;	/* Needed for drm_vm_open() */
435     	DRM(vm_open)(vma);
436     	return 0;
437     }
438     
439     #ifndef DRIVER_GET_MAP_OFS
440     #define DRIVER_GET_MAP_OFS()	(map->offset)
441     #endif
442     
443     #ifndef DRIVER_GET_REG_OFS
444     #ifdef __alpha__
445     #define DRIVER_GET_REG_OFS()	(dev->hose->dense_mem_base -	\
446     				 dev->hose->mem_space->start)
447     #else
448     #define DRIVER_GET_REG_OFS()	0
449     #endif
450     #endif
451     
452     int DRM(mmap)(struct file *filp, struct vm_area_struct *vma)
453     {
454     	drm_file_t	*priv	= filp->private_data;
455     	drm_device_t	*dev	= priv->dev;
456     	drm_map_t	*map	= NULL;
457     	drm_map_list_t  *r_list;
458     	unsigned long   offset  = 0;
459     	struct list_head *list;
460     
461     	DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
462     		  vma->vm_start, vma->vm_end, VM_OFFSET(vma));
463     
464     	if ( !priv->authenticated ) return -EACCES;
465     
466     	if (!VM_OFFSET(vma)) return DRM(mmap_dma)(filp, vma);
467     
468     				/* A sequential search of a linked list is
469     				   fine here because: 1) there will only be
470     				   about 5-10 entries in the list and, 2) a
471     				   DRI client only has to do this mapping
472     				   once, so it doesn't have to be optimized
473     				   for performance, even if the list was a
474     				   bit longer. */
475     	list_for_each(list, &dev->maplist->head) {
476     		unsigned long off;
477     
478     		r_list = (drm_map_list_t *)list;
479     		map = r_list->map;
480     		if (!map) continue;
481     		off = DRIVER_GET_MAP_OFS();
482     		if (off == VM_OFFSET(vma)) break;
483     	}
484     
485     	if (!map || ((map->flags&_DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN)))
486     		return -EPERM;
487     
488     				/* Check for valid size. */
489     	if (map->size != vma->vm_end - vma->vm_start) return -EINVAL;
490     
491     	if (!capable(CAP_SYS_ADMIN) && (map->flags & _DRM_READ_ONLY)) {
492     		vma->vm_flags &= VM_MAYWRITE;
493     #if defined(__i386__)
494     		pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW;
495     #else
496     				/* Ye gads this is ugly.  With more thought
497                                        we could move this up higher and use
498                                        `protection_map' instead.  */
499     		vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect(
500     			__pte(pgprot_val(vma->vm_page_prot)))));
501     #endif
502     	}
503     
504     	switch (map->type) {
505             case _DRM_AGP:
506     #if defined(__alpha__)
507                     /*
508                      * On Alpha we can't talk to bus dma address from the
509                      * CPU, so for memory of type DRM_AGP, we'll deal with
510                      * sorting out the real physical pages and mappings
511                      * in nopage()
512                      */
513                     vma->vm_ops = &DRM(vm_ops);
514                     break;
515     #endif
516                     /* fall through to _DRM_FRAME_BUFFER... */        
517     	case _DRM_FRAME_BUFFER:
518     	case _DRM_REGISTERS:
519     		if (VM_OFFSET(vma) >= __pa(high_memory)) {
520     #if defined(__i386__)
521     			if (boot_cpu_data.x86 > 3 && map->type != _DRM_AGP) {
522     				pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
523     				pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT;
524     			}
525     #elif defined(__ia64__)
526     			if (map->type != _DRM_AGP)
527     				vma->vm_page_prot =
528     					pgprot_writecombine(vma->vm_page_prot);
529     #elif defined(__powerpc__)
530     			pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE | _PAGE_GUARDED;
531     #endif
532     			vma->vm_flags |= VM_IO;	/* not in core dump */
533     		}
534     		offset = DRIVER_GET_REG_OFS();
535     		if (remap_page_range(vma->vm_start,
536     				     VM_OFFSET(vma) + offset,
537     				     vma->vm_end - vma->vm_start,
538     				     vma->vm_page_prot))
539     				return -EAGAIN;
540     		DRM_DEBUG("   Type = %d; start = 0x%lx, end = 0x%lx,"
541     			  " offset = 0x%lx\n",
542     			  map->type,
543     			  vma->vm_start, vma->vm_end, VM_OFFSET(vma) + offset);
544     		vma->vm_ops = &DRM(vm_ops);
545     		break;
546     	case _DRM_SHM:
547     		vma->vm_ops = &DRM(vm_shm_ops);
548     #if LINUX_VERSION_CODE >= 0x020300
549     		vma->vm_private_data = (void *)map;
550     #else
551     		vma->vm_pte = (unsigned long)map;
552     #endif
553     				/* Don't let this area swap.  Change when
554     				   DRM_KERNEL advisory is supported. */
555     		vma->vm_flags |= VM_LOCKED;
556     		break;
557     	case _DRM_SCATTER_GATHER:
558     		vma->vm_ops = &DRM(vm_sg_ops);
559     #if LINUX_VERSION_CODE >= 0x020300
560     		vma->vm_private_data = (void *)map;
561     #else
562     		vma->vm_pte = (unsigned long)map;
563     #endif
564                     vma->vm_flags |= VM_LOCKED;
565                     break;
566     	default:
567     		return -EINVAL;	/* This should never happen. */
568     	}
569     	vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */
570     
571     #if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */
572     				/* In Linux 2.2.3 and above, this is
573     				   handled in do_mmap() in mm/mmap.c. */
574     	++filp->f_count;
575     #endif
576     	vma->vm_file  =	 filp;	/* Needed for drm_vm_open() */
577     	DRM(vm_open)(vma);
578     	return 0;
579     }
580