drm_vm.c 19.6 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 "drm_legacy.h"
L
Linus Torvalds 已提交
44

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

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

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

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

L
Linus Torvalds 已提交
90
/**
91
 * \c fault method for AGP virtual memory.
L
Linus Torvalds 已提交
92 93 94 95
 *
 * \param vma virtual memory area.
 * \param address access address.
 * \return pointer to the page structure.
D
Dave Airlie 已提交
96
 *
L
Linus Torvalds 已提交
97 98 99 100
 * 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
101
static int drm_do_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
L
Linus Torvalds 已提交
102
{
103
	struct drm_file *priv = vma->vm_file->private_data;
104
	struct drm_device *dev = priv->minor->dev;
105
	struct drm_local_map *map = NULL;
D
Dave Airlie 已提交
106
	struct drm_map_list *r_list;
107
	struct drm_hash_item *hash;
L
Linus Torvalds 已提交
108 109

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

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

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

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

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

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

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

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

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

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

/**
 * \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 已提交
186
 *
187
 * Get the mapping, find the real physical page to map, get the page, and
L
Linus Torvalds 已提交
188 189
 * return it.
 */
190
static int drm_do_vm_shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
L
Linus Torvalds 已提交
191
{
192
	struct drm_local_map *map = vma->vm_private_data;
D
Dave Airlie 已提交
193 194 195
	unsigned long offset;
	unsigned long i;
	struct page *page;
L
Linus Torvalds 已提交
196

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

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

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

/**
 * \c close method for shared virtual memory.
D
Dave Airlie 已提交
214
 *
L
Linus Torvalds 已提交
215
 * \param vma virtual memory area.
D
Dave Airlie 已提交
216
 *
L
Linus Torvalds 已提交
217 218 219
 * Deletes map information if we are the last
 * person to close a mapping and it's not in the global maplist.
 */
D
Dave Airlie 已提交
220
static void drm_vm_shm_close(struct vm_area_struct *vma)
L
Linus Torvalds 已提交
221
{
222
	struct drm_file *priv = vma->vm_file->private_data;
223
	struct drm_device *dev = priv->minor->dev;
224
	struct drm_vma_entry *pt, *temp;
225
	struct drm_local_map *map;
D
Dave Airlie 已提交
226
	struct drm_map_list *r_list;
L
Linus Torvalds 已提交
227 228 229 230 231 232 233
	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 已提交
234
	mutex_lock(&dev->struct_mutex);
235
	list_for_each_entry_safe(pt, temp, &dev->vmalist, head) {
D
Dave Airlie 已提交
236 237
		if (pt->vma->vm_private_data == map)
			found_maps++;
L
Linus Torvalds 已提交
238
		if (pt->vma == vma) {
239
			list_del(&pt->head);
240
			kfree(pt);
L
Linus Torvalds 已提交
241 242
		}
	}
243

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

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

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

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

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

306 307
	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 */
308
	page = virt_to_page((void *)dma->pagelist[page_nr]);
L
Linus Torvalds 已提交
309 310

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/**
 * \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);
466
	drm_vm_close_locked(dev, vma);
D
Dave Airlie 已提交
467
	mutex_unlock(&dev->struct_mutex);
L
Linus Torvalds 已提交
468 469 470 471 472
}

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

487
	dev = priv->minor->dev;
D
Dave Airlie 已提交
488
	dma = dev->dma;
489 490
	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 已提交
491

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

497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
	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 已提交
513
	vma->vm_ops = &drm_vm_dma_ops;
L
Linus Torvalds 已提交
514

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

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

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

L
Linus Torvalds 已提交
530 531 532
/**
 * mmap DMA memory.
 *
533
 * \param file_priv DRM file private.
L
Linus Torvalds 已提交
534 535
 * \param vma virtual memory area.
 * \return zero on success or a negative number on failure.
D
Dave Airlie 已提交
536
 *
L
Linus Torvalds 已提交
537 538 539 540 541 542
 * 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 已提交
543
int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
L
Linus Torvalds 已提交
544
{
545
	struct drm_file *priv = filp->private_data;
546
	struct drm_device *dev = priv->minor->dev;
547
	struct drm_local_map *map = NULL;
548
	resource_size_t offset = 0;
549
	struct drm_hash_item *hash;
L
Linus Torvalds 已提交
550

551 552
	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 已提交
553

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

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

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

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

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

	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 已提交
587 588 589 590 591 592 593
		/* 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 已提交
594 595 596 597
#endif
	}

	switch (map->type) {
J
Jordan Crouse 已提交
598
#if !defined(__arm__)
D
Dave Airlie 已提交
599
	case _DRM_AGP:
D
Daniel Vetter 已提交
600
		if (dev->agp && dev->agp->cant_use_aperture) {
D
Dave Airlie 已提交
601 602 603
			/*
			 * 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
604
			 * pages and mappings in fault()
D
Dave Airlie 已提交
605
			 */
L
Linus Torvalds 已提交
606
#if defined(__powerpc__)
D
Dave Airlie 已提交
607
			pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE;
L
Linus Torvalds 已提交
608
#endif
D
Dave Airlie 已提交
609 610 611 612
			vma->vm_ops = &drm_vm_ops;
			break;
		}
		/* fall through to _DRM_FRAME_BUFFER... */
J
Jordan Crouse 已提交
613
#endif
L
Linus Torvalds 已提交
614 615
	case _DRM_FRAME_BUFFER:
	case _DRM_REGISTERS:
D
Daniel Vetter 已提交
616
		offset = drm_core_get_reg_ofs(dev);
617
		vma->vm_page_prot = drm_io_prot(map, vma);
618
		if (io_remap_pfn_range(vma, vma->vm_start,
D
Dave Airlie 已提交
619 620 621 622
				       (map->offset + offset) >> PAGE_SHIFT,
				       vma->vm_end - vma->vm_start,
				       vma->vm_page_prot))
			return -EAGAIN;
L
Linus Torvalds 已提交
623
		DRM_DEBUG("   Type = %d; start = 0x%lx, end = 0x%lx,"
624
			  " offset = 0x%llx\n",
L
Linus Torvalds 已提交
625
			  map->type,
626
			  vma->vm_start, vma->vm_end, (unsigned long long)(map->offset + offset));
J
Jordan Crouse 已提交
627

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

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

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

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

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

	return ret;
}
L
Linus Torvalds 已提交
672
EXPORT_SYMBOL(drm_mmap);
673 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

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