#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 decode_src[]= ".text\n" " JMPP r64@0x00000\n" " CALLP r64@0x00008\n" " HALT\n"; static const char run_src[]= ".data\n" ".align 8\n" "table:\n" " .cptr leaf\n" " .cptr done\n" ".text\n" " LEA r64@0x00000, table\n" " LOADP r64@0x00008, r64@0x00000\n" " CALLP r64@0x00008\n" " PADDI r64@0x00000, r64@0x00000, 8\n" " LOADP r64@0x00010, r64@0x00000\n" " JMPP r64@0x00010\n" " LDI r64@0x00020, 99\n" " HALT\n" "leaf:\n" " LDI r64@0x00020, 42\n" " RET\n" "done:\n" " HALT\n"; static void put_u64_reg( unsigned addr, __mpu_uint64_t v ) { __cpu_data_put_u64( __cpu_reg_ptr( 64, addr ), v ); } int main( void ) { cpu_asm_image decim, im, bad; cpu_instruction insn, execi; cpu_register r64 = { 64, 0x00100 } , r32 = { 32, 0x00100 }; __mpu_uint64_t p0, p1, pvalid, pbad, ret, saved_sp, saved_pc; __mpu_uint32_t psw; mpu_int64_t forty_two; unsigned char before[16], after[16]; char err[256]; __cpu_init(); CHECK( cpu_assemble_text( decode_src, &decim, err, sizeof( err ) ) == 0, err ); CHECK( decim.code_size == 12, "JMPP/CALLP bytecode lengths" ); CHECK( __cpu_program_set( decim.code, decim.code_size ) == 0, "decode program set" ); __cpu_pc = 0; CHECK( __cpu_decode( &insn ) == CPU_EXEC_SUCCESS, "decode JMPP" ); CHECK( insn.opcode == CPU_OP_JMPP, "JMPP opcode" ); CHECK( insn.length == CPU_OPCODE_SIZE + CPU_REG24_SIZE, "JMPP length 5" ); CHECK( insn.operand[0].type == CPU_OPERAND_REGISTER, "JMPP register operand" ); CHECK( insn.operand[0].value.reg.bits == 64, "JMPP r64 width" ); __cpu_pc = 5; CHECK( __cpu_decode( &insn ) == CPU_EXEC_SUCCESS, "decode CALLP" ); CHECK( insn.opcode == CPU_OP_CALLP, "CALLP opcode" ); CHECK( insn.length == CPU_OPCODE_SIZE + CPU_REG24_SIZE, "CALLP length 5" ); cpu_asm_image_free( &decim ); __cpu_program_free(); CHECK( cpu_assemble_text( run_src, &im, err, sizeof( err ) ) == 0, err ); CHECK( im.data_size == CPU_DATA_NULL_GUARD_SIZE + 16, "two .cptr entries in DATA" ); p0 = __cpu_data_get_u64( im.data + CPU_DATA_NULL_GUARD_SIZE ); p1 = __cpu_data_get_u64( im.data + CPU_DATA_NULL_GUARD_SIZE + 8 ); CHECK( __cpu_ptr_segment( p0 ) == CPU_PTR_SEG_PROGRAM, "first .cptr PROGRAM segment" ); CHECK( __cpu_ptr_segment( p1 ) == CPU_PTR_SEG_PROGRAM, "second .cptr PROGRAM segment" ); CHECK( __cpu_ptr_offset( p0 ) < im.code_size, "first .cptr target in PROGRAM" ); CHECK( __cpu_ptr_offset( p1 ) < im.code_size, "second .cptr target in PROGRAM" ); CHECK( p0 != p1, ".cptr labels resolve to distinct targets" ); CHECK( __cpu_program_set( im.code, im.code_size ) == 0, "indirect program set" ); CHECK( __cpu_data_set( im.data, im.data_size ) == 0, "indirect data set" ); CHECK( __cpu_stack_alloc( 512 ) == 0, "indirect stack alloc" ); __cpu_pc = 0; CHECK( __cpu_run() == CPU_EXEC_SUCCESS, "indirect CALLP/JMPP run" ); iatoi( ( mpu_int * ) forty_two, (__mpu_char8_t *)"42", NB_I64 ); CHECK( !memcmp( __cpu_reg_ptr( 64, 0x00020 ), forty_two, NB_I64 ), "CALLP returned and JMPP reached done" ); CHECK( __cpu_sp == 512, "CALLP/RET restored SP" ); /* Direct executor tests: JMPP must only install a validated PROGRAM PTR64. */ memset( &execi, 0, sizeof( execi ) ); execi.opcode = CPU_OP_JMPP; execi.operand[0].type = CPU_OPERAND_REGISTER; execi.operand[0].value.reg = r64; pvalid = p0; put_u64_reg( 0x00100, pvalid ); __cpu_pc = 7; __cpu_sp = 512; __cpu_set_psw( PSW_CF_MASK | PSW_TF_MASK ); psw = __cpu_get_psw(); CHECK( __cpu_exec( &execi ) == CPU_EXEC_BRANCH, "JMPP executor success" ); CHECK( __cpu_pc == __cpu_ptr_offset( pvalid ), "JMPP installed target PC" ); CHECK( __cpu_sp == 512, "JMPP does not change SP" ); CHECK( __cpu_get_psw() == psw, "JMPP preserves PSW" ); CHECK( __cpu_ptr_make( &pbad, CPU_PTR_SEG_DATA, CPU_DATA_NULL_GUARD_SIZE ) == 0, "make DATA pointer" ); put_u64_reg( 0x00100, pbad ); __cpu_pc = 9; saved_pc = __cpu_pc; saved_sp = __cpu_sp; CHECK( __cpu_exec( &execi ) == CPU_EXEC_POINTER_FAULT, "JMPP rejects non-PROGRAM PTR64" ); CHECK( __cpu_pc == saved_pc && __cpu_sp == saved_sp, "JMPP pointer fault is atomic" ); CHECK( __cpu_ptr_make( &pbad, CPU_PTR_SEG_PROGRAM, im.code_size ) == 0, "make out-of-range PROGRAM pointer" ); put_u64_reg( 0x00100, pbad ); __cpu_pc = 11; saved_pc = __cpu_pc; CHECK( __cpu_exec( &execi ) == CPU_EXEC_POINTER_FAULT, "JMPP rejects PROGRAM end pointer" ); CHECK( __cpu_pc == saved_pc, "JMPP range fault preserves PC" ); put_u64_reg( 0x00100, CPU_NULL_PTR ); CHECK( __cpu_exec( &execi ) == CPU_EXEC_POINTER_FAULT, "JMPP rejects NULL PTR64" ); execi.operand[0].value.reg = r32; CHECK( __cpu_exec( &execi ) == CPU_EXEC_WIDTH_FAULT, "JMPP requires physical r64" ); /* CALLP validates pointer, return PC and stack before architectural mutation. */ memset( &execi, 0, sizeof( execi ) ); execi.opcode = CPU_OP_CALLP; execi.operand[0].type = CPU_OPERAND_REGISTER; execi.operand[0].value.reg = r64; put_u64_reg( 0x00100, pvalid ); __cpu_pc = 10; __cpu_sp = 512; memset( __cpu_stack_ptr() + 496, 0xa5, 16 ); __cpu_set_psw( PSW_CF_MASK | PSW_TF_MASK ); psw = __cpu_get_psw(); CHECK( __cpu_exec( &execi ) == CPU_EXEC_BRANCH, "CALLP executor success" ); CHECK( __cpu_pc == __cpu_ptr_offset( pvalid ), "CALLP installed target PC" ); CHECK( __cpu_sp == 504, "CALLP pushed 8-byte return PC" ); ret = __cpu_data_get_u64( __cpu_stack_ptr() + 504 ); CHECK( ret == 15, "CALLP return PC is next 5-byte instruction" ); CHECK( __cpu_get_psw() == psw, "CALLP preserves PSW" ); CHECK( __cpu_ptr_make( &pbad, CPU_PTR_SEG_STACK, 0 ) == 0, "make STACK pointer" ); put_u64_reg( 0x00100, pbad ); __cpu_pc = 20; __cpu_sp = 512; saved_pc = __cpu_pc; saved_sp = __cpu_sp; memcpy( before, __cpu_stack_ptr() + 496, 16 ); CHECK( __cpu_exec( &execi ) == CPU_EXEC_POINTER_FAULT, "CALLP rejects non-PROGRAM PTR64" ); memcpy( after, __cpu_stack_ptr() + 496, 16 ); CHECK( __cpu_pc == saved_pc && __cpu_sp == saved_sp && !memcmp( before, after, 16 ), "CALLP pointer fault is atomic" ); put_u64_reg( 0x00100, pvalid ); __cpu_pc = 20; __cpu_sp = 4; saved_pc = __cpu_pc; saved_sp = __cpu_sp; CHECK( __cpu_exec( &execi ) == CPU_EXEC_STACK_FAULT, "CALLP checks return-address stack space" ); CHECK( __cpu_pc == saved_pc && __cpu_sp == saved_sp, "CALLP stack fault is atomic" ); execi.operand[0].value.reg = r32; __cpu_sp = 512; CHECK( __cpu_exec( &execi ) == CPU_EXEC_WIDTH_FAULT, "CALLP requires physical r64" ); CHECK( cpu_assemble_text( ".text\n JMPP r32@0x00000\n", &bad, err, sizeof( err ) ) != 0, "assembler rejects JMPP r32" ); CHECK( cpu_assemble_text( ".text\n CALLP c64@0x00000\n", &bad, err, sizeof( err ) ) != 0, "assembler rejects CALLP c64 spelling" ); CHECK( cpu_assemble_text( ".data\n .cptr nowhere\n.text\n HALT\n", &bad, err, sizeof( err ) ) != 0, "assembler rejects undefined .cptr label" ); cpu_asm_image_free( &im ); __cpu_free_context(); puts( "libcpu indirect control-flow regression: PASS" ); return( 0 ); }