summaryrefslogtreecommitdiff
path: root/tests/tst-indirect-control.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/tst-indirect-control.c')
-rw-r--r--tests/tst-indirect-control.c184
1 files changed, 184 insertions, 0 deletions
diff --git a/tests/tst-indirect-control.c b/tests/tst-indirect-control.c
new file mode 100644
index 0000000..acb0a7b
--- /dev/null
+++ b/tests/tst-indirect-control.c
@@ -0,0 +1,184 @@
+#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 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];
+ int rc;
+
+ __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, "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 );
+}