drm_vm.c 19.7 KB
Newer Older
L
Linus Torvalds 已提交
1
/**
D
Dave Airlie 已提交
2
 * \file drm_vm.c
L
Linus Torvalds 已提交
3
 * Memory mapping for DRM
D
Dave Airlie 已提交
4
 *
L
Linus Torvalds 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 * \author Rickard E. (Rik) Faith <faith@valinux.com>
 * \author Gareth Hughes <gareth@valinux.com>
 */

/*
 * Created: Mon Jan  4 08:58:31 1999 by faith@valinux.com
 *
 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

36
#include <drm/drmP.h>
37
#include <linux/export.h>
38
#include <linux/seq_file.h>
L
Linus Torvalds 已提交
39 40
#if defined(__ia64__)
#include <linux/efi.h>
41
#include <linux/slab.h>
L
Linus Torvalds 已提交
42
#endif
43
#include <asm/pgtable.h>
44
#include "drm_legacy.h"
L
Linus Torvalds 已提交
45

46 47 48 49 50 51
struct drm_vma_entry {
	struct list_head head;
	struct vm_area_struct *vma;
	pid_t pid;
};

D
Dave Airlie 已提交
52 53
static void drm_vm_open(struct vm_area_struct *vma);
static void drm_vm_close(struct vm_area_struct *vma);
L
Linus Torvalds 已提交
54

55 56
static pgprot_t drm_io_prot(struct drm_local_map *map,
			    struct vm_area_struct *vma)
57 58 59 60
{
	pgprot_t tmp = vm_get_page_prot(vma->vm_flags);

#if defined(__i386__) || defined(__x86_64__)
61 62 63 64
	if (map->type == _DRM_REGISTERS && !(map->flags & _DRM_WRITE_COMBINING))
		tmp = pgprot_noncached(tmp);
	else
		tmp = pgprot_writecombine(tmp);
65 66
#elif defined(__powerpc__)
	pgprot_val(tmp) |= _PAGE_NO_CACHE;
67
	if (map->type == _DRM_REGISTERS)
68
		pgprot_val(tmp) |= _PAGE_GUARDED;
69
#elif defined(__ia64__)
70 71 72 73 74
	if (efi_range_is_wc(vma->vm_start, vma->vm_end -
				    vma->vm_start))
		tmp = pgprot_writecombine(tmp);
	else
		tmp = pgprot_noncached(tmp);
75
#elif defined(__sparc__) || defined(__arm__) || defined(__mips__)
76 77 78 79 80 81 82 83 84 85 86
	tmp = pgprot_noncached(tmp);
#endif
	return tmp;
}

static pgprot_t drm_dma_prot(uint32_t map_type, struct vm_area_struct *vma)
{
	pgprot_t tmp = vm_get_page_prot(vma->vm_flags);

#if defined(__powerpc__) && defined(CONFIG_NOT_COHERENT_CACHE)
	tmp |= _PAGE_NO_CACHE;
87 88 89 90
#endif
	return tmp;
}

L
Linus Torvalds 已提交
91
/**
92
 * \c fault method for AGP virtual memory.
L
Linus Torvalds 已提交
93 94 95 96
 *
 * \param vma virtual memory area.
 * \param address access address.
 * \return pointer to the page structure.
D
Dave Airlie 已提交
97
 *
L
Linus Torvalds 已提交
98 99 100 101
 * Find the right map and if it's AGP memory find the real physical page to
 * map, get the page, increment the use count and return it.
 */
#if __OS_HAS_AGP
102
static int drm_do_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
L
Linus Torvalds 已提交
103
{
104
	struct drm_file *priv = vma->vm_file->private_data;
105
	struct drm_device *dev = priv->minor->dev;
106
	struct drm_local_map *map = NULL;
D
Dave Airlie 已提交
107
	struct drm_map_list *r_list;
108
	struct drm_hash_item *hash;
L
Linus Torvalds 已提交
109 110

	/*
D
Dave Airlie 已提交
111 112
	 * Find the right map
	 */
D
Daniel Vetter 已提交
113
	if (!dev->agp)
114
		goto vm_fault_error;
L
Linus Torvalds 已提交
115

D
Dave Airlie 已提交
116
	if (!dev->agp || !dev->agp->cant_use_aperture)
117
		goto vm_fault_error;
L
Linus Torvalds 已提交
118

119
	if (drm_ht_find_item(&dev->map_hash, vma->vm_pgoff, &hash))
120
		goto vm_fault_error;
121

D
Dave Airlie 已提交
122
	r_list = drm_hash_entry(hash, struct drm_map_list, hash);
123
	map = r_list->map;
L
Linus Torvalds 已提交
124 125

	if (map && map->type == _DRM_AGP) {
126 127 128 129
		/*
		 * Using vm_pgoff as a selector forces us to use this unusual
		 * addressing scheme.
		 */
130 131 132
		resource_size_t offset = (unsigned long)vmf->virtual_address -
			vma->vm_start;
		resource_size_t baddr = map->offset + offset;
L
Linus Torvalds 已提交
133 134 135 136 137
		struct drm_agp_mem *agpmem;
		struct page *page;

#ifdef __alpha__
		/*
D
Dave Airlie 已提交
138 139
		 * Adjust to a bus-relative address
		 */
L
Linus Torvalds 已提交
140 141 142 143
		baddr -= dev->hose->mem_space->start;
#endif

		/*
D
Dave Airlie 已提交
144 145
		 * It's AGP memory - find the real physical page to map
		 */
146
		list_for_each_entry(agpmem, &dev->agp->memory, head) {
L
Linus Torvalds 已提交
147
			if (agpmem->bound <= baddr &&
D
Dave Airlie 已提交
148
			    agpmem->bound + agpmem->pages * PAGE_SIZE > baddr)
L
Linus Torvalds 已提交
149 150 151
				break;
		}

D
Dan Carpenter 已提交
152
		if (&agpmem->head == &dev->agp->memory)
153
			goto vm_fault_error;
L
Linus Torvalds 已提交
154 155

		/*
D
Dave Airlie 已提交
156 157
		 * Get the page, inc the use count, and return it
		 */
L
Linus Torvalds 已提交
158
		offset = (baddr - agpmem->bound) >> PAGE_SHIFT;
159
		page = agpmem->memory->pages[offset];
L
Linus Torvalds 已提交
160
		get_page(page);
161
		vmf->page = page;
L
Linus Torvalds 已提交
162

D
Dave Airlie 已提交
163
		DRM_DEBUG
164 165
		    ("baddr = 0x%llx page = 0x%p, offset = 0x%llx, count=%d\n",
		     (unsigned long long)baddr,
166
		     agpmem->memory->pages[offset],
167
		     (unsigned long long)offset,
D
Dave Airlie 已提交
168
		     page_count(page));
169
		return 0;
D
Dave Airlie 已提交
170
	}
171 172
vm_fault_error:
	return VM_FAULT_SIGBUS;	/* Disallow mremap */
L
Linus Torvalds 已提交
173
}
D
Dave Airlie 已提交
174
#else				/* __OS_HAS_AGP */
175
static int drm_do_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
L
Linus Torvalds 已提交
176
{
177
	return VM_FAULT_SIGBUS;
L
Linus Torvalds 已提交
178
}
D
Dave Airlie 已提交
179
#endif				/* __OS_HAS_AGP */
L
Linus Torvalds 已提交
180 181 182 183 184 185 186

/**
 * \c nopage method for shared virtual memory.
 *
 * \param vma virtual memory area.
 * \param address access address.
 * \return pointer to the page structure.
D
Dave Airlie 已提交
187
 *
188
 * Get the mapping, find the real physical page to map, get the page, and
L
Linus Torvalds 已提交
189 190
 * return it.
 */
191
static int drm_do_vm_shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
L
Linus Torvalds 已提交
192
{
193
	struct drm_local_map *map = vma->vm_private_data;
D
Dave Airlie 已提交
194 195 196
	unsigned long offset;
	unsigned long i;
	struct page *page;
L
Linus Torvalds 已提交
197

D
Dave Airlie 已提交
198
	if (!map)
199
		return VM_FAULT_SIGBUS;	/* Nothing allocated */
L
Linus Torvalds 已提交
200

201
	offset = (unsigned long)vmf->virtual_address - vma->vm_start;
L
Linus Torvalds 已提交
202
	i = (unsigned long)map->handle + offset;
H
Hugh Dickins 已提交
203
	page = vmalloc_to_page((void *)i);
L
Linus Torvalds 已提交
204
	if (!page)
205
		return VM_FAULT_SIGBUS;
L
Linus Torvalds 已提交
206
	get_page(page);
207
	vmf->page = page;
L
Linus Torvalds 已提交
208

209 210
	DRM_DEBUG("shm_fault 0x%lx\n", offset);
	return 0;
L
Linus Torvalds 已提交
211 212 213 214
}

/**
 * \c close method for shared virtual memory.
D
Dave Airlie 已提交
215
 *
L
Linus Torvalds 已提交
216
 * \param vma virtual memory area.
D
Dave Airlie 已提交
217
 *
L
Linus Torvalds 已提交
218 219 220
 * Deletes map information if we are the last
 * person to close a mapping and it's not in the global maplist.
 */
D
Dave Airlie 已提交
221
static void drm_vm_shm_close(struct vm_area_struct *vma)
L
Linus Torvalds 已提交
222
{
223
	struct drm_file *priv = vma->vm_file->private_data;
224
	struct drm_device *dev = priv->minor->dev;
225
	struct drm_vma_entry *pt, *temp;
226
	struct drm_local_map *map;
D
Dave Airlie 已提交
227
	struct drm_map_list *r_list;
L
Linus Torvalds 已提交
228 229 230 231 232 233 234
	int found_maps = 0;

	DRM_DEBUG("0x%08lx,0x%08lx\n",
		  vma->vm_start, vma->vm_end - vma->vm_start);

	map = vma->vm_private_data;

D
Dave Airlie 已提交
235
	mutex_lock(&dev->struct_mutex);
236
	list_for_each_entry_safe(pt, temp, &dev->vmalist, head) {
D
Dave Airlie 已提交
237 238
		if (pt->vma->vm_private_data == map)
			found_maps++;
L
Linus Torvalds 已提交
239
		if (pt->vma == vma) {
240
			list_del(&pt->head);
241
			kfree(pt);
L
Linus Torvalds 已提交
242 243
		}
	}
244

L
Linus Torvalds 已提交
245
	/* We were the only map that was found */
D
Dave Airlie 已提交
246
	if (found_maps == 1 && map->flags & _DRM_REMOVABLE) {
L
Linus Torvalds 已提交
247 248 249 250
		/* Check to see if we are in the maplist, if we are not, then
		 * we delete this mappings information.
		 */
		found_maps = 0;
251
		list_for_each_entry(r_list, &dev->maplist, head) {
D
Dave Airlie 已提交
252 253
			if (r_list->map == map)
				found_maps++;
L
Linus Torvalds 已提交
254 255
		}

D
Dave Airlie 已提交
256
		if (!found_maps) {
257 258
			drm_dma_handle_t dmah;

L
Linus Torvalds 已提交
259 260 261
			switch (map->type) {
			case _DRM_REGISTERS:
			case _DRM_FRAME_BUFFER:
262
				arch_phys_wc_del(map->mtrr);
263
				iounmap(map->handle);
L
Linus Torvalds 已提交
264 265 266 267 268 269 270
				break;
			case _DRM_SHM:
				vfree(map->handle);
				break;
			case _DRM_AGP:
			case _DRM_SCATTER_GATHER:
				break;
D
Dave Airlie 已提交
271
			case _DRM_CONSISTENT:
272 273 274 275
				dmah.vaddr = map->handle;
				dmah.busaddr = map->offset;
				dmah.size = map->size;
				__drm_pci_free(dev, &dmah);
D
Dave Airlie 已提交
276
				break;
L
Linus Torvalds 已提交
277
			}
278
			kfree(map);
L
Linus Torvalds 已提交
279 280
		}
	}
D
Dave Airlie 已提交
281
	mutex_unlock(&dev->struct_mutex);
L
Linus Torvalds 已提交
282 283 284
}

/**
285
 * \c fault method for DMA virtual memory.
L
Linus Torvalds 已提交
286 287 288 289
 *
 * \param vma virtual memory area.
 * \param address access address.
 * \return pointer to the page structure.
D
Dave Airlie 已提交
290
 *
L
Linus Torvalds 已提交
291 292
 * Determine the page number from the page offset and get it from drm_device_dma::pagelist.
 */
293
static int drm_do_vm_dma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
L
Linus Torvalds 已提交
294
{
295
	struct drm_file *priv = vma->vm_file->private_data;
296
	struct drm_device *dev = priv->minor->dev;
297
	struct drm_device_dma *dma = dev->dma;
D
Dave Airlie 已提交
298 299 300 301 302
	unsigned long offset;
	unsigned long page_nr;
	struct page *page;

	if (!dma)
303
		return VM_FAULT_SIGBUS;	/* Error */
D
Dave Airlie 已提交
304
	if (!dma->pagelist)
305
		return VM_FAULT_SIGBUS;	/* Nothing allocated */
D
Dave Airlie 已提交
306

307 308
	offset = (unsigned long)vmf->virtual_address - vma->vm_start;	/* vm_[pg]off[set] should be 0 */
	page_nr = offset >> PAGE_SHIFT; /* page_nr could just be vmf->pgoff */
309
	page = virt_to_page((void *)dma->pagelist[page_nr]);
L
Linus Torvalds 已提交
310 311

	get_page(page);
312
	vmf->page = page;
L
Linus Torvalds 已提交
313

314 315
	DRM_DEBUG("dma_fault 0x%lx (page %lu)\n", offset, page_nr);
	return 0;
L
Linus Torvalds 已提交
316 317 318
}

/**
319
 * \c fault method for scatter-gather virtual memory.
L
Linus Torvalds 已提交
320 321 322 323
 *
 * \param vma virtual memory area.
 * \param address access address.
 * \return pointer to the page structure.
D
Dave Airlie 已提交
324
 *
L
Linus Torvalds 已提交
325 326
 * Determine the map offset from the page offset and get it from drm_sg_mem::pagelist.
 */
327
static int drm_do_vm_sg_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
L
Linus Torvalds 已提交
328
{
329
	struct drm_local_map *map = vma->vm_private_data;
330
	struct drm_file *priv = vma->vm_file->private_data;
331
	struct drm_device *dev = priv->minor->dev;
D
Dave Airlie 已提交
332
	struct drm_sg_mem *entry = dev->sg;
L
Linus Torvalds 已提交
333 334 335 336 337
	unsigned long offset;
	unsigned long map_offset;
	unsigned long page_offset;
	struct page *page;

D
Dave Airlie 已提交
338
	if (!entry)
339
		return VM_FAULT_SIGBUS;	/* Error */
D
Dave Airlie 已提交
340
	if (!entry->pagelist)
341
		return VM_FAULT_SIGBUS;	/* Nothing allocated */
L
Linus Torvalds 已提交
342

343
	offset = (unsigned long)vmf->virtual_address - vma->vm_start;
344
	map_offset = map->offset - (unsigned long)dev->sg->virtual;
L
Linus Torvalds 已提交
345 346 347
	page_offset = (offset >> PAGE_SHIFT) + (map_offset >> PAGE_SHIFT);
	page = entry->pagelist[page_offset];
	get_page(page);
348
	vmf->page = page;
L
Linus Torvalds 已提交
349

350
	return 0;
L
Linus Torvalds 已提交
351 352
}

353
static int drm_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
D
Dave Airlie 已提交
354
{
355
	return drm_do_vm_fault(vma, vmf);
L
Linus Torvalds 已提交
356 357
}

358
static int drm_vm_shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
D
Dave Airlie 已提交
359
{
360
	return drm_do_vm_shm_fault(vma, vmf);
L
Linus Torvalds 已提交
361 362
}

363
static int drm_vm_dma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
D
Dave Airlie 已提交
364
{
365
	return drm_do_vm_dma_fault(vma, vmf);
L
Linus Torvalds 已提交
366 367
}

368
static int drm_vm_sg_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
D
Dave Airlie 已提交
369
{
370
	return drm_do_vm_sg_fault(vma, vmf);
L
Linus Torvalds 已提交
371 372 373
}

/** AGP virtual memory operations */
374
static const struct vm_operations_struct drm_vm_ops = {
375
	.fault = drm_vm_fault,
D
Dave Airlie 已提交
376 377
	.open = drm_vm_open,
	.close = drm_vm_close,
L
Linus Torvalds 已提交
378 379 380
};

/** Shared virtual memory operations */
381
static const struct vm_operations_struct drm_vm_shm_ops = {
382
	.fault = drm_vm_shm_fault,
D
Dave Airlie 已提交
383 384
	.open = drm_vm_open,
	.close = drm_vm_shm_close,
L
Linus Torvalds 已提交
385 386 387
};

/** DMA virtual memory operations */
388
static const struct vm_operations_struct drm_vm_dma_ops = {
389
	.fault = drm_vm_dma_fault,
D
Dave Airlie 已提交
390 391
	.open = drm_vm_open,
	.close = drm_vm_close,
L
Linus Torvalds 已提交
392 393 394
};

/** Scatter-gather virtual memory operations */
395
static const struct vm_operations_struct drm_vm_sg_ops = {
396
	.fault = drm_vm_sg_fault,
D
Dave Airlie 已提交
397 398
	.open = drm_vm_open,
	.close = drm_vm_close,
L
Linus Torvalds 已提交
399 400 401 402
};

/**
 * \c open method for shared virtual memory.
D
Dave Airlie 已提交
403
 *
L
Linus Torvalds 已提交
404
 * \param vma virtual memory area.
D
Dave Airlie 已提交
405
 *
L
Linus Torvalds 已提交
406 407 408
 * Create a new drm_vma_entry structure as the \p vma private data entry and
 * add it to drm_device::vmalist.
 */
409 410
void drm_vm_open_locked(struct drm_device *dev,
		struct vm_area_struct *vma)
L
Linus Torvalds 已提交
411
{
412
	struct drm_vma_entry *vma_entry;
L
Linus Torvalds 已提交
413 414 415 416

	DRM_DEBUG("0x%08lx,0x%08lx\n",
		  vma->vm_start, vma->vm_end - vma->vm_start);

417
	vma_entry = kmalloc(sizeof(*vma_entry), GFP_KERNEL);
L
Linus Torvalds 已提交
418
	if (vma_entry) {
D
Dave Airlie 已提交
419 420
		vma_entry->vma = vma;
		vma_entry->pid = current->pid;
421
		list_add(&vma_entry->head, &dev->vmalist);
L
Linus Torvalds 已提交
422 423
	}
}
A
Arnd Bergmann 已提交
424
EXPORT_SYMBOL_GPL(drm_vm_open_locked);
L
Linus Torvalds 已提交
425

426 427
static void drm_vm_open(struct vm_area_struct *vma)
{
428
	struct drm_file *priv = vma->vm_file->private_data;
429
	struct drm_device *dev = priv->minor->dev;
430 431

	mutex_lock(&dev->struct_mutex);
432
	drm_vm_open_locked(dev, vma);
433 434 435
	mutex_unlock(&dev->struct_mutex);
}

436 437
void drm_vm_close_locked(struct drm_device *dev,
		struct vm_area_struct *vma)
L
Linus Torvalds 已提交
438
{
439
	struct drm_vma_entry *pt, *temp;
L
Linus Torvalds 已提交
440 441 442 443

	DRM_DEBUG("0x%08lx,0x%08lx\n",
		  vma->vm_start, vma->vm_end - vma->vm_start);

444
	list_for_each_entry_safe(pt, temp, &dev->vmalist, head) {
L
Linus Torvalds 已提交
445
		if (pt->vma == vma) {
446
			list_del(&pt->head);
447
			kfree(pt);
L
Linus Torvalds 已提交
448 449 450
			break;
		}
	}
C
Chris Wilson 已提交
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
}

/**
 * \c close method for all virtual memory types.
 *
 * \param vma virtual memory area.
 *
 * Search the \p vma private data entry in drm_device::vmalist, unlink it, and
 * free it.
 */
static void drm_vm_close(struct vm_area_struct *vma)
{
	struct drm_file *priv = vma->vm_file->private_data;
	struct drm_device *dev = priv->minor->dev;

	mutex_lock(&dev->struct_mutex);
467
	drm_vm_close_locked(dev, vma);
D
Dave Airlie 已提交
468
	mutex_unlock(&dev->struct_mutex);
L
Linus Torvalds 已提交
469 470 471 472 473
}

/**
 * mmap DMA memory.
 *
474
 * \param file_priv DRM file private.
L
Linus Torvalds 已提交
475 476
 * \param vma virtual memory area.
 * \return zero on success or a negative number on failure.
D
Dave Airlie 已提交
477
 *
L
Linus Torvalds 已提交
478 479 480
 * Sets the virtual memory area operations structure to vm_dma_ops, the file
 * pointer, and calls vm_open().
 */
D
Dave Airlie 已提交
481
static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma)
L
Linus Torvalds 已提交
482
{
483 484
	struct drm_file *priv = filp->private_data;
	struct drm_device *dev;
485
	struct drm_device_dma *dma;
D
Dave Airlie 已提交
486
	unsigned long length = vma->vm_end - vma->vm_start;
L
Linus Torvalds 已提交
487

488
	dev = priv->minor->dev;
D
Dave Airlie 已提交
489
	dma = dev->dma;
490 491
	DRM_DEBUG("start = 0x%lx, end = 0x%lx, page offset = 0x%lx\n",
		  vma->vm_start, vma->vm_end, vma->vm_pgoff);
L
Linus Torvalds 已提交
492

D
Dave Airlie 已提交
493
	/* Length must match exact page count */
L
Linus Torvalds 已提交
494 495 496 497
	if (!dma || (length >> PAGE_SHIFT) != dma->page_count) {
		return -EINVAL;
	}

498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
	if (!capable(CAP_SYS_ADMIN) &&
	    (dma->flags & _DRM_DMA_USE_PCI_RO)) {
		vma->vm_flags &= ~(VM_WRITE | VM_MAYWRITE);
#if defined(__i386__) || defined(__x86_64__)
		pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW;
#else
		/* Ye gads this is ugly.  With more thought
		   we could move this up higher and use
		   `protection_map' instead.  */
		vma->vm_page_prot =
		    __pgprot(pte_val
			     (pte_wrprotect
			      (__pte(pgprot_val(vma->vm_page_prot)))));
#endif
	}

D
Dave Airlie 已提交
514
	vma->vm_ops = &drm_vm_dma_ops;
L
Linus Torvalds 已提交
515

516
	vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
L
Linus Torvalds 已提交
517

518
	drm_vm_open_locked(dev, vma);
L
Linus Torvalds 已提交
519 520 521
	return 0;
}

D
Daniel Vetter 已提交
522
static resource_size_t drm_core_get_reg_ofs(struct drm_device *dev)
L
Linus Torvalds 已提交
523 524
{
#ifdef __alpha__
525
	return dev->hose->dense_mem_base;
L
Linus Torvalds 已提交
526 527 528 529
#else
	return 0;
#endif
}
D
Dave Airlie 已提交
530

L
Linus Torvalds 已提交
531 532 533
/**
 * mmap DMA memory.
 *
534
 * \param file_priv DRM file private.
L
Linus Torvalds 已提交
535 536
 * \param vma virtual memory area.
 * \return zero on success or a negative number on failure.
D
Dave Airlie 已提交
537
 *
L
Linus Torvalds 已提交
538 539 540 541 542 543
 * If the virtual memory area has no offset associated with it then it's a DMA
 * area, so calls mmap_dma(). Otherwise searches the map in drm_device::maplist,
 * checks that the restricted flag is not set, sets the virtual memory operations
 * according to the mapping type and remaps the pages. Finally sets the file
 * pointer and calls vm_open().
 */
J
Jesse Barnes 已提交
544
int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
L
Linus Torvalds 已提交
545
{
546
	struct drm_file *priv = filp->private_data;
547
	struct drm_device *dev = priv->minor->dev;
548
	struct drm_local_map *map = NULL;
549
	resource_size_t offset = 0;
550
	struct drm_hash_item *hash;
L
Linus Torvalds 已提交
551

552 553
	DRM_DEBUG("start = 0x%lx, end = 0x%lx, page offset = 0x%lx\n",
		  vma->vm_start, vma->vm_end, vma->vm_pgoff);
L
Linus Torvalds 已提交
554

D
Dave Airlie 已提交
555 556
	if (!priv->authenticated)
		return -EACCES;
L
Linus Torvalds 已提交
557 558 559 560 561

	/* We check for "dma". On Apple's UniNorth, it's valid to have
	 * the AGP mapped at physical address 0
	 * --BenH.
	 */
562
	if (!vma->vm_pgoff
L
Linus Torvalds 已提交
563
#if __OS_HAS_AGP
D
Dave Airlie 已提交
564 565
	    && (!dev->agp
		|| dev->agp->agp_info.device->vendor != PCI_VENDOR_ID_APPLE)
L
Linus Torvalds 已提交
566 567 568 569
#endif
	    )
		return drm_mmap_dma(filp, vma);

570
	if (drm_ht_find_item(&dev->map_hash, vma->vm_pgoff, &hash)) {
571 572
		DRM_ERROR("Could not find map\n");
		return -EINVAL;
L
Linus Torvalds 已提交
573 574
	}

D
Dave Airlie 已提交
575
	map = drm_hash_entry(hash, struct drm_map_list, hash)->map;
D
Dave Airlie 已提交
576
	if (!map || ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN)))
L
Linus Torvalds 已提交
577 578
		return -EPERM;

D
Dave Airlie 已提交
579
	/* Check for valid size. */
580
	if (map->size < vma->vm_end - vma->vm_start)
D
Dave Airlie 已提交
581
		return -EINVAL;
L
Linus Torvalds 已提交
582 583 584 585 586 587

	if (!capable(CAP_SYS_ADMIN) && (map->flags & _DRM_READ_ONLY)) {
		vma->vm_flags &= ~(VM_WRITE | VM_MAYWRITE);
#if defined(__i386__) || defined(__x86_64__)
		pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW;
#else
D
Dave Airlie 已提交
588 589 590 591 592 593 594
		/* Ye gads this is ugly.  With more thought
		   we could move this up higher and use
		   `protection_map' instead.  */
		vma->vm_page_prot =
		    __pgprot(pte_val
			     (pte_wrprotect
			      (__pte(pgprot_val(vma->vm_page_prot)))));
L
Linus Torvalds 已提交
595 596 597 598
#endif
	}

	switch (map->type) {
J
Jordan Crouse 已提交
599
#if !defined(__arm__)
D
Dave Airlie 已提交
600
	case _DRM_AGP:
D
Daniel Vetter 已提交
601
		if (dev->agp && dev->agp->cant_use_aperture) {
D
Dave Airlie 已提交
602 603 604
			/*
			 * On some platforms we can't talk to bus dma address from the CPU, so for
			 * memory of type DRM_AGP, we'll deal with sorting out the real physical
605
			 * pages and mappings in fault()
D
Dave Airlie 已提交
606
			 */
L
Linus Torvalds 已提交
607
#if defined(__powerpc__)
D
Dave Airlie 已提交
608
			pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE;
L
Linus Torvalds 已提交
609
#endif
D
Dave Airlie 已提交
610 611 612 613
			vma->vm_ops = &drm_vm_ops;
			break;
		}
		/* fall through to _DRM_FRAME_BUFFER... */
J
Jordan Crouse 已提交
614
#endif
L
Linus Torvalds 已提交
615 616
	case _DRM_FRAME_BUFFER:
	case _DRM_REGISTERS:
D
Daniel Vetter 已提交
617
		offset = drm_core_get_reg_ofs(dev);
618
		vma->vm_page_prot = drm_io_prot(map, vma);
619
		if (io_remap_pfn_range(vma, vma->vm_start,
D
Dave Airlie 已提交
620 621 622 623
				       (map->offset + offset) >> PAGE_SHIFT,
				       vma->vm_end - vma->vm_start,
				       vma->vm_page_prot))
			return -EAGAIN;
L
Linus Torvalds 已提交
624
		DRM_DEBUG("   Type = %d; start = 0x%lx, end = 0x%lx,"
625
			  " offset = 0x%llx\n",
L
Linus Torvalds 已提交
626
			  map->type,
627
			  vma->vm_start, vma->vm_end, (unsigned long long)(map->offset + offset));
J
Jordan Crouse 已提交
628

L
Linus Torvalds 已提交
629 630
		vma->vm_ops = &drm_vm_ops;
		break;
D
Dave Airlie 已提交
631
	case _DRM_CONSISTENT:
H
Hugh Dickins 已提交
632
		/* Consistent memory is really like shared memory. But
633
		 * it's allocated in a different way, so avoid fault */
H
Hugh Dickins 已提交
634 635 636 637
		if (remap_pfn_range(vma, vma->vm_start,
		    page_to_pfn(virt_to_page(map->handle)),
		    vma->vm_end - vma->vm_start, vma->vm_page_prot))
			return -EAGAIN;
638
		vma->vm_page_prot = drm_dma_prot(map->type, vma);
H
Hugh Dickins 已提交
639 640
	/* fall through to _DRM_SHM */
	case _DRM_SHM:
L
Linus Torvalds 已提交
641 642 643 644 645 646
		vma->vm_ops = &drm_vm_shm_ops;
		vma->vm_private_data = (void *)map;
		break;
	case _DRM_SCATTER_GATHER:
		vma->vm_ops = &drm_vm_sg_ops;
		vma->vm_private_data = (void *)map;
647
		vma->vm_page_prot = drm_dma_prot(map->type, vma);
D
Dave Airlie 已提交
648
		break;
L
Linus Torvalds 已提交
649 650 651
	default:
		return -EINVAL;	/* This should never happen. */
	}
652
	vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
L
Linus Torvalds 已提交
653

654
	drm_vm_open_locked(dev, vma);
L
Linus Torvalds 已提交
655 656
	return 0;
}
D
Dave Airlie 已提交
657

658 659
int drm_mmap(struct file *filp, struct vm_area_struct *vma)
{
660
	struct drm_file *priv = filp->private_data;
661
	struct drm_device *dev = priv->minor->dev;
662 663
	int ret;

664 665 666
	if (drm_device_is_unplugged(dev))
		return -ENODEV;

667 668 669 670 671 672
	mutex_lock(&dev->struct_mutex);
	ret = drm_mmap_locked(filp, vma);
	mutex_unlock(&dev->struct_mutex);

	return ret;
}
L
Linus Torvalds 已提交
673
EXPORT_SYMBOL(drm_mmap);
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742

void drm_legacy_vma_flush(struct drm_device *dev)
{
	struct drm_vma_entry *vma, *vma_temp;

	/* Clear vma list (only needed for legacy drivers) */
	list_for_each_entry_safe(vma, vma_temp, &dev->vmalist, head) {
		list_del(&vma->head);
		kfree(vma);
	}
}

#if DRM_DEBUG_CODE

int drm_vma_info(struct seq_file *m, void *data)
{
	struct drm_info_node *node = (struct drm_info_node *) m->private;
	struct drm_device *dev = node->minor->dev;
	struct drm_vma_entry *pt;
	struct vm_area_struct *vma;
	unsigned long vma_count = 0;
#if defined(__i386__)
	unsigned int pgprot;
#endif

	mutex_lock(&dev->struct_mutex);
	list_for_each_entry(pt, &dev->vmalist, head)
		vma_count++;

	seq_printf(m, "vma use count: %lu, high_memory = %pK, 0x%pK\n",
		   vma_count, high_memory,
		   (void *)(unsigned long)virt_to_phys(high_memory));

	list_for_each_entry(pt, &dev->vmalist, head) {
		vma = pt->vma;
		if (!vma)
			continue;
		seq_printf(m,
			   "\n%5d 0x%pK-0x%pK %c%c%c%c%c%c 0x%08lx000",
			   pt->pid,
			   (void *)vma->vm_start, (void *)vma->vm_end,
			   vma->vm_flags & VM_READ ? 'r' : '-',
			   vma->vm_flags & VM_WRITE ? 'w' : '-',
			   vma->vm_flags & VM_EXEC ? 'x' : '-',
			   vma->vm_flags & VM_MAYSHARE ? 's' : 'p',
			   vma->vm_flags & VM_LOCKED ? 'l' : '-',
			   vma->vm_flags & VM_IO ? 'i' : '-',
			   vma->vm_pgoff);

#if defined(__i386__)
		pgprot = pgprot_val(vma->vm_page_prot);
		seq_printf(m, " %c%c%c%c%c%c%c%c%c",
			   pgprot & _PAGE_PRESENT ? 'p' : '-',
			   pgprot & _PAGE_RW ? 'w' : 'r',
			   pgprot & _PAGE_USER ? 'u' : 's',
			   pgprot & _PAGE_PWT ? 't' : 'b',
			   pgprot & _PAGE_PCD ? 'u' : 'c',
			   pgprot & _PAGE_ACCESSED ? 'a' : '-',
			   pgprot & _PAGE_DIRTY ? 'd' : '-',
			   pgprot & _PAGE_PSE ? 'm' : 'k',
			   pgprot & _PAGE_GLOBAL ? 'g' : 'l');
#endif
		seq_printf(m, "\n");
	}
	mutex_unlock(&dev->struct_mutex);
	return 0;
}

#endif