#include #include #include /* 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); }