diff options
Diffstat (limited to 'tests/tst-syscall.c')
| -rw-r--r-- | tests/tst-syscall.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/tests/tst-syscall.c b/tests/tst-syscall.c new file mode 100644 index 0000000..92c3fd3 --- /dev/null +++ b/tests/tst-syscall.c @@ -0,0 +1,229 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#include <libmcpu.h> + +#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() */ |
