#line 2 "suites/target_test.function" #include "greentea-client/test_env.h" /** * \brief Increments pointer and asserts that it does not overflow. * * \param p Pointer to byte array * \param start Pointer to start of byte array * \param len Length of byte array * \param step Increment size * */ #define INCR_ASSERT(p, start, len, step) do \ { \ TEST_HELPER_ASSERT( ( p ) >= ( start ) ); \ TEST_HELPER_ASSERT( sizeof( *( p ) ) == sizeof( *( start ) ) ); \ /* <= is checked to support use inside a loop where \ pointer is incremented after reading data. */ \ TEST_HELPER_ASSERT( (uint32_t)( ( ( p ) - ( start ) ) + ( step ) ) <= ( len ) );\ ( p ) += ( step ); \ } \ while( 0 ) /** * \brief 4 byte align unsigned char pointer * * \param p Pointer to byte array * \param start Pointer to start of byte array * \param len Length of byte array * */ #define ALIGN_32BIT(p, start, len) do \ { \ uint32_t align = ( - (uintptr_t)( p ) ) % 4; \ INCR_ASSERT( ( p ), ( start ), ( len ), align );\ } \ while( 0 ) /** * \brief Verify dependencies. Dependency identifiers are * encoded in the buffer as 8 bit unsigned integers. * * \param count Number of dependencies. * \param dep_p Pointer to buffer. * * \return DEPENDENCY_SUPPORTED if success else DEPENDENCY_NOT_SUPPORTED. */ int verify_dependencies( uint8_t count, uint8_t * dep_p ) { uint8_t i; for ( i = 0; i < count; i++ ) { if ( dep_check( (int)(dep_p[i]) ) != DEPENDENCY_SUPPORTED ) return( DEPENDENCY_NOT_SUPPORTED ); } return( DEPENDENCY_SUPPORTED ); } /** * \brief Receives hex string on serial interface, and converts to a byte. * * \param none * * \return unsigned int8 */ uint8_t receive_byte() { uint8_t byte; uint8_t c; c = greentea_getc(); if( c >= '0' && c <= '9' ) c -= '0'; else if( c >= 'a' && c <= 'f' ) c = ( c -'a' ) + 10; else if( c >= 'A' && c <= 'F' ) c = ( c - 'A' ) + 10; byte = c * 0x10; c = greentea_getc(); if( c >= '0' && c <= '9' ) c -= '0'; else if( c >= 'a' && c <= 'f' ) c = ( c -'a' ) + 10; else if( c >= 'A' && c <= 'F' ) c = ( c - 'A' ) + 10; byte += c ; return( byte); } /** * \brief Receives unsigned integer on serial interface. * Integers are encoded in network order, and sent as hex ascii string. * * \param none * * \return unsigned int */ uint32_t receive_uint32() { uint32_t value; value = receive_byte() << 24; value |= receive_byte() << 16; value |= receive_byte() << 8; value |= receive_byte(); return( (uint32_t)value ); } /** * \brief Parses out an unsigned 32 int value from the byte array. * Integers are encoded in network order. * * \param p Pointer to byte array * * \return unsigned int */ uint32_t parse_uint32( uint8_t * p ) { uint32_t value; value = *p++ << 24; value |= *p++ << 16; value |= *p++ << 8; value |= *p; return( value ); } /** * \brief Receives test data on serial as greentea key,value pair: * {{;}} * * \param data_len Out pointer to hold received data length. * * \return Byte array. */ uint8_t * receive_data( uint32_t * data_len ) { uint32_t i = 0, errors = 0; char c; uint8_t * data = NULL; /* Read opening braces */ i = 0; while ( i < 2 ) { c = greentea_getc(); /* Ignore any prevous CR LF characters */ if ( c == '\n' || c == '\r' ) continue; i++; if ( c != '{' ) return( NULL ); } /* Read data length */ *data_len = receive_uint32(); data = (uint8_t *)malloc( *data_len ); TEST_HELPER_ASSERT( data != NULL ); greentea_getc(); // read ';' received after key i.e. *data_len for( i = 0; i < *data_len; i++ ) data[i] = receive_byte(); /* Read closing braces */ for( i = 0; i < 2; i++ ) { c = greentea_getc(); if ( c != '}' ) { errors++; break; } } if ( errors ) { free( data ); data = NULL; *data_len = 0; } return( data ); } /** * \brief Parse the received byte array and count the number of arguments * to the test function passed as type hex. * * \param count Parameter count * \param data Received Byte array * \param data_len Byte array length * * \return count of hex params */ uint32_t find_hex_count( uint8_t count, uint8_t * data, uint32_t data_len ) { uint32_t i = 0, sz = 0; char c; uint8_t * p = NULL; uint32_t hex_count = 0; p = data; for( i = 0; i < count; i++ ) { c = (char)*p; INCR_ASSERT( p, data, data_len, 1 ); /* Align p to 4 bytes for int, expression, string len or hex length */ ALIGN_32BIT( p, data, data_len ); /* Network to host conversion */ sz = (int32_t)parse_uint32( p ); INCR_ASSERT( p, data, data_len, sizeof( int32_t ) ); if ( c == 'H' || c == 'S' ) { INCR_ASSERT( p, data, data_len, sz ); hex_count += ( c == 'H' )?1:0; } } return( hex_count ); } /** * \brief Parses received byte array for test parameters. * * \param count Parameter count * \param data Received Byte array * \param data_len Byte array length * \param error Parsing error out variable. * * \return Array of parsed parameters allocated on heap. * Note: Caller has the responsibility to delete * the memory after use. */ void ** parse_parameters( uint8_t count, uint8_t * data, uint32_t data_len, int * error ) { uint32_t i = 0, hex_count = 0; char c; void ** params = NULL; void ** cur = NULL; uint8_t * p = NULL; hex_count = find_hex_count(count, data, data_len); params = (void **)malloc( sizeof( void *) * ( count + hex_count ) ); TEST_HELPER_ASSERT( params != NULL ); cur = params; p = data; /* Parameters */ for( i = 0; i < count; i++ ) { c = (char)*p; INCR_ASSERT( p, data, data_len, 1 ); /* Align p to 4 bytes for int, expression, string len or hex length */ ALIGN_32BIT( p, data, data_len ); /* Network to host conversion */ *( (int32_t *)p ) = (int32_t)parse_uint32( p ); switch( c ) { case 'E': { if ( get_expression( *( (int32_t *)p ), (int32_t *)p ) ) { *error = KEY_VALUE_MAPPING_NOT_FOUND; goto exit; } } /* Intentional fall through */ case 'I': { *cur++ = (void *)p; INCR_ASSERT( p, data, data_len, sizeof( int32_t ) ); } break; case 'H': /* Intentional fall through */ case 'S': { uint32_t * sz = (uint32_t *)p; INCR_ASSERT( p, data, data_len, sizeof( int32_t ) ); *cur++ = (void *)p; if ( c == 'H' ) *cur++ = (void *)sz; INCR_ASSERT( p, data, data_len, ( *sz ) ); } break; default: { *error = DISPATCH_INVALID_TEST_DATA; goto exit; } break; } } exit: if ( *error ) { free( params ); params = NULL; } return( params ); } /** * \brief Sends greentea key and int value pair to host. * * \param key key string * \param value integer value * * \return void */ void send_key_integer( char * key, int value ) { char str[50]; snprintf( str, sizeof( str ), "%d", value ); greentea_send_kv( key, str ); } /** * \brief Sends test setup failure to the host. * * \param failure Test set failure * * \return void */ void send_failure( int failure ) { send_key_integer( "F", failure ); } /** * \brief Sends test status to the host. * * \param status Test status (PASS=0/FAIL=!0) * * \return void */ void send_status( int status ) { send_key_integer( "R", status ); } /** * \brief Embedded implementation of execute_tests(). * Ignores command line and received test data * on serial. * * \param argc not used * \param argv not used * * \return Program exit status. */ int execute_tests( int args, const char ** argv ) { int ret = 0; uint32_t data_len = 0; uint8_t count = 0, function_id; void ** params = NULL; uint8_t * data = NULL, * p = NULL; GREENTEA_SETUP( 180, "mbedtls_test" ); greentea_send_kv( "GO", " " ); while ( 1 ) { ret = 0; test_info.failed = 0; data_len = 0; data = receive_data( &data_len ); if ( data == NULL ) continue; p = data; do { /* Read dependency count */ count = *p; TEST_HELPER_ASSERT( count < data_len ); INCR_ASSERT( p, data, data_len, sizeof( uint8_t ) ); ret = verify_dependencies( count, p ); if ( ret != DEPENDENCY_SUPPORTED ) break; if ( count ) INCR_ASSERT( p, data, data_len, count ); /* Read function id */ function_id = *p; INCR_ASSERT( p, data, data_len, sizeof( uint8_t ) ); if ( ( ret = check_test( function_id ) ) != DISPATCH_TEST_SUCCESS ) break; /* Read number of parameters */ count = *p; INCR_ASSERT( p, data, data_len, sizeof( uint8_t ) ); /* Parse parameters if present */ if ( count ) { params = parse_parameters( count, p, data_len - ( p - data ), &ret ); if ( ret ) break; } ret = dispatch_test( function_id, params ); } while ( 0 ); if ( data ) { free( data ); data = NULL; } if ( params ) { free( params ); params = NULL; } if ( ret ) send_failure( ret ); else send_status( test_info.failed ); } return( 0 ); }