diff --git a/Makefile b/Makefile index d779cd2..49f099a 100644 --- a/Makefile +++ b/Makefile @@ -225,22 +225,22 @@ $(OUT)/test-output-virt.txt: $(OUT)/os_virt @echo "OK" $(OUT)/smoke-test-output-u32.txt: $(OUT)/os_sifive_u32 - @$(QEMU_LAUNCHER) --bootargs smoke-test --timeout=5s --binary=$< > $@ + @$(QEMU_LAUNCHER) --bootargs test-script=/home/smoke-test.sh --timeout=5s --binary=$< > $@ @diff -u testdata/want-smoke-test-output-u32.txt $@ @echo "OK" $(OUT)/smoke-test-output-u64.txt: $(OUT)/os_sifive_u - @$(QEMU_LAUNCHER) --bootargs smoke-test --timeout=5s --binary=$< > $@ + @$(QEMU_LAUNCHER) --bootargs test-script=/home/smoke-test.sh --timeout=5s --binary=$< > $@ @diff -u testdata/want-smoke-test-output-u64.txt $@ @echo "OK" $(OUT)/smoke-test-output-virt.txt: $(OUT)/os_virt - @$(QEMU_LAUNCHER) --bootargs smoke-test --timeout=5s --binary=$< > $@ + @$(QEMU_LAUNCHER) --bootargs test-script=/home/smoke-test.sh --timeout=5s --binary=$< > $@ @diff -u testdata/want-smoke-test-output-virt.txt $@ @echo "OK" $(OUT)/smoke-test-output-tiny-stack.txt: $(OUT)/os_virt - @$(QEMU_LAUNCHER) --bootargs tiny-stack --timeout=5s --binary=$< > $@ + @$(QEMU_LAUNCHER) --bootargs tiny-stack=/home/smoke-test.sh --timeout=5s --binary=$< > $@ @diff -u testdata/want-smoke-test-output-tiny-stack.txt $@ @echo "OK" diff --git a/include/proc.h b/include/proc.h index ead7d37..af0c677 100644 --- a/include/proc.h +++ b/include/proc.h @@ -130,7 +130,6 @@ void swtch(context_t *old, context_t *new); // processes that will get executed by default. Kind of like what an initrd // would do, but a poor man's version until we can do better. void init_test_processes(uint32_t runflags); -void assign_init_program(char const* prog); void init_process_table(uint32_t runflags, unsigned int hart_id); void scheduler(); @@ -242,5 +241,6 @@ void fd_free(process_t *proc, int32_t fd); process_t* find_proc_by_pid(uint32_t pid); regsize_t reoffset_user_stack(process_t *dest, process_t *src, int reg); +void inject_argv(process_t *proc, int argc, char const *argv[]); #endif // ifndef _PROC_H_ diff --git a/include/programs.h b/include/programs.h index 44d4364..f84d4a4 100644 --- a/include/programs.h +++ b/include/programs.h @@ -13,5 +13,6 @@ extern user_program_t userland_programs[MAX_USERLAND_PROGS]; // defined in proc_test.c: user_program_t* find_user_program(char const *name); +void assign_init_program(char const* prog, char const *test_script); #endif // ifndef _PROGRAMS_H_ diff --git a/include/runflags.h b/include/runflags.h index 8187b30..cd3388d 100644 --- a/include/runflags.h +++ b/include/runflags.h @@ -12,6 +12,9 @@ #define RUNFLAGS_SMOKE_TEST ((1 << 2) | RUNFLAGS_TESTS) #define RUNFLAGS_TINY_STACK ((1 << 3) | RUNFLAGS_TESTS) +// defined in runflags.c +extern char const *test_script; + uint32_t parse_runflags(); #endif // ifndef _RUNFLAGS_H_ diff --git a/include/string.h b/include/string.h index 6eaf375..abd9c8f 100644 --- a/include/string.h +++ b/include/string.h @@ -2,6 +2,7 @@ #define _STRING_H_ int strncmp(char const *a, char const *b, unsigned int num); +int has_prefix(char const *str, char const *prefix, int *end_of_prefix); char* strncpy(char *dest, char const *src, unsigned int num); int kstrlen(char const *s); diff --git a/scripts/qemu-launcher.py b/scripts/qemu-launcher.py index ea7aa72..1b86e7d 100755 --- a/scripts/qemu-launcher.py +++ b/scripts/qemu-launcher.py @@ -141,6 +141,19 @@ def make_docker_cmd(is_32bit, is_interactive): return cmd +def is_interactive(args): + if 'test' in args.binary: + return False + if args.bootargs == 'dry-run': + return False + ba = args.bootargs + if not ba: + return True + if ba.startswith('test-script') or ba.startswith('tiny-stack'): + return False + return True + + def make_qemu_command(args): binary = args.binary @@ -156,10 +169,7 @@ def make_qemu_command(args): else: machine = 'sifive_e' - is_interactive = ('test' not in binary) and ( - args.bootargs not in ['dry-run', 'smoke-test', 'tiny-stack'] - ) - cmd = make_docker_cmd(is_32bit, is_interactive) + cmd = make_docker_cmd(is_32bit, is_interactive(args)) # Override our guesses if args were passed explicitly: if args.qemu is not None: diff --git a/src/proc.c b/src/proc.c index 0c036a3..8d3b16c 100644 --- a/src/proc.c +++ b/src/proc.c @@ -222,6 +222,27 @@ sp_argv_t copy_argv(process_t *proc, uintptr_t *sp, regsize_t argc, char const* }; } +// inject_argv assigns a given argc and argv to a given process. It stores the +// values at the end of the page allocated for stack, pushing the stack pointer +// (and thus, the bottom of the stack) below them. That should only ever be +// done to a freshly initialized process, otherwise the bottom of the stack +// would be ruined. Intended for use in the code path for automated tests. +void inject_argv(process_t *proc, int argc, char const *argv[]) { + void *stack_bottom = proc->perrno - argc - 1; + char const **new_argv = stack_bottom; + for (int i = 0; i < argc; i++) { + int len = kstrlen(argv[i]); + stack_bottom -= len + 1; + strncpy(stack_bottom, argv[i], len + 1); + *new_argv = (char const*)USR_STK_VIRT(stack_bottom); + new_argv++; + } + *new_argv = 0; + proc->trap.regs[REG_SP] = STK_ROUND(USR_STK_VIRT(stack_bottom)); + proc->trap.regs[REG_A0] = argc; + proc->trap.regs[REG_A1] = USR_STK_VIRT(new_argv - argc); +} + uintptr_t* _set_perrno(void *sp) { return (uintptr_t*)(sp + user_stack_size) - 1; } diff --git a/src/proc_test.c b/src/proc_test.c index a1d9d13..daf8b87 100644 --- a/src/proc_test.c +++ b/src/proc_test.c @@ -11,7 +11,6 @@ extern int u_main_hello1(); extern int u_main_hello2(); extern int u_main_sysinfo(); extern int u_main_fmt(); -extern int u_main_smoke_test(); extern int u_main_hanger(); extern int u_main_ps(); extern int u_main_cat(); @@ -46,10 +45,6 @@ user_program_t userland_programs[MAX_USERLAND_PROGS] _rodata = { .entry_point = &u_main_fmt, .name = "fmt", }, - (user_program_t){ - .entry_point = &u_main_smoke_test, - .name = "smoke-test", - }, (user_program_t){ .entry_point = &u_main_hanger, .name = "hang", @@ -110,13 +105,13 @@ void init_test_processes(uint32_t runflags) { return; } if (runflags == RUNFLAGS_SMOKE_TEST || runflags == RUNFLAGS_TINY_STACK) { - assign_init_program("smoke-test"); + assign_init_program("sh", test_script); } else { - assign_init_program("sh"); + assign_init_program("sh", 0); } } -void assign_init_program(char const* prog) { +void assign_init_program(char const* prog, char const *test_script) { user_program_t *program = find_user_program(prog); if (!program) { panic("no init program"); @@ -128,6 +123,10 @@ void assign_init_program(char const* prog) { return; } uintptr_t status = init_proc(p0, USR_VIRT(program->entry_point), program->name); + if (test_script != 0) { + char const *args[] = {program->name, "-f", test_script}; + inject_argv(p0, 3, args); + } release(&p0->lock); if (status != 0) { panic("init p0 process"); diff --git a/src/runflags.c b/src/runflags.c index 3f77b3b..4c55582 100644 --- a/src/runflags.c +++ b/src/runflags.c @@ -2,14 +2,20 @@ #include "runflags.h" #include "string.h" +char const *test_script = 0; + uint32_t parse_runflags() { if (!strncmp(fdt_get_bootargs(), "dry-run", ARRAY_LENGTH("dry-run"))) { return RUNFLAGS_DRY_RUN; } - if (!strncmp(fdt_get_bootargs(), "smoke-test", ARRAY_LENGTH("smoke-test"))) { + char const *bootargs = fdt_get_bootargs(); + int end_of_prefix = 0; + if (has_prefix(bootargs, "test-script", &end_of_prefix)) { + test_script = bootargs + end_of_prefix + 1; return RUNFLAGS_SMOKE_TEST; } - if (!strncmp(fdt_get_bootargs(), "tiny-stack", ARRAY_LENGTH("tiny-stack"))) { + if (has_prefix(bootargs, "tiny-stack", &end_of_prefix)) { + test_script = bootargs + end_of_prefix + 1; return RUNFLAGS_TINY_STACK; } return 0; diff --git a/src/string.c b/src/string.c index 65c125e..19d5428 100644 --- a/src/string.c +++ b/src/string.c @@ -25,6 +25,21 @@ int strncmp(char const *a, char const *b, unsigned int num) { return *a - *b; } +// has_prefix checks whether str has a given prefix. Returns 0 if at least one +// character of str is different than the corresponding character in prefix. If +// end_of_prefix is not null, it will be set to the index of the first +// character within str after the prefix. +int has_prefix(char const *str, char const *prefix, int *end_of_prefix) { + int prefix_len = kstrlen(prefix); + if (strncmp(str, prefix, prefix_len)) { + return 0; + } + if (end_of_prefix != 0) { + *end_of_prefix = prefix_len; + } + return 1; +} + char* strncpy(char *dest, char const *src, unsigned int num) { char *orig_dest = dest; for (unsigned int i = 0; i < num - 1 && *src; i++) { diff --git a/testdata/want-smoke-test-output-tiny-stack.txt b/testdata/want-smoke-test-output-tiny-stack.txt index 89be39e..de28959 100644 --- a/testdata/want-smoke-test-output-tiny-stack.txt +++ b/testdata/want-smoke-test-output-tiny-stack.txt @@ -1,23 +1,21 @@ kinit: cpu 0 Reading FDT... FDT ok -bootargs: tiny-stack +bootargs: tiny-stack=/home/smoke-test.sh kprintf test: str=foo, ptr=0xabcdf10a, pos int=1337, neg int=-9223372036854775807 -Init userland smoke test! Total RAM: 48 -Free RAM: 17 -Num procs: 3 +Free RAM: 24 +Num procs: 2 formatted string: num=387, zero=0, char=X, hex=0xaddbeef, str=foo only groks 7 args: 11 12 13 14 15 16 17 %d %d I will hang now, bye Total RAM: 48 -Free RAM: 10 -Num procs: 4 +Free RAM: 17 +Num procs: 3 PID STATE NAME -0 S smoke-test -1 S sh -4 S hang -6 R ps +0 S sh +3 S hang +5 R ps 23 formatted string: num=387, zero=0, char=X, hex=0xaddbeef, str=foo diff --git a/testdata/want-smoke-test-output-u32.txt b/testdata/want-smoke-test-output-u32.txt index f246518..b3567ab 100644 --- a/testdata/want-smoke-test-output-u32.txt +++ b/testdata/want-smoke-test-output-u32.txt @@ -1,23 +1,21 @@ kinit: cpu 0 Reading FDT... FDT ok -bootargs: smoke-test +bootargs: test-script=/home/smoke-test.sh kprintf test: str=foo, ptr=0xabcdf10a, pos int=1337, neg int=-2147483647 -Init userland smoke test! Total RAM: 32 -Free RAM: 23 -Num procs: 3 +Free RAM: 25 +Num procs: 2 formatted string: num=387, zero=0, char=X, hex=0xaddbeef, str=foo only groks 7 args: 11 12 13 14 15 16 17 %d %d I will hang now, bye Total RAM: 32 -Free RAM: 21 -Num procs: 4 +Free RAM: 23 +Num procs: 3 PID STATE NAME -0 S smoke-test -1 S sh -4 S hang -6 R ps +0 S sh +3 S hang +5 R ps 23 formatted string: num=387, zero=0, char=X, hex=0xaddbeef, str=foo diff --git a/testdata/want-smoke-test-output-u64.txt b/testdata/want-smoke-test-output-u64.txt index d5e7cff..ae140f7 100644 --- a/testdata/want-smoke-test-output-u64.txt +++ b/testdata/want-smoke-test-output-u64.txt @@ -1,23 +1,21 @@ kinit: cpu 0 Reading FDT... FDT ok -bootargs: smoke-test +bootargs: test-script=/home/smoke-test.sh kprintf test: str=foo, ptr=0xabcdf10a, pos int=1337, neg int=-9223372036854775807 -Init userland smoke test! Total RAM: 32 -Free RAM: 23 -Num procs: 3 +Free RAM: 25 +Num procs: 2 formatted string: num=387, zero=0, char=X, hex=0xaddbeef, str=foo only groks 7 args: 11 12 13 14 15 16 17 %d %d I will hang now, bye Total RAM: 32 -Free RAM: 21 -Num procs: 4 +Free RAM: 23 +Num procs: 3 PID STATE NAME -0 S smoke-test -1 S sh -4 S hang -6 R ps +0 S sh +3 S hang +5 R ps 23 formatted string: num=387, zero=0, char=X, hex=0xaddbeef, str=foo diff --git a/testdata/want-smoke-test-output-virt.txt b/testdata/want-smoke-test-output-virt.txt index 5244258..d19368e 100644 --- a/testdata/want-smoke-test-output-virt.txt +++ b/testdata/want-smoke-test-output-virt.txt @@ -1,23 +1,21 @@ kinit: cpu 0 Reading FDT... FDT ok -bootargs: smoke-test +bootargs: test-script=/home/smoke-test.sh kprintf test: str=foo, ptr=0xabcdf10a, pos int=1337, neg int=-9223372036854775807 -Init userland smoke test! Total RAM: 48 -Free RAM: 17 -Num procs: 3 +Free RAM: 24 +Num procs: 2 formatted string: num=387, zero=0, char=X, hex=0xaddbeef, str=foo only groks 7 args: 11 12 13 14 15 16 17 %d %d I will hang now, bye Total RAM: 48 -Free RAM: 10 -Num procs: 4 +Free RAM: 17 +Num procs: 3 PID STATE NAME -0 S smoke-test -1 S sh -4 S hang -6 R ps +0 S sh +3 S hang +5 R ps 23 formatted string: num=387, zero=0, char=X, hex=0xaddbeef, str=foo diff --git a/user/src/shell.c b/user/src/shell.c index a25f110..6f6dec7 100644 --- a/user/src/shell.c +++ b/user/src/shell.c @@ -77,7 +77,7 @@ int _userland run_shell_script(char const *filepath, cmdbuf_t cmdpool) { return -1; } char *fbuf = (char*)pgalloc(); - int32_t nread = read(fd, fbuf, PAGE_SIZE); + int32_t nread = read(fd, fbuf, PAGE_SIZE - 32); // reserve 32 bytes for pbuf if (nread == -1) { pgfree(fbuf); prints("ERROR: read=-1\n"); @@ -87,7 +87,7 @@ int _userland run_shell_script(char const *filepath, cmdbuf_t cmdpool) { fbuf[nread] = 0; int start = 0; int end = 0; - char pbuf[32]; + char *pbuf = fbuf + PAGE_SIZE - 32; while (fbuf[end] != 0) { pbuf[0] = 0; int i = 0; @@ -225,7 +225,17 @@ int _userland u_main_shell(int argc, char* argv[]) { cmdbuf_t cmdpool = sh_init_cmd_slots(cmd_slots, num_slots); if (argc > 1) { - int code = run_shell_script(argv[1], cmdpool); + int freeze = 0; + int script_i = 1; + if (argv[1][0] == '-' && argv[1][1] == 'f') { + freeze = 1; + script_i = 2; + } + int code = run_shell_script(argv[script_i], cmdpool); + if (freeze) { + for (;;) + sleep(1000); + } exit(code); return code; } diff --git a/user/src/userland.c b/user/src/userland.c index 751fb26..378087c 100644 --- a/user/src/userland.c +++ b/user/src/userland.c @@ -68,29 +68,6 @@ int _userland u_main_hanger() { return 0; } -int _userland u_main_smoke_test(int argc, char const *argv[]) { - prints("Init userland smoke test!\n"); - char *sh_args[] = {"sh", "/home/smoke-test.sh"}; - uint32_t pid = fork(); - if (pid == -1) { - prints("ERROR: fork!\n"); - } else if (pid == 0) { // child - uint32_t code = execv("sh", (char const**)sh_args); - // normally exec doesn't return, but if it did, it's an error: - prints("ERROR: execv\n"); - exit(code); - } else { // parent - wait(); - } - - // any trailing arg will cause it to exit and not hang forever - if (argc > 1) { - exit(0); - } - for (;;) - ; -} - char ps_header_fmt[] _user_rodata = "PID STATE NAME\n"; char ps_process_info_fmt[] _user_rodata = "%d %c %s\n"; char ps_dash_s_flag[] _user_rodata = "-s";