Test parsing of syscall args, and responses to invalid behaviour

This commit is contained in:
Toby Jaffey 2025-12-10 22:29:33 +00:00
parent 46b6d1efba
commit 9d9e7542fc
5 changed files with 301 additions and 0 deletions

View file

@ -0,0 +1,33 @@
C_COMPILER=gcc
UNITY_ROOT=../unity
CFLAGS=-std=c99
CFLAGS += -Wall
CFLAGS += -Werror
CFLAGS += -DUVM32_MEMORY_SIZE=16384
SUITE_NAME=tests
TARGET_BASE1=test1
TARGET1 = $(TARGET_BASE1)
SRC_FILES1=$(UNITY_ROOT)/src/unity.c test/${SUITE_NAME}.c test/test_runners/${SUITE_NAME}_Runner.c ../../uvm32/uvm32.c
INC_DIRS=-I$(UNITY_ROOT)/src -I../../uvm32/ -I../../common -Irom
.PHONY: rom
default: $(SRC_FILES1) rom
@$(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_FILES1) rom/rom-header.c -o $(TARGET1)
@ ./$(TARGET1)
rom:
@(cd rom && make)
test/test_runners/${SUITE_NAME}_Runner.c: test/${SUITE_NAME}.c
@mkdir -p test/test_runners
@ruby $(UNITY_ROOT)/auto/generate_test_runner.rb test/${SUITE_NAME}.c test/test_runners/${SUITE_NAME}_Runner.c
clean:
rm -rf $(TARGET1) test/test_runners
(cd rom && make clean)

View file

@ -0,0 +1,13 @@
TOPDIR=../../../
PROJECT:=$(shell basename ${PWD})
SRCS=${PROJECT}.c ${TOPDIR}/apps/crt0.S
all: all_common
@# Convert ROM to C file and header
@xxd -i ${PROJECT}.bin > ${PROJECT}-header.c
@echo "extern unsigned char ${PROJECT}_bin[]; extern int ${PROJECT}_bin_len;" > ${PROJECT}-header.h
test: test_common
clean: clean_common
rm -f ${PROJECT}-header.h ${PROJECT}-header.c
include ${TOPDIR}/apps/makefile.common

View file

@ -0,0 +1,68 @@
#include "uvm32_target.h"
#include "../shared.h"
void syscall_a_test(void) {
// two strings
syscall((uint32_t)SYSCALL_A, (uint32_t)SYSCALL_A_DATA0, (uint32_t)SYSCALL_A_DATA1);
}
void syscall_b_test(void) {
// two null values
syscall((uint32_t)SYSCALL_B, 0, 0);
}
void syscall_c_test(void) {
// valid buffer
uint8_t buf[32];
for (int i=0;i<32;i++) {
buf[i] = i;
}
syscall((uint32_t)SYSCALL_C, (uint32_t)buf, sizeof(buf));
}
void syscall_d_test(void) {
// invalid address
syscall((uint32_t)SYSCALL_D, 0xFF000000, 100);
}
void syscall_ef_test(void) {
// ask for two values, then send them back
uint32_t a, b;
syscall((uint32_t)SYSCALL_E, (uint32_t)&a, (uint32_t)&b);
syscall((uint32_t)SYSCALL_F, a, b);
}
void syscall_gh_test(void) {
// ask for a buffer, mutate it, send it back
uint8_t buf[32];
syscall((uint32_t)SYSCALL_G, (uint32_t)buf, 32);
for (int i=0;i<32;i++) {
buf[i] *= 2;
}
syscall((uint32_t)SYSCALL_H, (uint32_t)buf, 32);
}
void main(void) {
switch(syscall(SYSCALL_PICKTEST, 0, 0)) {
case SYSCALL_A:
syscall_a_test();
break;
case SYSCALL_B:
syscall_b_test();
break;
case SYSCALL_C:
syscall_c_test();
break;
case SYSCALL_D:
syscall_d_test();
break;
case SYSCALL_E:
syscall_ef_test();
break;
case SYSCALL_G:
syscall_gh_test();
break;
}
}

View file

@ -0,0 +1,16 @@
#define SYSCALL_BASE 0x200
#define SYSCALL_A_DATA0 "Hello\033" // non-printing char
#define SYSCALL_A_DATA1 "" // empty string
#define SYSCALL_PICKTEST SYSCALL_BASE+0
#define SYSCALL_A SYSCALL_BASE+1
#define SYSCALL_B SYSCALL_BASE+2
#define SYSCALL_C SYSCALL_BASE+3
#define SYSCALL_D SYSCALL_BASE+4
#define SYSCALL_E SYSCALL_BASE+5
#define SYSCALL_F SYSCALL_BASE+6
#define SYSCALL_G SYSCALL_BASE+7
#define SYSCALL_H SYSCALL_BASE+8

View file

@ -0,0 +1,171 @@
#include <string.h>
#include "unity.h"
#include "uvm32.h"
#include "../common/uvm32_common_custom.h"
#include "rom-header.h"
#include "../shared.h"
static uvm32_state_t vmst;
static uvm32_evt_t evt;
void setUp(void) {
// runs before each test
uvm32_init(&vmst);
uvm32_load(&vmst, rom_bin, rom_bin_len);
}
void tearDown(void) {
}
void test_syscall_args_two_strings(void) {
// run the vm
uvm32_run(&vmst, &evt, 1000);
// check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, SYSCALL_A);
uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_A);
TEST_ASSERT_EQUAL(0, strcmp(uvm32_getcstr(&vmst, &evt, ARG0), SYSCALL_A_DATA0));
TEST_ASSERT_EQUAL(0, strcmp(uvm32_getcstr(&vmst, &evt, ARG1), SYSCALL_A_DATA1));
// run vm to completion
uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_END);
}
void test_syscall_args_bad_string(void) {
// run the vm
uvm32_run(&vmst, &evt, 1000);
// check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, SYSCALL_B);
uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_B);
// try to read C string from the first null ptr
const char *s0 = uvm32_getcstr(&vmst, &evt, ARG0);
TEST_ASSERT_EQUAL(0, strlen(s0)); // tried to read a null pointer, get back a zero length string (not a NULL we might read)
// attempt to run vm, should be stuck in err
uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_ERR);
// try to read C string from the second null ptr
const char *s1 = uvm32_getcstr(&vmst, &evt, ARG1);
TEST_ASSERT_EQUAL(0, strlen(s1)); // tried to read a null pointer, get back a zero length string (not a NULL we might read)
// attempt to run vm, should be stuck in err
uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_ERR);
}
void test_syscall_args_bufrd(void) {
// run the vm
uvm32_run(&vmst, &evt, 1000);
// check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, SYSCALL_C);
uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_C);
// get buffer described by ptr=ARG0,len=ARG1
uvm32_evt_syscall_buf_t buf = uvm32_getbuf(&vmst, &evt, ARG0, ARG1);
uint8_t expected[32];
for (int i=0;i<32;i++) {
expected[i] = i;
}
TEST_ASSERT_EQUAL(32, buf.len);
TEST_ASSERT_EQUAL(0, memcmp(buf.ptr, expected, 32));
}
void test_syscall_args_bufrd_bad_addr(void) {
// run the vm
uvm32_run(&vmst, &evt, 1000);
// check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, SYSCALL_D);
uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_D);
// get buffer described by ptr=ARG0,len=ARG1
uvm32_evt_syscall_buf_t buf = uvm32_getbuf(&vmst, &evt, ARG0, ARG1);
TEST_ASSERT_EQUAL(0, buf.len); // it was invalid, should get a safe zero length buffer
// attempt to run vm, should be stuck in err
uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_ERR);
}
void test_syscall_args_bufrd_ptrs(void) {
// run the vm
uvm32_run(&vmst, &evt, 1000);
// check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, SYSCALL_E);
uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_E);
// get uint32_t buffer described by ptr=ARG0
uvm32_evt_syscall_buf_t buf0 = uvm32_getbuf_fixed(&vmst, &evt, ARG0, 4);
TEST_ASSERT_EQUAL(4, buf0.len); // check read ok
uint32_t rspa = 0xABCD1234;
memcpy(buf0.ptr, &rspa, 4); // send it back
// get uint32_t buffer described by ptr=ARG1
uvm32_evt_syscall_buf_t buf1 = uvm32_getbuf_fixed(&vmst, &evt, ARG1, 4);
TEST_ASSERT_EQUAL(4, buf1.len); // check read ok
uint32_t rspb = 0x9876DECF;
memcpy(buf1.ptr, &rspb, 4); // send it back
// check the same values are sent back
uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_F);
TEST_ASSERT_EQUAL(rspa, uvm32_getval(&vmst, &evt, ARG0));
TEST_ASSERT_EQUAL(rspb, uvm32_getval(&vmst, &evt, ARG1));
}
void test_syscall_args_buf_pass(void) {
// run the vm
uvm32_run(&vmst, &evt, 1000);
// check for picktest syscall
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_PICKTEST);
uvm32_setval(&vmst, &evt, RET, SYSCALL_G);
uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_G);
// get buffer described by ptr=ARG0,len=ARG1
uvm32_evt_syscall_buf_t buf0 = uvm32_getbuf(&vmst, &evt, ARG0, ARG1);
TEST_ASSERT_EQUAL(32, buf0.len);
// fill with a pattern
for (int i=0;i<32;i++) {
buf0.ptr[i] = i;
}
// expect every element to be doubled then sent back
uvm32_run(&vmst, &evt, 1000);
TEST_ASSERT_EQUAL(evt.typ, UVM32_EVT_SYSCALL);
TEST_ASSERT_EQUAL(evt.data.syscall.code, SYSCALL_H);
// get buffer described by ptr=ARG0,len=ARG1
uvm32_evt_syscall_buf_t buf1 = uvm32_getbuf(&vmst, &evt, ARG0, ARG1);
TEST_ASSERT_EQUAL(32, buf1.len);
for (int i=0;i<32;i++) {
TEST_ASSERT_EQUAL(i*2, buf1.ptr[i]);
}
}