summaryrefslogtreecommitdiff
path: root/tests/tst-indirect-control.c
blob: df8879805d048b147848d8bf1103cdb5c23c844a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#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];

  __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 );
}