Bug 6242: [mach64] Use private DMA buffers, part #4.

mach64_state.c: convert the DRM_MACH64_BLIT ioctl to submit a pointer to
user-space memory rather than a DMA buffer index, similar to DRM_MACH64_VERTEX.

This change allows the DDX to map the DMA buffers read-only and eliminate a
security problem where a client can alter the contents of the DMA buffer after
submission to the DRM.

This change also affects the DRI/DRM interface. Performace-wise, it basically
affects PCI mode where I get a ~12% speedup for some Mesa demos I tested.
This is mainly due to eliminating an ioctl for allocating the DMA buffer.

mach64_dma.c: move the responsibility for allocating memory for the DMA ring
in PCI mode to the DDX.

This change affects the DDX/DRM interface and unifies a couple of PCI/AGP code
paths for ring memory in the DRM.

Bump the mach64 DRM version major and date.
main
George Sapountzis 2006-10-02 06:13:38 +03:00
parent f3deef730d
commit c9e3aa961e
4 changed files with 62 additions and 79 deletions

View File

@ -815,17 +815,18 @@ static int mach64_do_dma_init(drm_device_t * dev, drm_mach64_init_t * init)
return DRM_ERR(EINVAL); return DRM_ERR(EINVAL);
} }
dev_priv->ring_map = drm_core_findmap(dev, init->ring_offset);
if (!dev_priv->ring_map) {
DRM_ERROR("can not find ring map!\n");
dev->dev_private = (void *)dev_priv;
mach64_do_cleanup_dma(dev);
return DRM_ERR(EINVAL);
}
dev_priv->sarea_priv = (drm_mach64_sarea_t *) dev_priv->sarea_priv = (drm_mach64_sarea_t *)
((u8 *) dev_priv->sarea->handle + init->sarea_priv_offset); ((u8 *) dev_priv->sarea->handle + init->sarea_priv_offset);
if (!dev_priv->is_pci) { if (!dev_priv->is_pci) {
dev_priv->ring_map = drm_core_findmap(dev, init->ring_offset);
if (!dev_priv->ring_map) {
DRM_ERROR("can not find ring map!\n");
dev->dev_private = (void *)dev_priv;
mach64_do_cleanup_dma(dev);
return DRM_ERR(EINVAL);
}
drm_core_ioremap(dev_priv->ring_map, dev); drm_core_ioremap(dev_priv->ring_map, dev);
if (!dev_priv->ring_map->handle) { if (!dev_priv->ring_map->handle) {
DRM_ERROR("can not ioremap virtual address for" DRM_ERROR("can not ioremap virtual address for"
@ -891,27 +892,9 @@ static int mach64_do_dma_init(drm_device_t * dev, drm_mach64_init_t * init)
} }
} }
/* allocate descriptor memory from pci pool */
DRM_DEBUG("Allocating dma descriptor ring\n");
dev_priv->ring.size = 0x4000; /* 16KB */ dev_priv->ring.size = 0x4000; /* 16KB */
dev_priv->ring.start = dev_priv->ring_map->handle;
if (dev_priv->is_pci) { dev_priv->ring.start_addr = (u32) dev_priv->ring_map->offset;
dev_priv->ring.dmah = drm_pci_alloc(dev, dev_priv->ring.size,
dev_priv->ring.size,
0xfffffffful);
if (!dev_priv->ring.dmah) {
DRM_ERROR("Allocating dma descriptor ring failed\n");
return DRM_ERR(ENOMEM);
} else {
dev_priv->ring.start = dev_priv->ring.dmah->vaddr;
dev_priv->ring.start_addr =
(u32) dev_priv->ring.dmah->busaddr;
}
} else {
dev_priv->ring.start = dev_priv->ring_map->handle;
dev_priv->ring.start_addr = (u32) dev_priv->ring_map->offset;
}
memset(dev_priv->ring.start, 0, dev_priv->ring.size); memset(dev_priv->ring.start, 0, dev_priv->ring.size);
DRM_INFO("descriptor ring: cpu addr %p, bus addr: 0x%08x\n", DRM_INFO("descriptor ring: cpu addr %p, bus addr: 0x%08x\n",
@ -1149,18 +1132,14 @@ int mach64_do_cleanup_dma(drm_device_t * dev)
if (dev->dev_private) { if (dev->dev_private) {
drm_mach64_private_t *dev_priv = dev->dev_private; drm_mach64_private_t *dev_priv = dev->dev_private;
if (dev_priv->is_pci) { if (!dev_priv->is_pci) {
if (dev_priv->ring.dmah) {
drm_pci_free(dev, dev_priv->ring.dmah);
}
} else {
if (dev_priv->ring_map) if (dev_priv->ring_map)
drm_core_ioremapfree(dev_priv->ring_map, dev); drm_core_ioremapfree(dev_priv->ring_map, dev);
}
if (dev->agp_buffer_map) { if (dev->agp_buffer_map) {
drm_core_ioremapfree(dev->agp_buffer_map, dev); drm_core_ioremapfree(dev->agp_buffer_map, dev);
dev->agp_buffer_map = NULL; dev->agp_buffer_map = NULL;
}
} }
mach64_destroy_freelist(dev); mach64_destroy_freelist(dev);

View File

@ -237,7 +237,7 @@ typedef struct drm_mach64_vertex {
} drm_mach64_vertex_t; } drm_mach64_vertex_t;
typedef struct drm_mach64_blit { typedef struct drm_mach64_blit {
int idx; void *buf;
int pitch; int pitch;
int offset; int offset;
int format; int format;

View File

@ -42,9 +42,9 @@
#define DRIVER_NAME "mach64" #define DRIVER_NAME "mach64"
#define DRIVER_DESC "DRM module for the ATI Rage Pro" #define DRIVER_DESC "DRM module for the ATI Rage Pro"
#define DRIVER_DATE "20020904" #define DRIVER_DATE "20060718"
#define DRIVER_MAJOR 1 #define DRIVER_MAJOR 2
#define DRIVER_MINOR 0 #define DRIVER_MINOR 0
#define DRIVER_PATCHLEVEL 0 #define DRIVER_PATCHLEVEL 0
@ -61,7 +61,6 @@ typedef struct drm_mach64_freelist {
} drm_mach64_freelist_t; } drm_mach64_freelist_t;
typedef struct drm_mach64_descriptor_ring { typedef struct drm_mach64_descriptor_ring {
drm_dma_handle_t *dmah; /* Handle to pci dma memory */
void *start; /* write pointer (cpu address) to start of descriptor ring */ void *start; /* write pointer (cpu address) to start of descriptor ring */
u32 start_addr; /* bus address of beginning of descriptor ring */ u32 start_addr; /* bus address of beginning of descriptor ring */
int size; /* size of ring in bytes */ int size; /* size of ring in bytes */

View File

@ -480,16 +480,16 @@ static int mach64_do_get_frames_queued(drm_mach64_private_t * dev_priv)
/* Copy and verify a client submited buffer. /* Copy and verify a client submited buffer.
* FIXME: Make an assembly optimized version * FIXME: Make an assembly optimized version
*/ */
static __inline__ int copy_and_verify_from_user(u32 *to, static __inline__ int copy_from_user_vertex(u32 *to,
const u32 __user *ufrom, const u32 __user *ufrom,
unsigned long bytes) unsigned long bytes)
{ {
unsigned long n = bytes; /* dwords remaining in buffer */ unsigned long n = bytes; /* dwords remaining in buffer */
u32 *from, *orig_from; u32 *from, *orig_from;
from = drm_alloc(bytes, DRM_MEM_DRIVER); from = drm_alloc(bytes, DRM_MEM_DRIVER);
if (from == NULL) if (from == NULL)
return ENOMEM; return DRM_ERR(ENOMEM);
if (DRM_COPY_FROM_USER(from, ufrom, bytes)) { if (DRM_COPY_FROM_USER(from, ufrom, bytes)) {
drm_free(from, bytes, DRM_MEM_DRIVER); drm_free(from, bytes, DRM_MEM_DRIVER);
@ -571,7 +571,7 @@ static int mach64_dma_dispatch_vertex(DRMFILE filp, drm_device_t * dev,
return DRM_ERR(EAGAIN); return DRM_ERR(EAGAIN);
} }
verify_ret = copy_and_verify_from_user(GETBUFPTR(copy_buf), buf, used); verify_ret = copy_from_user_vertex(GETBUFPTR(copy_buf), buf, used);
if (verify_ret != 0) { if (verify_ret != 0) {
mach64_freelist_put(dev_priv, copy_buf); mach64_freelist_put(dev_priv, copy_buf);
@ -627,13 +627,27 @@ _vertex_done:
return verify_ret; return verify_ret;
} }
static __inline__ int copy_from_user_blit(u32 *to,
const u32 __user *ufrom,
unsigned long bytes)
{
to = (u32 *)((char *)to + MACH64_HOSTDATA_BLIT_OFFSET);
if (DRM_COPY_FROM_USER(to, ufrom, bytes)) {
return DRM_ERR(EFAULT);
}
return 0;
}
static int mach64_dma_dispatch_blit(DRMFILE filp, drm_device_t * dev, static int mach64_dma_dispatch_blit(DRMFILE filp, drm_device_t * dev,
drm_mach64_blit_t * blit) drm_mach64_blit_t * blit)
{ {
drm_mach64_private_t *dev_priv = dev->dev_private; drm_mach64_private_t *dev_priv = dev->dev_private;
drm_device_dma_t *dma = dev->dma;
int dword_shift, dwords; int dword_shift, dwords;
drm_buf_t *buf; unsigned long used;
drm_buf_t *copy_buf;
int verify_ret = 0;
DMALOCALS; DMALOCALS;
/* The compiler won't optimize away a division by a variable, /* The compiler won't optimize away a division by a variable,
@ -660,34 +674,34 @@ static int mach64_dma_dispatch_blit(DRMFILE filp, drm_device_t * dev,
return DRM_ERR(EINVAL); return DRM_ERR(EINVAL);
} }
/* Dispatch the blit buffer.
*/
buf = dma->buflist[blit->idx];
if (buf->filp != filp) {
DRM_ERROR("process %d (filp %p) using buffer with filp %p\n",
DRM_CURRENTPID, filp, buf->filp);
return DRM_ERR(EINVAL);
}
if (buf->pending) {
DRM_ERROR("sending pending buffer %d\n", blit->idx);
return DRM_ERR(EINVAL);
}
/* Set buf->used to the bytes of blit data based on the blit dimensions /* Set buf->used to the bytes of blit data based on the blit dimensions
* and verify the size. When the setup is emitted to the buffer with * and verify the size. When the setup is emitted to the buffer with
* the DMA* macros below, buf->used is incremented to include the bytes * the DMA* macros below, buf->used is incremented to include the bytes
* used for setup as well as the blit data. * used for setup as well as the blit data.
*/ */
dwords = (blit->width * blit->height) >> dword_shift; dwords = (blit->width * blit->height) >> dword_shift;
buf->used = dwords << 2; used = dwords << 2;
if (buf->used <= 0 || if (used <= 0 ||
buf->used > MACH64_BUFFER_SIZE - MACH64_HOSTDATA_BLIT_OFFSET) { used > MACH64_BUFFER_SIZE - MACH64_HOSTDATA_BLIT_OFFSET) {
DRM_ERROR("Invalid blit size: %d bytes\n", buf->used); DRM_ERROR("Invalid blit size: %lu bytes\n", used);
return DRM_ERR(EINVAL); return DRM_ERR(EINVAL);
} }
copy_buf = mach64_freelist_get(dev_priv);
if (copy_buf == NULL) {
DRM_ERROR("%s: couldn't get buffer\n", __FUNCTION__);
return DRM_ERR(EAGAIN);
}
verify_ret = copy_from_user_blit(GETBUFPTR(copy_buf), blit->buf, used);
if (verify_ret != 0) {
mach64_freelist_put(dev_priv, copy_buf);
goto _blit_done;
}
copy_buf->used = used;
/* FIXME: Use a last buffer flag and reduce the state emitted for subsequent, /* FIXME: Use a last buffer flag and reduce the state emitted for subsequent,
* continuation buffers? * continuation buffers?
*/ */
@ -696,7 +710,7 @@ static int mach64_dma_dispatch_blit(DRMFILE filp, drm_device_t * dev,
* a register command every 16 dwords. State setup is added at the start of the * a register command every 16 dwords. State setup is added at the start of the
* buffer -- the client leaves space for this based on MACH64_HOSTDATA_BLIT_OFFSET * buffer -- the client leaves space for this based on MACH64_HOSTDATA_BLIT_OFFSET
*/ */
DMASETPTR(buf); DMASETPTR(copy_buf);
DMAOUTREG(MACH64_Z_CNTL, 0); DMAOUTREG(MACH64_Z_CNTL, 0);
DMAOUTREG(MACH64_SCALE_3D_CNTL, 0); DMAOUTREG(MACH64_SCALE_3D_CNTL, 0);
@ -726,12 +740,13 @@ static int mach64_dma_dispatch_blit(DRMFILE filp, drm_device_t * dev,
DMAOUTREG(MACH64_DST_X_Y, (blit->y << 16) | blit->x); DMAOUTREG(MACH64_DST_X_Y, (blit->y << 16) | blit->x);
DMAOUTREG(MACH64_DST_WIDTH_HEIGHT, (blit->height << 16) | blit->width); DMAOUTREG(MACH64_DST_WIDTH_HEIGHT, (blit->height << 16) | blit->width);
DRM_DEBUG("%s: %d bytes\n", __FUNCTION__, buf->used); DRM_DEBUG("%s: %lu bytes\n", __FUNCTION__, used);
/* Add the buffer to the queue */ /* Add the buffer to the queue */
DMAADVANCEHOSTDATA(dev_priv); DMAADVANCEHOSTDATA(dev_priv);
return 0; _blit_done:
return verify_ret;
} }
/* ================================================================ /* ================================================================
@ -829,7 +844,6 @@ int mach64_dma_vertex(DRM_IOCTL_ARGS)
int mach64_dma_blit(DRM_IOCTL_ARGS) int mach64_dma_blit(DRM_IOCTL_ARGS)
{ {
DRM_DEVICE; DRM_DEVICE;
drm_device_dma_t *dma = dev->dma;
drm_mach64_private_t *dev_priv = dev->dev_private; drm_mach64_private_t *dev_priv = dev->dev_private;
drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv; drm_mach64_sarea_t *sarea_priv = dev_priv->sarea_priv;
drm_mach64_blit_t blit; drm_mach64_blit_t blit;
@ -840,15 +854,6 @@ int mach64_dma_blit(DRM_IOCTL_ARGS)
DRM_COPY_FROM_USER_IOCTL(blit, (drm_mach64_blit_t *) data, DRM_COPY_FROM_USER_IOCTL(blit, (drm_mach64_blit_t *) data,
sizeof(blit)); sizeof(blit));
DRM_DEBUG("%s: pid=%d index=%d\n",
__FUNCTION__, DRM_CURRENTPID, blit.idx);
if (blit.idx < 0 || blit.idx >= dma->buf_count) {
DRM_ERROR("buffer index %d (of %d max)\n",
blit.idx, dma->buf_count - 1);
return DRM_ERR(EINVAL);
}
ret = mach64_dma_dispatch_blit(filp, dev, &blit); ret = mach64_dma_dispatch_blit(filp, dev, &blit);
/* Make sure we restore the 3D state next time. /* Make sure we restore the 3D state next time.