2015-11-30 12:13:37 -08:00
/*
* Copyright © 2014 Broadcom
*
* 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 , sublicense ,
* 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 NONINFRINGEMENT . IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS 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 .
*/
/**
2017-02-27 12:11:41 -08:00
* DOC : Command list validator for VC4 .
2015-11-30 12:13:37 -08:00
*
2017-02-27 12:11:43 -08:00
* Since the VC4 has no IOMMU between it and system memory , a user
* with access to execute command lists could escalate privilege by
2015-11-30 12:13:37 -08:00
* overwriting system memory ( drawing to it as a framebuffer ) or
2017-02-27 12:11:43 -08:00
* reading system memory it shouldn ' t ( reading it as a vertex buffer
* or index buffer )
2015-11-30 12:13:37 -08:00
*
2017-02-27 12:11:43 -08:00
* We validate binner command lists to ensure that all accesses are
* within the bounds of the GEM objects referenced by the submitted
* job . It explicitly whitelists packets , and looks at the offsets in
* any address fields to make sure they ' re contained within the BOs
* they reference .
2015-11-30 12:13:37 -08:00
*
2017-02-27 12:11:43 -08:00
* Note that because CL validation is already reading the
* user - submitted CL and writing the validated copy out to the memory
* that the GPU will actually read , this is also where GEM relocation
* processing ( turning BO references into actual addresses for the GPU
* to use ) happens .
2015-11-30 12:13:37 -08:00
*/
# include "uapi/drm/vc4_drm.h"
# include "vc4_drv.h"
# include "vc4_packet.h"
# define VALIDATE_ARGS \
struct vc4_exec_info * exec , \
void * validated , \
void * untrusted
/** Return the width in pixels of a 64-byte microtile. */
static uint32_t
utile_width ( int cpp )
{
switch ( cpp ) {
case 1 :
case 2 :
return 8 ;
case 4 :
return 4 ;
case 8 :
return 2 ;
default :
DRM_ERROR ( " unknown cpp: %d \n " , cpp ) ;
return 1 ;
}
}
/** Return the height in pixels of a 64-byte microtile. */
static uint32_t
utile_height ( int cpp )
{
switch ( cpp ) {
case 1 :
return 8 ;
case 2 :
case 4 :
case 8 :
return 4 ;
default :
DRM_ERROR ( " unknown cpp: %d \n " , cpp ) ;
return 1 ;
}
}
/**
2017-02-27 12:11:41 -08:00
* size_is_lt ( ) - Returns whether a miplevel of the given size will
* use the lineartile ( LT ) tiling layout rather than the normal T
* tiling layout .
* @ width : Width in pixels of the miplevel
* @ height : Height in pixels of the miplevel
* @ cpp : Bytes per pixel of the pixel format
2015-11-30 12:13:37 -08:00
*/
static bool
size_is_lt ( uint32_t width , uint32_t height , int cpp )
{
return ( width < = 4 * utile_width ( cpp ) | |
height < = 4 * utile_height ( cpp ) ) ;
}
struct drm_gem_cma_object *
vc4_use_bo ( struct vc4_exec_info * exec , uint32_t hindex )
{
struct drm_gem_cma_object * obj ;
struct vc4_bo * bo ;
if ( hindex > = exec - > bo_count ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " BO index %d greater than BO count %d \n " ,
2015-11-30 12:13:37 -08:00
hindex , exec - > bo_count ) ;
return NULL ;
}
obj = exec - > bo [ hindex ] ;
bo = to_vc4_bo ( & obj - > base ) ;
if ( bo - > validated_shader ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Trying to use shader BO as something other than "
2015-11-30 12:13:37 -08:00
" a shader \n " ) ;
return NULL ;
}
return obj ;
}
static struct drm_gem_cma_object *
vc4_use_handle ( struct vc4_exec_info * exec , uint32_t gem_handles_packet_index )
{
return vc4_use_bo ( exec , exec - > bo_index [ gem_handles_packet_index ] ) ;
}
static bool
validate_bin_pos ( struct vc4_exec_info * exec , void * untrusted , uint32_t pos )
{
/* Note that the untrusted pointer passed to these functions is
* incremented past the packet byte .
*/
return ( untrusted - 1 = = exec - > bin_u + pos ) ;
}
static uint32_t
gl_shader_rec_size ( uint32_t pointer_bits )
{
uint32_t attribute_count = pointer_bits & 7 ;
bool extended = pointer_bits & 8 ;
if ( attribute_count = = 0 )
attribute_count = 8 ;
if ( extended )
return 100 + attribute_count * 4 ;
else
return 36 + attribute_count * 8 ;
}
bool
vc4_check_tex_size ( struct vc4_exec_info * exec , struct drm_gem_cma_object * fbo ,
uint32_t offset , uint8_t tiling_format ,
uint32_t width , uint32_t height , uint8_t cpp )
{
uint32_t aligned_width , aligned_height , stride , size ;
uint32_t utile_w = utile_width ( cpp ) ;
uint32_t utile_h = utile_height ( cpp ) ;
/* The shaded vertex format stores signed 12.4 fixed point
* ( - 2048 , 2047 ) offsets from the viewport center , so we should
* never have a render target larger than 4096. The texture
* unit can only sample from 2048 x2048 , so it ' s even more
* restricted . This lets us avoid worrying about overflow in
* our math .
*/
if ( width > 4096 | | height > 4096 ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Surface dimensions (%d,%d) too large " ,
2017-05-14 18:00:16 +01:00
width , height ) ;
2015-11-30 12:13:37 -08:00
return false ;
}
switch ( tiling_format ) {
case VC4_TILING_FORMAT_LINEAR :
aligned_width = round_up ( width , utile_w ) ;
aligned_height = height ;
break ;
case VC4_TILING_FORMAT_T :
aligned_width = round_up ( width , utile_w * 8 ) ;
aligned_height = round_up ( height , utile_h * 8 ) ;
break ;
case VC4_TILING_FORMAT_LT :
aligned_width = round_up ( width , utile_w ) ;
aligned_height = round_up ( height , utile_h ) ;
break ;
default :
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " buffer tiling %d unsupported \n " , tiling_format ) ;
2015-11-30 12:13:37 -08:00
return false ;
}
stride = aligned_width * cpp ;
size = stride * aligned_height ;
if ( size + offset < size | |
size + offset > fbo - > base . size ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Overflow in %dx%d (%dx%d) fbo size (%d + %d > %zd) \n " ,
2015-11-30 12:13:37 -08:00
width , height ,
aligned_width , aligned_height ,
size , offset , fbo - > base . size ) ;
return false ;
}
return true ;
}
static int
validate_flush ( VALIDATE_ARGS )
{
if ( ! validate_bin_pos ( exec , untrusted , exec - > args - > bin_cl_size - 1 ) ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Bin CL must end with VC4_PACKET_FLUSH \n " ) ;
2015-11-30 12:13:37 -08:00
return - EINVAL ;
}
exec - > found_flush = true ;
return 0 ;
}
static int
validate_start_tile_binning ( VALIDATE_ARGS )
{
if ( exec - > found_start_tile_binning_packet ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Duplicate VC4_PACKET_START_TILE_BINNING \n " ) ;
2015-11-30 12:13:37 -08:00
return - EINVAL ;
}
exec - > found_start_tile_binning_packet = true ;
if ( ! exec - > found_tile_binning_mode_config_packet ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " missing VC4_PACKET_TILE_BINNING_MODE_CONFIG \n " ) ;
2015-11-30 12:13:37 -08:00
return - EINVAL ;
}
return 0 ;
}
static int
validate_increment_semaphore ( VALIDATE_ARGS )
{
if ( ! validate_bin_pos ( exec , untrusted , exec - > args - > bin_cl_size - 2 ) ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Bin CL must end with "
2015-11-30 12:13:37 -08:00
" VC4_PACKET_INCREMENT_SEMAPHORE \n " ) ;
return - EINVAL ;
}
exec - > found_increment_semaphore_packet = true ;
return 0 ;
}
static int
validate_indexed_prim_list ( VALIDATE_ARGS )
{
struct drm_gem_cma_object * ib ;
uint32_t length = * ( uint32_t * ) ( untrusted + 1 ) ;
uint32_t offset = * ( uint32_t * ) ( untrusted + 5 ) ;
uint32_t max_index = * ( uint32_t * ) ( untrusted + 9 ) ;
uint32_t index_size = ( * ( uint8_t * ) ( untrusted + 0 ) > > 4 ) ? 2 : 1 ;
struct vc4_shader_state * shader_state ;
/* Check overflow condition */
if ( exec - > shader_state_count = = 0 ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " shader state must precede primitives \n " ) ;
2015-11-30 12:13:37 -08:00
return - EINVAL ;
}
shader_state = & exec - > shader_state [ exec - > shader_state_count - 1 ] ;
if ( max_index > shader_state - > max_index )
shader_state - > max_index = max_index ;
ib = vc4_use_handle ( exec , 0 ) ;
if ( ! ib )
return - EINVAL ;
2016-09-27 09:03:13 -07:00
exec - > bin_dep_seqno = max ( exec - > bin_dep_seqno ,
to_vc4_bo ( & ib - > base ) - > write_seqno ) ;
2015-11-30 12:13:37 -08:00
if ( offset > ib - > base . size | |
( ib - > base . size - offset ) / index_size < length ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " IB access overflow (%d + %d*%d > %zd) \n " ,
2015-11-30 12:13:37 -08:00
offset , length , index_size , ib - > base . size ) ;
return - EINVAL ;
}
* ( uint32_t * ) ( validated + 5 ) = ib - > paddr + offset ;
return 0 ;
}
static int
validate_gl_array_primitive ( VALIDATE_ARGS )
{
uint32_t length = * ( uint32_t * ) ( untrusted + 1 ) ;
uint32_t base_index = * ( uint32_t * ) ( untrusted + 5 ) ;
uint32_t max_index ;
struct vc4_shader_state * shader_state ;
/* Check overflow condition */
if ( exec - > shader_state_count = = 0 ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " shader state must precede primitives \n " ) ;
2015-11-30 12:13:37 -08:00
return - EINVAL ;
}
shader_state = & exec - > shader_state [ exec - > shader_state_count - 1 ] ;
if ( length + base_index < length ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " primitive vertex count overflow \n " ) ;
2015-11-30 12:13:37 -08:00
return - EINVAL ;
}
max_index = length + base_index - 1 ;
if ( max_index > shader_state - > max_index )
shader_state - > max_index = max_index ;
return 0 ;
}
static int
validate_gl_shader_state ( VALIDATE_ARGS )
{
uint32_t i = exec - > shader_state_count + + ;
if ( i > = exec - > shader_state_size ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " More requests for shader states than declared \n " ) ;
2015-11-30 12:13:37 -08:00
return - EINVAL ;
}
exec - > shader_state [ i ] . addr = * ( uint32_t * ) untrusted ;
exec - > shader_state [ i ] . max_index = 0 ;
if ( exec - > shader_state [ i ] . addr & ~ 0xf ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " high bits set in GL shader rec reference \n " ) ;
2015-11-30 12:13:37 -08:00
return - EINVAL ;
}
* ( uint32_t * ) validated = ( exec - > shader_rec_p +
exec - > shader_state [ i ] . addr ) ;
exec - > shader_rec_p + =
roundup ( gl_shader_rec_size ( exec - > shader_state [ i ] . addr ) , 16 ) ;
return 0 ;
}
static int
validate_tile_binning_config ( VALIDATE_ARGS )
{
struct drm_device * dev = exec - > exec_bo - > base . dev ;
2017-03-27 16:10:25 -07:00
struct vc4_dev * vc4 = to_vc4_dev ( dev ) ;
2015-11-30 12:13:37 -08:00
uint8_t flags ;
2017-03-27 16:10:25 -07:00
uint32_t tile_state_size ;
uint32_t tile_count , bin_addr ;
int bin_slot ;
2015-11-30 12:13:37 -08:00
if ( exec - > found_tile_binning_mode_config_packet ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Duplicate VC4_PACKET_TILE_BINNING_MODE_CONFIG \n " ) ;
2015-11-30 12:13:37 -08:00
return - EINVAL ;
}
exec - > found_tile_binning_mode_config_packet = true ;
exec - > bin_tiles_x = * ( uint8_t * ) ( untrusted + 12 ) ;
exec - > bin_tiles_y = * ( uint8_t * ) ( untrusted + 13 ) ;
tile_count = exec - > bin_tiles_x * exec - > bin_tiles_y ;
flags = * ( uint8_t * ) ( untrusted + 14 ) ;
if ( exec - > bin_tiles_x = = 0 | |
exec - > bin_tiles_y = = 0 ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Tile binning config of %dx%d too small \n " ,
2015-11-30 12:13:37 -08:00
exec - > bin_tiles_x , exec - > bin_tiles_y ) ;
return - EINVAL ;
}
if ( flags & ( VC4_BIN_CONFIG_DB_NON_MS |
VC4_BIN_CONFIG_TILE_BUFFER_64BIT ) ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " unsupported binning config flags 0x%02x \n " , flags ) ;
2015-11-30 12:13:37 -08:00
return - EINVAL ;
}
2017-03-27 16:10:25 -07:00
bin_slot = vc4_v3d_get_bin_slot ( vc4 ) ;
if ( bin_slot < 0 ) {
if ( bin_slot ! = - EINTR & & bin_slot ! = - ERESTARTSYS ) {
DRM_ERROR ( " Failed to allocate binner memory: %d \n " ,
bin_slot ) ;
}
return bin_slot ;
}
/* The slot we allocated will only be used by this job, and is
* free when the job completes rendering .
*/
exec - > bin_slots | = BIT ( bin_slot ) ;
bin_addr = vc4 - > bin_bo - > base . paddr + bin_slot * vc4 - > bin_alloc_size ;
2015-11-30 12:13:37 -08:00
/* The tile state data array is 48 bytes per tile, and we put it at
* the start of a BO containing both it and the tile alloc .
*/
tile_state_size = 48 * tile_count ;
/* Since the tile alloc array will follow us, align. */
2017-03-27 16:10:25 -07:00
exec - > tile_alloc_offset = bin_addr + roundup ( tile_state_size , 4096 ) ;
2015-11-30 12:13:37 -08:00
* ( uint8_t * ) ( validated + 14 ) =
( ( flags & ~ ( VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK |
VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_MASK ) ) |
VC4_BIN_CONFIG_AUTO_INIT_TSDA |
VC4_SET_FIELD ( VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_32 ,
VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE ) |
VC4_SET_FIELD ( VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128 ,
VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE ) ) ;
/* tile alloc address. */
2017-03-27 16:10:25 -07:00
* ( uint32_t * ) ( validated + 0 ) = exec - > tile_alloc_offset ;
2015-11-30 12:13:37 -08:00
/* tile alloc size. */
2017-03-27 16:10:25 -07:00
* ( uint32_t * ) ( validated + 4 ) = ( bin_addr + vc4 - > bin_alloc_size -
exec - > tile_alloc_offset ) ;
2015-11-30 12:13:37 -08:00
/* tile state address. */
2017-03-27 16:10:25 -07:00
* ( uint32_t * ) ( validated + 8 ) = bin_addr ;
2015-11-30 12:13:37 -08:00
return 0 ;
}
static int
validate_gem_handles ( VALIDATE_ARGS )
{
memcpy ( exec - > bo_index , untrusted , sizeof ( exec - > bo_index ) ) ;
return 0 ;
}
# define VC4_DEFINE_PACKET(packet, func) \
[ packet ] = { packet # # _SIZE , # packet , func }
static const struct cmd_info {
uint16_t len ;
const char * name ;
int ( * func ) ( struct vc4_exec_info * exec , void * validated ,
void * untrusted ) ;
} cmd_info [ ] = {
VC4_DEFINE_PACKET ( VC4_PACKET_HALT , NULL ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_NOP , NULL ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_FLUSH , validate_flush ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_FLUSH_ALL , NULL ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_START_TILE_BINNING ,
validate_start_tile_binning ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_INCREMENT_SEMAPHORE ,
validate_increment_semaphore ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_GL_INDEXED_PRIMITIVE ,
validate_indexed_prim_list ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_GL_ARRAY_PRIMITIVE ,
validate_gl_array_primitive ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_PRIMITIVE_LIST_FORMAT , NULL ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_GL_SHADER_STATE , validate_gl_shader_state ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_CONFIGURATION_BITS , NULL ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_FLAT_SHADE_FLAGS , NULL ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_POINT_SIZE , NULL ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_LINE_WIDTH , NULL ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_RHT_X_BOUNDARY , NULL ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_DEPTH_OFFSET , NULL ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_CLIP_WINDOW , NULL ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_VIEWPORT_OFFSET , NULL ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_CLIPPER_XY_SCALING , NULL ) ,
/* Note: The docs say this was also 105, but it was 106 in the
* initial userland code drop .
*/
VC4_DEFINE_PACKET ( VC4_PACKET_CLIPPER_Z_SCALING , NULL ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_TILE_BINNING_MODE_CONFIG ,
validate_tile_binning_config ) ,
VC4_DEFINE_PACKET ( VC4_PACKET_GEM_HANDLES , validate_gem_handles ) ,
} ;
int
vc4_validate_bin_cl ( struct drm_device * dev ,
void * validated ,
void * unvalidated ,
struct vc4_exec_info * exec )
{
uint32_t len = exec - > args - > bin_cl_size ;
uint32_t dst_offset = 0 ;
uint32_t src_offset = 0 ;
while ( src_offset < len ) {
void * dst_pkt = validated + dst_offset ;
void * src_pkt = unvalidated + src_offset ;
u8 cmd = * ( uint8_t * ) src_pkt ;
const struct cmd_info * info ;
if ( cmd > = ARRAY_SIZE ( cmd_info ) ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " 0x%08x: packet %d out of bounds \n " ,
2015-11-30 12:13:37 -08:00
src_offset , cmd ) ;
return - EINVAL ;
}
info = & cmd_info [ cmd ] ;
if ( ! info - > name ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " 0x%08x: packet %d invalid \n " ,
2015-11-30 12:13:37 -08:00
src_offset , cmd ) ;
return - EINVAL ;
}
if ( src_offset + info - > len > len ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " 0x%08x: packet %d (%s) length 0x%08x "
2015-11-30 12:13:37 -08:00
" exceeds bounds (0x%08x) \n " ,
src_offset , cmd , info - > name , info - > len ,
src_offset + len ) ;
return - EINVAL ;
}
if ( cmd ! = VC4_PACKET_GEM_HANDLES )
memcpy ( dst_pkt , src_pkt , info - > len ) ;
if ( info - > func & & info - > func ( exec ,
dst_pkt + 1 ,
src_pkt + 1 ) ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " 0x%08x: packet %d (%s) failed to validate \n " ,
2015-11-30 12:13:37 -08:00
src_offset , cmd , info - > name ) ;
return - EINVAL ;
}
src_offset + = info - > len ;
/* GEM handle loading doesn't produce HW packets. */
if ( cmd ! = VC4_PACKET_GEM_HANDLES )
dst_offset + = info - > len ;
/* When the CL hits halt, it'll stop reading anything else. */
if ( cmd = = VC4_PACKET_HALT )
break ;
}
exec - > ct0ea = exec - > ct0ca + dst_offset ;
if ( ! exec - > found_start_tile_binning_packet ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Bin CL missing VC4_PACKET_START_TILE_BINNING \n " ) ;
2015-11-30 12:13:37 -08:00
return - EINVAL ;
}
/* The bin CL must be ended with INCREMENT_SEMAPHORE and FLUSH. The
* semaphore is used to trigger the render CL to start up , and the
* FLUSH is what caps the bin lists with
* VC4_PACKET_RETURN_FROM_SUB_LIST ( so they jump back to the main
* render CL when they get called to ) and actually triggers the queued
* semaphore increment .
*/
if ( ! exec - > found_increment_semaphore_packet | | ! exec - > found_flush ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Bin CL missing VC4_PACKET_INCREMENT_SEMAPHORE + "
2015-11-30 12:13:37 -08:00
" VC4_PACKET_FLUSH \n " ) ;
return - EINVAL ;
}
return 0 ;
}
static bool
reloc_tex ( struct vc4_exec_info * exec ,
void * uniform_data_u ,
struct vc4_texture_sample_info * sample ,
2016-09-27 09:03:13 -07:00
uint32_t texture_handle_index , bool is_cs )
2015-11-30 12:13:37 -08:00
{
struct drm_gem_cma_object * tex ;
uint32_t p0 = * ( uint32_t * ) ( uniform_data_u + sample - > p_offset [ 0 ] ) ;
uint32_t p1 = * ( uint32_t * ) ( uniform_data_u + sample - > p_offset [ 1 ] ) ;
uint32_t p2 = ( sample - > p_offset [ 2 ] ! = ~ 0 ?
* ( uint32_t * ) ( uniform_data_u + sample - > p_offset [ 2 ] ) : 0 ) ;
uint32_t p3 = ( sample - > p_offset [ 3 ] ! = ~ 0 ?
* ( uint32_t * ) ( uniform_data_u + sample - > p_offset [ 3 ] ) : 0 ) ;
uint32_t * validated_p0 = exec - > uniforms_v + sample - > p_offset [ 0 ] ;
uint32_t offset = p0 & VC4_TEX_P0_OFFSET_MASK ;
uint32_t miplevels = VC4_GET_FIELD ( p0 , VC4_TEX_P0_MIPLVLS ) ;
uint32_t width = VC4_GET_FIELD ( p1 , VC4_TEX_P1_WIDTH ) ;
uint32_t height = VC4_GET_FIELD ( p1 , VC4_TEX_P1_HEIGHT ) ;
uint32_t cpp , tiling_format , utile_w , utile_h ;
uint32_t i ;
uint32_t cube_map_stride = 0 ;
enum vc4_texture_data_type type ;
tex = vc4_use_bo ( exec , texture_handle_index ) ;
if ( ! tex )
return false ;
if ( sample - > is_direct ) {
uint32_t remaining_size = tex - > base . size - p0 ;
if ( p0 > tex - > base . size - 4 ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " UBO offset greater than UBO size \n " ) ;
2015-11-30 12:13:37 -08:00
goto fail ;
}
if ( p1 > remaining_size - 4 ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " UBO clamp would allow reads "
2015-11-30 12:13:37 -08:00
" outside of UBO \n " ) ;
goto fail ;
}
* validated_p0 = tex - > paddr + p0 ;
return true ;
}
if ( width = = 0 )
width = 2048 ;
if ( height = = 0 )
height = 2048 ;
if ( p0 & VC4_TEX_P0_CMMODE_MASK ) {
if ( VC4_GET_FIELD ( p2 , VC4_TEX_P2_PTYPE ) = =
VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE )
cube_map_stride = p2 & VC4_TEX_P2_CMST_MASK ;
if ( VC4_GET_FIELD ( p3 , VC4_TEX_P2_PTYPE ) = =
VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE ) {
if ( cube_map_stride ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Cube map stride set twice \n " ) ;
2015-11-30 12:13:37 -08:00
goto fail ;
}
cube_map_stride = p3 & VC4_TEX_P2_CMST_MASK ;
}
if ( ! cube_map_stride ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Cube map stride not set \n " ) ;
2015-11-30 12:13:37 -08:00
goto fail ;
}
}
type = ( VC4_GET_FIELD ( p0 , VC4_TEX_P0_TYPE ) |
( VC4_GET_FIELD ( p1 , VC4_TEX_P1_TYPE4 ) < < 4 ) ) ;
switch ( type ) {
case VC4_TEXTURE_TYPE_RGBA8888 :
case VC4_TEXTURE_TYPE_RGBX8888 :
case VC4_TEXTURE_TYPE_RGBA32R :
cpp = 4 ;
break ;
case VC4_TEXTURE_TYPE_RGBA4444 :
case VC4_TEXTURE_TYPE_RGBA5551 :
case VC4_TEXTURE_TYPE_RGB565 :
case VC4_TEXTURE_TYPE_LUMALPHA :
case VC4_TEXTURE_TYPE_S16F :
case VC4_TEXTURE_TYPE_S16 :
cpp = 2 ;
break ;
case VC4_TEXTURE_TYPE_LUMINANCE :
case VC4_TEXTURE_TYPE_ALPHA :
case VC4_TEXTURE_TYPE_S8 :
cpp = 1 ;
break ;
case VC4_TEXTURE_TYPE_ETC1 :
2016-11-03 18:53:10 -07:00
/* ETC1 is arranged as 64-bit blocks, where each block is 4x4
* pixels .
*/
cpp = 8 ;
width = ( width + 3 ) > > 2 ;
height = ( height + 3 ) > > 2 ;
break ;
2015-11-30 12:13:37 -08:00
case VC4_TEXTURE_TYPE_BW1 :
case VC4_TEXTURE_TYPE_A4 :
case VC4_TEXTURE_TYPE_A1 :
case VC4_TEXTURE_TYPE_RGBA64 :
case VC4_TEXTURE_TYPE_YUV422R :
default :
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Texture format %d unsupported \n " , type ) ;
2015-11-30 12:13:37 -08:00
goto fail ;
}
utile_w = utile_width ( cpp ) ;
utile_h = utile_height ( cpp ) ;
if ( type = = VC4_TEXTURE_TYPE_RGBA32R ) {
tiling_format = VC4_TILING_FORMAT_LINEAR ;
} else {
if ( size_is_lt ( width , height , cpp ) )
tiling_format = VC4_TILING_FORMAT_LT ;
else
tiling_format = VC4_TILING_FORMAT_T ;
}
if ( ! vc4_check_tex_size ( exec , tex , offset + cube_map_stride * 5 ,
tiling_format , width , height , cpp ) ) {
goto fail ;
}
/* The mipmap levels are stored before the base of the texture. Make
* sure there is actually space in the BO .
*/
for ( i = 1 ; i < = miplevels ; i + + ) {
uint32_t level_width = max ( width > > i , 1u ) ;
uint32_t level_height = max ( height > > i , 1u ) ;
uint32_t aligned_width , aligned_height ;
uint32_t level_size ;
/* Once the levels get small enough, they drop from T to LT. */
if ( tiling_format = = VC4_TILING_FORMAT_T & &
size_is_lt ( level_width , level_height , cpp ) ) {
tiling_format = VC4_TILING_FORMAT_LT ;
}
switch ( tiling_format ) {
case VC4_TILING_FORMAT_T :
aligned_width = round_up ( level_width , utile_w * 8 ) ;
aligned_height = round_up ( level_height , utile_h * 8 ) ;
break ;
case VC4_TILING_FORMAT_LT :
aligned_width = round_up ( level_width , utile_w ) ;
aligned_height = round_up ( level_height , utile_h ) ;
break ;
default :
aligned_width = round_up ( level_width , utile_w ) ;
aligned_height = level_height ;
break ;
}
level_size = aligned_width * cpp * aligned_height ;
if ( offset < level_size ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Level %d (%dx%d -> %dx%d) size %db "
2015-11-30 12:13:37 -08:00
" overflowed buffer bounds (offset %d) \n " ,
i , level_width , level_height ,
aligned_width , aligned_height ,
level_size , offset ) ;
goto fail ;
}
offset - = level_size ;
}
* validated_p0 = tex - > paddr + p0 ;
2016-09-27 09:03:13 -07:00
if ( is_cs ) {
exec - > bin_dep_seqno = max ( exec - > bin_dep_seqno ,
to_vc4_bo ( & tex - > base ) - > write_seqno ) ;
}
2015-11-30 12:13:37 -08:00
return true ;
fail :
DRM_INFO ( " Texture p0 at %d: 0x%08x \n " , sample - > p_offset [ 0 ] , p0 ) ;
DRM_INFO ( " Texture p1 at %d: 0x%08x \n " , sample - > p_offset [ 1 ] , p1 ) ;
DRM_INFO ( " Texture p2 at %d: 0x%08x \n " , sample - > p_offset [ 2 ] , p2 ) ;
DRM_INFO ( " Texture p3 at %d: 0x%08x \n " , sample - > p_offset [ 3 ] , p3 ) ;
return false ;
}
static int
validate_gl_shader_rec ( struct drm_device * dev ,
struct vc4_exec_info * exec ,
struct vc4_shader_state * state )
{
uint32_t * src_handles ;
void * pkt_u , * pkt_v ;
static const uint32_t shader_reloc_offsets [ ] = {
4 , /* fs */
16 , /* vs */
28 , /* cs */
} ;
uint32_t shader_reloc_count = ARRAY_SIZE ( shader_reloc_offsets ) ;
2018-03-13 09:31:51 -05:00
struct drm_gem_cma_object * bo [ ARRAY_SIZE ( shader_reloc_offsets ) + 8 ] ;
2015-11-30 12:13:37 -08:00
uint32_t nr_attributes , nr_relocs , packet_size ;
int i ;
nr_attributes = state - > addr & 0x7 ;
if ( nr_attributes = = 0 )
nr_attributes = 8 ;
packet_size = gl_shader_rec_size ( state - > addr ) ;
nr_relocs = ARRAY_SIZE ( shader_reloc_offsets ) + nr_attributes ;
if ( nr_relocs * 4 > exec - > shader_rec_size ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " overflowed shader recs reading %d handles "
2015-11-30 12:13:37 -08:00
" from %d bytes left \n " ,
nr_relocs , exec - > shader_rec_size ) ;
return - EINVAL ;
}
src_handles = exec - > shader_rec_u ;
exec - > shader_rec_u + = nr_relocs * 4 ;
exec - > shader_rec_size - = nr_relocs * 4 ;
if ( packet_size > exec - > shader_rec_size ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " overflowed shader recs copying %db packet "
2015-11-30 12:13:37 -08:00
" from %d bytes left \n " ,
packet_size , exec - > shader_rec_size ) ;
return - EINVAL ;
}
pkt_u = exec - > shader_rec_u ;
pkt_v = exec - > shader_rec_v ;
memcpy ( pkt_v , pkt_u , packet_size ) ;
exec - > shader_rec_u + = packet_size ;
/* Shader recs have to be aligned to 16 bytes (due to the attribute
* flags being in the low bytes ) , so round the next validated shader
* rec address up . This should be safe , since we ' ve got so many
* relocations in a shader rec packet .
*/
BUG_ON ( roundup ( packet_size , 16 ) - packet_size > nr_relocs * 4 ) ;
exec - > shader_rec_v + = roundup ( packet_size , 16 ) ;
exec - > shader_rec_size - = packet_size ;
for ( i = 0 ; i < shader_reloc_count ; i + + ) {
if ( src_handles [ i ] > exec - > bo_count ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Shader handle %d too big \n " , src_handles [ i ] ) ;
2015-11-30 12:13:37 -08:00
return - EINVAL ;
}
bo [ i ] = exec - > bo [ src_handles [ i ] ] ;
if ( ! bo [ i ] )
return - EINVAL ;
}
for ( i = shader_reloc_count ; i < nr_relocs ; i + + ) {
bo [ i ] = vc4_use_bo ( exec , src_handles [ i ] ) ;
if ( ! bo [ i ] )
return - EINVAL ;
}
2016-11-08 00:18:39 +01:00
if ( ( ( * ( uint16_t * ) pkt_u & VC4_SHADER_FLAG_FS_SINGLE_THREAD ) = = 0 ) ! =
to_vc4_bo ( & bo [ 0 ] - > base ) - > validated_shader - > is_threaded ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Thread mode of CL and FS do not match \n " ) ;
2016-11-08 00:18:39 +01:00
return - EINVAL ;
}
if ( to_vc4_bo ( & bo [ 1 ] - > base ) - > validated_shader - > is_threaded | |
to_vc4_bo ( & bo [ 2 ] - > base ) - > validated_shader - > is_threaded ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " cs and vs cannot be threaded \n " ) ;
2016-11-08 00:18:39 +01:00
return - EINVAL ;
}
2015-11-30 12:13:37 -08:00
for ( i = 0 ; i < shader_reloc_count ; i + + ) {
struct vc4_validated_shader_info * validated_shader ;
uint32_t o = shader_reloc_offsets [ i ] ;
uint32_t src_offset = * ( uint32_t * ) ( pkt_u + o ) ;
uint32_t * texture_handles_u ;
void * uniform_data_u ;
2016-07-02 12:17:10 -07:00
uint32_t tex , uni ;
2015-11-30 12:13:37 -08:00
* ( uint32_t * ) ( pkt_v + o ) = bo [ i ] - > paddr + src_offset ;
if ( src_offset ! = 0 ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Shaders must be at offset 0 of "
2015-11-30 12:13:37 -08:00
" the BO. \n " ) ;
return - EINVAL ;
}
validated_shader = to_vc4_bo ( & bo [ i ] - > base ) - > validated_shader ;
if ( ! validated_shader )
return - EINVAL ;
if ( validated_shader - > uniforms_src_size >
exec - > uniforms_size ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " Uniforms src buffer overflow \n " ) ;
2015-11-30 12:13:37 -08:00
return - EINVAL ;
}
texture_handles_u = exec - > uniforms_u ;
uniform_data_u = ( texture_handles_u +
validated_shader - > num_texture_samples ) ;
memcpy ( exec - > uniforms_v , uniform_data_u ,
validated_shader - > uniforms_size ) ;
for ( tex = 0 ;
tex < validated_shader - > num_texture_samples ;
tex + + ) {
if ( ! reloc_tex ( exec ,
uniform_data_u ,
& validated_shader - > texture_samples [ tex ] ,
2016-09-27 09:03:13 -07:00
texture_handles_u [ tex ] ,
i = = 2 ) ) {
2015-11-30 12:13:37 -08:00
return - EINVAL ;
}
}
2016-07-02 12:17:10 -07:00
/* Fill in the uniform slots that need this shader's
* start - of - uniforms address ( used for resetting the uniform
* stream in the presence of control flow ) .
*/
for ( uni = 0 ;
uni < validated_shader - > num_uniform_addr_offsets ;
uni + + ) {
uint32_t o = validated_shader - > uniform_addr_offsets [ uni ] ;
( ( uint32_t * ) exec - > uniforms_v ) [ o ] = exec - > uniforms_p ;
}
2015-11-30 12:13:37 -08:00
* ( uint32_t * ) ( pkt_v + o + 4 ) = exec - > uniforms_p ;
exec - > uniforms_u + = validated_shader - > uniforms_src_size ;
exec - > uniforms_v + = validated_shader - > uniforms_size ;
exec - > uniforms_p + = validated_shader - > uniforms_size ;
}
for ( i = 0 ; i < nr_attributes ; i + + ) {
struct drm_gem_cma_object * vbo =
bo [ ARRAY_SIZE ( shader_reloc_offsets ) + i ] ;
uint32_t o = 36 + i * 8 ;
uint32_t offset = * ( uint32_t * ) ( pkt_u + o + 0 ) ;
uint32_t attr_size = * ( uint8_t * ) ( pkt_u + o + 4 ) + 1 ;
uint32_t stride = * ( uint8_t * ) ( pkt_u + o + 5 ) ;
uint32_t max_index ;
2016-09-27 09:03:13 -07:00
exec - > bin_dep_seqno = max ( exec - > bin_dep_seqno ,
to_vc4_bo ( & vbo - > base ) - > write_seqno ) ;
2015-11-30 12:13:37 -08:00
if ( state - > addr & 0x8 )
stride | = ( * ( uint32_t * ) ( pkt_u + 100 + i * 4 ) ) & ~ 0xff ;
if ( vbo - > base . size < offset | |
vbo - > base . size - offset < attr_size ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " BO offset overflow (%d + %d > %zu) \n " ,
2015-11-30 12:13:37 -08:00
offset , attr_size , vbo - > base . size ) ;
return - EINVAL ;
}
if ( stride ! = 0 ) {
max_index = ( ( vbo - > base . size - offset - attr_size ) /
stride ) ;
if ( state - > max_index > max_index ) {
2017-07-25 09:27:32 -07:00
DRM_DEBUG ( " primitives use index %d out of "
2015-11-30 12:13:37 -08:00
" supplied %d \n " ,
state - > max_index , max_index ) ;
return - EINVAL ;
}
}
* ( uint32_t * ) ( pkt_v + o ) = vbo - > paddr + offset ;
}
return 0 ;
}
int
vc4_validate_shader_recs ( struct drm_device * dev ,
struct vc4_exec_info * exec )
{
uint32_t i ;
int ret = 0 ;
for ( i = 0 ; i < exec - > shader_state_count ; i + + ) {
ret = validate_gl_shader_rec ( dev , exec , & exec - > shader_state [ i ] ) ;
if ( ret )
return ret ;
}
return ret ;
}