#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "mcpu-asm.h" #define TEST_NR_brk 10 #define CHECK(cond,msg) \ do { \ if( !(cond) ) \ { \ fprintf( stderr, "FAIL: %s\n", (msg) ); \ return( 1 ); \ } \ } while( 0 ) static __mpu_uint64_t reg64( __mpu_uint32_t address ) { return( __cpu_data_get_u64( __cpu_reg_ptr( 64, address ) ) ); } static void put32( __mpu_uint32_t address, __mpu_uint32_t value ) { __cpu_data_put_u32( __cpu_reg_ptr( 32, address ), value ); } static void put64( __mpu_uint32_t address, __mpu_uint64_t value ) { __cpu_data_put_u64( __cpu_reg_ptr( 64, address ), value ); } static int run_syscall( __mpu_uint32_t nargs, __mpu_uint32_t sysno, __mpu_uint64_t arg0, __mpu_uint64_t *ret ) { __mpu_uint64_t keep1 = __mpu_UINT64_C( 0x1111111111111111 ); __mpu_uint64_t keep2 = __mpu_UINT64_C( 0x2222222222222222 ); __mpu_uint64_t keep3 = __mpu_UINT64_C( 0x3333333333333333 ); __mpu_uint64_t keep4 = __mpu_UINT64_C( 0x4444444444444444 ); __mpu_uint64_t keep5 = __mpu_UINT64_C( 0x5555555555555555 ); int rc; put32( CPU_SYSCALL_NARGS_ADDR, nargs ); put32( CPU_SYSCALL_SYSNO_ADDR, sysno ); put64( CPU_SYSCALL_ARG0_ADDR, arg0 ); put64( CPU_SYSCALL_ARG1_ADDR, keep1 ); put64( CPU_SYSCALL_ARG2_ADDR, keep2 ); put64( CPU_SYSCALL_ARG3_ADDR, keep3 ); put64( CPU_SYSCALL_ARG4_ADDR, keep4 ); put64( CPU_SYSCALL_ARG5_ADDR, keep5 ); __cpu_pc = 0; rc = __cpu_run(); if( rc != CPU_EXEC_SUCCESS ) return( rc ); *ret = reg64( CPU_SYSCALL_RET_ADDR ); if( reg64( CPU_SYSCALL_ARG0_ADDR ) != arg0 || reg64( CPU_SYSCALL_ARG1_ADDR ) != keep1 || reg64( CPU_SYSCALL_ARG2_ADDR ) != keep2 || reg64( CPU_SYSCALL_ARG3_ADDR ) != keep3 || reg64( CPU_SYSCALL_ARG4_ADDR ) != keep4 || reg64( CPU_SYSCALL_ARG5_ADDR ) != keep5 ) return( -1000 ); return( 0 ); } static void regop( cpu_operand *o, unsigned int bits, __mpu_uint32_t address ) { memset( o, 0, sizeof( *o ) ); o->type = CPU_OPERAND_REGISTER; o->value.reg.bits = bits; o->value.reg.address = address; } int main( void ) { static const char source[] = ".text\n" "SYSCALL\n" "HALT\n"; cpu_asm_image image; cpu_instruction store; char err[256]; __mpu_uint64_t ptr, ret; __mpu_int64_t sret; size_t i; memset( &image, 0, sizeof( image ) ); __cpu_init(); CHECK( CPU_SYSCALL_NARGS_ADDR == CPU_SYSCALL_RET_ADDR, "NARGS overlaps low-address half of RET storage" ); CHECK( CPU_SYSCALL_SYSNO_ADDR == CPU_SYSCALL_RET_ADDR + 4, "SYSNO overlaps second half of RET storage" ); CHECK( CPU_SYSCALL_ARG0_ADDR + 8 == CPU_SYSCALL_RET_ADDR, "ARG0 is immediately below RET" ); CHECK( cpu_assemble_text( source, &image, err, sizeof( err ) ) == 0, err ); CHECK( image.code_size == 4, "SYSCALL/HALT code size" ); CHECK( image.code[0] == 0x00 && image.code[1] == 0x04, "SYSCALL opcode encoding" ); CHECK( __cpu_program_set( image.code, image.code_size ) == 0, "program load" ); CHECK( __cpu_heap_alloc( 64, 8192 ) == 0, "heap allocation" ); CHECK( __cpu_ptr_make( &ptr, CPU_PTR_SEG_HEAP, CPU_HEAP_BEGIN ) == 0, "make initial HEAP pointer" ); put64( 0x00000, ptr ); put64( 0x00008, __mpu_UINT64_C( 0xa5a5a5a5a5a5a5a5 ) ); memset( &store, 0, sizeof( store ) ); store.opcode = CPU_OP_STOREP; regop( &store.operand[0], 64, 0x00000 ); regop( &store.operand[1], 64, 0x00008 ); CHECK( __cpu_exec( &store ) == CPU_EXEC_MEMORY_FAULT, "initial break protects HEAP above _end" ); CHECK( run_syscall( 1, TEST_NR_brk, CPU_NULL_PTR, &ret ) == 0, "SYS_brk query" ); CHECK( __cpu_ptr_segment( ret ) == CPU_PTR_SEG_HEAP && __cpu_ptr_offset( ret ) == CPU_HEAP_BEGIN, "initial SYS_brk is HEAP:16" ); CHECK( __cpu_ptr_make( &ptr, CPU_PTR_SEG_HEAP, CPU_HEAP_BEGIN + 8 ) == 0, "make grown break" ); CHECK( run_syscall( 1, TEST_NR_brk, ptr, &ret ) == 0, "SYS_brk grow inside initial heap size" ); CHECK( ret == ptr, "SYS_brk grow return" ); CHECK( __cpu_exec( &store ) == CPU_EXEC_SUCCESS, "HEAP becomes writable below new break" ); memset( __cpu_heap_ptr() + CPU_HEAP_BEGIN, 0x5a, 8 ); CHECK( __cpu_ptr_make( &ptr, CPU_PTR_SEG_HEAP, CPU_HEAP_BEGIN ) == 0, "make shrink break" ); CHECK( run_syscall( 1, TEST_NR_brk, ptr, &ret ) == 0, "SYS_brk shrink to _end" ); CHECK( ret == ptr, "SYS_brk shrink return" ); for( i = 0; i < 8; ++i ) CHECK( __cpu_heap_ptr()[CPU_HEAP_BEGIN + i] == 0, "shrink zeroes released HEAP" ); CHECK( __cpu_heap_size() == 64, "shrink does not reduce logical backing size" ); CHECK( __cpu_exec( &store ) == CPU_EXEC_MEMORY_FAULT, "shrunk HEAP is inaccessible above _end" ); CHECK( __cpu_ptr_make( &ptr, CPU_PTR_SEG_HEAP, CPU_HEAP_BEGIN + 8 ) == 0, "make regrown break" ); CHECK( run_syscall( 1, TEST_NR_brk, ptr, &ret ) == 0, "SYS_brk regrow inside retained backing" ); CHECK( ret == ptr, "SYS_brk regrow return" ); for( i = 0; i < 8; ++i ) CHECK( __cpu_heap_ptr()[CPU_HEAP_BEGIN + i] == 0, "regrown released HEAP remains zero" ); CHECK( __cpu_ptr_make( &ptr, CPU_PTR_SEG_HEAP, 1000 ) == 0, "make break inside host capacity" ); CHECK( run_syscall( 1, TEST_NR_brk, ptr, &ret ) == 0, "SYS_brk extends logical heap" ); CHECK( ret == ptr && __cpu_heap_size() == 1000, "logical heap extension" ); __cpu_heap_ptr()[900] = 0x6b; CHECK( __cpu_ptr_make( &ptr, CPU_PTR_SEG_HEAP, 5000 ) == 0, "make break requiring backing growth" ); CHECK( run_syscall( 1, TEST_NR_brk, ptr, &ret ) == 0, "SYS_brk grows host backing" ); CHECK( ret == ptr && __cpu_heap_size() == 5000, "host-backed heap extension" ); CHECK( __cpu_heap_ptr()[900] == 0x6b, "heap relocation preserves old contents" ); CHECK( __cpu_heap_ptr()[4999] == 0, "newly allocated heap memory is zero" ); CHECK( __cpu_ptr_make( &ptr, CPU_PTR_SEG_HEAP, 8193 ) == 0, "make break above immutable limit" ); CHECK( run_syscall( 1, TEST_NR_brk, ptr, &ret ) == 0, "SYS_brk above limit returns normally" ); CHECK( __cpu_ptr_segment( ret ) == CPU_PTR_SEG_HEAP && __cpu_ptr_offset( ret ) == 5000 && __cpu_heap_size() == 5000, "heap limit rejection preserves current break" ); CHECK( __cpu_ptr_make( &ptr, CPU_PTR_SEG_DATA, 32 ) == 0, "make non-HEAP pointer" ); CHECK( run_syscall( 1, TEST_NR_brk, ptr, &ret ) == 0, "SYS_brk rejects non-HEAP pointer" ); CHECK( __cpu_ptr_offset( ret ) == 5000, "invalid SYS_brk returns current break" ); CHECK( __cpu_ptr_make( &ptr, CPU_PTR_SEG_HEAP, 6000 ) == 0, "make break for bad NARGS" ); CHECK( run_syscall( 2, TEST_NR_brk, ptr, &ret ) == 0, "SYS_brk bad NARGS" ); CHECK( __cpu_ptr_offset( ret ) == 5000, "bad SYS_brk NARGS returns current break" ); CHECK( run_syscall( 0, 9, 0, &ret ) == 0, "unimplemented syscall execution" ); memcpy( &sret, &ret, sizeof( sret ) ); CHECK( sret == -(__mpu_int64_t)ENOSYS, "unimplemented syscall returns -ENOSYS" ); cpu_asm_image_free( &image ); __cpu_free_context(); puts( "libmcpu SYSCALL/SYS_brk regression: PASS" ); return( 0 ); } /* End of main() */