diff options
Diffstat (limited to 'utils/mcpu-run.c')
| -rw-r--r-- | utils/mcpu-run.c | 870 |
1 files changed, 870 insertions, 0 deletions
diff --git a/utils/mcpu-run.c b/utils/mcpu-run.c new file mode 100644 index 0000000..d79e034 --- /dev/null +++ b/utils/mcpu-run.c @@ -0,0 +1,870 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <libmcpu.h> +#include <libmpuio.h> +#include "mcpu-exec.h" +#include "mcpu-dependency.h" + +#include "mcpu-run-options.h" + +static const char * +memory_name( memory_kind memory ) +{ + switch( memory ) + { + case MEMORY_DATA: return( "DATA" ); + case MEMORY_CONTROL: return( "CONTROL" ); + case MEMORY_HEAP: return( "HEAP" ); + case MEMORY_INPUT: return( "INPUT" ); + case MEMORY_OUTPUT: return( "OUTPUT" ); + case MEMORY_TRACE: return( "TRACE" ); + default: return( "UNKNOWN" ); + } + +} /* End of memory_name() */ + + +static __mpu_UNIT_t * +memory_ptr( memory_kind memory ) +{ + switch( memory ) + { + case MEMORY_DATA: return( __cpu_data_ptr() ); + case MEMORY_CONTROL: return( __cpu_control_ptr() ); + case MEMORY_HEAP: return( __cpu_heap_ptr() ); + case MEMORY_INPUT: return( __cpu_input_ptr() ); + case MEMORY_OUTPUT: return( __cpu_output_ptr() ); + case MEMORY_TRACE: return( __cpu_trace_ptr() ); + default: return( (__mpu_UNIT_t *)0 ); + } + +} /* End of memory_ptr() */ + + +static __mpu_uint64_t +memory_size( memory_kind memory ) +{ + switch( memory ) + { + case MEMORY_DATA: return( __cpu_data_size() ); + case MEMORY_CONTROL: return( __cpu_control_size() ); + case MEMORY_HEAP: return( __cpu_heap_size() ); + case MEMORY_INPUT: return( __cpu_input_size() ); + case MEMORY_OUTPUT: return( __cpu_output_size() ); + case MEMORY_TRACE: return( __cpu_trace_size() ); + default: return( 0 ); + } + +} /* End of memory_size() */ + + + +static int +make_integer_format( __mpu_char16_t *format, size_t size, + unsigned int bits, int newline ) +{ + char ascii[64]; + int len, i; + + len = snprintf( ascii, sizeof( ascii ), newline ? "%%Z%ud\n" : "%%Z%ud", bits ); + if( len < 0 || (size_t)len >= sizeof( ascii ) || (size_t)len + 1U > size ) + return( -1 ); + + for( i = 0; i <= len; ++i ) + format[i] = (__mpu_char16_t)(unsigned char)ascii[i]; + + return( 0 ); + +} /* End of make_integer_format() */ + + +static __mpu_UNIT_t * +memory_object_ptr( const print_spec *spec, __mpu_uint64_t bytes, + const char *object_name ) +{ + __mpu_UNIT_t *p; + __mpu_uint64_t size; + + p = memory_ptr( spec->memory ); + size = memory_size( spec->memory ); + + if( !p || spec->address > size || bytes > size - spec->address ) + { + fprintf( stderr, "mcpu-run: %s %s object is outside %s memory\n", + object_name, memory_name( spec->memory ), + memory_name( spec->memory ) ); + return( (__mpu_UNIT_t *)0 ); + } + + return( p + (size_t)spec->address ); + +} /* End of memory_object_ptr() */ + + +static int +print_integer_value( const print_spec *spec, int newline ) +{ + __mpu_char16_t format[64]; + __mpu_UNIT_t *value; + __mpu_uint64_t bytes; + + if( make_integer_format( format, sizeof( format ) / sizeof( format[0] ), + spec->bits, newline ) ) + return( -1 ); + + bytes = spec->bits / 8U; + if( spec->kind == PRINT_INTEGER_REGISTER ) + value = __cpu_reg_ptr( spec->bits, (__mpu_uint32_t)spec->address ); + else if( spec->kind == PRINT_INTEGER_MEMORY ) + value = memory_object_ptr( spec, bytes, "integer" ); + else + return( -1 ); + + if( !value ) return( -1 ); + if( fflush( stdout ) ) return( -1 ); + + if( mpu_dprintf( STDOUT_FILENO, format, (mpu_int *)value ) < 0 ) + { + fprintf( stderr, "mcpu-run: LibMPUIO integer output failed\n" ); + return( -1 ); + } + + return( 0 ); + +} /* End of print_integer_value() */ + + +static int +print_integer( const print_spec *spec ) +{ + return( print_integer_value( spec, 1 ) ); + +} /* End of print_integer() */ + + +static int +make_real_format( __mpu_char16_t *format, size_t size, + unsigned int bits, unsigned int digits, int newline ) +{ + char ascii[96]; + int len, i; + + len = snprintf( ascii, sizeof( ascii ), newline ? "%%.%uZ%uR\n" : "%%.%uZ%uR", + digits, bits ); + if( len < 0 || (size_t)len >= sizeof( ascii ) || (size_t)len + 1U > size ) + return( -1 ); + + for( i = 0; i <= len; ++i ) + format[i] = (__mpu_char16_t)(unsigned char)ascii[i]; + + return( 0 ); + +} /* End of make_real_format() */ + + +static int +print_real_value( const print_spec *spec, unsigned int digits, int newline ) +{ + __mpu_char16_t format[96]; + __mpu_UNIT_t *value; + __mpu_uint64_t bytes; + + if( make_real_format( format, sizeof( format ) / sizeof( format[0] ), + spec->bits, digits, newline ) ) + return( -1 ); + + bytes = spec->bits / 8U; + if( spec->kind == PRINT_REAL_REGISTER ) + value = __cpu_reg_ptr( spec->bits, (__mpu_uint32_t)spec->address ); + else if( spec->kind == PRINT_REAL_MEMORY ) + value = memory_object_ptr( spec, bytes, "real" ); + else + return( -1 ); + + if( !value ) return( -1 ); + if( fflush( stdout ) ) return( -1 ); + + if( mpu_dprintf( STDOUT_FILENO, format, (mpu_real *)value ) < 0 ) + { + fprintf( stderr, "mcpu-run: LibMPUIO real output failed\n" ); + return( -1 ); + } + + return( 0 ); + +} /* End of print_real_value() */ + + +static int +print_real( const print_spec *spec, unsigned int digits ) +{ + return( print_real_value( spec, digits, 1 ) ); + +} /* End of print_real() */ + +static int +raw_memory_range( const print_spec *spec, const cpu_exec_loaded *loaded, + __mpu_UNIT_t **ptr, __mpu_uint64_t *count ) +{ + __mpu_uint64_t begin, end, size; + __mpu_UNIT_t *p; + + if( spec->memory == MEMORY_DATA ) + { + if( !loaded->has_data_section ) + { + fprintf( stderr, "mcpu-run: executable has no .data section\n" ); + return( -1 ); + } + + begin = loaded->data_section_begin; + if( loaded->data_section_size > UINT64_MAX - begin ) return( -1 ); + end = begin + loaded->data_section_size; + + if( spec->address < begin || spec->address >= end ) + { + fprintf( stderr, "mcpu-run: raw DATA address is outside .data\n" ); + return( -1 ); + } + + p = __cpu_data_ptr(); + } + else + { + begin = 0; + size = memory_size( spec->memory ); + p = memory_ptr( spec->memory ); + end = size; + + if( !p || spec->address >= end ) + { + fprintf( stderr, "mcpu-run: raw %s address is outside %s memory\n", + memory_name( spec->memory ), memory_name( spec->memory ) ); + return( -1 ); + } + } + + *count = spec->count; + if( *count > end - spec->address ) *count = end - spec->address; + + *ptr = p + (size_t)spec->address; + + return( 0 ); + +} /* End of raw_memory_range() */ + + +static int +print_raw_memory( const print_spec *spec, const cpu_exec_loaded *loaded ) +{ + __mpu_UNIT_t *p; + __mpu_uint64_t count, off; + + if( spec->kind != PRINT_RAW_MEMORY ) return( -1 ); + if( raw_memory_range( spec, loaded, &p, &count ) ) return( -1 ); + + printf( "%s memory dump: (%llu bytes):\n", + memory_name( spec->memory ), (unsigned long long)count ); + + for( off = 0; off < count; off += 16 ) + { + unsigned int j; + unsigned int row = (unsigned int)((count - off) > 16 ? 16 : (count - off)); + + printf( " %04llx : ", (unsigned long long)off ); + for( j = 0; j < 16; ++j ) + { + if( j < row ) + printf( "%02x ", (unsigned int)p[(size_t)off + j] ); + else + printf( " " ); + } + printf( " | " ); + for( j = 0; j < row; ++j ) + { + unsigned int c = (unsigned int)p[(size_t)off + j]; + putchar( c >= 0x20U && c <= 0x7eU ? (int)c : '.' ); + } + putchar( '\n' ); + } + + return( ferror( stdout ) ? -1 : 0 ); + +} /* End of print_raw_memory() */ + + +static int +write_stdout( const char *s, size_t n ) +{ + while( n ) + { + ssize_t rc = write( STDOUT_FILENO, s, n ); + + if( rc < 0 ) + { + if( errno == EINTR ) continue; + return( -1 ); + } + if( !rc ) return( -1 ); + + s += (size_t)rc; + n -= (size_t)rc; + } + + return( 0 ); + +} /* End of write_stdout() */ + + +static __mpu_char16_t * +ascii_to_ucs2( const char *s ) +{ + __mpu_char16_t *p; + size_t i, n; + + if( !s ) return( (__mpu_char16_t *)0 ); + + n = strlen( s ); + if( n > (SIZE_MAX / sizeof( *p )) - 1U ) return( (__mpu_char16_t *)0 ); + + p = (__mpu_char16_t *)malloc( (n + 1U) * sizeof( *p ) ); + if( !p ) return( (__mpu_char16_t *)0 ); + + for( i = 0; i < n; ++i ) + { + unsigned char c = (unsigned char)s[i]; + + if( c >= 0x80U ) + { + free( p ); + return( (__mpu_char16_t *)0 ); + } + p[i] = (__mpu_char16_t)c; + } + p[n] = 0; + + return( p ); + +} /* End of ascii_to_ucs2() */ + + +static int +scan_action_value( const char *value, unsigned int bits, + int real_object, __mpu_UNIT_t *dst ) +{ + __mpu_char16_t *text = (__mpu_char16_t *)0; + __mpu_char16_t *format = (__mpu_char16_t *)0; + __mpu_uint32_t psw; + char ascii[64]; + size_t n; + int len, consumed = -1, rc; + + n = strlen( value ); + len = snprintf( ascii, sizeof( ascii ), real_object ? "%%Z%uR%%n" : "%%Z%ui%%n", + bits ); + if( len < 0 || (size_t)len >= sizeof( ascii ) ) return( -1 ); + + text = ascii_to_ucs2( value ); + format = ascii_to_ucs2( ascii ); + if( !text || !format ) goto fail; + + psw = __cpu_get_psw(); + if( real_object ) + rc = mpu_sscanf( text, format, (mpu_real *)dst, &consumed ); + else + rc = mpu_sscanf( text, format, (mpu_int *)dst, &consumed ); + __cpu_set_psw( psw ); + + free( format ); + free( text ); + + if( rc != 1 || consumed < 0 || (size_t)consumed != n ) return( -1 ); + + return( 0 ); + +fail: + free( format ); + free( text ); + return( -1 ); + +} /* End of scan_action_value() */ + + +static void +make_action_print_spec( const stage_action *action, const action_item *item, + print_spec *spec ) +{ + memset( spec, 0, sizeof( *spec ) ); + spec->memory = action->memory; + spec->bits = item->bits; + spec->address = item->address; + + if( action->kind == ACTION_PRINT_INTEGER_REGISTER || + action->kind == ACTION_SET_INTEGER_REGISTER ) + spec->kind = PRINT_INTEGER_REGISTER; + else if( action->kind == ACTION_PRINT_REAL_REGISTER || + action->kind == ACTION_SET_REAL_REGISTER ) + spec->kind = PRINT_REAL_REGISTER; + else if( action->kind == ACTION_PRINT_INTEGER_MEMORY || + action->kind == ACTION_SET_INTEGER_MEMORY ) + spec->kind = PRINT_INTEGER_MEMORY; + else if( action->kind == ACTION_PRINT_REAL_MEMORY || + action->kind == ACTION_SET_REAL_MEMORY ) + spec->kind = PRINT_REAL_MEMORY; + +} /* End of make_action_print_spec() */ + + +static __mpu_UNIT_t * +action_object_ptr( const stage_action *action, const action_item *item, + const char *object_name ) +{ + print_spec spec; + + make_action_print_spec( action, item, &spec ); + if( spec.kind == PRINT_INTEGER_REGISTER || spec.kind == PRINT_REAL_REGISTER ) + return( __cpu_reg_ptr( spec.bits, (__mpu_uint32_t)spec.address ) ); + + return( memory_object_ptr( &spec, spec.bits / 8U, object_name ) ); + +} /* End of action_object_ptr() */ + + +static int +execute_print_action( const stage_action *action, unsigned int digits ) +{ + print_spec spec; + size_t i; + + /* Validate the whole action before producing any output. */ + for( i = 0; i < action->count; ++i ) + { + if( !action_object_ptr( action, &action->item[i], + (action->kind == ACTION_PRINT_REAL_REGISTER || + action->kind == ACTION_PRINT_REAL_MEMORY) ? + "real" : "integer" ) ) + return( -1 ); + } + + for( i = 0; i < action->count; ++i ) + { + make_action_print_spec( action, &action->item[i], &spec ); + + if( i && write_stdout( " ", 1U ) ) return( -1 ); + + if( spec.kind == PRINT_INTEGER_REGISTER || spec.kind == PRINT_INTEGER_MEMORY ) + { + if( print_integer_value( &spec, 0 ) ) return( -1 ); + } + else + { + if( print_real_value( &spec, digits, 0 ) ) return( -1 ); + } + } + + if( write_stdout( "\n", 1U ) ) return( -1 ); + + return( 0 ); + +} /* End of execute_print_action() */ + + +typedef struct __set_value +{ + __mpu_UNIT_t *dst; + __mpu_UNIT_t *value; + size_t bytes; +} set_value; + + +static int +execute_set_action( const stage_action *action ) +{ + set_value *value; + size_t i, j; + int real_object; + + value = (set_value *)calloc( action->count, sizeof( *value ) ); + if( !value ) return( -1 ); + + real_object = (action->kind == ACTION_SET_REAL_REGISTER || + action->kind == ACTION_SET_REAL_MEMORY); + + /* Parse and validate every assignment before changing guest state. */ + for( i = 0; i < action->count; ++i ) + { + value[i].bytes = action->item[i].bits / 8U; + value[i].dst = action_object_ptr( action, &action->item[i], + real_object ? "real" : "integer" ); + if( !value[i].dst ) goto fail; + + value[i].value = (__mpu_UNIT_t *)calloc( value[i].bytes, 1U ); + if( !value[i].value ) goto fail; + + if( scan_action_value( action->item[i].value, action->item[i].bits, + real_object, value[i].value ) ) + { + fprintf( stderr, "mcpu-run: cannot parse stage value '%s' as %s%u\n", + action->item[i].value, real_object ? "real" : "int", + action->item[i].bits ); + goto fail; + } + } + + for( i = 0; i < action->count; ++i ) + memcpy( value[i].dst, value[i].value, value[i].bytes ); + + for( i = 0; i < action->count; ++i ) free( value[i].value ); + free( value ); + + return( 0 ); + +fail: + for( j = 0; j < action->count; ++j ) free( value[j].value ); + free( value ); + return( -1 ); + +} /* End of execute_set_action() */ + + +static int +execute_stage_action( const stage_action *action, unsigned int digits, + const cpu_exec_loaded *loaded ) +{ + switch( action->kind ) + { + case ACTION_PRINT_INTEGER_REGISTER: + case ACTION_PRINT_REAL_REGISTER: + case ACTION_PRINT_INTEGER_MEMORY: + case ACTION_PRINT_REAL_MEMORY: + return( execute_print_action( action, digits ) ); + + case ACTION_SET_INTEGER_REGISTER: + case ACTION_SET_REAL_REGISTER: + case ACTION_SET_INTEGER_MEMORY: + case ACTION_SET_REAL_MEMORY: + return( execute_set_action( action ) ); + + case ACTION_PRINT_RAW_MEMORY: + if( print_raw_memory( &action->raw, loaded ) ) return( -1 ); + if( fflush( stdout ) ) return( -1 ); + return( 0 ); + + default: + return( -1 ); + } + +} /* End of execute_stage_action() */ + + +static int +execute_stage_action_list( const stage_action_list *list, unsigned int digits, + const cpu_exec_loaded *loaded ) +{ + size_t i; + + for( i = 0; i < list->count; ++i ) + if( execute_stage_action( &list->action[i], digits, loaded ) ) + return( -1 ); + + return( 0 ); + +} /* End of execute_stage_action_list() */ + + +static int +execution_fault( int rc ) +{ + fprintf( stderr, "mcpu-run: execution fault %d at PC=0x%016llx, PSW=0x%08x\n", + rc, (unsigned long long)__cpu_pc, (unsigned int)__cpu_get_psw()); + return( 1 ); +} + + +static int +mcpu_run_execute( const mcpu_run_options *options ) +{ + unsigned long long completed = 0; + cpu_exec_loaded loaded; + char err[512]; + char *resolved_preload = NULL; + char *resolved_modules = NULL; + size_t preload_count = 0; + int rc; + int host_status = 0; + + if( options == NULL ) + return( 1 ); + + if( mcpu_run_resolve_preload(options, &resolved_preload, + err, sizeof(err)) != 0 ) + { + fprintf( stderr, "%s: preload resolution error: %s\n", + options->common.progname, err ); + return( 1 ); + } + + if( mcpu_dependency_build_load_list(&options->common, options->exe, + resolved_preload, &resolved_modules, + &preload_count, err, sizeof(err)) != 0 ) + { + fprintf( stderr, "%s: dependency resolution error: %s\n", + options->common.progname, err ); + free( resolved_preload ); + return( 1 ); + } + + __cpu_init(); + + /* Guest argv begins with the executable spelling supplied to mcpu-run. */ + if( cpu_exec_load_resolved(options->exe, resolved_modules, + preload_count, + options->program_argc, options->program_argv, + &loaded, err, sizeof(err)) ) + { + fprintf( stderr, "%s: load error: %s\n", + options->common.progname, err ); + __cpu_free_context(); + free( resolved_modules ); + free( resolved_preload ); + return( 1 ); + } + + if( execute_stage_action_list(&options->before_run, + options->digits, &loaded) ) + { + fprintf( stderr, "%s: --before-run action failed\n", + options->common.progname ); + host_status = 1; + goto done; + } + + if( loaded.has_init ) + { + __cpu_pc = loaded.init_entry; + rc = __cpu_run(); + if( rc < 0 ) + { + host_status = execution_fault( rc ); + goto done; + } + if( rc == CPU_EXEC_EXIT ) + goto graceful_exit; + if( __cpu_get_stop_reason() == CPU_STOP_HALT ) + goto stopped; + if( rc != CPU_EXEC_END ) + { + fprintf( stderr, "%s: INIT did not terminate with END\n", + options->common.progname ); + host_status = 1; + goto done; + } + if( __cpu_sp != loaded.startup_base || __cpu_fp != loaded.stack_size ) + { + fprintf( stderr, "%s: INIT did not restore top-level STACK state\n", + options->common.progname ); + host_status = 1; + goto done; + } + __cpu_pc = loaded.life_entry; + } + + if( execute_stage_action_list(&options->after_init, + options->digits, &loaded) ) + { + fprintf( stderr, "%s: --after-init action failed\n", + options->common.progname ); + host_status = 1; + goto done; + } + + if( options->init_only ) + goto stopped; + + for( ; ; ) + { + rc = __cpu_run(); + if( rc < 0 ) + { + host_status = execution_fault( rc ); + goto done; + } + if( rc == CPU_EXEC_EXIT ) + goto graceful_exit; + if( __cpu_get_stop_reason() == CPU_STOP_HALT ) + goto stopped; + if( rc != CPU_EXEC_END ) + { + fprintf( stderr, "%s: LIFE phase stopped unexpectedly (%d)\n", + options->common.progname, rc ); + host_status = 1; + goto done; + } + ++completed; + if( execute_stage_action_list(&options->on_step, + options->digits, &loaded) ) + { + fprintf( stderr, + "%s: --on-step action failed after LIFE step %llu\n", + options->common.progname, completed ); + host_status = 1; + goto done; + } + if( !options->steps_set || completed >= options->steps ) + goto stopped; + /* Resume from the PC already advanced past END. */ + } + +graceful_exit: + host_status = (int)__cpu_get_exit_status(); + if( loaded.has_fini ) + { + if( __cpu_sp != loaded.startup_base || __cpu_fp != loaded.stack_size ) + { + fprintf( stderr, + "%s: LIFE did not restore top-level STACK state before FINI\n", + options->common.progname ); + host_status = 1; + goto done; + } + __cpu_pc = loaded.fini_entry; + rc = __cpu_run(); + if( rc < 0 ) + { + host_status = execution_fault( rc ); + goto done; + } + if( rc != CPU_EXEC_END ) + { + fprintf( stderr, "%s: FINI must terminate with END\n", + options->common.progname ); + host_status = 1; + goto done; + } + if( __cpu_sp != loaded.startup_base || __cpu_fp != loaded.stack_size ) + { + fprintf( stderr, "%s: FINI did not restore top-level STACK state\n", + options->common.progname ); + host_status = 1; + goto done; + } + } + +stopped: + if( execute_stage_action_list(&options->after_run, + options->digits, &loaded) ) + { + fprintf( stderr, "%s: --after-run action failed\n", + options->common.progname ); + host_status = 1; + goto done; + } + + if( options->output.kind == PRINT_INTEGER_REGISTER || + options->output.kind == PRINT_INTEGER_MEMORY ) + { + if( print_integer(&options->output) ) + { + host_status = 1; + goto done; + } + } + else if( options->output.kind == PRINT_REAL_REGISTER || + options->output.kind == PRINT_REAL_MEMORY ) + { + if( print_real(&options->output, options->digits) ) + { + host_status = 1; + goto done; + } + } + else if( options->output.kind == PRINT_RAW_MEMORY ) + { + if( print_raw_memory(&options->output, &loaded) ) + { + host_status = 1; + goto done; + } + } + +done: + __cpu_free_context(); + free( resolved_modules ); + free( resolved_preload ); + if( host_status < 0 || host_status > 255 ) + return( 1 ); + return( host_status ); + +} /* End of mcpu_run_execute() */ + + +int +main( int argc, char **argv ) +{ + mcpu_run_options options; + int rc = 2; + + mcpu_run_options_init( &options, argv[0] ); + + if( mcpu_run_handle_options(&options, argc, argv) != 0 ) + goto done; + + if( options.common.help ) + { + mcpu_run_usage( &options ); + rc = 0; + goto done; + } + + if( options.common.version_action != MCPU_VERSION_NONE ) + { + if( mcpu_version_print(options.common.progname, + options.common.version_action) != 0 ) + { + rc = 1; + goto done; + } + rc = 0; + goto done; + } + + if( mcpu_tool_options_prepare(&options.common, argv[0]) != 0 ) + { + rc = 1; + goto done; + } + + if( mcpu_tool_has_information_action(&options.common) ) + { + if( options.common.dump_config && + mcpu_tool_dump_config(&options.common, stdout) != 0 ) + { + rc = 1; + goto done; + } + if( options.common.print_search_dirs && + mcpu_tool_print_search_dirs(&options.common, stdout) != 0 ) + { + rc = 1; + goto done; + } + rc = 0; + goto done; + } + + rc = mcpu_run_execute( &options ); + +done: + mcpu_run_options_free( &options ); + return( rc ); + +} /* End of main() */ |
