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;
+}
+