#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 const char src[]= ".data\n" ".align 8\n" "global_value:\n" " .integer 64, 41\n" ".text\n" " LEA r64@0x00000, global_value\n" " LOADP r64@0x00008, r64@0x00000\n" " LDI r64@0x00010, 42\n" " STOREP r64@0x00000, r64@0x00010\n" " LOADP r64@0x00018, r64@0x00000\n" " ADJSP -16\n" " LDI r64@0x00020, 11\n" " LDI r64@0x00028, 22\n" " STSP 0, r64@0x00020\n" " STSP 8, r64@0x00028\n" " LEASP r64@0x00030, 0\n" " LOADP r64@0x00038, r64@0x00030\n" " LEASP r64@0x00040, 8\n" " LOADP r64@0x00048, r64@0x00040\n" " LDI r64@0x00050, 33\n" " STOREP r64@0x00040, r64@0x00050\n" " LDSP r64@0x00058, 8\n" " ADJSP 16\n" " HALT\n"; static int eq64( unsigned address, const char *text ) { mpu_int64_t x; iatoi( ( mpu_int * ) x, (__mpu_char8_t *)text, NB_I64 ); return( !memcmp( __cpu_reg_ptr( 64, address ), x, NB_I64 ) ); } static __mpu_uint64_t reg_u64( unsigned address ) { __mpu_uint64_t v = 0; memcpy( &v, __cpu_reg_ptr( 64, address ), 8 ); return( v ); } int main( void ) { cpu_asm_image im; cpu_instruction i; __mpu_uint64_t p; __mpu_UNIT_t saved[8]; char err[256]; int rc; __cpu_init(); CHECK( CPU_PTR_SEG_SHIFT == 60U, "PTR64 segment shift" ); CHECK( CPU_PTR_OFFSET_MASK == __mpu_UINT64_C( 0x0fffffffffffffff ), "PTR64 offset mask" ); CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_DATA, 0x1234 ) == 0, "make DATA pointer" ); CHECK( p == __mpu_UINT64_C( 0x0000000000001234 ), "DATA pointer encoding" ); CHECK( __cpu_ptr_segment( p ) == CPU_PTR_SEG_DATA, "DATA pointer segment" ); CHECK( __cpu_ptr_offset( p ) == 0x1234, "DATA pointer offset" ); CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_STACK, 0x1234 ) == 0, "make STACK pointer" ); CHECK( p == __mpu_UINT64_C( 0x1000000000001234 ), "STACK pointer encoding" ); CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_PROGRAM, 0x1234 ) == 0, "make PROGRAM pointer" ); CHECK( p == __mpu_UINT64_C( 0x2000000000001234 ), "PROGRAM pointer encoding" ); CHECK( __cpu_ptr_make( &p, 0x10, 0 ) == -1, "reject segment wider than four bits" ); CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_DATA, CPU_PTR_OFFSET_MASK + 1 ) == -1, "reject offset wider than 60 bits" ); 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" ); CHECK( __cpu_stack_alloc( 128 ) == 0, "stack alloc" ); __cpu_pc = 0; CHECK( __cpu_run() == CPU_EXEC_SUCCESS, "PTR64 program run" ); p = reg_u64( 0x00000 ); CHECK( __cpu_ptr_segment( p ) == CPU_PTR_SEG_DATA, "LEA creates DATA pointer" ); CHECK( __cpu_ptr_offset( p ) == CPU_DATA_NULL_GUARD_SIZE, "LEA DATA label offset after NULL guard" ); CHECK( eq64( 0x00008, "41" ), "LOADP reads DATA" ); CHECK( eq64( 0x00018, "42" ), "STOREP writes DATA and LOADP reads it back" ); CHECK( eq64( 0x00038, "11" ), "LEASP/LOADP first local object field" ); CHECK( eq64( 0x00048, "22" ), "LEASP/LOADP second local object field" ); CHECK( eq64( 0x00058, "33" ), "STOREP writes STACK through PTR64" ); CHECK( __cpu_sp == 128, "stack frame released" ); p = reg_u64( 0x00030 ); CHECK( __cpu_ptr_segment( p ) == CPU_PTR_SEG_STACK, "LEASP segment selector" ); CHECK( __cpu_ptr_offset( p ) == 112, "LEASP absolute stack offset for SP+0" ); /* PROGRAM is readable through PTR64 but is not writable. */ CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_PROGRAM, 0 ) == 0, "make PROGRAM pointer for indirect access" ); memcpy( __cpu_reg_ptr( 64, 0x00060 ), &p, 8 ); memset( &i, 0, sizeof( i ) ); i.opcode = CPU_OP_LOADP; i.operand[0].type = CPU_OPERAND_REGISTER; i.operand[0].value.reg.bits = 16; i.operand[0].value.reg.address = 0x00068; i.operand[1].type = CPU_OPERAND_REGISTER; i.operand[1].value.reg.bits = 64; i.operand[1].value.reg.address = 0x00060; CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "LOADP may read PROGRAM" ); CHECK( !memcmp( __cpu_reg_ptr( 16, 0x00068 ), __cpu_program_ptr(), 2 ), "LOADP PROGRAM bytes" ); memcpy( saved, __cpu_program_ptr(), sizeof( saved ) ); memset( &i, 0, sizeof( i ) ); i.opcode = CPU_OP_STOREP; i.operand[0].type = CPU_OPERAND_REGISTER; i.operand[0].value.reg.bits = 64; i.operand[0].value.reg.address = 0x00060; i.operand[1].type = CPU_OPERAND_REGISTER; i.operand[1].value.reg.bits = 64; i.operand[1].value.reg.address = 0x00010; CHECK( __cpu_exec( &i ) == CPU_EXEC_MEMORY_FAULT, "STOREP rejects PROGRAM write" ); CHECK( !memcmp( saved, __cpu_program_ptr(), sizeof( saved ) ), "PROGRAM write fault is atomic" ); /* ABI NULL is all-zero PTR64 and is never dereferenceable. */ p = CPU_NULL_PTR; memcpy( __cpu_reg_ptr( 64, 0x00060 ), &p, 8 ); memset( &i, 0, sizeof( i ) ); i.opcode = CPU_OP_LOADP; i.operand[0].type = CPU_OPERAND_REGISTER; i.operand[0].value.reg.bits = 8; i.operand[0].value.reg.address = 0x00070; i.operand[1].type = CPU_OPERAND_REGISTER; i.operand[1].value.reg.bits = 64; i.operand[1].value.reg.address = 0x00060; CHECK( __cpu_exec( &i ) == CPU_EXEC_POINTER_FAULT, "NULL PTR64 dereference faults" ); /* Reserved segment selectors decode but cannot currently be dereferenced. */ CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_FIRST_RESERVED, 0 ) == 0, "reserved segment fits PTR64" ); memcpy( __cpu_reg_ptr( 64, 0x00060 ), &p, 8 ); memset( &i, 0, sizeof( i ) ); i.opcode = CPU_OP_LOADP; i.operand[0].type = CPU_OPERAND_REGISTER; i.operand[0].value.reg.bits = 8; i.operand[0].value.reg.address = 0x00070; i.operand[1].type = CPU_OPERAND_REGISTER; i.operand[1].value.reg.bits = 64; i.operand[1].value.reg.address = 0x00060; rc = __cpu_exec( &i ); CHECK( rc == CPU_EXEC_POINTER_FAULT, "reserved PTR64 segment faults on dereference" ); cpu_asm_image_free( &im ); __cpu_free_context(); puts( "libcpu PTR64/indirect-addressing regression: PASS" ); return( 0 ); }