This patch removes the need for root to run DRM. It is attached too in case gmail destroys the formatting.
Major highlights: 1) removal of the general root check on the IOCTLs, added root check in AddMap 1) permanent SAREA map 2) user space can AddMap AGP maps, AGP maps are restricted to creation in AGP space. 3) Deprecation of some radeon variables now calculated by the driver I haven't converted PCI GART support. You still need to be root for it to work. Please look it over and tell me if I have created any security holes. -- Jon Smirl [EMAIL PROTECTED] diff --git a/linux-core/drmP.h b/linux-core/drmP.h --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -280,7 +280,7 @@ typedef int drm_ioctl_compat_t(struct fi typedef struct drm_ioctl_desc { drm_ioctl_t *func; int auth_needed; - int root_only; + int master; } drm_ioctl_desc_t; typedef struct drm_devstate { @@ -375,6 +375,7 @@ typedef struct drm_buf_entry { /** File private data */ typedef struct drm_file { int authenticated; + int master; int minor; pid_t pid; uid_t uid; diff --git a/linux-core/drm_bufs.c b/linux-core/drm_bufs.c --- a/linux-core/drm_bufs.c +++ b/linux-core/drm_bufs.c @@ -56,7 +56,8 @@ static drm_local_map_t *drm_find_matchin list_for_each(list, &dev->maplist->head) { drm_map_list_t *entry = list_entry(list, drm_map_list_t, head); if (entry->map && map->type == entry->map->type && - entry->map->offset == map->offset) { + ((entry->map->offset == map->offset) || + (map->type == _DRM_SHM))) { return entry->map; } } @@ -163,6 +164,19 @@ int drm_addmap(drm_device_t * dev, unsig map->handle = drm_ioremap(map->offset, map->size, dev); break; case _DRM_SHM: + found_map = drm_find_matching_map(dev, map); + if (found_map != NULL) { + if (found_map->size != map->size) { + DRM_DEBUG("Matching maps of type %d with " + "mismatched sizes, (%ld vs %ld)\n", + map->type, map->size, found_map->size); + found_map->size = map->size; + } + + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + *map_ptr = found_map; + return 0; + } map->handle = vmalloc_32(map->size); DRM_DEBUG("%lu %d %p\n", map->size, drm_order(map->size), map->handle); @@ -181,15 +195,34 @@ int drm_addmap(drm_device_t * dev, unsig dev->sigdata.lock = dev->lock.hw_lock = map->handle; /* Pointer to lock */ } break; - case _DRM_AGP: - if (drm_core_has_AGP(dev)) { + case _DRM_AGP: { + drm_agp_mem_t *entry; + int valid = 0; + + if (!drm_core_has_AGP(dev)) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } #ifdef __alpha__ - map->offset += dev->hose->mem_space->start; + map->offset += dev->hose->mem_space->start; #endif - map->offset += dev->agp->base; - map->mtrr = dev->agp->agp_mtrr; /* for getmap */ + map->offset += dev->agp->base; + map->mtrr = dev->agp->agp_mtrr; /* for getmap */ + + for (entry = dev->agp->memory; entry; entry = entry->next) { + if ((map->offset >= entry->bound) && + (map->offset + map->offset <= entry->bound + entry->pages * PAGE_SIZE)) { + valid = 1; + break; + } + } + if (!valid) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EPERM; } + DRM_DEBUG("AGP offset = 0x%08lx, size = 0x%08lx\n", map->offset, map->size); break; + } case _DRM_SCATTER_GATHER: if (!dev->sg) { drm_free(map, sizeof(*map), DRM_MEM_MAPS); @@ -258,6 +291,9 @@ int drm_addmap_ioctl(struct inode *inode return -EFAULT; } + if (!(capable(CAP_SYS_ADMIN) || map.type == _DRM_AGP)) + return -EPERM; + err = drm_addmap( dev, map.offset, map.size, map.type, map.flags, & map_ptr ); diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -545,13 +545,14 @@ int drm_ioctl(struct inode *inode, struc if (!func) { DRM_DEBUG("no function\n"); retcode = -EINVAL; - } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN)) || - (ioctl->auth_needed && !priv->authenticated)) { + } else if (((ioctl->master && !priv->master) || + (ioctl->auth_needed && !priv->authenticated)) && + (!capable(CAP_SYS_ADMIN))) { retcode = -EACCES; } else { retcode = func(inode, filp, cmd, arg); } - err_i1: +err_i1: atomic_dec(&dev->ioctl_count); if (retcode) DRM_DEBUG("ret = %x\n", retcode); diff --git a/linux-core/drm_fops.c b/linux-core/drm_fops.c --- a/linux-core/drm_fops.c +++ b/linux-core/drm_fops.c @@ -34,18 +34,26 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#include "drmP.h" #include <linux/poll.h> +#include "drmP.h" +#include "drm_sarea.h" + static int drm_open_helper(struct inode *inode, struct file *filp, drm_device_t * dev); static int drm_setup(drm_device_t * dev) { + drm_local_map_t *map; int i; if (dev->driver->presetup) dev->driver->presetup(dev); + /* prebuild the SAREA */ + i = drm_addmap(dev, 0, SAREA_MAX, _DRM_SHM, _DRM_CONTAINS_LOCK, &map); + if (i != 0) + return i; + atomic_set(&dev->ioctl_count, 0); atomic_set(&dev->vma_count, 0); dev->buf_use = 0; @@ -244,6 +252,7 @@ static int drm_open_helper(struct inode priv->minor = minor; priv->head = drm_heads[minor]; priv->ioctl_count = 0; + /* for compatibility root is always authenticated */ priv->authenticated = capable(CAP_SYS_ADMIN); priv->lock_count = 0; @@ -259,6 +268,9 @@ static int drm_open_helper(struct inode priv->prev = NULL; dev->file_first = priv; dev->file_last = priv; + /* first opener automatically becomes master and authenticated */ + priv->master = 1; + priv->authenticated = 1; } else { priv->next = NULL; priv->prev = dev->file_last; diff --git a/linux-core/drm_stub.c b/linux-core/drm_stub.c diff --git a/shared-core/drm.h b/shared-core/drm.h --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -62,6 +62,12 @@ #define __user #endif +#ifdef __GNUC__ +# define DEPRECATED __attribute__ ((deprecated)) +#else +# define DEPRECATED +#endif + #if defined(__linux__) #if defined(__KERNEL__) #include <linux/config.h> diff --git a/shared-core/radeon_cp.c b/shared-core/radeon_cp.c --- a/shared-core/radeon_cp.c +++ b/shared-core/radeon_cp.c @@ -1245,7 +1245,7 @@ static void radeon_set_pciegart(drm_rade u32 tmp = RADEON_READ_PCIE(dev_priv, RADEON_PCIE_TX_GART_CNTL); if (on) { - DRM_DEBUG("programming pcie %08X %08lX %08X\n", dev_priv->gart_vm_start, dev_priv->bus_pci_gart,dev_priv->gart_size); + DRM_DEBUG("programming pcie %08X %08X %08X\n", dev_priv->gart_vm_start, dev_priv->bus_pci_gart,dev_priv->gart_size); RADEON_WRITE_PCIE(RADEON_PCIE_TX_DISCARD_RD_ADDR_LO, dev_priv->gart_vm_start); RADEON_WRITE_PCIE(RADEON_PCIE_TX_GART_BASE, dev_priv->bus_pci_gart); RADEON_WRITE_PCIE(RADEON_PCIE_TX_GART_START_LO, dev_priv->gart_vm_start); @@ -1398,8 +1398,6 @@ static int radeon_do_init_cp(drm_device_ DRM_GETSAREA(); - dev_priv->fb_offset = init->fb_offset; - dev_priv->mmio_offset = init->mmio_offset; dev_priv->ring_offset = init->ring_offset; dev_priv->ring_rptr_offset = init->ring_rptr_offset; dev_priv->buffers_offset = init->buffers_offset; @@ -1411,12 +1409,6 @@ static int radeon_do_init_cp(drm_device_ return DRM_ERR(EINVAL); } - dev_priv->mmio = drm_core_findmap(dev, init->mmio_offset); - if (!dev_priv->mmio) { - DRM_ERROR("could not find mmio region!\n"); - radeon_do_cleanup_cp(dev); - return DRM_ERR(EINVAL); - } dev_priv->cp_ring = drm_core_findmap(dev, init->ring_offset); if (!dev_priv->cp_ring) { DRM_ERROR("could not find cp ring region!\n"); diff --git a/shared-core/radeon_drm.h b/shared-core/radeon_drm.h --- a/shared-core/radeon_drm.h +++ b/shared-core/radeon_drm.h @@ -492,7 +492,7 @@ typedef struct drm_radeon_init { RADEON_INIT_R300_CP = 0x04 } func; unsigned long sarea_priv_offset; - int is_pci; /* not used, driver asks hardware */ + int is_pci DEPRECATED; /* deprecated, driver asks hardware */ int cp_mode; int gart_size; int ring_size; @@ -504,8 +504,8 @@ typedef struct drm_radeon_init { unsigned int depth_bpp; unsigned int depth_offset, depth_pitch; - unsigned long fb_offset; - unsigned long mmio_offset; + unsigned long fb_offset DEPRECATED; /* deprecated, driver asks hardware */ + unsigned long mmio_offset DEPRECATED; /* deprecated, driver asks hardware */ unsigned long ring_offset; unsigned long ring_rptr_offset; unsigned long buffers_offset; diff --git a/shared-core/radeon_drv.h b/shared-core/radeon_drv.h --- a/shared-core/radeon_drv.h +++ b/shared-core/radeon_drv.h @@ -242,8 +242,6 @@ typedef struct drm_radeon_private { drm_radeon_depth_clear_t depth_clear; - unsigned long fb_offset; - unsigned long mmio_offset; unsigned long ring_offset; unsigned long ring_rptr_offset; unsigned long buffers_offset; diff --git a/shared-core/radeon_state.c b/shared-core/radeon_state.c --- a/shared-core/radeon_state.c +++ b/shared-core/radeon_state.c @@ -2917,7 +2917,7 @@ static int radeon_cp_getparam(DRM_IOCTL_ value = dev_priv->gart_vm_start; break; case RADEON_PARAM_REGISTER_HANDLE: - value = dev_priv->mmio_offset; + value = dev_priv->mmio->offset; break; case RADEON_PARAM_STATUS_HANDLE: value = dev_priv->ring_rptr_offset;
diff --git a/linux-core/drmP.h b/linux-core/drmP.h --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -280,7 +280,7 @@ typedef int drm_ioctl_compat_t(struct fi typedef struct drm_ioctl_desc { drm_ioctl_t *func; int auth_needed; - int root_only; + int master; } drm_ioctl_desc_t; typedef struct drm_devstate { @@ -375,6 +375,7 @@ typedef struct drm_buf_entry { /** File private data */ typedef struct drm_file { int authenticated; + int master; int minor; pid_t pid; uid_t uid; diff --git a/linux-core/drm_bufs.c b/linux-core/drm_bufs.c --- a/linux-core/drm_bufs.c +++ b/linux-core/drm_bufs.c @@ -56,7 +56,8 @@ static drm_local_map_t *drm_find_matchin list_for_each(list, &dev->maplist->head) { drm_map_list_t *entry = list_entry(list, drm_map_list_t, head); if (entry->map && map->type == entry->map->type && - entry->map->offset == map->offset) { + ((entry->map->offset == map->offset) || + (map->type == _DRM_SHM))) { return entry->map; } } @@ -163,6 +164,19 @@ int drm_addmap(drm_device_t * dev, unsig map->handle = drm_ioremap(map->offset, map->size, dev); break; case _DRM_SHM: + found_map = drm_find_matching_map(dev, map); + if (found_map != NULL) { + if (found_map->size != map->size) { + DRM_DEBUG("Matching maps of type %d with " + "mismatched sizes, (%ld vs %ld)\n", + map->type, map->size, found_map->size); + found_map->size = map->size; + } + + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + *map_ptr = found_map; + return 0; + } map->handle = vmalloc_32(map->size); DRM_DEBUG("%lu %d %p\n", map->size, drm_order(map->size), map->handle); @@ -181,15 +195,34 @@ int drm_addmap(drm_device_t * dev, unsig dev->sigdata.lock = dev->lock.hw_lock = map->handle; /* Pointer to lock */ } break; - case _DRM_AGP: - if (drm_core_has_AGP(dev)) { + case _DRM_AGP: { + drm_agp_mem_t *entry; + int valid = 0; + + if (!drm_core_has_AGP(dev)) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } #ifdef __alpha__ - map->offset += dev->hose->mem_space->start; + map->offset += dev->hose->mem_space->start; #endif - map->offset += dev->agp->base; - map->mtrr = dev->agp->agp_mtrr; /* for getmap */ + map->offset += dev->agp->base; + map->mtrr = dev->agp->agp_mtrr; /* for getmap */ + + for (entry = dev->agp->memory; entry; entry = entry->next) { + if ((map->offset >= entry->bound) && + (map->offset + map->offset <= entry->bound + entry->pages * PAGE_SIZE)) { + valid = 1; + break; + } + } + if (!valid) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EPERM; } + DRM_DEBUG("AGP offset = 0x%08lx, size = 0x%08lx\n", map->offset, map->size); break; + } case _DRM_SCATTER_GATHER: if (!dev->sg) { drm_free(map, sizeof(*map), DRM_MEM_MAPS); @@ -258,6 +291,9 @@ int drm_addmap_ioctl(struct inode *inode return -EFAULT; } + if (!(capable(CAP_SYS_ADMIN) || map.type == _DRM_AGP)) + return -EPERM; + err = drm_addmap( dev, map.offset, map.size, map.type, map.flags, & map_ptr ); diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -545,13 +545,14 @@ int drm_ioctl(struct inode *inode, struc if (!func) { DRM_DEBUG("no function\n"); retcode = -EINVAL; - } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN)) || - (ioctl->auth_needed && !priv->authenticated)) { + } else if (((ioctl->master && !priv->master) || + (ioctl->auth_needed && !priv->authenticated)) && + (!capable(CAP_SYS_ADMIN))) { retcode = -EACCES; } else { retcode = func(inode, filp, cmd, arg); } - err_i1: +err_i1: atomic_dec(&dev->ioctl_count); if (retcode) DRM_DEBUG("ret = %x\n", retcode); diff --git a/linux-core/drm_fops.c b/linux-core/drm_fops.c --- a/linux-core/drm_fops.c +++ b/linux-core/drm_fops.c @@ -34,18 +34,26 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#include "drmP.h" #include <linux/poll.h> +#include "drmP.h" +#include "drm_sarea.h" + static int drm_open_helper(struct inode *inode, struct file *filp, drm_device_t * dev); static int drm_setup(drm_device_t * dev) { + drm_local_map_t *map; int i; if (dev->driver->presetup) dev->driver->presetup(dev); + /* prebuild the SAREA */ + i = drm_addmap(dev, 0, SAREA_MAX, _DRM_SHM, _DRM_CONTAINS_LOCK, &map); + if (i != 0) + return i; + atomic_set(&dev->ioctl_count, 0); atomic_set(&dev->vma_count, 0); dev->buf_use = 0; @@ -244,6 +252,7 @@ static int drm_open_helper(struct inode priv->minor = minor; priv->head = drm_heads[minor]; priv->ioctl_count = 0; + /* for compatibility root is always authenticated */ priv->authenticated = capable(CAP_SYS_ADMIN); priv->lock_count = 0; @@ -259,6 +268,9 @@ static int drm_open_helper(struct inode priv->prev = NULL; dev->file_first = priv; dev->file_last = priv; + /* first opener automatically becomes master and authenticated */ + priv->master = 1; + priv->authenticated = 1; } else { priv->next = NULL; priv->prev = dev->file_last; diff --git a/linux-core/drm_stub.c b/linux-core/drm_stub.c diff --git a/shared-core/drm.h b/shared-core/drm.h --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -62,6 +62,12 @@ #define __user #endif +#ifdef __GNUC__ +# define DEPRECATED __attribute__ ((deprecated)) +#else +# define DEPRECATED +#endif + #if defined(__linux__) #if defined(__KERNEL__) #include <linux/config.h> diff --git a/shared-core/radeon_cp.c b/shared-core/radeon_cp.c --- a/shared-core/radeon_cp.c +++ b/shared-core/radeon_cp.c @@ -1245,7 +1245,7 @@ static void radeon_set_pciegart(drm_rade u32 tmp = RADEON_READ_PCIE(dev_priv, RADEON_PCIE_TX_GART_CNTL); if (on) { - DRM_DEBUG("programming pcie %08X %08lX %08X\n", dev_priv->gart_vm_start, dev_priv->bus_pci_gart,dev_priv->gart_size); + DRM_DEBUG("programming pcie %08X %08X %08X\n", dev_priv->gart_vm_start, dev_priv->bus_pci_gart,dev_priv->gart_size); RADEON_WRITE_PCIE(RADEON_PCIE_TX_DISCARD_RD_ADDR_LO, dev_priv->gart_vm_start); RADEON_WRITE_PCIE(RADEON_PCIE_TX_GART_BASE, dev_priv->bus_pci_gart); RADEON_WRITE_PCIE(RADEON_PCIE_TX_GART_START_LO, dev_priv->gart_vm_start); @@ -1398,8 +1398,6 @@ static int radeon_do_init_cp(drm_device_ DRM_GETSAREA(); - dev_priv->fb_offset = init->fb_offset; - dev_priv->mmio_offset = init->mmio_offset; dev_priv->ring_offset = init->ring_offset; dev_priv->ring_rptr_offset = init->ring_rptr_offset; dev_priv->buffers_offset = init->buffers_offset; @@ -1411,12 +1409,6 @@ static int radeon_do_init_cp(drm_device_ return DRM_ERR(EINVAL); } - dev_priv->mmio = drm_core_findmap(dev, init->mmio_offset); - if (!dev_priv->mmio) { - DRM_ERROR("could not find mmio region!\n"); - radeon_do_cleanup_cp(dev); - return DRM_ERR(EINVAL); - } dev_priv->cp_ring = drm_core_findmap(dev, init->ring_offset); if (!dev_priv->cp_ring) { DRM_ERROR("could not find cp ring region!\n"); diff --git a/shared-core/radeon_drm.h b/shared-core/radeon_drm.h --- a/shared-core/radeon_drm.h +++ b/shared-core/radeon_drm.h @@ -492,7 +492,7 @@ typedef struct drm_radeon_init { RADEON_INIT_R300_CP = 0x04 } func; unsigned long sarea_priv_offset; - int is_pci; /* not used, driver asks hardware */ + int is_pci DEPRECATED; /* deprecated, driver asks hardware */ int cp_mode; int gart_size; int ring_size; @@ -504,8 +504,8 @@ typedef struct drm_radeon_init { unsigned int depth_bpp; unsigned int depth_offset, depth_pitch; - unsigned long fb_offset; - unsigned long mmio_offset; + unsigned long fb_offset DEPRECATED; /* deprecated, driver asks hardware */ + unsigned long mmio_offset DEPRECATED; /* deprecated, driver asks hardware */ unsigned long ring_offset; unsigned long ring_rptr_offset; unsigned long buffers_offset; diff --git a/shared-core/radeon_drv.h b/shared-core/radeon_drv.h --- a/shared-core/radeon_drv.h +++ b/shared-core/radeon_drv.h @@ -242,8 +242,6 @@ typedef struct drm_radeon_private { drm_radeon_depth_clear_t depth_clear; - unsigned long fb_offset; - unsigned long mmio_offset; unsigned long ring_offset; unsigned long ring_rptr_offset; unsigned long buffers_offset; diff --git a/shared-core/radeon_state.c b/shared-core/radeon_state.c --- a/shared-core/radeon_state.c +++ b/shared-core/radeon_state.c @@ -2917,7 +2917,7 @@ static int radeon_cp_getparam(DRM_IOCTL_ value = dev_priv->gart_vm_start; break; case RADEON_PARAM_REGISTER_HANDLE: - value = dev_priv->mmio_offset; + value = dev_priv->mmio->offset; break; case RADEON_PARAM_STATUS_HANDLE: value = dev_priv->ring_rptr_offset;