From aff0a2548561ecbe3411b57cd31f46cbb1b4f6b8 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Mon, 13 Dec 2004 13:53:12 +0000 Subject: VIA drm updates: 1. Improved security check of AGP texture adresses. 2. Hopefully last fix of ring-buffer jump oddities. 3. Added ioctl to check available space and command regulator lag in ring-buffer. This is needed for 3D application responsiveness. --- shared-core/via_dma.c | 160 ++++++++++++++++++++++++++++++++++++++------ shared-core/via_drm.h | 48 +++++++------ shared-core/via_drv.c | 3 +- shared-core/via_drv.h | 7 +- shared-core/via_verifier.c | 32 ++++++--- shared/via.h | 24 ++++--- shared/via_dma.c | 163 ++++++++++++++++++++++++++++++++++++++------- shared/via_drm.h | 12 +++- shared/via_drv.h | 1 + shared/via_verifier.c | 32 ++++++--- 10 files changed, 373 insertions(+), 109 deletions(-) diff --git a/shared-core/via_dma.c b/shared-core/via_dma.c index ac7b7bea..4b894add 100644 --- a/shared-core/via_dma.c +++ b/shared-core/via_dma.c @@ -26,7 +26,7 @@ *vb++ = (w2); \ dev_priv->dma_low += 8; -#define PCI_BUF_SIZE 1024000 +#define PCI_BUF_SIZE 512000 static char pci_buf[PCI_BUF_SIZE]; static unsigned long pci_bufsiz = PCI_BUF_SIZE; @@ -37,6 +37,42 @@ 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); +/* + * Free space in command buffer. + */ + +static uint32_t +via_cmdbuf_space(drm_via_private_t *dev_priv) +{ + uint32_t agp_base = dev_priv->dma_offset + + (uint32_t) dev_priv->agpAddr; + uint32_t hw_addr = *(dev_priv->hw_addr_ptr) - agp_base; + + return ((hw_addr <= dev_priv->dma_low) ? + (dev_priv->dma_high + hw_addr - dev_priv->dma_low) : + (hw_addr - dev_priv->dma_low)); +} + +/* + * How much does the command regulator lag behind? + */ + +static uint32_t +via_cmdbuf_lag(drm_via_private_t *dev_priv) +{ + uint32_t agp_base = dev_priv->dma_offset + + (uint32_t) dev_priv->agpAddr; + uint32_t hw_addr = *(dev_priv->hw_addr_ptr) - agp_base; + + return ((hw_addr <= dev_priv->dma_low) ? + (dev_priv->dma_low - hw_addr) : + (dev_priv->dma_wrap + dev_priv->dma_low - hw_addr)); +} + +/* + * Check that the given size fits in the buffer, otherwise wait. + */ + static inline int via_cmdbuf_wait(drm_via_private_t * dev_priv, unsigned int size) { @@ -46,8 +82,8 @@ 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 + 512*1024; - count = 1000000; /* How long is this? */ + next_addr = cur_addr + size; + count = 1000000; do { hw_addr = *hw_addr_ptr - agp_base; if (count-- == 0) { @@ -59,12 +95,14 @@ via_cmdbuf_wait(drm_via_private_t * dev_priv, unsigned int size) return 0; } + /* * Checks whether buffer head has reach the end. Rewind the ring buffer * when necessary. * * Returns virtual pointer to ring buffer. */ + static inline uint32_t *via_check_dma(drm_via_private_t * dev_priv, unsigned int size) { @@ -131,6 +169,7 @@ static int via_initialize(drm_device_t * dev, dev_priv->dma_ptr = dev_priv->ring.virtual_start; dev_priv->dma_low = 0; dev_priv->dma_high = init->size; + dev_priv->dma_wrap = init->size; dev_priv->dma_offset = init->offset; dev_priv->last_pause_ptr = NULL; dev_priv->hw_addr_ptr = dev_priv->mmio->handle + init->reg_pause_addr; @@ -158,7 +197,7 @@ int via_dma_init(DRM_IOCTL_ARGS) retcode = via_dma_cleanup(dev); break; case VIA_DMA_INITIALIZED: - retcode = (dev_priv->ring.virtual_start != NULL) ? 0: DRM_ERR( EFAULT ); + retcode = (dev_priv->ring.virtual_start != NULL) ? 0: DRM_ERR( EFAULT ); break; default: retcode = DRM_ERR(EINVAL); @@ -348,11 +387,11 @@ int via_pci_cmdbuffer(DRM_IOCTL_ARGS) #define VIA_3D_ENG_BUSY 0x00000002 /* 3D Engine is busy */ #define VIA_VR_QUEUE_BUSY 0x00020000 /* Virtual Queue is busy */ -#define SetReg2DAGP(nReg, nData) { \ - *((uint32_t *)(vb)) = ((nReg) >> 2) | HALCYON_HEADER1; \ - *((uint32_t *)(vb) + 1) = (nData); \ - vb = ((uint32_t *)vb) + 2; \ - dev_priv->dma_low +=8; \ +#define SetReg2DAGP(nReg, nData) { \ + *((uint32_t *)(vb)) = ((nReg) >> 2) | HALCYON_HEADER1; \ + *((uint32_t *)(vb) + 1) = (nData); \ + vb = ((uint32_t *)vb) + 2; \ + dev_priv->dma_low +=8; \ } static inline uint32_t *via_align_buffer(drm_via_private_t * dev_priv, @@ -447,7 +486,7 @@ static uint32_t *via_align_cmd(drm_via_private_t * dev_priv, uint32_t cmd_type, uint32_t qw_pad_count; if (!skip_wait) - via_cmdbuf_wait(dev_priv, 2*CMDBUF_ALIGNMENT_SIZE); + 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) | @@ -509,6 +548,7 @@ static void via_cmdbuf_start(drm_via_private_t * dev_priv) VIA_WRITE(VIA_REG_TRANSPACE, command | HC_HAGPCMNT_MASK); } + static inline void via_dummy_bitblt(drm_via_private_t * dev_priv) { uint32_t *vb = via_get_dma(dev_priv); @@ -524,37 +564,53 @@ static void via_cmdbuf_jump(drm_via_private_t * dev_priv) uint32_t pause_addr_lo, pause_addr_hi; uint32_t jump_addr_lo, jump_addr_hi; volatile uint32_t *last_pause_ptr; + uint32_t dma_low_save1, dma_low_save2; 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, 0); + dev_priv->dma_wrap = dev_priv->dma_low; + + + /* + * Wrap command buffer to the beginning. + */ + dev_priv->dma_low = 0; if (via_cmdbuf_wait(dev_priv, CMDBUF_ALIGNMENT_SIZE) != 0) { DRM_ERROR("via_cmdbuf_jump failed\n"); } - /* - * The command regulator needs to stall for a while since it probably - * had a concussion during the jump... It will stall at the second - * bitblt since the 2D engine is busy with the first. - */ - 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, 0) -1; + via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, + &pause_addr_lo, 0); + + *last_pause_ptr = pause_addr_lo; + dma_low_save1 = dev_priv->dma_low; /* - * The regulator may still be suffering from the shock of the jump. - * Add another pause command to make sure it really will get itself together - * and pause. + * Now, set a trap that will pause the regulator if it tries to rerun the old + * command buffer. (Which may happen if via_hook_segment detecs a command regulator pause + * and reissues the jump command over PCI, while the regulator has already taken the jump + * and actually paused at the current buffer end). + * There appears to be no other way to detect this condition, since the hw_addr_pointer + * does not seem to get updated immediately when a jump occurs. */ + last_pause_ptr = via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, + &pause_addr_lo, 0) -1; via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, &pause_addr_lo, 0); - *last_pause_ptr = pause_addr_lo; + *last_pause_ptr = pause_addr_lo; + + dma_low_save2 = dev_priv->dma_low; + dev_priv->dma_low = dma_low_save1; via_hook_segment( dev_priv, jump_addr_hi, jump_addr_lo, 0); + dev_priv->dma_low = dma_low_save2; + via_hook_segment( dev_priv, pause_addr_hi, pause_addr_lo, 0); } @@ -583,4 +639,64 @@ static void via_cmdbuf_reset(drm_via_private_t * dev_priv) via_wait_idle(dev_priv); } -/************************************************************************/ +/* + * User interface to the space and lag function. + */ + +int +via_cmdbuf_size(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_via_cmdbuf_size_t d_siz; + int ret = 0; + uint32_t tmp_size, count; + drm_via_private_t *dev_priv; + + DRM_DEBUG("via cmdbuf_size\n"); + LOCK_TEST_WITH_RETURN( dev, filp ); + + dev_priv = (drm_via_private_t *) dev->dev_private; + + if (dev_priv->ring.virtual_start == NULL) { + DRM_ERROR("%s called without initializing AGP ring buffer.\n", + __FUNCTION__); + return DRM_ERR(EFAULT); + } + + DRM_COPY_FROM_USER_IOCTL(d_siz, (drm_via_cmdbuffer_t *) data, + sizeof(d_siz)); + + + count = 1000000; + tmp_size = d_siz.size; + switch(d_siz.func) { + case VIA_CMDBUF_SPACE: + while (count-- && ((tmp_size = via_cmdbuf_space(dev_priv)) < d_siz.size)) { + if (!d_siz.wait) { + break; + } + } + if (!count) { + DRM_ERROR("VIA_CMDBUF_SPACE timed out.\n"); + ret = DRM_ERR(EAGAIN); + } + break; + case VIA_CMDBUF_LAG: + while (count-- && ((tmp_size = via_cmdbuf_lag(dev_priv)) > d_siz.size)) { + if (!d_siz.wait) { + break; + } + } + if (!count) { + DRM_ERROR("VIA_CMDBUF_SPACE timed out.\n"); + ret = DRM_ERR(EAGAIN); + } + break; + default: + return DRM_ERR(EFAULT); + } + d_siz.size = tmp_size; + DRM_COPY_TO_USER_IOCTL((drm_via_cmdbuffer_t *) data, d_siz, + sizeof(d_siz)); + return ret; +} diff --git a/shared-core/via_drm.h b/shared-core/via_drm.h index 142b8dac..b4c11dab 100644 --- a/shared-core/via_drm.h +++ b/shared-core/via_drm.h @@ -71,17 +71,20 @@ #define DRM_VIA_CMDBUFFER 0x08 #define DRM_VIA_FLUSH 0x09 #define DRM_VIA_PCICMD 0x0a - -#define DRM_IOCTL_VIA_ALLOCMEM DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_ALLOCMEM, drm_via_mem_t) -#define DRM_IOCTL_VIA_FREEMEM DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_FREEMEM, drm_via_mem_t) -#define DRM_IOCTL_VIA_AGP_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_AGP_INIT, drm_via_agp_t) -#define DRM_IOCTL_VIA_FB_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_FB_INIT, drm_via_fb_t) -#define DRM_IOCTL_VIA_MAP_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_MAP_INIT, drm_via_init_t) -#define DRM_IOCTL_VIA_DEC_FUTEX DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_DEC_FUTEX, drm_via_futex_t) -#define DRM_IOCTL_VIA_DMA_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_DMA_INIT, drm_via_dma_init_t) -#define DRM_IOCTL_VIA_CMDBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_CMDBUFFER, drm_via_cmdbuffer_t) -#define DRM_IOCTL_VIA_FLUSH DRM_IO( DRM_COMMAND_BASE + DRM_VIA_FLUSH) -#define DRM_IOCTL_VIA_PCICMD DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_PCICMD, drm_via_cmdbuffer_t) +#define DRM_VIA_CMDBUF_SIZE 0x0b + +#define DRM_IOCTL_VIA_ALLOCMEM DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_ALLOCMEM, drm_via_mem_t) +#define DRM_IOCTL_VIA_FREEMEM DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_FREEMEM, drm_via_mem_t) +#define DRM_IOCTL_VIA_AGP_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_AGP_INIT, drm_via_agp_t) +#define DRM_IOCTL_VIA_FB_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_FB_INIT, drm_via_fb_t) +#define DRM_IOCTL_VIA_MAP_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_MAP_INIT, drm_via_init_t) +#define DRM_IOCTL_VIA_DEC_FUTEX DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_DEC_FUTEX, drm_via_futex_t) +#define DRM_IOCTL_VIA_DMA_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_DMA_INIT, drm_via_dma_init_t) +#define DRM_IOCTL_VIA_CMDBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_CMDBUFFER, drm_via_cmdbuffer_t) +#define DRM_IOCTL_VIA_FLUSH DRM_IO( DRM_COMMAND_BASE + DRM_VIA_FLUSH) +#define DRM_IOCTL_VIA_PCICMD DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_PCICMD, drm_via_cmdbuffer_t) +#define DRM_IOCTL_VIA_CMDBUF_SIZE DRM_IOWR( DRM_COMMAND_BASE + DRM_VIA_CMDBUF_SIZE, \ + drm_via_cmdbuf_size_t) /* Indices into buf.Setup where various bits of state are mirrored per * context and per buffer. These can be fired at the card as a unit, @@ -187,20 +190,14 @@ typedef struct _drm_via_sarea { } drm_via_sarea_t; -typedef struct _drm_via_flush_agp { - unsigned int offset; - unsigned int size; - unsigned int index; - int discard; /* client is finished with the buffer? */ -} drm_via_flush_agp_t; - -typedef struct _drm_via_flush_sys { - unsigned int offset; - unsigned int size; - unsigned long index; - int discard; /* client is finished with the buffer? */ -} drm_via_flush_sys_t; - +typedef struct _drm_via_cmdbuf_size { + enum { + VIA_CMDBUF_SPACE = 0x01, + VIA_CMDBUF_LAG = 0x02 + } func; + int wait; + uint32_t size; +} drm_via_cmdbuf_size_t; #ifdef __KERNEL__ @@ -215,6 +212,7 @@ int via_dma_init(DRM_IOCTL_ARGS); int via_cmdbuffer(DRM_IOCTL_ARGS); int via_flush_ioctl(DRM_IOCTL_ARGS); int via_pci_cmdbuffer(DRM_IOCTL_ARGS); +int via_cmdbuf_size(DRM_IOCTL_ARGS); #endif #endif /* _VIA_DRM_H_ */ diff --git a/shared-core/via_drv.c b/shared-core/via_drv.c index 59690600..16233380 100644 --- a/shared-core/via_drv.c +++ b/shared-core/via_drv.c @@ -68,7 +68,8 @@ static drm_ioctl_desc_t ioctls[] = { [DRM_IOCTL_NR(DRM_VIA_DMA_INIT)] = {via_dma_init, 1, 0}, [DRM_IOCTL_NR(DRM_VIA_CMDBUFFER)] = {via_cmdbuffer, 1, 0}, [DRM_IOCTL_NR(DRM_VIA_FLUSH)] = {via_flush_ioctl, 1, 0}, - [DRM_IOCTL_NR(DRM_VIA_PCICMD)] = {via_pci_cmdbuffer, 1, 0} + [DRM_IOCTL_NR(DRM_VIA_PCICMD)] = {via_pci_cmdbuffer, 1, 0}, + [DRM_IOCTL_NR(DRM_VIA_CMDBUF_SIZE)] = {via_cmdbuf_size, 1, 0} }; static int probe(struct pci_dev *pdev, const struct pci_device_id *ent); diff --git a/shared-core/via_drv.h b/shared-core/via_drv.h index e905a606..a23ee209 100644 --- a/shared-core/via_drv.h +++ b/shared-core/via_drv.h @@ -28,11 +28,11 @@ #define DRIVER_NAME "via" #define DRIVER_DESC "VIA Unichrome" -#define DRIVER_DATE "20041206" +#define DRIVER_DATE "20041212" #define DRIVER_MAJOR 2 -#define DRIVER_MINOR 2 -#define DRIVER_PATCHLEVEL 1 +#define DRIVER_MINOR 3 +#define DRIVER_PATCHLEVEL 0 typedef struct drm_via_ring_buffer { drm_map_t map; @@ -50,6 +50,7 @@ typedef struct drm_via_private { unsigned int dma_low; unsigned int dma_high; unsigned int dma_offset; + uint32_t dma_wrap; volatile uint32_t *last_pause_ptr; volatile uint32_t *hw_addr_ptr; drm_via_ring_buffer_t ring; diff --git a/shared-core/via_verifier.c b/shared-core/via_verifier.c index 1ce28c88..dac09e83 100644 --- a/shared-core/via_verifier.c +++ b/shared-core/via_verifier.c @@ -253,39 +253,48 @@ typedef struct{ sequence_t unfinished; int agp_texture; drm_device_t *dev; + drm_map_t *map_cache; } sequence_context_t; static sequence_context_t hc_sequence; /* - * stolen from drm_memory.h + * Partially stolen from drm_memory.h */ static __inline__ drm_map_t * -via_drm_lookup_map (unsigned long offset, unsigned long size, drm_device_t *dev) +via_drm_lookup_agp_map (sequence_context_t *seq, unsigned long offset, unsigned long size, + drm_device_t *dev) { struct list_head *list; drm_map_list_t *r_list; - drm_map_t *map; + drm_map_t *map = seq->map_cache; + if (map && map->offset <= offset && (offset + size) <= (map->offset + map->size)) { + return map; + } + list_for_each(list, &dev->maplist->head) { r_list = (drm_map_list_t *) list; map = r_list->map; if (!map) continue; - if (map->offset <= offset && (offset + size) <= (map->offset + map->size)) + if (map->offset <= offset && (offset + size) <= (map->offset + map->size) && + !(map->flags & _DRM_RESTRICTED) && (map->type == _DRM_AGP)) { + seq->map_cache = map; return map; + } } return NULL; } /* - * Require that all AGP texture levels reside in the same map which should - * be mapped by the client. This is not a big restriction. - * FIXME: To actually enforce this security policy strictly, drm_unmap - * would have to wait for dma quiescent before unmapping an AGP page. - * The via_drm_lookup_map call in reality seems to take + * Require that all AGP texture levels reside in the same AGP map which should + * be mappable by the client. This is not a big restriction. + * FIXME: To actually enforce this security policy strictly, drm_rmmap + * would have to wait for dma quiescent before removing an AGP map. + * The via_drm_lookup_agp_map call in reality seems to take * very little CPU time. */ @@ -322,8 +331,8 @@ finish_current_sequence(sequence_context_t *cur_seq) if (tmp > hi) hi = tmp; } - if (! via_drm_lookup_map (lo, hi - lo, cur_seq->dev)) { - DRM_ERROR("AGP texture is not in client map\n"); + if (! via_drm_lookup_agp_map (cur_seq, lo, hi - lo, cur_seq->dev)) { + DRM_ERROR("AGP texture is not in allowed map\n"); return 2; } } @@ -619,6 +628,7 @@ via_verify_command_stream(const uint32_t * buf, unsigned int size, drm_device_t hc_sequence.dev = dev; hc_sequence.unfinished = no_sequence; + hc_sequence.map_cache = NULL; while (buf < buf_end) { switch (state) { diff --git a/shared/via.h b/shared/via.h index 5a5091b6..c1e48a75 100644 --- a/shared/via.h +++ b/shared/via.h @@ -30,22 +30,24 @@ #define DRIVER_NAME "via" #define DRIVER_DESC "VIA Unichrome" -#define DRIVER_DATE "20041206" +#define DRIVER_DATE "20041212" #define DRIVER_MAJOR 2 -#define DRIVER_MINOR 2 -#define DRIVER_PATCHLEVEL 1 +#define DRIVER_MINOR 3 +#define DRIVER_PATCHLEVEL 0 #define DRIVER_IOCTLS \ [DRM_IOCTL_NR(DRM_IOCTL_VIA_ALLOCMEM)] = { via_mem_alloc, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_VIA_FREEMEM)] = { via_mem_free, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_VIA_AGP_INIT)] = { via_agp_init, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_VIA_FB_INIT)] = { via_fb_init, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_VIA_MAP_INIT)] = { via_map_init, 1, 0 }, \ - [DRM_IOCTL_NR(DRM_IOCTL_VIA_DEC_FUTEX)] = { via_decoder_futex, 1, 0}, \ - [DRM_IOCTL_NR(DRM_IOCTL_VIA_DMA_INIT)] = { via_dma_init, 1, 0}, \ - [DRM_IOCTL_NR(DRM_IOCTL_VIA_CMDBUFFER)] = { via_cmdbuffer, 1, 0}, \ + [DRM_IOCTL_NR(DRM_IOCTL_VIA_FREEMEM)] = { via_mem_free, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_VIA_AGP_INIT)] = { via_agp_init, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_VIA_FB_INIT)] = { via_fb_init, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_VIA_MAP_INIT)] = { via_map_init, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_VIA_DEC_FUTEX)] = { via_decoder_futex, 1, 0}, \ + [DRM_IOCTL_NR(DRM_IOCTL_VIA_DMA_INIT)] = { via_dma_init, 1, 0}, \ + [DRM_IOCTL_NR(DRM_IOCTL_VIA_CMDBUFFER)] = { via_cmdbuffer, 1, 0}, \ [DRM_IOCTL_NR(DRM_IOCTL_VIA_FLUSH)] = { via_flush_ioctl, 1, 0}, \ - [DRM_IOCTL_NR(DRM_IOCTL_VIA_PCICMD)] = { via_pci_cmdbuffer, 1, 0} + [DRM_IOCTL_NR(DRM_IOCTL_VIA_PCICMD)] = { via_pci_cmdbuffer, 1, 0}, \ + [DRM_IOCTL_NR(DRM_IOCTL_VIA_CMDBUF_SIZE)] = {via_cmdbuf_size, 1, 0} + #endif diff --git a/shared/via_dma.c b/shared/via_dma.c index cfb1ac38..1159f6d2 100644 --- a/shared/via_dma.c +++ b/shared/via_dma.c @@ -27,7 +27,7 @@ *vb++ = (w2); \ dev_priv->dma_low += 8; -#define PCI_BUF_SIZE 1024000 +#define PCI_BUF_SIZE 512000 static char pci_buf[PCI_BUF_SIZE]; static unsigned long pci_bufsiz = PCI_BUF_SIZE; @@ -38,6 +38,42 @@ 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); +/* + * Free space in command buffer. + */ + +static uint32_t +via_cmdbuf_space(drm_via_private_t *dev_priv) +{ + uint32_t agp_base = dev_priv->dma_offset + + (uint32_t) dev_priv->agpAddr; + uint32_t hw_addr = *(dev_priv->hw_addr_ptr) - agp_base; + + return ((hw_addr <= dev_priv->dma_low) ? + (dev_priv->dma_high + hw_addr - dev_priv->dma_low) : + (hw_addr - dev_priv->dma_low)); +} + +/* + * How much does the command regulator lag behind? + */ + +static uint32_t +via_cmdbuf_lag(drm_via_private_t *dev_priv) +{ + uint32_t agp_base = dev_priv->dma_offset + + (uint32_t) dev_priv->agpAddr; + uint32_t hw_addr = *(dev_priv->hw_addr_ptr) - agp_base; + + return ((hw_addr <= dev_priv->dma_low) ? + (dev_priv->dma_low - hw_addr) : + (dev_priv->dma_wrap + dev_priv->dma_low - hw_addr)); +} + +/* + * Check that the given size fits in the buffer, otherwise wait. + */ + static inline int via_cmdbuf_wait(drm_via_private_t * dev_priv, unsigned int size) { @@ -47,8 +83,8 @@ 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 + 512*1024; - count = 1000000; /* How long is this? */ + next_addr = cur_addr + size; + count = 1000000; do { hw_addr = *hw_addr_ptr - agp_base; if (count-- == 0) { @@ -60,12 +96,14 @@ via_cmdbuf_wait(drm_via_private_t * dev_priv, unsigned int size) return 0; } + /* * Checks whether buffer head has reach the end. Rewind the ring buffer * when necessary. * * Returns virtual pointer to ring buffer. */ + static inline uint32_t *via_check_dma(drm_via_private_t * dev_priv, unsigned int size) { @@ -132,6 +170,7 @@ static int via_initialize(drm_device_t * dev, dev_priv->dma_ptr = dev_priv->ring.virtual_start; dev_priv->dma_low = 0; dev_priv->dma_high = init->size; + dev_priv->dma_wrap = init->size; dev_priv->dma_offset = init->offset; dev_priv->last_pause_ptr = NULL; dev_priv->hw_addr_ptr = dev_priv->mmio->handle + init->reg_pause_addr; @@ -159,7 +198,7 @@ int via_dma_init(DRM_IOCTL_ARGS) retcode = via_dma_cleanup(dev); break; case VIA_DMA_INITIALIZED: - retcode = (dev_priv->ring.virtual_start != NULL) ? 0: DRM_ERR( EFAULT ); + retcode = (dev_priv->ring.virtual_start != NULL) ? 0: DRM_ERR( EFAULT ); break; default: retcode = DRM_ERR(EINVAL); @@ -171,11 +210,10 @@ int via_dma_init(DRM_IOCTL_ARGS) static int via_dispatch_cmdbuffer(drm_device_t * dev, drm_via_cmdbuffer_t * cmd) { - drm_via_private_t *dev_priv; + drm_via_private_t *dev_priv; uint32_t *vb; int ret; - dev_priv = (drm_via_private_t *) dev->dev_private; if (dev_priv->ring.virtual_start == NULL) { @@ -350,11 +388,11 @@ int via_pci_cmdbuffer(DRM_IOCTL_ARGS) #define VIA_3D_ENG_BUSY 0x00000002 /* 3D Engine is busy */ #define VIA_VR_QUEUE_BUSY 0x00020000 /* Virtual Queue is busy */ -#define SetReg2DAGP(nReg, nData) { \ - *((uint32_t *)(vb)) = ((nReg) >> 2) | HALCYON_HEADER1; \ - *((uint32_t *)(vb) + 1) = (nData); \ - vb = ((uint32_t *)vb) + 2; \ - dev_priv->dma_low +=8; \ +#define SetReg2DAGP(nReg, nData) { \ + *((uint32_t *)(vb)) = ((nReg) >> 2) | HALCYON_HEADER1; \ + *((uint32_t *)(vb) + 1) = (nData); \ + vb = ((uint32_t *)vb) + 2; \ + dev_priv->dma_low +=8; \ } static inline uint32_t *via_align_buffer(drm_via_private_t * dev_priv, @@ -449,7 +487,7 @@ static uint32_t *via_align_cmd(drm_via_private_t * dev_priv, uint32_t cmd_type, uint32_t qw_pad_count; if (!skip_wait) - via_cmdbuf_wait(dev_priv, 2*CMDBUF_ALIGNMENT_SIZE); + 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) | @@ -511,6 +549,7 @@ static void via_cmdbuf_start(drm_via_private_t * dev_priv) VIA_WRITE(VIA_REG_TRANSPACE, command | HC_HAGPCMNT_MASK); } + static inline void via_dummy_bitblt(drm_via_private_t * dev_priv) { uint32_t *vb = via_get_dma(dev_priv); @@ -526,37 +565,53 @@ static void via_cmdbuf_jump(drm_via_private_t * dev_priv) uint32_t pause_addr_lo, pause_addr_hi; uint32_t jump_addr_lo, jump_addr_hi; volatile uint32_t *last_pause_ptr; + uint32_t dma_low_save1, dma_low_save2; 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, 0); + dev_priv->dma_wrap = dev_priv->dma_low; + + + /* + * Wrap command buffer to the beginning. + */ + dev_priv->dma_low = 0; if (via_cmdbuf_wait(dev_priv, CMDBUF_ALIGNMENT_SIZE) != 0) { DRM_ERROR("via_cmdbuf_jump failed\n"); } - /* - * The command regulator needs to stall for a while since it probably - * had a concussion during the jump... It will stall at the second - * bitblt since the 2D engine is busy with the first. - */ - 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, 0) -1; + via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, + &pause_addr_lo, 0); + + *last_pause_ptr = pause_addr_lo; + dma_low_save1 = dev_priv->dma_low; /* - * The regulator may still be suffering from the shock of the jump. - * Add another pause command to make sure it really will get itself together - * and pause. + * Now, set a trap that will pause the regulator if it tries to rerun the old + * command buffer. (Which may happen if via_hook_segment detecs a command regulator pause + * and reissues the jump command over PCI, while the regulator has already taken the jump + * and actually paused at the current buffer end). + * There appears to be no other way to detect this condition, since the hw_addr_pointer + * does not seem to get updated immediately when a jump occurs. */ + last_pause_ptr = via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, + &pause_addr_lo, 0) -1; via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, &pause_addr_lo, 0); - *last_pause_ptr = pause_addr_lo; + *last_pause_ptr = pause_addr_lo; + + dma_low_save2 = dev_priv->dma_low; + dev_priv->dma_low = dma_low_save1; via_hook_segment( dev_priv, jump_addr_hi, jump_addr_lo, 0); + dev_priv->dma_low = dma_low_save2; + via_hook_segment( dev_priv, pause_addr_hi, pause_addr_lo, 0); } @@ -585,4 +640,64 @@ static void via_cmdbuf_reset(drm_via_private_t * dev_priv) via_wait_idle(dev_priv); } -/************************************************************************/ +/* + * User interface to the space and lag function. + */ + +int +via_cmdbuf_size(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_via_cmdbuf_size_t d_siz; + int ret = 0; + uint32_t tmp_size, count; + drm_via_private_t *dev_priv; + + DRM_DEBUG("via cmdbuf_size\n"); + LOCK_TEST_WITH_RETURN( dev, filp ); + + dev_priv = (drm_via_private_t *) dev->dev_private; + + if (dev_priv->ring.virtual_start == NULL) { + DRM_ERROR("%s called without initializing AGP ring buffer.\n", + __FUNCTION__); + return DRM_ERR(EFAULT); + } + + DRM_COPY_FROM_USER_IOCTL(d_siz, (drm_via_cmdbuffer_t *) data, + sizeof(d_siz)); + + + count = 1000000; + tmp_size = d_siz.size; + switch(d_siz.func) { + case VIA_CMDBUF_SPACE: + while (count-- && ((tmp_size = via_cmdbuf_space(dev_priv)) < d_siz.size)) { + if (!d_siz.wait) { + break; + } + } + if (!count) { + DRM_ERROR("VIA_CMDBUF_SPACE timed out.\n"); + ret = DRM_ERR(EAGAIN); + } + break; + case VIA_CMDBUF_LAG: + while (count-- && ((tmp_size = via_cmdbuf_lag(dev_priv)) > d_siz.size)) { + if (!d_siz.wait) { + break; + } + } + if (!count) { + DRM_ERROR("VIA_CMDBUF_SPACE timed out.\n"); + ret = DRM_ERR(EAGAIN); + } + break; + default: + return DRM_ERR(EFAULT); + } + d_siz.size = tmp_size; + DRM_COPY_TO_USER_IOCTL((drm_via_cmdbuffer_t *) data, d_siz, + sizeof(d_siz)); + return ret; +} diff --git a/shared/via_drm.h b/shared/via_drm.h index ee703c79..2327bec5 100644 --- a/shared/via_drm.h +++ b/shared/via_drm.h @@ -70,6 +70,8 @@ #define DRM_IOCTL_VIA_CMDBUFFER DRM_IOW(0x48, drm_via_cmdbuffer_t) #define DRM_IOCTL_VIA_FLUSH DRM_IO(0x49) #define DRM_IOCTL_VIA_PCICMD DRM_IOW(0x4A, drm_via_cmdbuffer_t) +#define DRM_IOCTL_VIA_CMDBUF_SIZE DRM_IOWR(0x4B, drm_via_cmdbuf_size_t) + /* Indices into buf.Setup where various bits of state are mirrored per * context and per buffer. These can be fired at the card as a unit, @@ -189,7 +191,14 @@ typedef struct _drm_via_flush_sys { int discard; /* client is finished with the buffer? */ } drm_via_flush_sys_t; - +typedef struct _drm_via_cmdbuf_size { + enum { + VIA_CMDBUF_SPACE = 0x01, + VIA_CMDBUF_LAG = 0x02 + } func; + int wait; + uint32_t size; +} drm_via_cmdbuf_size_t; #ifdef __KERNEL__ @@ -203,6 +212,7 @@ int via_dma_init(DRM_IOCTL_ARGS); int via_cmdbuffer(DRM_IOCTL_ARGS); int via_flush_ioctl(DRM_IOCTL_ARGS); int via_pci_cmdbuffer(DRM_IOCTL_ARGS); +int via_cmdbuf_size(DRM_IOCTL_ARGS); #endif #endif /* _VIA_DRM_H_ */ diff --git a/shared/via_drv.h b/shared/via_drv.h index 057956f0..83686474 100644 --- a/shared/via_drv.h +++ b/shared/via_drv.h @@ -42,6 +42,7 @@ typedef struct drm_via_private { unsigned int dma_low; unsigned int dma_high; unsigned int dma_offset; + uint32_t dma_wrap; volatile uint32_t *last_pause_ptr; volatile uint32_t *hw_addr_ptr; drm_via_ring_buffer_t ring, fb_blit; diff --git a/shared/via_verifier.c b/shared/via_verifier.c index e0f25546..f88c5451 100644 --- a/shared/via_verifier.c +++ b/shared/via_verifier.c @@ -254,39 +254,48 @@ typedef struct{ sequence_t unfinished; int agp_texture; drm_device_t *dev; + drm_map_t *map_cache; } sequence_context_t; static sequence_context_t hc_sequence; /* - * stolen from drm_memory.h + * Partially stolen from drm_memory.h */ static __inline__ drm_map_t * -via_drm_lookup_map (unsigned long offset, unsigned long size, drm_device_t *dev) +via_drm_lookup_agp_map (sequence_context_t *seq, unsigned long offset, unsigned long size, + drm_device_t *dev) { struct list_head *list; drm_map_list_t *r_list; - drm_map_t *map; + drm_map_t *map = seq->map_cache; + if (map && map->offset <= offset && (offset + size) <= (map->offset + map->size)) { + return map; + } + list_for_each(list, &dev->maplist->head) { r_list = (drm_map_list_t *) list; map = r_list->map; if (!map) continue; - if (map->offset <= offset && (offset + size) <= (map->offset + map->size)) + if (map->offset <= offset && (offset + size) <= (map->offset + map->size) && + !(map->flags & _DRM_RESTRICTED) && (map->type == _DRM_AGP)) { + seq->map_cache = map; return map; + } } return NULL; } /* - * Require that all AGP texture levels reside in the same map which should - * be mapped by the client. This is not a big restriction. - * FIXME: To actually enforce this security policy strictly, drm_unmap - * would have to wait for dma quiescent before unmapping an AGP page. - * The via_drm_lookup_map call in reality seems to take + * Require that all AGP texture levels reside in the same AGP map which should + * be mappable by the client. This is not a big restriction. + * FIXME: To actually enforce this security policy strictly, drm_rmmap + * would have to wait for dma quiescent before removing an AGP map. + * The via_drm_lookup_agp_map call in reality seems to take * very little CPU time. */ @@ -323,8 +332,8 @@ finish_current_sequence(sequence_context_t *cur_seq) if (tmp > hi) hi = tmp; } - if (! via_drm_lookup_map (lo, hi - lo, cur_seq->dev)) { - DRM_ERROR("AGP texture is not in client map\n"); + if (! via_drm_lookup_agp_map (cur_seq, lo, hi - lo, cur_seq->dev)) { + DRM_ERROR("AGP texture is not in allowed map\n"); return 2; } } @@ -620,6 +629,7 @@ via_verify_command_stream(const uint32_t * buf, unsigned int size, drm_device_t hc_sequence.dev = dev; hc_sequence.unfinished = no_sequence; + hc_sequence.map_cache = NULL; while (buf < buf_end) { switch (state) { -- cgit v1.2.3