diff --git a/libdrm/xf86drm.h b/libdrm/xf86drm.h index 2ad70807..d58baa76 100644 --- a/libdrm/xf86drm.h +++ b/libdrm/xf86drm.h @@ -149,7 +149,8 @@ typedef enum { DRM_PAGE_ALIGN = 0x01, DRM_AGP_BUFFER = 0x02, DRM_SG_BUFFER = 0x04, - DRM_FB_BUFFER = 0x08 + DRM_FB_BUFFER = 0x08, + DRM_PCI_BUFFER_RO = 0x10 } drmBufDescFlags; typedef enum { diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 5c9644e0..2bbec70c 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -449,7 +449,8 @@ typedef struct drm_device_dma { enum { _DRM_DMA_USE_AGP = 0x01, _DRM_DMA_USE_SG = 0x02, - _DRM_DMA_USE_FB = 0x04 + _DRM_DMA_USE_FB = 0x04, + _DRM_DMA_USE_PCI_RO = 0x08 } flags; } drm_device_dma_t; diff --git a/linux-core/drm_bufs.c b/linux-core/drm_bufs.c index abd7c829..a2a3dbf7 100644 --- a/linux-core/drm_bufs.c +++ b/linux-core/drm_bufs.c @@ -942,6 +942,9 @@ int drm_addbufs_pci(drm_device_t * dev, drm_buf_desc_t * request) request->count = entry->buf_count; request->size = size; + if (request->flags & _DRM_PCI_BUFFER_RO) + dma->flags = _DRM_DMA_USE_PCI_RO; + atomic_dec(&dev->buf_alloc); return 0; @@ -1528,9 +1531,10 @@ int drm_freebufs(struct inode *inode, struct file *filp, * \param arg pointer to a drm_buf_map structure. * \return zero on success or a negative number on failure. * - * Maps the AGP or SG buffer region with do_mmap(), and copies information - * about each buffer into user space. The PCI buffers are already mapped on the - * addbufs_pci() call. + * Maps the AGP, SG or PCI buffer region with do_mmap(), and copies information + * about each buffer into user space. For PCI buffers, it calls do_mmap() with + * offset equal to 0, which drm_mmap() interpretes as PCI buffers and calls + * drm_mmap_dma(). */ int drm_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) diff --git a/linux-core/drm_vm.c b/linux-core/drm_vm.c index 9672269a..adff7d1a 100644 --- a/linux-core/drm_vm.c +++ b/linux-core/drm_vm.c @@ -506,6 +506,22 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma) } unlock_kernel(); + 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 + } + vma->vm_ops = &drm_vm_dma_ops; #if LINUX_VERSION_CODE <= 0x02040e /* KERNEL_VERSION(2,4,14) */ diff --git a/shared-core/drm.h b/shared-core/drm.h index 7f90a96d..8c0c5d22 100644 --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -416,7 +416,8 @@ typedef struct drm_buf_desc { _DRM_PAGE_ALIGN = 0x01, /**< Align on page boundaries for DMA */ _DRM_AGP_BUFFER = 0x02, /**< Buffer is in AGP space */ _DRM_SG_BUFFER = 0x04, /**< Scatter/gather memory buffer */ - _DRM_FB_BUFFER = 0x08 /**< Buffer is in frame buffer */ + _DRM_FB_BUFFER = 0x08, /**< Buffer is in frame buffer */ + _DRM_PCI_BUFFER_RO = 0x10 /**< Map PCI DMA buffer read-only */ } flags; unsigned long agp_start; /**< * Start address of where the AGP buffers are