drm_vm.h
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:16k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  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. #define __NO_VERSION__
  32. #include "drmP.h"
  33. struct vm_operations_struct   DRM(vm_ops) = {
  34. nopage:  DRM(vm_nopage),
  35. open:  DRM(vm_open),
  36. close:  DRM(vm_close),
  37. };
  38. struct vm_operations_struct   DRM(vm_shm_ops) = {
  39. nopage:  DRM(vm_shm_nopage),
  40. open:  DRM(vm_open),
  41. close:  DRM(vm_shm_close),
  42. };
  43. struct vm_operations_struct   DRM(vm_dma_ops) = {
  44. nopage:  DRM(vm_dma_nopage),
  45. open:  DRM(vm_open),
  46. close:  DRM(vm_close),
  47. };
  48. struct vm_operations_struct   DRM(vm_sg_ops) = {
  49. nopage:  DRM(vm_sg_nopage),
  50. open:    DRM(vm_open),
  51. close:   DRM(vm_close),
  52. };
  53. #if LINUX_VERSION_CODE < 0x020317
  54. unsigned long DRM(vm_nopage)(struct vm_area_struct *vma,
  55.      unsigned long address,
  56.      int unused)
  57. #else
  58. /* Return type changed in 2.3.23 */
  59. struct page *DRM(vm_nopage)(struct vm_area_struct *vma,
  60.     unsigned long address,
  61.     int unused)
  62. #endif
  63. {
  64. #if __REALLY_HAVE_AGP
  65. drm_file_t *priv  = vma->vm_file->private_data;
  66. drm_device_t *dev = priv->dev;
  67. drm_map_t *map    = NULL;
  68. drm_map_list_t  *r_list;
  69. struct list_head *list;
  70. /*
  71.          * Find the right map
  72.          */
  73. if(!dev->agp->cant_use_aperture) goto vm_nopage_error;
  74. list_for_each(list, &dev->maplist->head) {
  75. r_list = (drm_map_list_t *)list;
  76. map = r_list->map;
  77. if (!map) continue;
  78. if (map->offset == VM_OFFSET(vma)) break;
  79. }
  80. if (map && map->type == _DRM_AGP) {
  81. unsigned long offset = address - vma->vm_start;
  82. unsigned long baddr = VM_OFFSET(vma) + offset;
  83. struct drm_agp_mem *agpmem;
  84. struct page *page;
  85. #if __alpha__
  86. /*
  87.                  * Adjust to a bus-relative address
  88.                  */
  89. baddr -= dev->hose->mem_space->start;
  90. #endif
  91. /*
  92.                  * It's AGP memory - find the real physical page to map
  93.                  */
  94. for(agpmem = dev->agp->memory; agpmem; agpmem = agpmem->next) {
  95. if (agpmem->bound <= baddr &&
  96.     agpmem->bound + agpmem->pages * PAGE_SIZE > baddr) 
  97. break;
  98. }
  99. if (!agpmem) goto vm_nopage_error;
  100. /*
  101.                  * Get the page, inc the use count, and return it
  102.                  */
  103. offset = (baddr - agpmem->bound) >> PAGE_SHIFT;
  104. agpmem->memory->memory[offset] &= dev->agp->page_mask;
  105. page = virt_to_page(__va(agpmem->memory->memory[offset]));
  106. get_page(page);
  107. DRM_DEBUG("baddr = 0x%lx page = 0x%p, offset = 0x%lxn",
  108.   baddr, __va(agpmem->memory->memory[offset]), offset);
  109. #if LINUX_VERSION_CODE < 0x020317
  110. return page_address(page);
  111. #else
  112. return page;
  113. #endif
  114.         }
  115. vm_nopage_error:
  116. #endif /* __REALLY_HAVE_AGP */
  117. return NOPAGE_SIGBUS; /* Disallow mremap */
  118. }
  119. #if LINUX_VERSION_CODE < 0x020317
  120. unsigned long DRM(vm_shm_nopage)(struct vm_area_struct *vma,
  121.  unsigned long address,
  122.  int unused)
  123. #else
  124. /* Return type changed in 2.3.23 */
  125. struct page *DRM(vm_shm_nopage)(struct vm_area_struct *vma,
  126. unsigned long address,
  127. int unused)
  128. #endif
  129. {
  130. #if LINUX_VERSION_CODE >= 0x020300
  131. drm_map_t  *map  = (drm_map_t *)vma->vm_private_data;
  132. #else
  133. drm_map_t  *map  = (drm_map_t *)vma->vm_pte;
  134. #endif
  135. unsigned long  offset;
  136. unsigned long  i;
  137. pgd_t  *pgd;
  138. pmd_t  *pmd;
  139. pte_t  *pte;
  140. struct page  *page;
  141. if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
  142. if (!map)        return NOPAGE_OOM;  /* Nothing allocated */
  143. offset  = address - vma->vm_start;
  144. i = (unsigned long)map->handle + offset;
  145. /* We have to walk page tables here because we need large SAREA's, and
  146.  * they need to be virtually contiguous in kernel space.
  147.  */
  148. pgd = pgd_offset_k( i );
  149. if( !pgd_present( *pgd ) ) return NOPAGE_OOM;
  150. pmd = pmd_offset( pgd, i );
  151. if( !pmd_present( *pmd ) ) return NOPAGE_OOM;
  152. pte = pte_offset( pmd, i );
  153. if( !pte_present( *pte ) ) return NOPAGE_OOM;
  154. page = pte_page(*pte);
  155. get_page(page);
  156. DRM_DEBUG("shm_nopage 0x%lxn", address);
  157. #if LINUX_VERSION_CODE < 0x020317
  158. return page_address(page);
  159. #else
  160. return page;
  161. #endif
  162. }
  163. /* Special close routine which deletes map information if we are the last
  164.  * person to close a mapping and its not in the global maplist.
  165.  */
  166. void DRM(vm_shm_close)(struct vm_area_struct *vma)
  167. {
  168. drm_file_t *priv = vma->vm_file->private_data;
  169. drm_device_t *dev = priv->dev;
  170. drm_vma_entry_t *pt, *prev, *next;
  171. drm_map_t *map;
  172. drm_map_list_t *r_list;
  173. struct list_head *list;
  174. int found_maps = 0;
  175. DRM_DEBUG("0x%08lx,0x%08lxn",
  176.   vma->vm_start, vma->vm_end - vma->vm_start);
  177. #if LINUX_VERSION_CODE < 0x020333
  178. MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
  179. #endif
  180. atomic_dec(&dev->vma_count);
  181. #if LINUX_VERSION_CODE >= 0x020300
  182. map = vma->vm_private_data;
  183. #else
  184. map = vma->vm_pte;
  185. #endif
  186. down(&dev->struct_sem);
  187. for (pt = dev->vmalist, prev = NULL; pt; pt = next) {
  188. next = pt->next;
  189. #if LINUX_VERSION_CODE >= 0x020300
  190. if (pt->vma->vm_private_data == map) found_maps++;
  191. #else
  192. if (pt->vma->vm_pte == map) found_maps++;
  193. #endif
  194. if (pt->vma == vma) {
  195. if (prev) {
  196. prev->next = pt->next;
  197. } else {
  198. dev->vmalist = pt->next;
  199. }
  200. DRM(free)(pt, sizeof(*pt), DRM_MEM_VMAS);
  201. } else {
  202. prev = pt;
  203. }
  204. }
  205. /* We were the only map that was found */
  206. if(found_maps == 1 &&
  207.    map->flags & _DRM_REMOVABLE) {
  208. /* Check to see if we are in the maplist, if we are not, then
  209.  * we delete this mappings information.
  210.  */
  211. found_maps = 0;
  212. list = &dev->maplist->head;
  213. list_for_each(list, &dev->maplist->head) {
  214. r_list = (drm_map_list_t *) list;
  215. if (r_list->map == map) found_maps++;
  216. }
  217. if(!found_maps) {
  218. switch (map->type) {
  219. case _DRM_REGISTERS:
  220. case _DRM_FRAME_BUFFER:
  221. #if __REALLY_HAVE_MTRR
  222. if (map->mtrr >= 0) {
  223. int retcode;
  224. retcode = mtrr_del(map->mtrr,
  225.    map->offset,
  226.    map->size);
  227. DRM_DEBUG("mtrr_del = %dn", retcode);
  228. }
  229. #endif
  230. DRM(ioremapfree)(map->handle, map->size);
  231. break;
  232. case _DRM_SHM:
  233. vfree(map->handle);
  234. break;
  235. case _DRM_AGP:
  236. case _DRM_SCATTER_GATHER:
  237. break;
  238. }
  239. DRM(free)(map, sizeof(*map), DRM_MEM_MAPS);
  240. }
  241. }
  242. up(&dev->struct_sem);
  243. }
  244. #if LINUX_VERSION_CODE < 0x020317
  245. unsigned long DRM(vm_dma_nopage)(struct vm_area_struct *vma,
  246.  unsigned long address,
  247.  int unused)
  248. #else
  249. /* Return type changed in 2.3.23 */
  250. struct page *DRM(vm_dma_nopage)(struct vm_area_struct *vma,
  251. unsigned long address,
  252. int unused)
  253. #endif
  254. {
  255. drm_file_t  *priv  = vma->vm_file->private_data;
  256. drm_device_t  *dev  = priv->dev;
  257. drm_device_dma_t *dma  = dev->dma;
  258. unsigned long  offset;
  259. unsigned long  page_nr;
  260. struct page  *page;
  261. if (!dma)    return NOPAGE_SIGBUS; /* Error */
  262. if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
  263. if (!dma->pagelist)    return NOPAGE_OOM ; /* Nothing allocated */
  264. offset  = address - vma->vm_start; /* vm_[pg]off[set] should be 0 */
  265. page_nr  = offset >> PAGE_SHIFT;
  266. page = virt_to_page((dma->pagelist[page_nr] + 
  267.      (offset & (~PAGE_MASK))));
  268. get_page(page);
  269. DRM_DEBUG("dma_nopage 0x%lx (page %lu)n", address, page_nr); 
  270. #if LINUX_VERSION_CODE < 0x020317
  271. return page_address(page);
  272. #else
  273. return page;
  274. #endif
  275. }
  276. #if LINUX_VERSION_CODE < 0x020317
  277. unsigned long DRM(vm_sg_nopage)(struct vm_area_struct *vma,
  278. unsigned long address,
  279. int unused)
  280. #else
  281. /* Return type changed in 2.3.23 */
  282. struct page *DRM(vm_sg_nopage)(struct vm_area_struct *vma,
  283.        unsigned long address,
  284.        int unused)
  285. #endif
  286. {
  287. #if LINUX_VERSION_CODE >= 0x020300
  288. drm_map_t        *map    = (drm_map_t *)vma->vm_private_data;
  289. #else
  290. drm_map_t        *map    = (drm_map_t *)vma->vm_pte;
  291. #endif
  292. drm_file_t *priv = vma->vm_file->private_data;
  293. drm_device_t *dev = priv->dev;
  294. drm_sg_mem_t *entry = dev->sg;
  295. unsigned long offset;
  296. unsigned long map_offset;
  297. unsigned long page_offset;
  298. struct page *page;
  299. if (!entry)                return NOPAGE_SIGBUS; /* Error */
  300. if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
  301. if (!entry->pagelist)      return NOPAGE_OOM ;  /* Nothing allocated */
  302. offset = address - vma->vm_start;
  303. map_offset = map->offset - dev->sg->handle;
  304. page_offset = (offset >> PAGE_SHIFT) + (map_offset >> PAGE_SHIFT);
  305. page = entry->pagelist[page_offset];
  306. get_page(page);
  307. #if LINUX_VERSION_CODE < 0x020317
  308. return page_address(page);
  309. #else
  310. return page;
  311. #endif
  312. }
  313. void DRM(vm_open)(struct vm_area_struct *vma)
  314. {
  315. drm_file_t *priv = vma->vm_file->private_data;
  316. drm_device_t *dev = priv->dev;
  317. drm_vma_entry_t *vma_entry;
  318. DRM_DEBUG("0x%08lx,0x%08lxn",
  319.   vma->vm_start, vma->vm_end - vma->vm_start);
  320. atomic_inc(&dev->vma_count);
  321. #if LINUX_VERSION_CODE < 0x020333
  322. /* The map can exist after the fd is closed. */
  323. MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
  324. #endif
  325. vma_entry = DRM(alloc)(sizeof(*vma_entry), DRM_MEM_VMAS);
  326. if (vma_entry) {
  327. down(&dev->struct_sem);
  328. vma_entry->vma = vma;
  329. vma_entry->next = dev->vmalist;
  330. vma_entry->pid = current->pid;
  331. dev->vmalist = vma_entry;
  332. up(&dev->struct_sem);
  333. }
  334. }
  335. void DRM(vm_close)(struct vm_area_struct *vma)
  336. {
  337. drm_file_t *priv = vma->vm_file->private_data;
  338. drm_device_t *dev = priv->dev;
  339. drm_vma_entry_t *pt, *prev;
  340. DRM_DEBUG("0x%08lx,0x%08lxn",
  341.   vma->vm_start, vma->vm_end - vma->vm_start);
  342. #if LINUX_VERSION_CODE < 0x020333
  343. MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
  344. #endif
  345. atomic_dec(&dev->vma_count);
  346. down(&dev->struct_sem);
  347. for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) {
  348. if (pt->vma == vma) {
  349. if (prev) {
  350. prev->next = pt->next;
  351. } else {
  352. dev->vmalist = pt->next;
  353. }
  354. DRM(free)(pt, sizeof(*pt), DRM_MEM_VMAS);
  355. break;
  356. }
  357. }
  358. up(&dev->struct_sem);
  359. }
  360. int DRM(mmap_dma)(struct file *filp, struct vm_area_struct *vma)
  361. {
  362. drm_file_t  *priv  = filp->private_data;
  363. drm_device_t  *dev;
  364. drm_device_dma_t *dma;
  365. unsigned long  length  = vma->vm_end - vma->vm_start;
  366. lock_kernel();
  367. dev  = priv->dev;
  368. dma  = dev->dma;
  369. DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lxn",
  370.   vma->vm_start, vma->vm_end, VM_OFFSET(vma));
  371. /* Length must match exact page count */
  372. if (!dma || (length >> PAGE_SHIFT) != dma->page_count) {
  373. unlock_kernel();
  374. return -EINVAL;
  375. }
  376. unlock_kernel();
  377. vma->vm_ops   = &DRM(vm_dma_ops);
  378. vma->vm_flags |= VM_RESERVED; /* Don't swap */
  379. #if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */
  380. /* In Linux 2.2.3 and above, this is
  381.    handled in do_mmap() in mm/mmap.c. */
  382. ++filp->f_count;
  383. #endif
  384. vma->vm_file  =  filp; /* Needed for drm_vm_open() */
  385. DRM(vm_open)(vma);
  386. return 0;
  387. }
  388. #ifndef DRIVER_GET_MAP_OFS
  389. #define DRIVER_GET_MAP_OFS() (map->offset)
  390. #endif
  391. #ifndef DRIVER_GET_REG_OFS
  392. #ifdef __alpha__
  393. #define DRIVER_GET_REG_OFS() (dev->hose->dense_mem_base -
  394.  dev->hose->mem_space->start)
  395. #else
  396. #define DRIVER_GET_REG_OFS() 0
  397. #endif
  398. #endif
  399. int DRM(mmap)(struct file *filp, struct vm_area_struct *vma)
  400. {
  401. drm_file_t *priv = filp->private_data;
  402. drm_device_t *dev = priv->dev;
  403. drm_map_t *map = NULL;
  404. drm_map_list_t  *r_list;
  405. unsigned long   offset  = 0;
  406. struct list_head *list;
  407. DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lxn",
  408.   vma->vm_start, vma->vm_end, VM_OFFSET(vma));
  409. if ( !priv->authenticated ) return -EACCES;
  410. if (!VM_OFFSET(vma)) return DRM(mmap_dma)(filp, vma);
  411. /* A sequential search of a linked list is
  412.    fine here because: 1) there will only be
  413.    about 5-10 entries in the list and, 2) a
  414.    DRI client only has to do this mapping
  415.    once, so it doesn't have to be optimized
  416.    for performance, even if the list was a
  417.    bit longer. */
  418. list_for_each(list, &dev->maplist->head) {
  419. unsigned long off;
  420. r_list = (drm_map_list_t *)list;
  421. map = r_list->map;
  422. if (!map) continue;
  423. off = DRIVER_GET_MAP_OFS();
  424. if (off == VM_OFFSET(vma)) break;
  425. }
  426. if (!map || ((map->flags&_DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN)))
  427. return -EPERM;
  428. /* Check for valid size. */
  429. if (map->size != vma->vm_end - vma->vm_start) return -EINVAL;
  430. if (!capable(CAP_SYS_ADMIN) && (map->flags & _DRM_READ_ONLY)) {
  431. vma->vm_flags &= VM_MAYWRITE;
  432. #if defined(__i386__)
  433. pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW;
  434. #else
  435. /* Ye gads this is ugly.  With more thought
  436.                                    we could move this up higher and use
  437.                                    `protection_map' instead.  */
  438. vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect(
  439. __pte(pgprot_val(vma->vm_page_prot)))));
  440. #endif
  441. }
  442. switch (map->type) {
  443.         case _DRM_AGP:
  444. #if defined(__alpha__)
  445.                 /*
  446.                  * On Alpha we can't talk to bus dma address from the
  447.                  * CPU, so for memory of type DRM_AGP, we'll deal with
  448.                  * sorting out the real physical pages and mappings
  449.                  * in nopage()
  450.                  */
  451.                 vma->vm_ops = &DRM(vm_ops);
  452.                 break;
  453. #endif
  454.                 /* fall through to _DRM_FRAME_BUFFER... */        
  455. case _DRM_FRAME_BUFFER:
  456. case _DRM_REGISTERS:
  457. if (VM_OFFSET(vma) >= __pa(high_memory)) {
  458. #if defined(__i386__)
  459. if (boot_cpu_data.x86 > 3 && map->type != _DRM_AGP) {
  460. pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
  461. pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT;
  462. }
  463. #elif defined(__ia64__)
  464. if (map->type != _DRM_AGP)
  465. vma->vm_page_prot =
  466. pgprot_writecombine(vma->vm_page_prot);
  467. #elif defined(__powerpc__)
  468. pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE | _PAGE_GUARDED;
  469. #endif
  470. vma->vm_flags |= VM_IO; /* not in core dump */
  471. }
  472. offset = DRIVER_GET_REG_OFS();
  473. #ifdef __sparc__
  474. if (io_remap_page_range(vma->vm_start,
  475. VM_OFFSET(vma) + offset,
  476. vma->vm_end - vma->vm_start,
  477. vma->vm_page_prot, 0))
  478. #else
  479. if (remap_page_range(vma->vm_start,
  480.      VM_OFFSET(vma) + offset,
  481.      vma->vm_end - vma->vm_start,
  482.      vma->vm_page_prot))
  483. #endif
  484. return -EAGAIN;
  485. DRM_DEBUG("   Type = %d; start = 0x%lx, end = 0x%lx,"
  486.   " offset = 0x%lxn",
  487.   map->type,
  488.   vma->vm_start, vma->vm_end, VM_OFFSET(vma) + offset);
  489. vma->vm_ops = &DRM(vm_ops);
  490. break;
  491. case _DRM_SHM:
  492. vma->vm_ops = &DRM(vm_shm_ops);
  493. #if LINUX_VERSION_CODE >= 0x020300
  494. vma->vm_private_data = (void *)map;
  495. #else
  496. vma->vm_pte = (unsigned long)map;
  497. #endif
  498. /* Don't let this area swap.  Change when
  499.    DRM_KERNEL advisory is supported. */
  500. vma->vm_flags |= VM_RESERVED;
  501. break;
  502. case _DRM_SCATTER_GATHER:
  503. vma->vm_ops = &DRM(vm_sg_ops);
  504. #if LINUX_VERSION_CODE >= 0x020300
  505. vma->vm_private_data = (void *)map;
  506. #else
  507. vma->vm_pte = (unsigned long)map;
  508. #endif
  509.                 vma->vm_flags |= VM_RESERVED;
  510.                 break;
  511. default:
  512. return -EINVAL; /* This should never happen. */
  513. }
  514. vma->vm_flags |= VM_RESERVED; /* Don't swap */
  515. #if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */
  516. /* In Linux 2.2.3 and above, this is
  517.    handled in do_mmap() in mm/mmap.c. */
  518. ++filp->f_count;
  519. #endif
  520. vma->vm_file  =  filp; /* Needed for drm_vm_open() */
  521. DRM(vm_open)(vma);
  522. return 0;
  523. }