diff options
-rw-r--r-- | shared/via.h | 6 | ||||
-rw-r--r-- | shared/via_3d_reg.h | 5 | ||||
-rw-r--r-- | shared/via_dma.c | 6 | ||||
-rw-r--r-- | shared/via_drv.h | 1 | ||||
-rw-r--r-- | shared/via_irq.c | 46 | ||||
-rw-r--r-- | shared/via_mm.c | 25 | ||||
-rw-r--r-- | shared/via_verifier.c | 239 |
7 files changed, 262 insertions, 66 deletions
diff --git a/shared/via.h b/shared/via.h index 1e253771..703c6f3c 100644 --- a/shared/via.h +++ b/shared/via.h @@ -30,11 +30,11 @@ #define DRIVER_NAME "via" #define DRIVER_DESC "VIA Unichrome" -#define DRIVER_DATE "20041231" +#define DRIVER_DATE "20050106" #define DRIVER_MAJOR 2 -#define DRIVER_MINOR 3 -#define DRIVER_PATCHLEVEL 4 +#define DRIVER_MINOR 4 +#define DRIVER_PATCHLEVEL 1 #define DRIVER_IOCTLS \ [DRM_IOCTL_NR(DRM_IOCTL_VIA_ALLOCMEM)] = { via_mem_alloc, 1, 0 }, \ diff --git a/shared/via_3d_reg.h b/shared/via_3d_reg.h index 90010cdc..f78fd2d1 100644 --- a/shared/via_3d_reg.h +++ b/shared/via_3d_reg.h @@ -1643,4 +1643,9 @@ #define HC_HAGPBpID_STOP 0x00000002 #define HC_HAGPBpH_MASK 0x00ffffff + +#define VIA_VIDEO_HEADER5 0xFE040000 +#define VIA_VIDEO_HEADER6 0xFE050000 +#define VIA_VIDEO_HEADER7 0xFE060000 +#define VIA_VIDEOMASK 0xFFFF0000 #endif diff --git a/shared/via_dma.c b/shared/via_dma.c index c559f931..ef789878 100644 --- a/shared/via_dma.c +++ b/shared/via_dma.c @@ -299,12 +299,12 @@ static int via_dispatch_cmdbuffer(drm_device_t * dev, drm_via_cmdbuffer_t * cmd) return 0; } -static int via_quiescent(drm_device_t * dev) +int via_driver_dma_quiescent(drm_device_t * dev) { drm_via_private_t *dev_priv = dev->dev_private; if (!via_wait_idle(dev_priv)) { - return DRM_ERR(EAGAIN); + return DRM_ERR(EBUSY); } return 0; } @@ -315,7 +315,7 @@ int via_flush_ioctl(DRM_IOCTL_ARGS) LOCK_TEST_WITH_RETURN( dev, filp ); - return via_quiescent(dev); + return via_driver_dma_quiescent(dev); } int via_cmdbuffer(DRM_IOCTL_ARGS) diff --git a/shared/via_drv.h b/shared/via_drv.h index 83686474..4423a7cf 100644 --- a/shared/via_drv.h +++ b/shared/via_drv.h @@ -75,6 +75,7 @@ extern void via_driver_irq_uninstall(drm_device_t * dev); extern int via_dma_cleanup(drm_device_t * dev); extern int via_wait_idle(drm_via_private_t * dev_priv); +extern int via_driver_dma_quiescent(drm_device_t * dev); extern void via_init_command_verifier( void ); extern int via_verify_command_stream(const uint32_t * buf, unsigned int size, drm_device_t *dev); diff --git a/shared/via_irq.c b/shared/via_irq.c index a58717ee..4d62c69a 100644 --- a/shared/via_irq.c +++ b/shared/via_irq.c @@ -44,53 +44,37 @@ /* VIA_REG_INTERRUPT */ #define VIA_IRQ_GLOBAL (1 << 31) #define VIA_IRQ_VBI_ENABLE (1 << 19) +#define VIA_IRQ_SEC_VBI_ENABLE (1 << 17) +#define VIA_IRQ_SEC_VBI_PENDING (1 << 15) #define VIA_IRQ_VBI_PENDING (1 << 3) -static unsigned time_diff(struct timeval *now,struct timeval *then) -{ - return (now->tv_usec >= then->tv_usec) ? - now->tv_usec - then->tv_usec : - 1000000 - (then->tv_usec - now->tv_usec); -} - irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS) { drm_device_t *dev = (drm_device_t *) arg; drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - u32 status; - int handled = 0; - struct timeval cur_vblank; + int handled = IRQ_NONE; + u32 status = VIA_READ(VIA_REG_INTERRUPT); - status = VIA_READ(VIA_REG_INTERRUPT); if (status & VIA_IRQ_VBI_PENDING) { atomic_inc(&dev->vbl_received); - if (!(atomic_read(&dev->vbl_received) & 0x0F)) { - do_gettimeofday(&cur_vblank); - if (dev_priv->last_vblank_valid) { - dev_priv->usec_per_vblank = - time_diff( &cur_vblank,&dev_priv->last_vblank) >> 4; - } - dev_priv->last_vblank = cur_vblank; - dev_priv->last_vblank_valid = 1; - } - if (!(atomic_read(&dev->vbl_received) & 0xFF)) { - DRM_DEBUG("US per vblank is: %u\n", - dev_priv->usec_per_vblank); - } DRM_WAKEUP(&dev->vbl_queue); DRM(vbl_send_signals) (dev); - handled = 1; + handled = IRQ_HANDLED; } - /* Acknowlege interrupts ?? */ - VIA_WRITE(VIA_REG_INTERRUPT, status); +#if 0 + if (status & VIA_IRQ_SEC_VBI_PENDING) { + atomic_inc(&dev->sec_vbl_received); + DRM_WAKEUP(&dev->sec_vbl_queue); + DRM(vbl_send_signals)(dev); /* KW: Need a parameter here? */ + handled = IRQ_HANDLED; + } +#endif - if (handled) - return IRQ_HANDLED; - else - return IRQ_NONE; + VIA_WRITE(VIA_REG_INTERRUPT, status); + return handled; } static __inline__ void viadrv_acknowledge_irqs(drm_via_private_t * dev_priv) diff --git a/shared/via_mm.c b/shared/via_mm.c index 79761caf..c6271367 100644 --- a/shared/via_mm.c +++ b/shared/via_mm.c @@ -131,8 +131,12 @@ int via_init_context(struct drm_device *dev, int context) } int via_final_context(struct drm_device *dev, int context) -{ - int i; +{ + int i; + volatile int *lock; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + drm_via_sarea_t *sAPriv = dev_priv->sarea_priv; + for (i = 0; i < MAX_CONTEXT; i++) if (global_ppriv[i].used && (global_ppriv[i].context == context)) @@ -167,6 +171,22 @@ int via_final_context(struct drm_device *dev, int context) global_ppriv[i].used = 0; } + + /* + * Release futex locks. + */ + + for (i=0; i < VIA_NR_XVMC_LOCKS; ++i) { + lock = XVMCLOCKPTR(sAPriv, i); + if ( (_DRM_LOCKING_CONTEXT( *lock ) == i) && + (_DRM_LOCK_IS_HELD( *lock ))) { + if ( *lock & _DRM_LOCK_CONT) { + DRM_WAKEUP( &(dev_priv->decoder_queue[i])); + } + *lock &= ~( _DRM_LOCK_HELD | _DRM_LOCK_CONT ); + } + } + #if defined(__linux__) /* Linux specific until context tracking code gets ported to BSD */ /* Last context, perform cleanup */ @@ -362,4 +382,5 @@ void DRM(driver_register_fns) (drm_device_t * dev) { dev->fn_tbl.irq_postinstall = via_driver_irq_postinstall; dev->fn_tbl.irq_uninstall = via_driver_irq_uninstall; dev->fn_tbl.irq_handler = via_driver_irq_handler; + dev->fn_tbl.dma_quiescent = via_driver_dma_quiescent; } diff --git a/shared/via_verifier.c b/shared/via_verifier.c index 355a64ff..1af4ea4c 100644 --- a/shared/via_verifier.c +++ b/shared/via_verifier.c @@ -36,6 +36,8 @@ typedef enum{ state_command, state_header2, state_header1, + state_vheader5, + state_vheader6, state_error } verifier_state_t; @@ -72,6 +74,8 @@ typedef enum{ check_texture_addr7, check_texture_addr8, check_texture_addr_mode, + check_for_vertex_count, + check_number_texunits, forbidden_command }hazard_t; @@ -165,7 +169,7 @@ static hz_init_t init_table1[] = { {0x7A, no_check}, {0x7B, no_check}, {0x7C, no_check}, - {0x7D, no_check} + {0x7D, check_for_vertex_count} }; @@ -232,7 +236,7 @@ static hz_init_t init_table3[] = { {0xf2, check_for_header2_err}, {0xf0, check_for_header1_err}, {0xcc, check_for_dummy}, - {0x00, no_check} + {0x00, check_number_texunits} }; @@ -254,8 +258,10 @@ typedef struct{ uint32_t tex_palette_size[2]; sequence_t unfinished; int agp_texture; + int multitex; drm_device_t *dev; drm_map_t *map_cache; + uint32_t vertex_count; } sequence_context_t; static sequence_context_t hc_sequence; @@ -497,6 +503,12 @@ investigate_hazard( uint32_t cmd, hazard_t hz, sequence_context_t *cur_seq) cur_seq->tex_palette_size[cur_seq->texture] = (cmd >> 16) & 0x000000007; return 0; + case check_for_vertex_count: + cur_seq->vertex_count = cmd & 0x0000FFFF; + return 0; + case check_number_texunits: + cur_seq->multitex = (cmd >> 3) & 1; + return 0; default: DRM_ERROR("Illegal DMA data: 0x%x\n", cmd); return 2; @@ -505,6 +517,84 @@ investigate_hazard( uint32_t cmd, hazard_t hz, sequence_context_t *cur_seq) } +static __inline__ int +via_check_prim_list(uint32_t const **buffer, const uint32_t *buf_end, + sequence_context_t *cur_seq) +{ + uint32_t a_fire, bcmd , dw_count; + int ret = 0; + int have_fire; + const uint32_t *buf = *buffer; + + while(buf < buf_end) { + have_fire = 0; + if ((buf_end - buf) < 2) { + DRM_ERROR("Unexpected termination of primitive list.\n"); + ret = 1; + break; + } + if ((*buf & HC_ACMD_MASK) != HC_ACMD_HCmdB) break; + bcmd = *buf++; + if ((*buf & HC_ACMD_MASK) != HC_ACMD_HCmdA) { + DRM_ERROR("Expected Vertex List A command, got 0x%x\n", + *buf); + ret = 1; + break; + } + a_fire = *buf++ | HC_HPLEND_MASK | HC_HPMValidN_MASK | HC_HE3Fire_MASK; + + /* + * How many dwords per vertex ? + */ + + if ((bcmd & (0xF << 11)) == 0) { + DRM_ERROR("Illegal B command vertex data for AGP.\n"); + ret = 1; + break; + } + + dw_count = 0; + if (bcmd & (1 << 7)) dw_count += (cur_seq->multitex) ? 2:1; + if (bcmd & (1 << 8)) dw_count += (cur_seq->multitex) ? 2:1; + if (bcmd & (1 << 9)) dw_count++; + if (bcmd & (1 << 10)) dw_count++; + if (bcmd & (1 << 11)) dw_count++; + if (bcmd & (1 << 12)) dw_count++; + if (bcmd & (1 << 13)) dw_count++; + if (bcmd & (1 << 14)) dw_count++; + + while(buf < buf_end) { + if (*buf == HALCYON_HEADER2) { + DRM_ERROR("Missing Vertex Fire command or verifier " + "lost sync.\n"); + ret = 1; + break; + } + if (*buf == a_fire) { + have_fire = 1; + buf++; + if (buf < buf_end && *buf == a_fire) + buf++; + break; + } + if ((ret = eat_words(&buf, buf_end, dw_count))) + break; + } + if (buf >= buf_end && !have_fire) { + DRM_ERROR("Missing Vertex Fire command or verifier " + "lost sync.\n"); + ret = 1; + break; + } + } + *buffer = buf; + return ret; +} + + + + + static __inline__ verifier_state_t via_check_header2( uint32_t const **buffer, const uint32_t *buf_end ) { @@ -514,6 +604,7 @@ via_check_header2( uint32_t const **buffer, const uint32_t *buf_end ) const uint32_t *buf = *buffer; const hazard_t *hz_table; + if ((buf_end - buf) < 2) { DRM_ERROR("Illegal termination of DMA HALCYON_HEADER2 sequence.\n"); return state_error; @@ -523,32 +614,10 @@ via_check_header2( uint32_t const **buffer, const uint32_t *buf_end ) switch(cmd) { case HC_ParaType_CmdVdata: - - /* - * Command vertex data. - * It is assumed that the command regulator remains in this state - * until it encounters a possibly double fire command or a header2 data. - * FIXME: Vertex data can accidently be header2 or fire. - * CHECK: What does the regulator do if it encounters a header1 - * cmd? - */ - - while (buf < buf_end) { - if (*buf == HALCYON_HEADER2) break; - if ((*buf & HALCYON_FIREMASK) == HALCYON_FIRECMD) { - buf++; - if ((buf < buf_end) && - ((*buf & HALCYON_FIREMASK) == HALCYON_FIRECMD)) - buf++; - if ((buf < buf_end) && - ((*buf & HALCYON_FIREMASK) == HALCYON_FIRECMD)) - break; - } - buf++; - } + if (via_check_prim_list(&buf, buf_end, &hc_sequence )) + return state_error; *buffer = buf; return state_command; - case HC_ParaType_NotTex: hz_table = table1; break; @@ -590,7 +659,8 @@ via_check_header2( uint32_t const **buffer, const uint32_t *buf_end ) */ DRM_ERROR("Invalid or unimplemented HALCYON_HEADER2 " - "DMA subcommand: 0x%x\n", cmd); + "DMA subcommand: 0x%x. Previous dword: 0x%x\n", + cmd, *(buf -2)); *buffer = buf; return state_error; } @@ -618,6 +688,41 @@ via_check_header2( uint32_t const **buffer, const uint32_t *buf_end ) } +static __inline__ int +verify_mmio_address( uint32_t address) +{ + if ((address > 0x3FF) && (address < 0xC00 )) { + DRM_ERROR("Invalid HALCYON_HEADER1 command. " + "Attempt to access 3D- or command burst area.\n"); + return 1; + } else if (address > 0xCFF ) { + DRM_ERROR("Invalid HALCYON_HEADER1 command. " + "Attempt to access VGA registers.\n"); + return 1; + } + return 0; +} + +static __inline__ int +verify_video_tail( uint32_t const **buffer, const uint32_t *buf_end, uint32_t dwords) +{ + const uint32_t *buf = *buffer; + + if (buf_end - buf < dwords) { + DRM_ERROR("Illegal termination of video command.\n"); + return 1; + } + while (dwords--) { + if (*buf++) { + DRM_ERROR("Illegal video command tail.\n"); + return 1; + } + } + *buffer = buf; + return 0; +} + + static __inline__ verifier_state_t via_check_header1( uint32_t const **buffer, const uint32_t *buf_end ) { @@ -650,6 +755,75 @@ via_check_header1( uint32_t const **buffer, const uint32_t *buf_end ) return ret; } +static __inline__ verifier_state_t +via_check_vheader5( uint32_t const **buffer, const uint32_t *buf_end ) +{ + uint32_t data; + const uint32_t *buf = *buffer; + + if (buf_end - buf < 4) { + DRM_ERROR("Illegal termination of video header5 command\n"); + return state_error; + } + + data = *buf++ & ~VIA_VIDEOMASK; + if (verify_mmio_address(data)) + return state_error; + + data = *buf++; + if (*buf++ != 0x00F50000) { + DRM_ERROR("Illegal header5 header data\n"); + return state_error; + } + if (*buf++ != 0x00000000) { + DRM_ERROR("Illegal header5 header data\n"); + return state_error; + } + if (eat_words(&buf, buf_end, data)) + return state_error; + if (verify_video_tail(&buf, buf_end, 4 - (data & 3))) + return state_error; + *buffer = buf; + return state_command; + +} + +static __inline__ verifier_state_t +via_check_vheader6( uint32_t const **buffer, const uint32_t *buf_end ) +{ + uint32_t data; + const uint32_t *buf = *buffer; + uint32_t i; + + DRM_ERROR("H6\n"); + + if (buf_end - buf < 4) { + DRM_ERROR("Illegal termination of video header6 command\n"); + return state_error; + } + + data = *buf++; + if (*buf++ != 0x00F60000) { + DRM_ERROR("Illegal header6 header data\n"); + return state_error; + } + if (*buf++ != 0x00000000) { + DRM_ERROR("Illegal header6 header data\n"); + return state_error; + } + if ((buf_end - buf) < (data << 1)) { + DRM_ERROR("Illegal termination of video header6 command\n"); + } + for (i=0; i<data; ++i) { + if (verify_mmio_address(*buf++)) + return state_error; + buf++; + } + if (verify_video_tail(&buf, buf_end, 4 - ((data << 1) & 3))) + return state_error; + *buffer = buf; + return state_command; +} int @@ -665,6 +839,7 @@ via_verify_command_stream(const uint32_t * buf, unsigned int size, drm_device_t hc_sequence.map_cache = NULL; while (buf < buf_end) { + switch (state) { case state_header2: state = via_check_header2( &buf, buf_end ); @@ -672,11 +847,21 @@ via_verify_command_stream(const uint32_t * buf, unsigned int size, drm_device_t case state_header1: state = via_check_header1( &buf, buf_end ); break; + case state_vheader5: + state = via_check_vheader5( &buf, buf_end ); + break; + case state_vheader6: + state = via_check_vheader6( &buf, buf_end ); + break; case state_command: if (HALCYON_HEADER2 == (cmd = *buf)) state = state_header2; else if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1) state = state_header1; + else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5) + state = state_vheader5; + else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6) + state = state_vheader6; else { DRM_ERROR("Invalid / Unimplemented DMA HEADER command. 0x%x\n", cmd); |