summaryrefslogtreecommitdiff
path: root/tests/tst-block-copy.c
blob: 463633bed4f588ed930ca67df16aac88a45d86e3 (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
#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 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 );
}