diff options
| -rw-r--r-- | arch/riscv64/kernel/vmem.c | 8 | ||||
| -rw-r--r-- | include/kmi/regions.h | 2 | ||||
| -rw-r--r-- | src/elf.c | 115 | ||||
| -rw-r--r-- | src/initrd.c | 3 | ||||
| -rw-r--r-- | src/proc.c | 3 | ||||
| -rw-r--r-- | src/regions.c | 10 | ||||
| -rw-r--r-- | src/uapi/proc.c | 35 | ||||
| -rw-r--r-- | src/vmem.c | 18 | ||||
| -rw-r--r-- | tests/common/cpio.h | 99 | ||||
| -rw-r--r-- | tests/common/printf.h | 6 | ||||
| -rw-r--r-- | tests/common/start.h | 3 | ||||
| -rw-r--r-- | tests/common/string.c | 510 | ||||
| -rw-r--r-- | tests/common/string.h | 26 | ||||
| -rw-r--r-- | tests/dangling_shmem/source.mk | 3 | ||||
| -rw-r--r-- | tests/exec/exec.c | 16 | ||||
| -rw-r--r-- | tests/exec/init.c | 38 | ||||
| -rw-r--r-- | tests/exec/source.mk | 1 | ||||
| -rw-r--r-- | tests/fork/source.mk | 2 | ||||
| -rwxr-xr-x | tests/scripts/gen-prog | 32 | ||||
| -rwxr-xr-x | tests/scripts/gen-simple | 25 | ||||
| -rw-r--r-- | tests/scripts/makefile | 8 | ||||
| -rw-r--r-- | tests/shmem/source.mk | 3 |
22 files changed, 849 insertions, 117 deletions
diff --git a/arch/riscv64/kernel/vmem.c b/arch/riscv64/kernel/vmem.c index ee5bbb4..1b70493 100644 --- a/arch/riscv64/kernel/vmem.c +++ b/arch/riscv64/kernel/vmem.c @@ -313,10 +313,12 @@ stat_t map_vpage(struct vmem *branch, pm_t paddr, vm_t vaddr, vmflags_t flags, } size_t idx = vm_to_index(vaddr, top); - assert(!is_branch(branch->leaf[idx])); + if (!__unused((pm_t)branch->leaf[idx])) + return ERR_INVAL; - branch->leaf[idx] = - (struct vmem *)to_pte((pm_t)__pa(paddr), vp_flags(flags)); + branch->leaf[idx] = (struct vmem *)to_pte( + (pm_t)__pa(paddr), + vp_flags(flags)); __add_graves(root, vm_to_index(vaddr, max_order())); return OK; diff --git a/include/kmi/regions.h b/include/kmi/regions.h index 5167d28..74d4387 100644 --- a/include/kmi/regions.h +++ b/include/kmi/regions.h @@ -236,6 +236,8 @@ struct mem_region *find_first_region(struct mem_region_root *r); */ struct mem_region *find_used_region(struct mem_region_root *r, vm_t start); +struct mem_region *find_addr_region(struct mem_region_root *r, vm_t addr); + /** * Find used memory region closest to \c start. * Useful when you don't necessarily need the exact region, just something close @@ -33,6 +33,41 @@ static uint8_t __elf_to_uvflags(uint8_t elf_flags) return uvflags; } +static stat_t __elf_map_section(struct tcb *t, + vm_t va, size_t vaz, + vm_t vf, size_t vfz, + uint8_t flags) +{ + size_t region_size; + vm_t v = alloc_fixed_region(&t->uvmem.region, va, vaz, ®ion_size, flags); + if (!v) + return ERR_INVAL; + + assert(v == align_down(va, BASE_PAGE_SIZE)); + + for (size_t runner = 0; runner < vaz; runner += BASE_PAGE_SIZE) { + pm_t page = alloc_page(BASE_PAGE); + if (!page) + return ERR_OOMEM; + + /* always zero out pages */ + memset((void *)page, 0, BASE_PAGE_SIZE); + + /* sometimes fill page with actual data */ + if (runner < vfz) { + size_t z = MIN(BASE_PAGE_SIZE, vfz - runner); + memcpy((void *)page, (void *)(vf + runner), z); + } + + if (map_vpage(t->proc.vmem, page, va + runner, flags, BASE_PAGE)) { + free_page(BASE_PAGE, page); + return ERR_OOMEM; + } + } + + return OK; +} + /** * Map ELF executable. * @@ -43,58 +78,76 @@ static uint8_t __elf_to_uvflags(uint8_t elf_flags) * @param phnum Number of program header entries. * @param phsize Size of page header entry. */ -static void __map_exec(struct tcb *t, vm_t bin, uint8_t ei_c, vm_t phstart, - size_t phnum, size_t phsize) +static stat_t __map_exec(struct tcb *t, + vm_t bin, + uint8_t ei_c, + vm_t phstart, + size_t phnum, + size_t phsize) { assert(t && is_proc(t)); /* temporarily visit process virtual memory */ use_vmem(t->proc.vmem); + /* create empty vmem so we don't have to worry about possible overlaps */ + struct vmem *new_vmem = create_vmem(); + if (!new_vmem) { + use_vmem(t->rpc.vmem); + return ERR_OOMEM; + } + struct vmem *old_vmem = t->proc.vmem; + t->proc.vmem = new_vmem; + + /* create new uvmem for same reason */ + struct uvmem old_uvmem = t->uvmem; + t->uvmem = (struct uvmem){0}; + + if (init_uvmem(t)) { + destroy_vmem(new_vmem); + t->uvmem = old_uvmem; + t->proc.vmem = old_vmem; + use_vmem(t->rpc.vmem); + return ERR_OOMEM; + } + /** \todo take alignment into consideration? */ - /** \todo take overlapping memory regions into account, probably mostly - * by keeping track of previously allocated area and seeing if the - * segment fits into it */ - /** \todo check if p_memsz is larger than p_filesz, the segment should be - * filled with zeroes. */ - /** \todo in general, make this a lot more clean. */ /* useful bit of info: all segments are sorted in ascending order of p_vaddr */ vm_t runner = phstart; - vmflags_t default_flags = VM_V | VM_R | VM_W | VM_X | VM_U; for (size_t i = 0; i < phnum; ++i, runner += phsize) { if (program_header_prop(ei_c, runner, p_type) != PT_LOAD) continue; + /* where to map section in virtual memory */ vm_t va = program_header_prop(ei_c, runner, p_vaddr); size_t vsz = program_header_prop(ei_c, runner, p_memsz); - vm_t start = alloc_fixed_uvmem(t, va, vsz, default_flags); - if (!start) - return; /* out of memory or something */ - - info("mapped ELF section to %lx\n", (long)start); + /* where section is in binary */ + vm_t vf = bin + program_header_prop(ei_c, runner, p_offset); + vm_t vfz = program_header_prop(ei_c, runner, p_filesz); uint8_t elf_flags = program_header_prop(ei_c, runner, p_flags); uint8_t uvflags = __elf_to_uvflags(elf_flags); - map_region(t->proc.vmem, start, vsz, max_order(), - default_flags); - memset((void *)start, 0, vsz); - - vm_t vo = bin + program_header_prop(ei_c, runner, p_offset); - vm_t vfz = program_header_prop(ei_c, runner, p_filesz); - memcpy((void *)va, (void *)vo, vfz); + if (__elf_map_section(t, va, vsz, vf, vfz, uvflags)) { + destroy_uvmem(t); - /* skip while testing - * \todo: also fix, this modifies only the first region. Create new - * function? - * - pm_t paddr = 0; - stat_vpage(t->b_r, va, &paddr, 0, 0); - mod_vpage(t->b_r, va, paddr, uvflags); - */ + t->proc.vmem = old_vmem; + t->uvmem = old_uvmem; + use_vmem(t->rpc.vmem); + return ERR_OOMEM; + } } + /* destroy old uvmem (kind of annoying to have it so agressively tied to + * the tcb but I guess it's find for now) */ + struct uvmem new_uvmem = t->uvmem; + t->uvmem = old_uvmem; + destroy_uvmem(t); + + t->proc.vmem = new_vmem; + t->uvmem = new_uvmem; use_vmem(t->rpc.vmem); + return OK; } /** @@ -140,7 +193,9 @@ static vm_t __prepare_proc(struct tcb *t, uint8_t ei_c, vm_t elf, vm_t interp) vm_t entry = elf_header_prop(ei_c, elf, e_entry); if (e_type == ET_EXEC) { - __map_exec(t, elf, ei_c, phstart, phnum, phsize); + if (__map_exec(t, elf, ei_c, phstart, phnum, phsize)) + return 0; + return entry; } else { vm_t o = __map_dyn(t, elf, ei_c, phstart, phnum, phsize); diff --git a/src/initrd.c b/src/initrd.c index 489d6f5..e1a0c1e 100644 --- a/src/initrd.c +++ b/src/initrd.c @@ -96,6 +96,9 @@ static struct cpio_header *__find_file(const char *c, const char *fname, continue; char *name = (char *)(cp + 1); + if (strcmp(name, "TAILER!!!") == 0) + return NULL; + if (fname[0] != '/') name += namelen - (fname_len + 1); /* match ending */ @@ -67,11 +67,14 @@ stat_t init_proc(void *fdt, vm_t *proc_fdt, vm_t *proc_initrd) *proc_fdt = map_shared_fixed_uvmem(t, (pm_t)fdt, fdt_totalsize(fdt), VM_V | VM_R | VM_U); + assert(*proc_fdt); pm_t initrd = (pm_t)__va(get_initrdbase(fdt)); *proc_initrd = map_shared_fixed_uvmem(t, initrd, get_initrdsize(fdt), VM_V | VM_R | VM_U); + assert(proc_initrd); + info("mapped fdt at %lx\n", *proc_fdt); info("mapped initrd at %lx\n", *proc_initrd); return OK; diff --git a/src/regions.c b/src/regions.c index 2db1a91..2c11db0 100644 --- a/src/regions.c +++ b/src/regions.c @@ -241,6 +241,16 @@ struct mem_region *find_used_region(struct mem_region_root *r, vm_t start) return 0; } +struct mem_region *find_addr_region(struct mem_region_root *r, vm_t addr) +{ + size_t ref = __page(addr); + struct mem_region *m = find_closest_used_region(r, addr); + if (!m || (ref < m->start || ref > m->end) || !is_region_used(m)) + return NULL; + + return m; +} + /** * Create memory region. * diff --git a/src/uapi/proc.c b/src/uapi/proc.c index 3b65a04..8d90687 100644 --- a/src/uapi/proc.c +++ b/src/uapi/proc.c @@ -108,46 +108,27 @@ SYSCALL_DEFINE2(exec)(struct tcb *t, sys_arg_t bin, sys_arg_t interp) return_args1(t, ERR_PERM); /* exec is only allowed if we own all our own resources */ - if (t->refcount) + if (t->refcount != 1) return_args1(t, ERR_INVAL); /* mark binary to be kept */ - struct mem_region *b = find_used_region(&t->uvmem.region, bin); + struct mem_region *b = find_addr_region(&t->uvmem.region, bin); if (!b) return_args1(t, ERR_ADDR); - set_bit(b->flags, MR_KEEP); - - struct mem_region *i = 0; + struct mem_region *i = NULL; if (interp) { /* mark interpreter to be kept */ - i = find_used_region(&t->uvmem.region, interp); + i = find_addr_region(&t->uvmem.region, interp); if (!i) - return_args1(t, ERR_INVAL); + return_args1(t, ERR_ADDR); - set_bit(i->flags, MR_KEEP); } - /* free everything except regions to be kept */ - clear_uvmem(t); - - /* restore to normal */ - clear_bit(b->flags, MR_KEEP); - if (interp) - clear_bit(b->flags, MR_KEEP); - - /* should hopefully never actually fail, but if it does, we don't really - * have any choice but to kill the thread. */ - if (prepare_proc(t, bin, interp)) { - /* this kills the thread */ - orphanize(t); - unorphanize(t); - /* should never be reached as we control the thread so we should - * be able to directly jump to pid 1 */ - assert(false); - } + if (prepare_proc(t, bin, interp)) + return_args1(t, ERR_INVAL); - return_args4(t, 0, t->tid, SYS_USER_SPAWNED, t->pid); + set_ret4(t, 0, t->tid, SYS_USER_SPAWNED, t->pid); } /** @@ -166,8 +166,8 @@ static vm_t __clone_shared_region(struct tcb *d, struct tcb *s, size_t size = end - start; vm_t v = alloc_shared_region(&d->uvmem.region, size, &size, MR_NONBACKED | m->flags, s->rid); - if (ERR_CODE(v)) - return v; + if (!v) + return 0; stat_t res = clone_region(d->uvmem.vmem, s->uvmem.vmem, start, v, size, flags); @@ -329,10 +329,10 @@ vm_t map_shared_fixed_uvmem(struct tcb *t, pm_t start, size_t size, { assert(is_aligned(start, BASE_PAGE_SIZE)); - const vm_t v = alloc_shared_region(&t->uvmem.region, size, &size, flags, - 0); - if (ERR_CODE(v)) - return v; + const vm_t v = alloc_shared_region(&t->uvmem.region, + size, &size, flags, 0); + if (!v) + return 0; stat_t ret = OK; if ((ret = map_fixed_region(t->proc.vmem, v, start, size, flags))) { @@ -438,10 +438,8 @@ void handle_pagefault(vm_t addr) } struct tcb *p = get_cproc(t); - size_t ref = __page(addr); - - struct mem_region *m = find_closest_used_region(&p->uvmem.region, addr); - if (!m || (ref < m->start || ref > m->end) || !is_region_used(m)) { + struct mem_region *m = find_addr_region(&p->uvmem.region, addr); + if (!m) { error("cannot handle actual page fault just yet :(\n"); kernel_panic(NULL, NULL, 0); return; diff --git a/tests/common/cpio.h b/tests/common/cpio.h new file mode 100644 index 0000000..708ce18 --- /dev/null +++ b/tests/common/cpio.h @@ -0,0 +1,99 @@ +#include <kmi/utils.h> + +/** GNU cpio, POSIX 'newc' format header. */ +struct __packed cpio_header { + /** Magic bytes. */ + char c_magic[6]; + + /** File inode. */ + char c_ino[8]; + + /** File type and permissions. */ + char c_mode[8]; + + /** User ID. */ + char c_uid[8]; + + /** Group ID. */ + char c_gid[8]; + + /** Number of links to this file. */ + char c_nlink[8]; + + /** Modification time. */ + char c_mtime[8]; + + /** Size of file. */ + char c_filesize[8]; + + /** Device major. */ + char c_devmajor[8]; + + /** Device minor. */ + char c_devminor[8]; + + /** Block device major. */ + char c_rdevmajor[8]; + + /** Block device minor. */ + char c_rdevminor[8]; + + /** Length of filename. */ + char c_namesize[8]; + + /** CRC check. */ + char c_check[8]; +}; + +/** + * Get next file in archive. + * Does not check for out of bounds. + * + * @param cp Pointer to current file header. + * @return Pointer to next file header. + */ +static inline struct cpio_header *cpio_next_entry(struct cpio_header *cp) +{ + size_t blen = align_up(sizeof(struct cpio_header) + + convnum(cp->c_namesize, 8, 16), 4); + size_t tlen = align_up(convnum(cp->c_filesize, 8, 16), 4); + + return (struct cpio_header *)(((char *)cp) + blen + tlen); +} + +/** + * Get file with name in archive. + * + * @param c Pointer to initrd. + * @param fname Filename to look for. + * @param fname_len Length of filename. + * @return Pointer to corresponding file header if found, \c NULL otherwise. + */ +static inline struct cpio_header *cpio_find_file(const char *c, + const char *fname, + size_t fname_len) +{ + struct cpio_header *cp = (struct cpio_header *)c; + for (; cp; cp = cpio_next_entry(cp)) { + size_t namelen = convnum(cp->c_namesize, 8, 16); + if (namelen == 0) + return NULL; + + if (namelen < fname_len) + continue; + + + char *name = (char *)(cp + 1); + if (strcmp(name, "TRAILER!!!") == 0) + return NULL; + + if (fname[0] != '/') + name += namelen - (fname_len + 1); /* match ending */ + + if (strncmp(name, fname, fname_len) == 0) + return cp; + } + + printf("???\n"); + return NULL; +} diff --git a/tests/common/printf.h b/tests/common/printf.h index b6e50e5..e1b3b43 100644 --- a/tests/common/printf.h +++ b/tests/common/printf.h @@ -1,8 +1,8 @@ -#ifndef KMI_PRINTF_H -#define KMI_PRINTF_H +#ifndef KMI_TESTS__PRINTF_H +#define KMI_TESTS__PRINTF_H #include <kmi/attrs.h> int printf(const char *fmt, ...) __printf; -#endif /* KMI_PRINTF_H */ +#endif /* KMI_TESTS_PRINTF_H */ diff --git a/tests/common/start.h b/tests/common/start.h index 3a49686..923d9ab 100644 --- a/tests/common/start.h +++ b/tests/common/start.h @@ -1,7 +1,8 @@ #ifndef KMI_START_H #define KMI_START_H -#define UNUSED(x) (void)x +#include <kmi/utils.h> + #define START(pid, tid, d0, d1, d2, d3)\ void _start(sys_arg_t pid, sys_arg_t tid,\ sys_arg_t d0, sys_arg_t d1, sys_arg_t d2, sys_arg_t d3) diff --git a/tests/common/string.c b/tests/common/string.c new file mode 100644 index 0000000..e1519b0 --- /dev/null +++ b/tests/common/string.c @@ -0,0 +1,510 @@ +/* copied straight from the kernel */ +#include <common/string.h> +#include <kmi/utils.h> +#include <kmi/types.h> + +char *strcpy(char * restrict dst, const char * restrict src) +{ + const char *s1 = src; + char *s2 = dst; + + while (*s1) + *(s2++) = *(s1++); + + return dst; +} + +char *strncpy(char * restrict dst, const char * restrict src, size_t num) +{ + const char *s1 = src; + char *s2 = dst; + + /* copy s1 into s2 */ + while (num-- && *s1) + *(s2++) = *(s1++); + + /* the previous loop always overshoots by one */ + num++; + + /* pad with zeroes if num is not yet zero */ + while (num--) + *(s2++) = 0; + + return dst; +} + +char *strcat(char * restrict dst, const char * restrict src) +{ + const char *s1 = src; + size_t l1 = strlen(s1); + char *s2 = dst + l1; + + while (*s1) + *(s2++) = *(s1++); + + /* append null character */ + *s2 = 0; + + return dst; +} + +char *strncat(char * restrict dst, const char * restrict src, size_t num) +{ + const char *s1 = src; + size_t l1 = strlen(s1); + char *s2 = dst + l1; + + while (num-- && *s1) + *(s2++) = *(s1++); + + /* append null character */ + *s2 = 0; + + return dst; +} + +int strcmp(const char *str1, const char *str2) +{ + const char *s1 = (const char *)str1; + const char *s2 = (const char *)str2; + + while ((*(s1++) == *(s2++)) && *s1 && *s2) + ; + + return (int)(s1[-1] - s2[-1]); +} + +int strncmp(const char *str1, const char *str2, size_t num) +{ + const char *s1 = (const char *)str1; + const char *s2 = (const char *)str2; + + while ((*(s1++) == *(s2++)) && *s1 && *s2 && --num) + ; + + return (int)(s1[-1] - s2[-1]); +} + +char *strchr(const char *str, int chr) +{ + const char *s1 = str; + ssize_t num = strlen(s1); + + while (num-- && *(s1++) != chr) + ; + + if (num < 0) + return 0; + + return (char *)(s1 - 1); +} + +char *strtok(char * restrict str, const char * restrict delims) +{ + static char *cont = 0; + const char *s1 = str; + + if (!s1) + s1 = cont; + + if (!s1) + return 0; + + s1 = strpbrk(s1, delims); + + if (!s1) + cont = 0; + else + cont = (char *)s1 + 1; + + return (char *)s1; +} + +/* should probably test out these functions somehwere, blergh */ +char *strstr(const char *str1, const char *str2) +{ + /* boyer-moore-horspool */ + char table[256] = { 0 }; + size_t sl = strlen(str1); + size_t pl = strlen(str2); + + const unsigned char *s1 = (const unsigned char *)str1; + const unsigned char *haystack = (const unsigned char *)s1; + const unsigned char *needle = (const unsigned char *)str2; + + for (size_t i = 0; i < 256; ++i) + table[i] = pl; + + /* generate deltas */ + for (size_t i = 0; i < pl - 1; ++i) + table[needle[i]] = pl - i - 1; + + size_t skip = 0; + while (sl - skip >= pl) { + s1 = &haystack[skip]; + + if (!memcmp(s1, needle, pl)) + return (char *)s1; + + skip += table[haystack[skip + pl - 1]]; + } + + return 0; +} + +char *strrchr(const char *str, int chr) +{ + ssize_t num = strlen(str); + const char *s1 = (str + num) - 1; + + while (num-- && *(s1--) != chr) + ; + + if (num < 0) + return 0; + + return (char *)(s1 + 1); +} + +char *strpbrk(const char *str1, const char *str2) +{ + size_t i = strcspn(str1, str2); + + if (!i) + return 0; + + return (char *)(str1 + i); +} + +size_t strcspn(const char *str1, const char *str2) +{ + char table[256] = { 0 }; + const unsigned char *s1 = (const unsigned char *)str1; + const unsigned char *s2 = (const unsigned char *)s1; + const unsigned char *t1 = (const unsigned char *)str2; + + /* populate table */ + while (*(t1++)) + table[*t1] = 1; + + for (;;) { + if (table[*(s2++)]) + break; + } + + /* the for loop overshoots by one */ + return (size_t)(s2 - s1) - 1; +} + +size_t strspn(const char *str1, const char *str2) +{ + char table[256] = { 0 }; + const unsigned char *s1 = (const unsigned char *)str1; + const unsigned char *s2 = (const unsigned char *)s1; + const unsigned char *t1 = (const unsigned char *)str2; + + /* populate table */ + while (*(t1++)) + table[*t1] = 1; + + for (;;) { + if (!table[*(s2++)]) + break; + } + + /* the for loop overshoots by one */ + return (size_t)(s2 - s1) - 1; +} + +size_t strlen(const char *str) +{ + const char *s1 = str; + while (*(s1++)) + ; + + /* the loop overshoots by one */ + return (size_t)(s1 - str) - 1; +} + +/* not a macro */ +size_t strnlen(const char *str, size_t num) +{ + const char *s1 = str; + while (num-- && *(s1++)) + ; + + return (size_t)(s1 - str) - 1; +} + +void *memchr(const void *ptr, int val, size_t num) +{ + const char *p1 = (char *)ptr; + ssize_t n = num; + char c = (char)val; + + while (n-- && *(p1++) != c) + ; + + if (n < 0) + return 0; + + return (void *)(p1 - 1); +} + +/** @defgroup group1 Optimized specializations + * + * The two most used memory operations get their special optimized versions for + * aligned manipulations, which is what the vast majority of our huge operations + * like page zeroing are. + * + * @{ + */ + +/** How many longs per iteration we want to process. An address and size must be + * aligned to MAGIC_NUMBER * sizeof(long) bytes. */ +#define MAGIC_NUMBER 8 + +/** + * Optimized version of memcpy() for big regions. + * All parameters must be aligned to MAGIC_NUMBER * sizeof(long). + * + * @param dst Destination to copy to. + * @param src Source to copy from. + * @param num Number of bytes to copy. + * @return dst + */ +static inline void *__aligned_memcpy(long *restrict dst, + const long *restrict src, size_t num) +{ + size_t count = num / sizeof(long); + for (size_t i = 0; i < count; i += MAGIC_NUMBER) { + dst[i + 0] = src[i + 0]; + dst[i + 1] = src[i + 1]; + dst[i + 2] = src[i + 2]; + dst[i + 3] = src[i + 3]; + dst[i + 4] = src[i + 4]; + dst[i + 5] = src[i + 5]; + dst[i + 6] = src[i + 6]; + dst[i + 7] = src[i + 7]; + /* would be kind of cool if there was some kind of forced unrolling, + * as we need to do MAGIC_NUMBER of steps, preferably even with + * optimizations disabled. For now I guess just be careful that + * MAGIC_NUMBER matches the number of steps within loop */ + } + + return dst; +} + +/** + * Optimized version of memset() for big regions. + * \p ptr and \p num must be aligned to MAGIC_NUMBER * sizeof(long). + * + * @param ptr Destination to write to. + * @param value Value (converted to unsigned char) to write. + * @param num Number of bytes to write. + * @return ptr + */ +static inline void *__aligned_memset(long *ptr, int value, size_t num) +{ + long bits = 0; + for (size_t i = 0; i < sizeof(long) / sizeof(unsigned char); ++i) + bits |= ((unsigned char)value) << i * CHAR_BIT; + + size_t count = num / sizeof(long); + for (size_t i = 0; i < count; i += MAGIC_NUMBER) { + ptr[i + 0] = bits; + ptr[i + 1] = bits; + ptr[i + 2] = bits; + ptr[i + 3] = bits; + ptr[i + 4] = bits; + ptr[i + 5] = bits; + ptr[i + 6] = bits; + ptr[i + 7] = bits; + } + + return ptr; +} + +/** @} */ + +void *memcpy(void * restrict dst, const void * restrict src, + size_t num) +{ + + if (is_aligned((uintptr_t)dst, sizeof(long) * MAGIC_NUMBER) + && is_aligned((uintptr_t)src, sizeof(long) * MAGIC_NUMBER) + && is_aligned(num, sizeof(long) * MAGIC_NUMBER)) + return __aligned_memcpy(dst, src, num); + + const char *m1 = (const char *)src; + char *m2 = (char *)dst; + + while (num--) + *(m2++) = *(m1++); + + return dst; +} + +void *memset(void *ptr, int value, size_t num) +{ + if (is_aligned((uintptr_t)ptr, sizeof(long) * MAGIC_NUMBER) + && is_aligned(num, sizeof(long) * MAGIC_NUMBER)) + return __aligned_memset(ptr, value, num); + + char *p = ptr; + char c = value; + + while (num--) + *(p++) = c; + + return ptr; +} + + +void *memmove(void *dst, const void *src, size_t num) +{ + const char *m1 = (const char *)src; + char *m2 = (char *)dst; + + m1 += num; + m2 += num; + + /* doesn't really take into account aliasing yet */ + while (num--) + *(--m2) = *(--m1); + + return dst; +} + +int memcmp(const void *ptr1, const void *ptr2, size_t num) +{ + const char *p1 = (const char *)ptr1; + const char *p2 = (const char *)ptr2; + + while ((*(p1++) == *(p2++)) && --num) + ; + + return (int)(p1[-1] - p2[-1]); +} + +/** + * Convert ASCII hex character to integer. + * Allows both upper- and lowercase letters. + * + * @param c Character to convert. + * @return Corresponding integer value. That is, '1' => 1, '2' => 2, etc. + * \c -1 if conversion failed. + */ +static int __hexval(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + + if (c >= 'a' && c <= 'f') + return 10 + c - 'a'; + + if (c >= 'A' && c <= 'F') + return 10 + c - 'A'; + + return -1; +} + +/** + * Convert string assumed to represent hex + * value to corresponding pointer. + * + * @param s String to convert to value. + * @return Corresponding pointer value. + */ +static uintptr_t __hexuintptr(const char *s) +{ + uintptr_t res = 0; + int val = 0; + while ((val = __hexval(*(s++))) != -1) { + res *= 16; + res += val; + } + + return res; +} + +/** + * Convert ASCII decimal character to integer. + * + * @param c Character to convert. + * @return Corresponding integer value. That is, '1' => 1, '2' => 2, etc. + * \c -1 if conversion failed. + */ +static int __decval(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + + return -1; +} + +/** + * Convert string assumed to represent decimal + * value to corresponding pointer. + * + * @param s String to convert to value. + * @return Corresponding pointer value. + */ +static uintptr_t __decuintptr(const char *s) +{ + uintptr_t res = 0; + int val = 0; + while ((val = __decval(*(s++))) != -1) { + res *= 10; + res += val; + } + + return res; +} + +static int __octval(char c) +{ + if (c >= '0' && c <= '7') + return c - '0'; + + return -1; +} + +static uintptr_t __octuintptr(const char *s) +{ + uintptr_t res = 0; + int val = 0; + while ((val = __octval(*(s++))) != -1) { + res *= 8; + res += val; + } + + return res; +} + +uintptr_t strtouintptr(const char *s) +{ + if (!s) + return 0; + + if (s[0] == 0) + return 0; + + if (s[0] == '0') { + if (s[1] == 0) + return 0; + + if (s[1] == 'x' || s[1] == 'X') + return __hexuintptr(s + 2); + + return __octuintptr(s + 1); + } + + if (s[0] == '-') + return -__decuintptr(s + 1); + + if (s[0] == '+') + return __decuintptr(s + 1); + + return __decuintptr(s); +} diff --git a/tests/common/string.h b/tests/common/string.h new file mode 100644 index 0000000..9a6dbeb --- /dev/null +++ b/tests/common/string.h @@ -0,0 +1,26 @@ +#ifndef KMI_TESTS_STRING_H +#define KMI_TESTS_STRING_H + +#include <kmi/types.h> + +char *strcpy(char * restrict dst, const char * restrict src); +char *strncpy(char * restrict dst, const char * restrict src, size_t num); +char *strncat(char * restrict dst, const char * restrict src, size_t num); +int strcmp(const char *str1, const char *str2); +int strncmp(const char *str1, const char *str2, size_t num); +char *strchr(const char *str, int chr); +char *strtok(char * restrict str, const char * restrict delims); +char *strstr(const char *str1, const char *str2); +char *strrchr(const char *str, int chr); +char *strpbrk(const char *str1, const char *str2); +size_t strspn(const char *str1, const char *str2); +size_t strcspn(const char *str1, const char *str2); +size_t strlen(const char *str); +size_t strnlen(const char *str, size_t num); +void *memset(void *ptr, int value, size_t num); +void *memchr(const void *ptr, int val, size_t num); +void *memcpy(void * restrict dst, const void * restrict src, size_t num); +void *memmove(void *dst, const void *src, size_t num); +int memcmp(const void *ptr1, const void *ptr2, size_t num); +uintptr_t strtouintptr(const char *s); +#endif /* KMI_TESTS_STRING_H */ diff --git a/tests/dangling_shmem/source.mk b/tests/dangling_shmem/source.mk index 2ecd7a6..35785ed 100644 --- a/tests/dangling_shmem/source.mk +++ b/tests/dangling_shmem/source.mk @@ -1,2 +1 @@ -DO != ./scripts/gen-prog -n dangling_shmem -p init init.c -DO != ./scripts/gen-simple -n dangling_shmem -p init +TESTS += dangling_shmem diff --git a/tests/exec/exec.c b/tests/exec/exec.c new file mode 100644 index 0000000..4f92b27 --- /dev/null +++ b/tests/exec/exec.c @@ -0,0 +1,16 @@ +#include <common/test.h> + +START(pid, tid, d0, d1, d2, d3) +{ + UNUSED(d2); + UNUSED(d3); + + printf("pid = %ld, tid = %ld, d0 = %ld, d1 = %ld\n", + pid, tid, d0, d1); + + check(pid == 0, "unexpected pid for exec\n"); + check(tid == 2, "unexpected tid for exec\n"); + check(d0 == SYS_USER_SPAWNED, "unexpected d0 for exec\n"); + check(d1 == 2, "unexpected d1 for exec\n"); + ok(); +} diff --git a/tests/exec/init.c b/tests/exec/init.c new file mode 100644 index 0000000..d56337d --- /dev/null +++ b/tests/exec/init.c @@ -0,0 +1,38 @@ +#include <common/test.h> +#include <common/cpio.h> + +START(pid, tid, d0, d1, d2, d3) +{ + UNUSED(pid); + UNUSED(tid); + UNUSED(d0); + UNUSED(d1); + UNUSED(d2); + UNUSED(d3); + + check(pid == 0, "illegal pid for init\n"); + id_t our_id = 0; + printf("forking\n"); + id_t new_id = sys_fork(&our_id); + check(new_id >= 0, "error from fork\n"); + + if (new_id == 0) { + printf("hello from child with pid %ld\n", (long int)our_id); + printf("finding exec in cpio archive %lx...\n", d2); + struct cpio_header *cp = cpio_find_file((const void *)d2, + "exec", sizeof("exec") - 1); + + check(cp, "couldn't find exec?\n"); + long name_len = convnum(cp->c_namesize, 8, 16); + uintptr_t exec = (uintptr_t)(cp) + align_up(sizeof(struct cpio_header) + name_len, 4); + printf("found exec at %lx\n", exec); + + printf("doing exec...\n"); + sys_exec(exec, 0); + error("exec failed\n"); + } + + printf("hello from parent\n"); + sys_swap(new_id); + check(0, "failed swapping to child\n"); +} diff --git a/tests/exec/source.mk b/tests/exec/source.mk new file mode 100644 index 0000000..fdd3d0e --- /dev/null +++ b/tests/exec/source.mk @@ -0,0 +1 @@ +DO != ./scripts/gen-simple -n exec -p init -p exec diff --git a/tests/fork/source.mk b/tests/fork/source.mk index e78d467..8e067b7 100644 --- a/tests/fork/source.mk +++ b/tests/fork/source.mk @@ -1 +1 @@ -TEST += fork +TESTS += fork diff --git a/tests/scripts/gen-prog b/tests/scripts/gen-prog deleted file mode 100755 index 6e99f37..0000000 --- a/tests/scripts/gen-prog +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh - -NAME= -PROG= -while getopts "n:p:" opt; do - case "$opt" in - n) NAME="$OPTARG";; - p) PROG="$OPTARG";; - *) echo "unrecognised options -$OPTARG" >&2; exit 1; - esac -done - -shift $((OPTIND - 1)) - -# create all subdirectories -mkdir -p $(echo "${@}" | tr ' ' '\n' | sed "s|[^/]*$||;s|^|build/$NAME|" | uniq) - -for s in ${@} -do - obj="build/${NAME}/${s%.*}.o" - dep="${obj}.d" - - echo "${NAME}_OBJS += ${obj}" >> tests.mk - echo "${dep}:" >> tests.mk - echo "-include ${dep}" >> tests.mk - echo "${obj}: $NAME/${s} \$(COMMON)" >> tests.mk - echo " \$(COMPILE_TEST) -c $NAME/${s} -o ${obj}" >> tests.mk -done - -echo "build/${NAME}/$PROG: \$(${NAME}_OBJS) \$(COMMON)" >> tests.mk -echo " \$(COMPILE_TEST) \$(${NAME}_OBJS) \$(COMMON) \ - -o build/${NAME}/$PROG" >> tests.mk diff --git a/tests/scripts/gen-simple b/tests/scripts/gen-simple index a942fb3..fe2d7a4 100755 --- a/tests/scripts/gen-simple +++ b/tests/scripts/gen-simple @@ -10,6 +10,8 @@ while getopts "n:p:" opt; do esac done +mkdir -p "build/$NAME" + if [ -z "$NAME" ]; then echo "No name given for tests" >&2; exit 2; fi @@ -18,13 +20,28 @@ if [ -z "$PROGS" ]; then echo "No programs to add to initrd" >&2; exit 3; fi -echo "build/$NAME/initrd: build/$NAME/init \$(COMMON) \$(KMI)" >> tests.mk -echo " echo build/$NAME/init | \$(GEN_INITRD) build/$NAME/initrd" >> tests.mk +for p in $PROGS +do + dep="build/${NAME}/${p}.d" + + echo "${NAME}_PROGS += build/${NAME}/${p}" >> tests.mk + echo "${dep}:" >> tests.mk + echo "-include ${dep}" >> tests.mk + echo "build/${NAME}/${p}: $NAME/$p.c \$(COMMON)" >> tests.mk + echo " \$(COMPILE_TEST) $NAME/${p}.c \$(COMMON) \\" >> tests.mk + echo " -o build/${NAME}/${p}" >> tests.mk +done + +echo "build/$NAME/initrd: \$(${NAME}_PROGS) \$(COMMON) \$(KMI)" >> tests.mk +echo " for p in \$(${NAME}_PROGS); do \\" >> tests.mk +echo " echo \$\$p; \\" >> tests.mk +echo " done | \$(GEN_INITRD) build/$NAME/initrd" >> tests.mk + echo "reports/$NAME/log: build/$NAME/initrd" >> tests.mk echo " mkdir -p reports/$NAME" >> tests.mk echo " rm -f reports/$NAME/*" >> tests.mk -echo " timeout --foreground 30s \$(QEMU) \ - build/$NAME/initrd > reports/$NAME/log" >> tests.mk +echo " timeout --foreground 30s \$(QEMU) \\" >> tests.mk +echo " build/$NAME/initrd > reports/$NAME/log" >> tests.mk echo "TESTS += $NAME" >> tests.mk echo ".PHONY: $NAME" >> tests.mk diff --git a/tests/scripts/makefile b/tests/scripts/makefile index ed2b2b4..63150b1 100644 --- a/tests/scripts/makefile +++ b/tests/scripts/makefile @@ -15,7 +15,8 @@ OBFLAGS := -ffreestanding -nostdlib -std=c17 -g -O2 INCLUDEFLAGS := -I ../include -I. WARNFLAGS := -Wall -Wextra DEPFLAGS = -MT $@ -MMD -MP -MF $@.d -COMPILE_TEST = $(COMPILER) $(WARNFLAGS) $(INCLUDEFLAGS) $(DEPFLAGS) $(OBFLAGS) $(ARCH_FLAGS) +COMPILE_TEST = $(COMPILER) $(WARNFLAGS) $(INCLUDEFLAGS) \ + $(DEPFLAGS) $(OBFLAGS) $(ARCH_FLAGS) GEN_INITRD := cpio -H newc -o > QEMU := qemu-system-$(ARCH) -machine virt -kernel ../kmi.bin \ @@ -27,11 +28,14 @@ QEMU := qemu-system-$(ARCH) -machine virt -kernel ../kmi.bin \ -initrd KMI := ../kmi.bin -COMMON := build/printf.o +COMMON := build/printf.o build/string.o build/printf.o: common/printf.c $(KMI) $(COMPILE_TEST) -c common/printf.c -o build/printf.o +build/string.o: common/string.c $(KMI) + $(COMPILE_TEST) -c common/string.c -o build/string.o + include tests.mk .PHONY: check diff --git a/tests/shmem/source.mk b/tests/shmem/source.mk index da84808..0fd9bee 100644 --- a/tests/shmem/source.mk +++ b/tests/shmem/source.mk @@ -1,2 +1 @@ -DO != ./scripts/gen-prog -n shmem -p init init.c -DO != ./scripts/gen-simple -n shmem -p init +TESTS += shmem |
