diff options
Diffstat (limited to 'shared/via_dma.c')
-rw-r--r-- | shared/via_dma.c | 100 |
1 files changed, 33 insertions, 67 deletions
diff --git a/shared/via_dma.c b/shared/via_dma.c index 4b591fa4..fbd3f6cb 100644 --- a/shared/via_dma.c +++ b/shared/via_dma.c @@ -37,57 +37,6 @@ static void via_cmdbuf_start(drm_via_private_t * dev_priv); static void via_cmdbuf_pause(drm_via_private_t * dev_priv); static void via_cmdbuf_reset(drm_via_private_t * dev_priv); static void via_cmdbuf_rewind(drm_via_private_t * dev_priv); -static int via_wait_idle(drm_via_private_t * dev_priv); - - -/* - * This function needs to be extended whenever a new command set - * is implemented. Currently it works only for the 2D engine - * command, which on the Unichrome allows writing to - * at least the 2D engine and the mpeg engine, but not the - * video engine. - * - * If you update this function with new commands, please also - * consider implementing these commands in - * via_parse_pci_cmdbuffer below. - * - * Carefully review this function for security holes - * after an update!!!!!!!!! - */ - -static int via_check_command_stream(const uint32_t * buf, unsigned int size) -{ - - uint32_t offset; - unsigned int i; - - if (size & 7) { - DRM_ERROR("Illegal command buffer size.\n"); - return DRM_ERR(EINVAL); - } - size >>= 3; - for (i = 0; i < size; ++i) { - offset = *buf; - buf += 2; - if ((offset > ((0x3FF >> 2) | HALCYON_HEADER1)) && - (offset < ((0xC00 >> 2) | HALCYON_HEADER1))) { - DRM_ERROR - ("Attempt to access Burst Command / 3D Area.\n"); - return DRM_ERR(EINVAL); - } else if (offset > ((0xDFF >> 2) | HALCYON_HEADER1)) { - DRM_ERROR("Attempt to access DMA or VGA registers.\n"); - return DRM_ERR(EINVAL); - } - - /* - * ... - * A volunteer should complete this to allow non-root - * usage of accelerated 3D OpenGL. - */ - - } - return 0; -} static inline int via_cmdbuf_wait(drm_via_private_t * dev_priv, unsigned int size) @@ -98,7 +47,7 @@ via_cmdbuf_wait(drm_via_private_t * dev_priv, unsigned int size) uint32_t count; hw_addr_ptr = dev_priv->hw_addr_ptr; cur_addr = dev_priv->dma_low; - next_addr = cur_addr + size; + next_addr = cur_addr + size + 512*1024; count = 1000000; /* How long is this? */ do { hw_addr = *hw_addr_ptr - agp_base; @@ -142,6 +91,7 @@ int via_dma_cleanup(drm_device_t * dev) drm_core_ioremapfree(&dev_priv->ring.map, dev); dev_priv->ring.virtual_start = NULL; } + } return 0; @@ -225,17 +175,32 @@ static int via_dispatch_cmdbuffer(drm_device_t * dev, drm_via_cmdbuffer_t * cmd) uint32_t *vb; int ret; + + if (cmd->size > pci_bufsiz && pci_bufsiz > 0) { + return DRM_ERR(ENOMEM); + } + vb = via_check_dma(dev_priv, cmd->size); if (vb == NULL) { return DRM_ERR(EAGAIN); } - if (DRM_COPY_FROM_USER(vb, cmd->buf, cmd->size)) { + + if (DRM_COPY_FROM_USER(pci_buf, cmd->buf, cmd->size)) return DRM_ERR(EFAULT); - } - if ((ret = via_check_command_stream(vb, cmd->size))) - return ret; + /* + * Running this function on AGP memory is dead slow. Therefore + * we run it on a temporary cacheable system memory buffer and + * copy it to AGP memory when ready. + */ + + if ((ret = via_verify_command_stream((uint32_t *)pci_buf, cmd->size, dev))) { + return ret; + } + + memcpy(vb, pci_buf, cmd->size); + dev_priv->dma_low += cmd->size; via_cmdbuf_pause(dev_priv); @@ -286,12 +251,12 @@ static int via_parse_pci_cmdbuffer(drm_device_t * dev, const char *buf, unsigned int size) { drm_via_private_t *dev_priv = dev->dev_private; - const uint32_t *regbuf = (uint32_t *) buf; + const uint32_t *regbuf = (const uint32_t *) buf; const uint32_t *regend = regbuf + (size >> 2); int ret; int check_2d_cmd = 1; - if ((ret = via_check_command_stream(regbuf, size))) + if ((ret = via_verify_command_stream(regbuf, size, dev))) return ret; while (regbuf != regend) { @@ -455,7 +420,7 @@ static int via_hook_segment(drm_via_private_t *dev_priv, -static int via_wait_idle(drm_via_private_t * dev_priv) +int via_wait_idle(drm_via_private_t * dev_priv) { int count = 10000000; while (count-- && (VIA_READ(VIA_REG_STATUS) & @@ -466,14 +431,16 @@ static int via_wait_idle(drm_via_private_t * dev_priv) static uint32_t *via_align_cmd(drm_via_private_t * dev_priv, uint32_t cmd_type, uint32_t addr, uint32_t *cmd_addr_hi, - uint32_t *cmd_addr_lo) + uint32_t *cmd_addr_lo, + int skip_wait) { uint32_t agp_base; uint32_t cmd_addr, addr_lo, addr_hi; uint32_t *vb; uint32_t qw_pad_count; - via_cmdbuf_wait(dev_priv, 2*CMDBUF_ALIGNMENT_SIZE); + if (!skip_wait) + via_cmdbuf_wait(dev_priv, 2*CMDBUF_ALIGNMENT_SIZE); vb = via_get_dma(dev_priv); VIA_OUT_RING_QW( HC_HEADER2 | ((VIA_REG_TRANSET >> 2) << 12) | @@ -520,7 +487,7 @@ static void via_cmdbuf_start(drm_via_private_t * dev_priv) dev_priv->last_pause_ptr = via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, - &pause_addr_hi, & pause_addr_lo) - 1; + &pause_addr_hi, & pause_addr_lo, 1) - 1; via_flush_write_combine(); while(! *dev_priv->last_pause_ptr); @@ -553,7 +520,7 @@ static void via_cmdbuf_jump(drm_via_private_t * dev_priv) agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; via_align_cmd(dev_priv, HC_HAGPBpID_JUMP, 0, &jump_addr_hi, - &jump_addr_lo); + &jump_addr_lo, 0); dev_priv->dma_low = 0; if (via_cmdbuf_wait(dev_priv, CMDBUF_ALIGNMENT_SIZE) != 0) { @@ -569,7 +536,7 @@ static void via_cmdbuf_jump(drm_via_private_t * dev_priv) via_dummy_bitblt(dev_priv); via_dummy_bitblt(dev_priv); last_pause_ptr = via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, - &pause_addr_lo) -1; + &pause_addr_lo, 0) -1; /* * The regulator may still be suffering from the shock of the jump. @@ -578,13 +545,12 @@ static void via_cmdbuf_jump(drm_via_private_t * dev_priv) */ via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, - &pause_addr_lo); + &pause_addr_lo, 0); *last_pause_ptr = pause_addr_lo; via_hook_segment( dev_priv, jump_addr_hi, jump_addr_lo, 0); } - static void via_cmdbuf_rewind(drm_via_private_t * dev_priv) { via_cmdbuf_jump(dev_priv); @@ -594,7 +560,7 @@ static void via_cmdbuf_flush(drm_via_private_t * dev_priv, uint32_t cmd_type) { uint32_t pause_addr_lo, pause_addr_hi; - via_align_cmd(dev_priv, cmd_type, 0, &pause_addr_hi, &pause_addr_lo); + via_align_cmd(dev_priv, cmd_type, 0, &pause_addr_hi, &pause_addr_lo, 0); via_hook_segment( dev_priv, pause_addr_hi, pause_addr_lo, 0); } |