1 /* vm.c -- Memory mapping for DRM -*- linux-c -*-
2 * Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com
4 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
5 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
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:
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
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 * PRECISION INSIGHT 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 OTHER
25 * DEALINGS IN THE SOFTWARE.
28 * Rickard E. (Rik) Faith <faith@valinux.com>
32 #define __NO_VERSION__
35 struct vm_operations_struct drm_vm_ops
= {
36 nopage
: drm_vm_nopage
,
41 struct vm_operations_struct drm_vm_shm_ops
= {
42 nopage
: drm_vm_shm_nopage
,
47 struct vm_operations_struct drm_vm_shm_lock_ops
= {
48 nopage
: drm_vm_shm_nopage_lock
,
53 struct vm_operations_struct drm_vm_dma_ops
= {
54 nopage
: drm_vm_dma_nopage
,
59 #if LINUX_VERSION_CODE < 0x020317
60 unsigned long drm_vm_nopage(struct vm_area_struct
*vma
,
61 unsigned long address
,
64 /* Return type changed in 2.3.23 */
65 struct page
*drm_vm_nopage(struct vm_area_struct
*vma
,
66 unsigned long address
,
70 return NOPAGE_SIGBUS
; /* Disallow mremap */
73 #if LINUX_VERSION_CODE < 0x020317
74 unsigned long drm_vm_shm_nopage(struct vm_area_struct
*vma
,
75 unsigned long address
,
78 /* Return type changed in 2.3.23 */
79 struct page
*drm_vm_shm_nopage(struct vm_area_struct
*vma
,
80 unsigned long address
,
84 #if LINUX_VERSION_CODE >= 0x020300
85 drm_map_t
*map
= (drm_map_t
*)vma
->vm_private_data
;
87 drm_map_t
*map
= (drm_map_t
*)vma
->vm_pte
;
89 unsigned long physical
;
92 if (address
> vma
->vm_end
) return NOPAGE_SIGBUS
; /* Disallow mremap */
93 if (!map
) return NOPAGE_OOM
; /* Nothing allocated */
95 offset
= address
- vma
->vm_start
;
96 physical
= (unsigned long)map
->handle
+ offset
;
97 atomic_inc(&virt_to_page(physical
)->count
); /* Dec. by kernel */
99 DRM_DEBUG("0x%08lx => 0x%08lx\n", address
, physical
);
100 #if LINUX_VERSION_CODE < 0x020317
103 return virt_to_page(physical
);
107 #if LINUX_VERSION_CODE < 0x020317
108 unsigned long drm_vm_shm_nopage_lock(struct vm_area_struct
*vma
,
109 unsigned long address
,
112 /* Return type changed in 2.3.23 */
113 struct page
*drm_vm_shm_nopage_lock(struct vm_area_struct
*vma
,
114 unsigned long address
,
118 drm_file_t
*priv
= vma
->vm_file
->private_data
;
119 drm_device_t
*dev
= priv
->dev
;
120 unsigned long physical
;
121 unsigned long offset
;
124 if (address
> vma
->vm_end
) return NOPAGE_SIGBUS
; /* Disallow mremap */
125 if (!dev
->lock
.hw_lock
) return NOPAGE_OOM
; /* Nothing allocated */
127 offset
= address
- vma
->vm_start
;
128 page
= offset
>> PAGE_SHIFT
;
129 physical
= (unsigned long)dev
->lock
.hw_lock
+ offset
;
130 atomic_inc(&virt_to_page(physical
)->count
); /* Dec. by kernel */
132 DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address
, page
, physical
);
133 #if LINUX_VERSION_CODE < 0x020317
136 return virt_to_page(physical
);
140 #if LINUX_VERSION_CODE < 0x020317
141 unsigned long drm_vm_dma_nopage(struct vm_area_struct
*vma
,
142 unsigned long address
,
145 /* Return type changed in 2.3.23 */
146 struct page
*drm_vm_dma_nopage(struct vm_area_struct
*vma
,
147 unsigned long address
,
151 drm_file_t
*priv
= vma
->vm_file
->private_data
;
152 drm_device_t
*dev
= priv
->dev
;
153 drm_device_dma_t
*dma
= dev
->dma
;
154 unsigned long physical
;
155 unsigned long offset
;
158 if (!dma
) return NOPAGE_SIGBUS
; /* Error */
159 if (address
> vma
->vm_end
) return NOPAGE_SIGBUS
; /* Disallow mremap */
160 if (!dma
->pagelist
) return NOPAGE_OOM
; /* Nothing allocated */
162 offset
= address
- vma
->vm_start
; /* vm_[pg]off[set] should be 0 */
163 page
= offset
>> PAGE_SHIFT
;
164 physical
= dma
->pagelist
[page
] + (offset
& (~PAGE_MASK
));
165 atomic_inc(&virt_to_page(physical
)->count
); /* Dec. by kernel */
167 DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address
, page
, physical
);
168 #if LINUX_VERSION_CODE < 0x020317
171 return virt_to_page(physical
);
175 void drm_vm_open(struct vm_area_struct
*vma
)
177 drm_file_t
*priv
= vma
->vm_file
->private_data
;
178 drm_device_t
*dev
= priv
->dev
;
180 drm_vma_entry_t
*vma_entry
;
183 DRM_DEBUG("0x%08lx,0x%08lx\n",
184 vma
->vm_start
, vma
->vm_end
- vma
->vm_start
);
185 atomic_inc(&dev
->vma_count
);
186 #if LINUX_VERSION_CODE < 0x020333
187 /* The map can exist after the fd is closed. */
188 MOD_INC_USE_COUNT
; /* Needed before Linux 2.3.51 */
193 vma_entry
= drm_alloc(sizeof(*vma_entry
), DRM_MEM_VMAS
);
195 down(&dev
->struct_sem
);
196 vma_entry
->vma
= vma
;
197 vma_entry
->next
= dev
->vmalist
;
198 vma_entry
->pid
= current
->pid
;
199 dev
->vmalist
= vma_entry
;
200 up(&dev
->struct_sem
);
205 void drm_vm_close(struct vm_area_struct
*vma
)
207 drm_file_t
*priv
= vma
->vm_file
->private_data
;
208 drm_device_t
*dev
= priv
->dev
;
210 drm_vma_entry_t
*pt
, *prev
;
213 DRM_DEBUG("0x%08lx,0x%08lx\n",
214 vma
->vm_start
, vma
->vm_end
- vma
->vm_start
);
215 #if LINUX_VERSION_CODE < 0x020333
216 MOD_DEC_USE_COUNT
; /* Needed before Linux 2.3.51 */
218 atomic_dec(&dev
->vma_count
);
221 down(&dev
->struct_sem
);
222 for (pt
= dev
->vmalist
, prev
= NULL
; pt
; prev
= pt
, pt
= pt
->next
) {
223 if (pt
->vma
== vma
) {
225 prev
->next
= pt
->next
;
227 dev
->vmalist
= pt
->next
;
229 drm_free(pt
, sizeof(*pt
), DRM_MEM_VMAS
);
233 up(&dev
->struct_sem
);
237 int drm_mmap_dma(struct file
*filp
, struct vm_area_struct
*vma
)
239 drm_file_t
*priv
= filp
->private_data
;
241 drm_device_dma_t
*dma
;
242 unsigned long length
= vma
->vm_end
- vma
->vm_start
;
247 DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
248 vma
->vm_start
, vma
->vm_end
, VM_OFFSET(vma
));
250 /* Length must match exact page count */
251 if (!dma
|| (length
>> PAGE_SHIFT
) != dma
->page_count
) {
257 vma
->vm_ops
= &drm_vm_dma_ops
;
258 vma
->vm_flags
|= VM_LOCKED
| VM_SHM
; /* Don't swap */
260 #if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */
261 /* In Linux 2.2.3 and above, this is
262 handled in do_mmap() in mm/mmap.c. */
265 vma
->vm_file
= filp
; /* Needed for drm_vm_open() */
270 int drm_mmap(struct file
*filp
, struct vm_area_struct
*vma
)
272 drm_file_t
*priv
= filp
->private_data
;
273 drm_device_t
*dev
= priv
->dev
;
274 drm_map_t
*map
= NULL
;
277 DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
278 vma
->vm_start
, vma
->vm_end
, VM_OFFSET(vma
));
280 if (!VM_OFFSET(vma
)) return drm_mmap_dma(filp
, vma
);
282 /* A sequential search of a linked list is
283 fine here because: 1) there will only be
284 about 5-10 entries in the list and, 2) a
285 DRI client only has to do this mapping
286 once, so it doesn't have to be optimized
287 for performance, even if the list was a
289 for (i
= 0; i
< dev
->map_count
; i
++) {
290 map
= dev
->maplist
[i
];
291 if (map
->offset
== VM_OFFSET(vma
)) break;
294 if (i
>= dev
->map_count
) return -EINVAL
;
295 if (!map
|| ((map
->flags
&_DRM_RESTRICTED
) && !capable(CAP_SYS_ADMIN
)))
298 /* Check for valid size. */
299 if (map
->size
!= vma
->vm_end
- vma
->vm_start
) return -EINVAL
;
301 if (!capable(CAP_SYS_ADMIN
) && (map
->flags
& _DRM_READ_ONLY
)) {
302 vma
->vm_flags
&= VM_MAYWRITE
;
303 #if defined(__i386__)
304 pgprot_val(vma
->vm_page_prot
) &= ~_PAGE_RW
;
306 /* Ye gads this is ugly. With more thought
307 we could move this up higher and use
308 `protection_map' instead. */
309 vma
->vm_page_prot
= __pgprot(pte_val(pte_wrprotect(
310 __pte(pgprot_val(vma
->vm_page_prot
)))));
315 case _DRM_FRAME_BUFFER
:
318 if (VM_OFFSET(vma
) >= __pa(high_memory
)) {
319 #if defined(__i386__)
320 if (boot_cpu_data
.x86
> 3 && map
->type
!= _DRM_AGP
) {
321 pgprot_val(vma
->vm_page_prot
) |= _PAGE_PCD
;
322 pgprot_val(vma
->vm_page_prot
) &= ~_PAGE_PWT
;
324 #elif defined(__ia64__)
325 if (map
->type
!= _DRM_AGP
)
326 vma
->vm_page_prot
= pgprot_writecombine(vma
->vm_page_prot
);
328 vma
->vm_flags
|= VM_IO
; /* not in core dump */
330 if (remap_page_range(vma
->vm_start
,
332 vma
->vm_end
- vma
->vm_start
,
335 DRM_DEBUG(" Type = %d; start = 0x%lx, end = 0x%lx,"
338 vma
->vm_start
, vma
->vm_end
, VM_OFFSET(vma
));
339 vma
->vm_ops
= &drm_vm_ops
;
342 if (map
->flags
& _DRM_CONTAINS_LOCK
)
343 vma
->vm_ops
= &drm_vm_shm_lock_ops
;
345 vma
->vm_ops
= &drm_vm_shm_ops
;
346 #if LINUX_VERSION_CODE >= 0x020300
347 vma
->vm_private_data
= (void *)map
;
349 vma
->vm_pte
= (unsigned long)map
;
353 /* Don't let this area swap. Change when
354 DRM_KERNEL advisory is supported. */
355 vma
->vm_flags
|= VM_LOCKED
;
358 return -EINVAL
; /* This should never happen. */
360 vma
->vm_flags
|= VM_LOCKED
| VM_SHM
; /* Don't swap */
362 #if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */
363 /* In Linux 2.2.3 and above, this is
364 handled in do_mmap() in mm/mmap.c. */
367 vma
->vm_file
= filp
; /* Needed for drm_vm_open() */