diff options
Diffstat (limited to 'tests/tst-block-copy.c')
| -rw-r--r-- | tests/tst-block-copy.c | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/tests/tst-block-copy.c b/tests/tst-block-copy.c new file mode 100644 index 0000000..463633b --- /dev/null +++ b/tests/tst-block-copy.c @@ -0,0 +1,171 @@ +#include <stdio.h> +#include <string.h> +#include <libmcpu.h> +#include "../utils/mcpu-asm.h" + +#define CHECK(c,m) do{if(!(c)){fprintf(stderr,"FAIL: %s\n",m);return 1;}}while(0) + +static void +regop( cpu_operand *o, unsigned address ) +{ + memset( o, 0, sizeof( *o ) ); + o->type = CPU_OPERAND_REGISTER; + o->value.reg.bits = 64; + o->value.reg.address = address; +} + +static void +set_reg_u64( unsigned address, __mpu_uint64_t v ) +{ + memcpy( __cpu_reg_ptr( 64, address ), &v, sizeof( v ) ); +} + +static __mpu_uint64_t +get_reg_u64( unsigned address ) +{ + __mpu_uint64_t v = 0; + memcpy( &v, __cpu_reg_ptr( 64, address ), sizeof( v ) ); + return( v ); +} + +int +main( void ) +{ + cpu_asm_image im; + cpu_instruction i; + __mpu_uint64_t p; + __mpu_uint32_t psw; + __mpu_UNIT_t before[32]; + unsigned k; + char err[256]; + static const char src[]= + ".data\n" + ".align 8\n" + "v:\n" + " .integer 64, 1\n" + " .integer 64, 2\n" + " .integer 64, 3\n" + " .integer 64, 4\n" + ".text\n" + " LEA r64@0x00000, v\n" + " ADJSP -32\n" + " LEASP r64@0x00008, 0\n" + " BMOVI r64@0x00008, r64@0x00000, 32\n" + " LDI r64@0x00010, 16\n" + " BMOV r64@0x00008, r64@0x00000, r64@0x00010\n" + " HALT\n"; + + __cpu_init(); + CHECK( __cpu_stack_alloc( 128 ) == 0, "stack alloc" ); + CHECK( cpu_assemble_text( src, &im, err, sizeof( err ) ) == 0, err ); + CHECK( __cpu_program_set( im.code, im.code_size ) == 0, "program set" ); + CHECK( __cpu_data_set( im.data, im.data_size ) == 0, "data set" ); + + /* Decoder knows both block-copy signatures. */ + __cpu_pc = 0; + CHECK( __cpu_decode( &i ) == CPU_EXEC_SUCCESS, "decode LEA" ); + __cpu_pc+=i.length; + CHECK( __cpu_decode( &i ) == CPU_EXEC_SUCCESS, "decode ADJSP" ); + __cpu_pc+=i.length; + CHECK( __cpu_decode( &i ) == CPU_EXEC_SUCCESS, "decode LEASP" ); + __cpu_pc+=i.length; + CHECK( __cpu_decode( &i ) == CPU_EXEC_SUCCESS, "decode BMOVI" ); + CHECK( i.opcode == CPU_OP_BMOVI, "BMOVI opcode" ); + CHECK( i.length == CPU_OPCODE_SIZE + 2 * CPU_REG24_SIZE + CPU_IMM64_SIZE, "BMOVI length" ); + CHECK( i.operand[2].type == CPU_OPERAND_IMM64 && i.operand[2].value.imm64 == 32, "BMOVI count" ); + + __cpu_pc = 0; + CHECK( __cpu_run() == CPU_EXEC_SUCCESS, "assembled block-copy program" ); + CHECK( memcmp( __cpu_stack_ptr() + 96, __cpu_data_ptr() + CPU_DATA_NULL_GUARD_SIZE, 16 ) == 0, "BMOV runtime count DATA->STACK" ); + + /* Direct executor: overlapping copy must have memmove semantics. */ + for( k = 0; k < 32; k++ ) __cpu_stack_ptr()[64 + k] = (__mpu_UNIT_t)k; + memcpy( before, __cpu_stack_ptr() + 64, sizeof( before ) ); + CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_STACK, 68 ) == 0, "make overlap destination" ); + set_reg_u64( 0x00100, p ); + CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_STACK, 64 ) == 0, "make overlap source" ); + set_reg_u64( 0x00108, p ); + memset( &i, 0, sizeof( i ) ); + i.opcode = CPU_OP_BMOVI; + regop( &i.operand[0], 0x00100 ); + regop( &i.operand[1], 0x00108 ); + i.operand[2].type = CPU_OPERAND_IMM64; + i.operand[2].value.imm64 = 16; + CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "BMOVI overlap" ); + CHECK( memcmp( __cpu_stack_ptr() + 68, before, 16 ) == 0, "BMOVI uses memmove semantics" ); + + /* Register-sized form uses an unsigned r64 byte count. */ + CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_DATA, CPU_DATA_NULL_GUARD_SIZE ) == 0, "make DATA source" ); + set_reg_u64( 0x00108, p ); + CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_STACK, 32 ) == 0, "make STACK destination" ); + set_reg_u64( 0x00100, p ); + set_reg_u64( 0x00110, 24 ); + memset( &i, 0, sizeof( i ) ); + i.opcode = CPU_OP_BMOV; + regop( &i.operand[0], 0x00100 ); + regop( &i.operand[1], 0x00108 ); + regop( &i.operand[2], 0x00110 ); + CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "BMOV register count" ); + CHECK( memcmp( __cpu_stack_ptr() + 32, __cpu_data_ptr() + CPU_DATA_NULL_GUARD_SIZE, 24 ) == 0, "BMOV copied runtime byte count" ); + + /* Copying from PROGRAM is permitted. */ + CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_PROGRAM, 0 ) == 0, "make PROGRAM source" ); + set_reg_u64( 0x00108, p ); + CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_DATA, CPU_DATA_NULL_GUARD_SIZE ) == 0, "make DATA destination" ); + set_reg_u64( 0x00100, p ); + set_reg_u64( 0x00110, 2 ); + CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "BMOV PROGRAM->DATA" ); + CHECK( __cpu_data_ptr()[CPU_DATA_NULL_GUARD_SIZE] == __cpu_program_ptr()[0] && __cpu_data_ptr()[CPU_DATA_NULL_GUARD_SIZE + 1] == __cpu_program_ptr()[1], "PROGRAM source bytes copied" ); + + /* PROGRAM is read-only as a destination; destination memory is unchanged. */ + memcpy( before, __cpu_program_ptr(), 16 ); + CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_PROGRAM, 0 ) == 0, "make PROGRAM destination" ); + set_reg_u64( 0x00100, p ); + CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_STACK, 32 ) == 0, "make STACK source" ); + set_reg_u64( 0x00108, p ); + set_reg_u64( 0x00110, 8 ); + CHECK( __cpu_exec( &i ) == CPU_EXEC_MEMORY_FAULT, "BMOV rejects PROGRAM destination" ); + CHECK( memcmp( before, __cpu_program_ptr(), 16 ) == 0, "PROGRAM unchanged on destination fault" ); + + /* Out-of-range source is detected before any destination bytes change. */ + memset( __cpu_stack_ptr() + 16, 0xa5, 16 ); + memcpy( before, __cpu_stack_ptr() + 16, 16 ); + CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_STACK, 16 ) == 0, "make atomic destination" ); + set_reg_u64( 0x00100, p ); + CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_DATA, __cpu_data_size() - 4 ) == 0, "make short source range" ); + set_reg_u64( 0x00108, p ); + set_reg_u64( 0x00110, 8 ); + CHECK( __cpu_exec( &i ) == CPU_EXEC_MEMORY_FAULT, "BMOV source range fault" ); + CHECK( memcmp( before, __cpu_stack_ptr() + 16, 16 ) == 0, "destination unchanged on source fault" ); + + /* Reserved segment fails as a pointer fault. */ + CHECK( __cpu_ptr_make( &p, 0x0f, 0 ) == 0, "make reserved pointer" ); + set_reg_u64( 0x00108, p ); + set_reg_u64( 0x00110, 1 ); + CHECK( __cpu_exec( &i ) == CPU_EXEC_POINTER_FAULT, "BMOV reserved source segment" ); + + /* PSW is not modified. */ + CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_DATA, CPU_DATA_NULL_GUARD_SIZE ) == 0, "make PSW source" ); + set_reg_u64( 0x00108, p ); + CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_STACK, 0 ) == 0, "make PSW destination" ); + set_reg_u64( 0x00100, p ); + set_reg_u64( 0x00110, 1 ); + __cpu_set_psw( PSW_CF_MASK | PSW_IF_MASK ); + psw = __cpu_get_psw(); + CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "BMOV PSW test" ); + CHECK( __cpu_get_psw() == psw, "BMOV preserves PSW" ); + + /* Zero length is a valid no-op at one-past offsets of allocated segments. */ + CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_DATA, __cpu_data_size() ) == 0, "make DATA one-past" ); + set_reg_u64( 0x00108, p ); + CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_STACK, __cpu_stack_size() ) == 0, "make STACK one-past" ); + set_reg_u64( 0x00100, p ); + set_reg_u64( 0x00110, 0 ); + CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "BMOV zero byte no-op" ); + CHECK( get_reg_u64( 0x00110 ) == 0, "zero count register retained" ); + + cpu_asm_image_free( &im ); + __cpu_free_context(); + puts( "libcpu block-copy regression: PASS" ); + return( 0 ); +} |
