Hello,

I modified some things in the VIA DRM driver to get it working properly
on my K8M800 Unichrome Pro chipset under Linux. I also got it working
on a x86_64 kernel with a 32-bit usermode system.

Could someone on this list please look through my patches and maybe
commit some things? I hope my work can be useful to others.

Below is a description of each patch. They are largely independent
of eachother. All patches are against the current CVS.

01_via_unichrome_pro
  Trivial patch to make the VIA driver recognize my PCI id as
  a Unichrome Pro chipset. This makes IRQ handling work properly
  and avoids the kernel message
    irq 201: nobody cared (try booting with the "irqpoll" option)

02_via_verifier_bugfix
  Trivial fix to the VIA command verifier. The bug caused it to accept
  some invalid commands on i386 and reject valid commands on x86_64.

03_via_mm_cleanup
  Rework the FB and AGP memory management in the VIA driver so that
  it no longer blindly passes kernel pointers to and from userspace.
  This was a security issue on i386 and a fundamental problem with
  32-bit compatibility on x86_64.

04_via_ioctl_security
  Enable privilege checking on those ioctls that seem to be intended
  for the X server only. Don't know if there was a particular reason
  why this wasn't done before.

05_via_futex_niceabort
  Avoid Oops and X server crash when something goes wrong during
  DRM initialization.

06_via_compat32
  Compatibility wrappers around the VIA ioctls to make it work with
  a 32-bit usermode system on a x86_64 kernel.

Thanks,
  Joris.
diff -urN -U 5 drm.orig/shared-core/via_map.c drm/shared-core/via_map.c
--- drm.orig/shared-core/via_map.c      2005-07-15 23:22:51.000000000 +0200
+++ drm/shared-core/via_map.c   2005-08-07 11:34:25.000000000 +0200
@@ -65,11 +65,12 @@
                                 init->sarea_priv_offset);
 
        dev_priv->agpAddr = init->agpAddr;
 
        via_init_futex( dev_priv );
-       dev_priv->pro_group_a = (dev->pdev->device == 0x3118);
+       dev_priv->pro_group_a = (dev->pdev->device == 0x3118 ||
+                                dev->pdev->device == 0x3108);
 
        dev->dev_private = (void *)dev_priv;
        return 0;
 }
 
diff -urN -U 5 drm.prev/shared-core/via_verifier.c 
drm/shared-core/via_verifier.c
--- drm.prev/shared-core/via_verifier.c 2005-04-18 10:26:00.000000000 +0200
+++ drm/shared-core/via_verifier.c      2005-08-13 19:17:06.000000000 +0200
@@ -244,11 +244,11 @@
 
 
 static __inline__ int
 eat_words(const uint32_t **buf, const uint32_t *buf_end, unsigned num_words)
 {
-       if ((*buf - buf_end) >= num_words) {
+       if ((buf_end - *buf) >= num_words) {
                *buf += num_words;
                return 0;
        } 
        DRM_ERROR("Illegal termination of DMA command buffer\n");
        return 1;
diff -urN -U 5 drm.orig/shared-core/via_mm.c drm/shared-core/via_mm.c
--- drm.orig/shared-core/via_mm.c       2005-07-15 23:22:51.000000000 +0200
+++ drm/shared-core/via_mm.c    2005-08-07 11:57:33.000000000 +0200
@@ -23,52 +23,124 @@
  */
 #include "drmP.h"
 #include "via_drm.h"
 #include "via_drv.h"
 #include "via_ds.h"
-#include "via_mm.h"
 
 #define MAX_CONTEXT 100
+#define MAX_MEMBLOCK_INDEX 5000
+
+/* Structure which maps indices to PMemBlock pointers.
+   index_base is used to make indices globally unique among
+   multiple mem_block_map_t structures, and to avoid ever using
+   the special value 0 as an index. */
+typedef struct {
+       unsigned int index_base;
+       PMemBlock m[MAX_MEMBLOCK_INDEX];
+} mem_block_map_t;
 
 typedef struct {
        int used;
        int context;
-       set_t *sets[2];         /* 0 for frame buffer, 1 for AGP , 2 for System 
*/
+       mem_block_map_t *map[2]; /* 0 for frame buffer, 1 for AGP */
 } via_context_t;
 
 static via_context_t global_ppriv[MAX_CONTEXT];
 
 static int via_agp_alloc(drm_via_mem_t * mem);
 static int via_agp_free(drm_via_mem_t * mem);
 static int via_fb_alloc(drm_via_mem_t * mem);
 static int via_fb_free(drm_via_mem_t * mem);
 
-static int add_alloc_set(int context, int type, unsigned int val)
+/*
+ * Allocate a mem_block_map_t and initialize it to make all indices
+ * point to NULL. index_base must be non-zero
+ */
+static mem_block_map_t * via_mem_block_map_init(unsigned int index_base)
 {
-       int i, retval = 0;
+       mem_block_map_t *map;
+       int i;
 
-       for (i = 0; i < MAX_CONTEXT; i++) {
-               if (global_ppriv[i].used && global_ppriv[i].context == context) 
{
-                       retval = via_setAdd(global_ppriv[i].sets[type], val);
-                       break;
-               }
+       map = drm_alloc(sizeof(mem_block_map_t), DRM_MEM_DRIVER);
+       if (map) {
+               map->index_base = index_base;
+               for (i = 0; i < MAX_MEMBLOCK_INDEX; i++)
+                       map->m[i] = NULL;
        }
-
-       return retval;
+       return map;
 }
 
-static int del_alloc_set(int context, int type, unsigned int val)
+/*
+ * Destroy a mem_block_map_t and release allocated memory.
+ */
+static void via_mem_block_map_destroy(mem_block_map_t *map)
 {
-       int i, retval = 0;
+       drm_free(map, sizeof(mem_block_map_t), DRM_MEM_DRIVER);
+}
 
-       for (i = 0; i < MAX_CONTEXT; i++)
-               if (global_ppriv[i].used && global_ppriv[i].context == context) 
{
-                       retval = via_setDel(global_ppriv[i].sets[type], val);
-                       break;
+
+/*
+ * Add a pointer to a mem_block_map_t and return the new index,
+ * or return 0 if all slots are in use.
+ */
+static unsigned int via_mem_block_map_add(mem_block_map_t *map, PMemBlock 
block)
+{
+       int i;
+       for (i = 0; i < MAX_MEMBLOCK_INDEX; i++)
+               if (!map->m[i]) {
+                       map->m[i] = block;
+                       return i + map->index_base;
                }
+       return 0;
+}
 
-       return retval;
+/*
+ * Remove a pointer from a given index in a mem_block_map_t and return
+ * the removed pointer, or return NULL if no pointer was present.
+ */
+static PMemBlock via_mem_block_map_remove(mem_block_map_t *map,
+                                          unsigned int index)
+{
+       PMemBlock block;
+       if (index < map->index_base)
+               return NULL;
+       index -= map->index_base;
+       if (index >= MAX_MEMBLOCK_INDEX)
+               return NULL;
+       block = map->m[index];
+       map->m[index] = NULL;
+       return block;
+}
+
+/*
+ * Add this block pointer to the allocation map of the specified context
+ * and return the block index, or return 0 for failure.
+ */
+static unsigned int add_alloc_map(int context, int type, PMemBlock block)
+{
+       int i;
+       for (i = 0; i < MAX_CONTEXT; i++) {
+               via_context_t *ctx = &global_ppriv[i];
+               if (ctx->used && ctx->context == context)
+                       return via_mem_block_map_add(ctx->map[type], block);
+       }
+       return 0;
+}
+
+/*
+ * Lookup and remove the specified block index from the context allocation map,
+ * and return the corresponding block pointer, or return NULL for not found.
+ */
+static PMemBlock del_alloc_map(int context, int type, unsigned int index)
+{
+       int i;
+       for (i = 0; i < MAX_CONTEXT; i++) {
+               via_context_t *ctx = &global_ppriv[i];
+               if (ctx->used && ctx->context == context)
+                       return via_mem_block_map_remove(ctx->map[type], index);
+       }
+       return NULL;
 }
 
 /* agp memory management */
 static memHeap_t *AgpHeap = NULL;
 
@@ -107,32 +179,37 @@
        int i;
 
        for (i = 0; i < MAX_CONTEXT; i++)
                if (global_ppriv[i].used &&
                    (global_ppriv[i].context == context))
-                       break;
+                       return 1;
 
-       if (i >= MAX_CONTEXT) {
-               for (i = 0; i < MAX_CONTEXT; i++) {
-                       if (!global_ppriv[i].used) {
-                               global_ppriv[i].context = context;
-                               global_ppriv[i].used = 1;
-                               global_ppriv[i].sets[0] = via_setInit();
-                               global_ppriv[i].sets[1] = via_setInit();
-                               DRM_DEBUG("init allocation set, socket=%d,"
-                                         " context = %d\n", i, context);
-                               break;
+       for (i = 0; i < MAX_CONTEXT; i++) {
+               if (!global_ppriv[i].used) {
+                       global_ppriv[i].map[0] = 
via_mem_block_map_init(2*i*MAX_MEMBLOCK_INDEX + 1);
+                       global_ppriv[i].map[1] = 
via_mem_block_map_init((2*i+1)*MAX_MEMBLOCK_INDEX + 1);
+                       if ( global_ppriv[i].map[0] == NULL ||
+                            global_ppriv[i].map[1] == NULL ) {
+                               if (global_ppriv[i].map[0] != NULL)
+                                       
via_mem_block_map_destroy(global_ppriv[i].map[0]);
+                               if (global_ppriv[i].map[1] != NULL)
+                                       
via_mem_block_map_destroy(global_ppriv[i].map[1]);
+                               DRM_DEBUG("init allocation set failed,"
+                                         " no memory, context=%d\n", context);
+                               return 0;
                        }
-               }
-
-               if ((i >= MAX_CONTEXT) || (global_ppriv[i].sets[0] == NULL) ||
-                   (global_ppriv[i].sets[1] == NULL)) {
-                       return 0;
+                       global_ppriv[i].context = context;
+                       global_ppriv[i].used = 1;
+                       DRM_DEBUG("init allocation set, socket=%d,"
+                                 " context = %d\n", i, context);
+                       return 1;
                }
        }
 
-       return 1;
+       DRM_DEBUG("init allocation set failed, no free socket, context=%d\n",
+                 context);
+       return 0;
 }
 
 int via_final_context(struct drm_device *dev, int context)
 {      
         int i;
@@ -142,35 +219,36 @@
                if (global_ppriv[i].used &&
                    (global_ppriv[i].context == context))
                        break;
 
        if (i < MAX_CONTEXT) {
-               set_t *set;
-               ITEM_TYPE item;
-               int retval;
+               mem_block_map_t *map;
+               int index;
 
                DRM_DEBUG("find socket %d, context = %d\n", i, context);
 
                /* Video Memory */
-               set = global_ppriv[i].sets[0];
-               retval = via_setFirst(set, &item);
-               while (retval) {
-                       DRM_DEBUG("free video memory 0x%lx\n", item);
-                       via_mmFreeMem((PMemBlock) item);
-                       retval = via_setNext(set, &item);
+               map = global_ppriv[i].map[0];
+               for (index = 0; index < MAX_MEMBLOCK_INDEX; index++) {
+                       PMemBlock item = map->m[index];
+                       if (item) {
+                               DRM_DEBUG("free video memory %p\n", item);
+                               via_mmFreeMem(item);
+                       }
                }
-               via_setDestroy(set);
+               via_mem_block_map_destroy(map);
 
                /* AGP Memory */
-               set = global_ppriv[i].sets[1];
-               retval = via_setFirst(set, &item);
-               while (retval) {
-                       DRM_DEBUG("free agp memory 0x%lx\n", item);
-                       via_mmFreeMem((PMemBlock) item);
-                       retval = via_setNext(set, &item);
+               map = global_ppriv[i].map[1];
+               for (index = 0; index < MAX_MEMBLOCK_INDEX; index++) {
+                       PMemBlock item = map->m[index];
+                       if (item) {
+                               DRM_DEBUG("free agp memory %p\n", item);
+                               via_mmFreeMem(item);
+                       }
                }
-               via_setDestroy(set);
+               via_mem_block_map_destroy(map);
                global_ppriv[i].used = 0;
        }
        via_release_futex(dev_priv, context); 
        
                        
@@ -215,77 +293,67 @@
        return -EFAULT;
 }
 
 static int via_fb_alloc(drm_via_mem_t * mem)
 {
-       drm_via_mm_t fb;
        PMemBlock block;
        int retval = 0;
 
        if (!FBHeap)
                return -1;
 
-       fb.size = mem->size;
-       fb.context = mem->context;
-
-       block = via_mmAllocMem(FBHeap, fb.size, 5, 0);
+       block = via_mmAllocMem(FBHeap, mem->size, 5, 0);
        if (block) {
-               fb.offset = block->ofs;
-               fb.free = (unsigned long)block;
-               if (!add_alloc_set(fb.context, VIDEO, fb.free)) {
+               int index = add_alloc_map(mem->context, VIDEO, block);
+               if (!index) {
                        DRM_DEBUG("adding to allocation set fails\n");
-                       via_mmFreeMem((PMemBlock) fb.free);
+                       via_mmFreeMem(block);
                        retval = -1;
+               } else {
+                       mem->offset = block->ofs;
+                       mem->index = index;
                }
        } else {
-               fb.offset = 0;
-               fb.size = 0;
-               fb.free = 0;
+               mem->offset = 0;
+               mem->index = 0;
                retval = -1;
        }
 
-       mem->offset = fb.offset;
-       mem->index = fb.free;
-
-       DRM_DEBUG("alloc fb, size = %d, offset = %d\n", fb.size,
-                 (int)fb.offset);
+       DRM_DEBUG("alloc fb, size = %d, offset = %d\n",
+                 mem->size, (unsigned int)mem->offset);
 
        return retval;
 }
 
 static int via_agp_alloc(drm_via_mem_t * mem)
 {
-       drm_via_mm_t agp;
        PMemBlock block;
        int retval = 0;
 
        if (!AgpHeap)
                return -1;
 
-       agp.size = mem->size;
-       agp.context = mem->context;
-
-       block = via_mmAllocMem(AgpHeap, agp.size, 5, 0);
+       block = via_mmAllocMem(AgpHeap, mem->size, 5, 0);
        if (block) {
-               agp.offset = block->ofs;
-               agp.free = (unsigned long)block;
-               if (!add_alloc_set(agp.context, AGP, agp.free)) {
+               int index = add_alloc_map(mem->context, AGP, block);
+               if (!index) {
                        DRM_DEBUG("adding to allocation set fails\n");
-                       via_mmFreeMem((PMemBlock) agp.free);
+                       via_mmFreeMem(block);
                        retval = -1;
+               } else {
+                       mem->offset = block->ofs;
+                       mem->index = index;
                }
        } else {
-               agp.offset = 0;
-               agp.size = 0;
-               agp.free = 0;
+               mem->offset = 0;
+               mem->index = 0;
+               /* should we set retval = -1 ?? */
        }
 
-       mem->offset = agp.offset;
-       mem->index = agp.free;
+       DRM_DEBUG("alloc agp, size = %d, offset = %d\n",
+                 mem->size, (unsigned int)mem->offset);
 
-       DRM_DEBUG("alloc agp, size = %d, offset = %d\n", agp.size,
-                 (unsigned int)agp.offset);
        return retval;
 }
 
 int via_mem_free(DRM_IOCTL_ARGS)
 {
@@ -309,53 +377,48 @@
        return -EFAULT;
 }
 
 static int via_fb_free(drm_via_mem_t * mem)
 {
-       drm_via_mm_t fb;
+       PMemBlock block;
        int retval = 0;
 
        if (!FBHeap) {
                return -1;
        }
 
-       fb.free = mem->index;
-       fb.context = mem->context;
-
-       if (!fb.free) {
+       if (!mem->index) {
                return -1;
 
        }
 
-       via_mmFreeMem((PMemBlock) fb.free);
-
-       if (!del_alloc_set(fb.context, VIDEO, fb.free)) {
+       block = del_alloc_map(mem->context, VIDEO, mem->index);
+       if (!block) {
                retval = -1;
+       } else {
+               via_mmFreeMem(block);
        }
 
-       DRM_DEBUG("free fb, free = %ld\n", fb.free);
+       DRM_DEBUG("free fb, index = %ld\n", mem->index);
 
        return retval;
 }
 
 static int via_agp_free(drm_via_mem_t * mem)
 {
-       drm_via_mm_t agp;
-
+       PMemBlock block;
        int retval = 0;
 
-       agp.free = mem->index;
-       agp.context = mem->context;
-
-       if (!agp.free)
+       if (!mem->index)
                return -1;
 
-       via_mmFreeMem((PMemBlock) agp.free);
-
-       if (!del_alloc_set(agp.context, AGP, agp.free)) {
+       block = del_alloc_map(mem->context, AGP, mem->index);
+       if (!block) {
                retval = -1;
+       } else {
+               via_mmFreeMem(block);
        }
 
-       DRM_DEBUG("free agp, free = %ld\n", agp.free);
+       DRM_DEBUG("free agp, index = %ld\n", mem->index);
 
        return retval;
 }
diff -urN -U 5 drm.orig/shared-core/via_mm.h drm/shared-core/via_mm.h
--- drm.orig/shared-core/via_mm.h       2005-05-23 22:56:54.000000000 +0200
+++ drm/shared-core/via_mm.h    1970-01-01 01:00:00.000000000 +0100
@@ -1,40 +0,0 @@
-/*
- * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.
- * Copyright 2001-2003 S3 Graphics, Inc. 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, sub license,
- * 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 NON-INFRINGEMENT. IN NO EVENT SHALL
- * VIA, S3 GRAPHICS, 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.
- */
-#ifndef _via_drm_mm_h_
-#define _via_drm_mm_h_
-
-typedef struct {
-       unsigned int context;
-       unsigned int size;
-       unsigned long offset;
-       unsigned long free;
-} drm_via_mm_t;
-
-typedef struct {
-       unsigned int size;
-       unsigned long handle;
-       void *virtual;
-} drm_via_dma_t;
-
-#endif
diff -urN -U 5 drm.orig/shared-core/via_drv.c drm/shared-core/via_drv.c
--- drm.orig/shared-core/via_drv.c      2005-08-05 05:50:23.000000000 +0200
+++ drm/shared-core/via_drv.c   2005-08-07 12:09:07.000000000 +0200
@@ -38,22 +38,22 @@
 static struct pci_device_id pciidlist[] = {
        viadrv_PCI_IDS
 };
 
 static drm_ioctl_desc_t ioctls[] = {
-       [DRM_IOCTL_NR(DRM_VIA_ALLOCMEM)] = {via_mem_alloc, 1, 0, 0},
-       [DRM_IOCTL_NR(DRM_VIA_FREEMEM)] = {via_mem_free, 1, 0, 0},
-       [DRM_IOCTL_NR(DRM_VIA_AGP_INIT)] = {via_agp_init, 1, 0, 0},
-       [DRM_IOCTL_NR(DRM_VIA_FB_INIT)] = {via_fb_init, 1, 0, 0},
-       [DRM_IOCTL_NR(DRM_VIA_MAP_INIT)] = {via_map_init, 1, 0, 0},
-       [DRM_IOCTL_NR(DRM_VIA_DEC_FUTEX)] = {via_decoder_futex, 1, 0, 0},
-       [DRM_IOCTL_NR(DRM_VIA_DMA_INIT)] = {via_dma_init, 1, 0, 0},
-       [DRM_IOCTL_NR(DRM_VIA_CMDBUFFER)] = {via_cmdbuffer, 1, 0, 0},
-       [DRM_IOCTL_NR(DRM_VIA_FLUSH)] = {via_flush_ioctl, 1, 0, 0},
-       [DRM_IOCTL_NR(DRM_VIA_PCICMD)] = {via_pci_cmdbuffer, 1, 0, 0},
-       [DRM_IOCTL_NR(DRM_VIA_CMDBUF_SIZE)] = {via_cmdbuf_size, 1, 0, 0},
-       [DRM_IOCTL_NR(DRM_VIA_WAIT_IRQ)] = {via_wait_irq, 1, 0, 0}
+       [DRM_IOCTL_NR(DRM_VIA_ALLOCMEM)] =    {via_mem_alloc,     1, 0, 0},
+       [DRM_IOCTL_NR(DRM_VIA_FREEMEM)] =     {via_mem_free,      1, 0, 0},
+       [DRM_IOCTL_NR(DRM_VIA_AGP_INIT)] =    {via_agp_init,      1, 1, 1},
+       [DRM_IOCTL_NR(DRM_VIA_FB_INIT)] =     {via_fb_init,       1, 1, 1},
+       [DRM_IOCTL_NR(DRM_VIA_MAP_INIT)] =    {via_map_init,      1, 1, 1},
+       [DRM_IOCTL_NR(DRM_VIA_DEC_FUTEX)] =   {via_decoder_futex, 1, 0, 0},
+       [DRM_IOCTL_NR(DRM_VIA_DMA_INIT)] =    {via_dma_init,      1, 1, 1},
+       [DRM_IOCTL_NR(DRM_VIA_CMDBUFFER)] =   {via_cmdbuffer,     1, 0, 0},
+       [DRM_IOCTL_NR(DRM_VIA_FLUSH)] =       {via_flush_ioctl,   1, 0, 0},
+       [DRM_IOCTL_NR(DRM_VIA_PCICMD)] =      {via_pci_cmdbuffer, 1, 0, 0},
+       [DRM_IOCTL_NR(DRM_VIA_CMDBUF_SIZE)] = {via_cmdbuf_size,   1, 0, 0},
+       [DRM_IOCTL_NR(DRM_VIA_WAIT_IRQ)] =    {via_wait_irq,      1, 0, 0}
 };
 
 static int probe(struct pci_dev *pdev, const struct pci_device_id *ent);
 static struct drm_driver driver = {
        .driver_features =
diff -urN -U 5 drm.prev/shared-core/via_video.c drm/shared-core/via_video.c
--- drm.prev/shared-core/via_video.c    2005-07-15 23:22:51.000000000 +0200
+++ drm/shared-core/via_video.c 2005-08-13 19:24:55.000000000 +0200
@@ -51,10 +51,14 @@
 via_release_futex(drm_via_private_t *dev_priv, int context)
 {
        unsigned int i;
        volatile int *lock;
 
+       /* don't do anything if initialization was not completed */
+       if (!dev_priv->sarea_priv)
+               return;
+
        for (i=0; i < VIA_NR_XVMC_LOCKS; ++i) {
                lock = (int *) XVMCLOCKPTR(dev_priv->sarea_priv, i);
                if ( (_DRM_LOCKING_CONTEXT( *lock ) == context)) {
                        if (_DRM_LOCK_IS_HELD( *lock ) && (*lock & 
_DRM_LOCK_CONT)) {
                                DRM_WAKEUP( &(dev_priv->decoder_queue[i]));
diff -urN -U 5 drm.prev/linux-core/Makefile.kernel 
drm/linux-core/Makefile.kernel
--- drm.prev/linux-core/Makefile.kernel 2005-07-20 23:17:47.000000000 +0200
+++ drm/linux-core/Makefile.kernel      2005-08-13 19:26:22.000000000 +0200
@@ -30,10 +30,11 @@
 drm-objs    += drm_ioc32.o
 radeon-objs += radeon_ioc32.o
 mga-objs    += mga_ioc32.o
 r128-objs   += r128_ioc32.o
 i915-objs   += i915_ioc32.o
+via-objs    += via_ioc32.o
 endif
 
 obj-m                  += drm.o
 obj-$(CONFIG_DRM_TDFX) += tdfx.o
 obj-$(CONFIG_DRM_R128) += r128.o
diff -urN -U 5 drm.prev/shared-core/via_drv.c drm/shared-core/via_drv.c
--- drm.prev/shared-core/via_drv.c      2005-08-13 19:22:57.000000000 +0200
+++ drm/shared-core/via_drv.c   2005-08-13 19:26:22.000000000 +0200
@@ -79,10 +79,13 @@
                .release = drm_release,
                .ioctl = drm_ioctl,
                .mmap = drm_mmap,
                .poll = drm_poll,
                .fasync = drm_fasync,
+#ifdef CONFIG_COMPAT
+               .compat_ioctl = via_compat_ioctl,
+#endif
                },
        .pci_driver = {
                .name = DRIVER_NAME,
                .id_table = pciidlist,
                .probe = probe,
diff -urN -U 5 drm.prev/shared-core/via_drv.h drm/shared-core/via_drv.h
--- drm.prev/shared-core/via_drv.h      2005-08-12 16:19:33.000000000 +0200
+++ drm/shared-core/via_drv.h   2005-08-13 19:26:22.000000000 +0200
@@ -112,7 +112,9 @@
 extern int via_driver_dma_quiescent(drm_device_t * dev);
 extern void via_init_futex(drm_via_private_t *dev_priv);
 extern void via_cleanup_futex(drm_via_private_t *dev_priv);
 extern void via_release_futex(drm_via_private_t *dev_priv, int context);
 
+extern long via_compat_ioctl(struct file *filp, unsigned int cmd,
+                            unsigned long arg);
 
 #endif
diff -urN -U 5 drm.prev/shared-core/via_ioc32.c drm/shared-core/via_ioc32.c
--- drm.prev/shared-core/via_ioc32.c    1970-01-01 01:00:00.000000000 +0100
+++ drm/shared-core/via_ioc32.c 2005-08-13 19:27:25.000000000 +0200
@@ -0,0 +1,241 @@
+/*
+ * 32-bit ioctl compatibility routines for the VIA DRM driver.
+ */
+
+#include <linux/compat.h>
+#include <linux/ioctl32.h>
+
+#include "drmP.h"
+#include "drm.h"
+#include "via_drm.h"
+#include "via_drv.h"
+
+#define VIA_IOCTL32_ARGS struct file *file, unsigned int cmd, unsigned long arg
+
+/* allocate memory for 64-bit structure */
+#define VIA_IOCTL32_INIT() {                                           \
+       req32 = (__typeof(*req32) __user *) arg;                        \
+       req = compat_alloc_user_space(sizeof(*req));                    \
+       if (!access_ok(VERIFY_WRITE, req, sizeof(*req)))                \
+               return -EFAULT;                                         \
+}
+
+/* copy simple field from 32-bit to 64-bit structure */
+#define VIA_IOCTL32_LOAD(f) {                                          \
+       __typeof__(req32->f) __val;                                     \
+       if (__get_user(__val, &(req32->f)) ||                           \
+           __put_user(__val, &(req->f)))                               \
+               return -EFAULT;                                         \
+}
+
+/* copy pointer from 32-bit to 64-bit structure */
+#define VIA_IOCTL32_LOAD_POINTER(f) {                                  \
+       uint32_t __val;                                                 \
+       if (__get_user(__val, &(req32->f)) ||                           \
+           __put_user((__typeof__(req->f))(unsigned long)__val, &(req->f))) \
+               return -EFAULT;                                         \
+}
+
+/* call 64-bit drm ioctl */
+#define VIA_IOCTL32_CALL(fn) \
+       drm_ioctl(file->f_dentry->d_inode, file, (fn), (unsigned long) req)
+
+typedef struct {
+       uint32_t context;
+       uint32_t type;
+       uint32_t size;
+       uint32_t index;
+       uint32_t offset;
+} drm_compat_via_mem_t;
+
+typedef struct {
+       int32_t func;
+       uint32_t sarea_priv_offset;
+       uint32_t fb_offset;
+       uint32_t mmio_offset;
+       uint32_t agpAddr;
+} drm_compat_via_init_t;
+
+typedef struct {
+       int32_t func;
+       uint32_t offset;
+       uint32_t size;
+       uint32_t reg_pause_addr;
+} drm_compat_via_dma_init_t;
+
+typedef struct {
+       uint32_t buf;
+       uint32_t size;
+} drm_compat_via_cmdbuffer_t;
+
+static int compat_via_mem_alloc(VIA_IOCTL32_ARGS)
+{
+       drm_compat_via_mem_t __user *req32;
+       drm_via_mem_t __user *req;
+       drm_via_mem_t mem;
+       int ret;
+       
+       VIA_IOCTL32_INIT()
+       VIA_IOCTL32_LOAD(context)
+       VIA_IOCTL32_LOAD(type)
+       VIA_IOCTL32_LOAD(size)
+       VIA_IOCTL32_LOAD_POINTER(index)
+       VIA_IOCTL32_LOAD_POINTER(offset)
+
+       ret = VIA_IOCTL32_CALL(DRM_IOCTL_VIA_ALLOCMEM);
+
+       if (ret >= 0) {
+               /* convert modified request buffer back to 32-bit */
+
+               if (copy_from_user(&mem, req, sizeof(mem)))
+                       return -EFAULT;
+
+               if ( (mem.index | mem.offset) & 0xffffffff00000000L ) {
+                       /* got out-of-range return value */
+                       VIA_IOCTL32_CALL(DRM_IOCTL_VIA_FREEMEM);
+                       DRM_ERROR("via_ioc32: "
+                         "out-of-range return value from via_mem_alloc\n");
+                       return -EFAULT;
+               }
+
+               if ( __put_user(mem.context, &(req32->context)) ||
+                    __put_user(mem.type, &(req32->type)) ||
+                    __put_user(mem.size, &(req32->size)) ||
+                    __put_user((uint32_t)mem.index, &(req32->index)) ||
+                    __put_user((uint32_t)mem.offset, &(req32->offset)) )
+                       return -EFAULT;
+       }
+
+       return ret;
+}
+
+static int compat_via_mem_free(VIA_IOCTL32_ARGS)
+{
+       drm_compat_via_mem_t __user *req32;
+       drm_via_mem_t __user *req;
+       int ret;
+
+       VIA_IOCTL32_INIT()
+       VIA_IOCTL32_LOAD(context)
+       VIA_IOCTL32_LOAD(type)
+       VIA_IOCTL32_LOAD(size)
+       VIA_IOCTL32_LOAD_POINTER(index)
+       VIA_IOCTL32_LOAD_POINTER(offset)
+
+       ret = VIA_IOCTL32_CALL(DRM_IOCTL_VIA_FREEMEM);
+
+       return ret;
+}
+
+static int compat_via_map_init(VIA_IOCTL32_ARGS)
+{
+       drm_compat_via_init_t __user *req32;
+       drm_via_init_t __user *req;
+       int ret;
+       
+       VIA_IOCTL32_INIT()
+       VIA_IOCTL32_LOAD(func)
+       VIA_IOCTL32_LOAD_POINTER(sarea_priv_offset)
+       VIA_IOCTL32_LOAD_POINTER(fb_offset)
+       VIA_IOCTL32_LOAD_POINTER(mmio_offset)
+       VIA_IOCTL32_LOAD_POINTER(agpAddr)
+
+       ret = VIA_IOCTL32_CALL(DRM_IOCTL_VIA_MAP_INIT);
+
+       return ret;
+}
+
+static int compat_via_dma_init(VIA_IOCTL32_ARGS)
+{
+       drm_compat_via_dma_init_t __user *req32;
+       drm_via_dma_init_t __user *req;
+       int ret;
+       
+       VIA_IOCTL32_INIT()
+       VIA_IOCTL32_LOAD(func)
+       VIA_IOCTL32_LOAD_POINTER(offset)
+       VIA_IOCTL32_LOAD_POINTER(size)
+       VIA_IOCTL32_LOAD_POINTER(reg_pause_addr)
+
+       ret = VIA_IOCTL32_CALL(DRM_IOCTL_VIA_DMA_INIT);
+
+       return ret;
+}
+
+static int compat_via_cmdbuffer(VIA_IOCTL32_ARGS)
+{
+       drm_compat_via_cmdbuffer_t __user *req32;
+       drm_via_cmdbuffer_t __user *req;
+       int ret;
+       
+       VIA_IOCTL32_INIT()
+       VIA_IOCTL32_LOAD_POINTER(buf)
+       VIA_IOCTL32_LOAD_POINTER(size)
+
+       ret = VIA_IOCTL32_CALL(DRM_IOCTL_VIA_CMDBUFFER);
+
+       return ret;
+}
+
+static int compat_via_pci_cmdbuffer(VIA_IOCTL32_ARGS)
+{
+       drm_compat_via_cmdbuffer_t __user *req32;
+       drm_via_cmdbuffer_t __user *req;
+       int ret;
+       
+       VIA_IOCTL32_INIT()
+       VIA_IOCTL32_LOAD_POINTER(buf)
+       VIA_IOCTL32_LOAD_POINTER(size)
+
+       ret = VIA_IOCTL32_CALL(DRM_IOCTL_VIA_PCICMD);
+
+       return ret;
+}
+
+static drm_ioctl_compat_t *via_compat_ioctls[] = {
+       [DRM_VIA_ALLOCMEM] =    compat_via_mem_alloc,
+       [DRM_VIA_FREEMEM] =     compat_via_mem_free,
+       [DRM_VIA_AGP_INIT] =    NULL,
+       [DRM_VIA_FB_INIT] =     NULL,
+       [DRM_VIA_MAP_INIT] =    compat_via_map_init,
+       [DRM_VIA_DEC_FUTEX] =   NULL,
+       [DRM_VIA_DMA_INIT] =    compat_via_dma_init,
+       [DRM_VIA_CMDBUFFER] =   compat_via_cmdbuffer,
+       [DRM_VIA_FLUSH] =       NULL,
+       [DRM_VIA_PCICMD] =      compat_via_pci_cmdbuffer,
+       [DRM_VIA_CMDBUF_SIZE] = NULL,
+       [DRM_VIA_WAIT_IRQ] =    NULL
+};
+
+/**
+ * Called whenever a 32-bit process running under a 64-bit kernel
+ * performs an ioctl on /dev/dri/card<n>.
+ *
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument.
+ * \return zero on success or negative number on failure.
+ */
+long via_compat_ioctl(struct file *filp, unsigned int cmd,
+                      unsigned long arg)
+{
+       unsigned int nr = DRM_IOCTL_NR(cmd);
+       drm_ioctl_compat_t *fn = NULL;
+       int ret;
+
+       if (nr < DRM_COMMAND_BASE)
+               return drm_compat_ioctl(filp, cmd, arg);
+
+       if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(via_compat_ioctls))
+               fn = via_compat_ioctls[nr - DRM_COMMAND_BASE];
+
+       lock_kernel();          /* XXX for now */
+       if (fn != NULL)
+               ret = (*fn)(filp, cmd, arg);
+       else
+               ret = drm_ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
+       unlock_kernel();
+
+       return ret;
+}
+

Reply via email to