#include #include #include #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 ); }