diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2026-01-25 17:14:53 +0200 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2026-01-25 17:14:53 +0200 |
| commit | cd41ed1758f4842d7b78870385e16b579cc89c38 (patch) | |
| tree | 271e1b437ec5a1511f4ffa400da447122576b94b /tests/dmem/init.c | |
| parent | e8a7fe3bd5c40168d531a6a4f00a7417a278d58c (diff) | |
| download | kmi-master.tar.gz kmi-master.zip | |
+ Add tests and fix virtual mapping, this is enough to start writing
proper userspace drivers
Diffstat (limited to 'tests/dmem/init.c')
| -rw-r--r-- | tests/dmem/init.c | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/tests/dmem/init.c b/tests/dmem/init.c new file mode 100644 index 0000000..c5d13f4 --- /dev/null +++ b/tests/dmem/init.c @@ -0,0 +1,146 @@ +#include <common/test.h> +#include <kmi/utils.h> +#include <libfdt.h> + +/* important bits taken from src/debug.c */ +#define UART_8250_DATA 0 +#define UART_8250_LSR 5 +#define LSR_THRE (1 << 5) + +static bool __tx_empty(char *ptr, size_t shift) +{ + volatile char *lsr = ptr + (UART_8250_LSR << shift); + return (*lsr) & LSR_THRE; +} + +static void __print_char(char *ptr, size_t shift, char c) +{ + while (__tx_empty(ptr, shift) == 0) + ; + + volatile char *data = ptr + (UART_8250_DATA << shift); + *data = c; +} + +static void __print_ok(char *ptr, size_t shift) +{ + /* theoretically the kernel might jump in and print something itself, + * but for now let's ignore that possibility. In the future, we might + * want to move printing completely to userspace to avoid that issue? */ + __print_char(ptr, shift, 'O'); + __print_char(ptr, shift, 'K'); + __print_char(ptr, shift, '\n'); +} + +static bool __supported(const char *dev_name) +{ + /* qemu, for example */ + if (strncmp("ns16550", dev_name, 7) == 0) + return true; + + /* mentioned in dtc documentation as an example */ + if (strncmp("ns8250", dev_name, 7) == 0) + return true; + + /* starfive visionfive 2, for example (hopefully works) */ + if (strncmp("snps,dw-apb-uart", dev_name, 16) == 0) + return true; + + return false; +} + +START(pid, tid, d0, d1, d2, d3) +{ + UNUSED(d2); + UNUSED(d3); + + /* this starting bit is more or less directly shared with the fdt test, + * but that can be seen as a requirement for the dmem setup to work I + * guess? */ + check(pid == 0, "wrong src process ID"); + check(tid == 1, "wrong root thread ID"); + check(d0 == SYS_USER_SPAWNED, "wrong op"); + printf("fdt at %lx\n", d1); + + void *fdt = (void *)d1; + + int chosen_offset = fdt_path_offset(fdt, "/chosen"); + check(chosen_offset > 0, "couldn't find /chosen"); + + const char *stdout = + fdt_getprop(fdt, chosen_offset, "stdout-path", NULL); + + check(stdout != NULL, "couldn't find stdout-path"); + printf("stdout at %s\n", stdout); + + /* discard options */ + size_t baselen = strlen(stdout); + const char *options = strchr(stdout, ':'); + if (options) + baselen = options - stdout; + + int stdout_offset = fdt_path_offset_namelen(fdt, stdout, baselen); + check(stdout_offset > 0, "couldn't find %s offset", stdout); + + /* get serial device type */ + const char *dev_name = (const char *)fdt_getprop(fdt, stdout_offset, + "compatible", NULL); + check(dev_name != NULL, "couldn't get %s devicename", stdout); + printf("devname %s\n", dev_name); + + check(__supported(dev_name), "unsupported device: %s", dev_name); + + /* get serial device address */ + const void *reg_ptr = fdt_getprop(fdt, stdout_offset, "reg", NULL); + check(reg_ptr != NULL, "couldn't get 'reg' property"); + + struct cell_info ci = get_cellinfo(fdt, stdout_offset); + pm_t dbg_ptr = (pm_t)fdt_load_reg_addr(ci, reg_ptr, 0); + printf("uart reg at %lx\n", dbg_ptr); + + /* get serial device offset if present */ + size_t shift = 0; + const void *shift_ptr = fdt_getprop(fdt, stdout_offset, "reg-shift", + NULL); + + if (shift_ptr) + shift = (size_t)fdt_load_int32_ptr(shift_ptr); + + printf("shift: %zu\n", shift); + + /* get size of zeroth page order, as all physical memory mappings are + * aligned to them and we must take care to align things ourselves */ + size_t page_size = sys_conf_get(CONF_PAGE_SIZE, 0); + printf("page size: %zu\n", page_size); + + size_t offset = dbg_ptr - align_down(dbg_ptr, page_size); + printf("offset: %zu\n", offset); + + /* map to userspace, size is not really accurate here but definitely + * large enough to handle the 8250 */ + void *ptr = sys_req_dmem(dbg_ptr, 1024, VM_R | VM_W); + check(ptr != NULL, "failed mapping device memory"); + + printf("user mapping at %p + %zu\n", ptr, offset); + + /* check that we can't allocate the same region twice */ + void *nptr = sys_req_dmem(dbg_ptr, 1024, VM_R | VM_W); + check(nptr == NULL, "allocated same devmem twice"); + + /* check that we can free the device mem */ + enum sys_status ok = sys_free_mem((uintptr_t)ptr); + check(ok == OK, "failed freeing devmem"); + + /* try to map the memory again now that it's free */ + ptr = sys_req_dmem(dbg_ptr, 1024, VM_R | VM_W); + check(ptr != NULL, "failed reallocating devmem"); + + /* try printing */ + __print_ok((char *)ptr + offset, shift); + + /** @todo check that a fork/thread creation handled the devmem + * references correctly */ + + /* don't run off the end of the _start */ + sys_poweroff(SYS_SHUTDOWN); +} |
