aboutsummaryrefslogtreecommitdiff
path: root/tests/dmem/init.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/dmem/init.c')
-rw-r--r--tests/dmem/init.c146
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);
+}