diff --git a/libsel4platsupport/src/common.c b/libsel4platsupport/src/common.c index a1f130e5..e4f72720 100644 --- a/libsel4platsupport/src/common.c +++ b/libsel4platsupport/src/common.c @@ -32,130 +32,108 @@ #include #include +#if defined(CONFIG_LIB_SEL4_PLAT_SUPPORT_USE_SEL4_DEBUG_PUTCHAR) && defined(CONFIG_DEBUG_BUILD) +#define USE_DEBUG_PUTCHAR +#endif + enum serial_setup_status { NOT_INITIALIZED = 0, START_REGULAR_SETUP, START_FAILSAFE_SETUP, SETUP_COMPLETE }; -static enum serial_setup_status setup_status = NOT_INITIALIZED; + +typedef struct { + enum serial_setup_status setup_status; +#ifndef USE_DEBUG_PUTCHAR + seL4_CPtr device_cap; + ps_io_ops_t io_ops; + vspace_t *vspace; + vka_t *vka; + /* To keep failsafe setup we need actual memory for a simple and a vka */ + simple_t simple_mem; + vka_t vka_mem; +#endif /* not USE_DEBUG_PUTCHAR */ +} ctx_t; /* Some globals for tracking initialisation variables. This is currently just to avoid * passing parameters down to the platform code for backwards compatibility reasons. This * is strictly to avoid refactoring all existing platform code */ -static vspace_t *vspace = NULL; -static UNUSED simple_t *simple = NULL; -static vka_t *vka = NULL; - -/* To keep failsafe setup we need actual memory for a simple and a vka */ -static simple_t _simple_mem; -static vka_t _vka_mem; - -/* Hacky constants / data structures for a failsafe mapping */ -#define DITE_HEADER_START ((seL4_Word)__executable_start - 0x1000) -static seL4_CPtr device_cap = 0; -extern char __executable_start[]; - -#if !(defined(CONFIG_LIB_SEL4_PLAT_SUPPORT_USE_SEL4_DEBUG_PUTCHAR) && defined(CONFIG_DEBUG_BUILD)) -static void *__map_device_page(void *cookie, uintptr_t paddr, size_t size, - int cached, ps_mem_flags_t flags); - -static ps_io_ops_t io_ops = { - .io_mapper = { - .io_map_fn = &__map_device_page, - .io_unmap_fn = NULL, - }, +static ctx_t ctx = { + .setup_status = NOT_INITIALIZED }; -#endif - -/* completely hacky way of getting a virtual address. This is used a last ditch attempt to - * get serial device going so we can print out an error */ -static seL4_Word platsupport_alloc_device_vaddr(seL4_Word bits) -{ - seL4_Word va; - - va = DITE_HEADER_START - (BIT(bits)); - - /* Ensure we are aligned to bits. If not, round down. */ - va = va & ~((BIT(bits)) - 1); +extern char __executable_start[]; - return va; -} +#ifndef USE_DEBUG_PUTCHAR -static void *__map_device_page_failsafe(void *cookie UNUSED, uintptr_t paddr, size_t size, - int cached UNUSED, ps_mem_flags_t flags UNUSED) +static void *__map_device_page(void *cookie, uintptr_t paddr, size_t size, + int cached, ps_mem_flags_t flags) { - int bits = CTZ(size); - int error; - seL4_Word vaddr = 0; - vka_object_t dest; + seL4_Error err; - if (device_cap != 0) { - /* we only support a single page for the serial */ - for (;;); + if (0 != ctx.device_cap) { + /* we only support a single page for the serial device. */ + abort(); + UNREACHABLE(); } - error = sel4platsupport_alloc_frame_at(vka, paddr, bits, &dest); - if (error != seL4_NoError) { - goto error; + vka_object_t dest; + int bits = CTZ(size); + err = sel4platsupport_alloc_frame_at(ctx.vka, paddr, bits, &dest); + if (err) { + ZF_LOGE("Failed to get cap for serial device frame"); + abort(); + UNREACHABLE(); } - device_cap = dest.cptr; - - vaddr = platsupport_alloc_device_vaddr(bits); - error = - seL4_ARCH_Page_Map( - device_cap, - seL4_CapInitThreadPD, - vaddr, - seL4_AllRights, - 0 - ); - -error: - if (error) - for (;;); - assert(!error); - - return (void *)vaddr; -} - -static void *__map_device_page_regular(void *cookie UNUSED, uintptr_t paddr, size_t size, - int cached UNUSED, ps_mem_flags_t flags UNUSED) -{ - int bits = CTZ(size); - void *vaddr; - int error; - vka_object_t dest; + ctx.device_cap = dest.cptr; - error = sel4platsupport_alloc_frame_at(vka, paddr, bits, &dest); - if (error) { - ZF_LOGF("Failed to get cap for serial device frame"); + if ((ctx.setup_status == START_REGULAR_SETUP) && ctx.vspace) { + /* map device page regularly */ + void *vaddr = vspace_map_pages(ctx.vspace, &dest.cptr, NULL, seL4_AllRights, 1, bits, 0); + if (!vaddr) { + ZF_LOGE("Failed to map serial device"); + abort(); + UNREACHABLE(); + } + return vaddr; } - vaddr = vspace_map_pages(vspace, &dest.cptr, NULL, seL4_AllRights, 1, bits, 0); - if (!vaddr) { - ZF_LOGF("Failed to map serial device :(\n"); - for (;;); + /* Try a last ditch attempt to get serial device going, so we can print out + * an error. Find a properly aligned virtual address and try to map the + * device cap there. + */ + if ((ctx.setup_status == START_FAILSAFE_SETUP) || !ctx.vspace) { + seL4_Word header_start = (seL4_Word)__executable_start - PAGE_SIZE_4K; + seL4_Word vaddr = (header_start - BIT(bits)) & ~(BIT(bits) - 1); + err = seL4_ARCH_Page_Map(ctx.device_cap, seL4_CapInitThreadPD, vaddr, seL4_AllRights, 0); + if (err) { + ZF_LOGE("Failed to map serial device in failsafe mode"); + abort(); + UNREACHABLE(); + } + return (void *)vaddr; } - device_cap = dest.cptr; - return (void *)vaddr; + ZF_LOGE("invalid setup state %d", ctx.setup_status); + abort(); } -void *__map_device_page(void *cookie, uintptr_t paddr, size_t size, - int cached, ps_mem_flags_t flags) +static int setup_io_ops( + simple_t *simple __attribute__((unused)), + vka_t *vka __attribute__((unused))) { - if (setup_status == START_REGULAR_SETUP && vspace) { - return __map_device_page_regular(cookie, paddr, size, cached, flags); - } else if (setup_status == START_FAILSAFE_SETUP || !vspace) { - return __map_device_page_failsafe(cookie, paddr, size, cached, flags); - } - printf("Unknown setup status!\n"); - for (;;); + ctx.io_ops.io_mapper.io_map_fn = &__map_device_page; + +#ifdef CONFIG_ARCH_X86 + sel4platsupport_get_io_port_ops(&ctx.io_ops.io_port_ops, simple, vka); +#endif + return platsupport_serial_setup_io_ops(&ctx.io_ops); } +#endif /* not USE_DEBUG_PUTCHAR */ + /* * This function is designed to be called when creating a new cspace/vspace, * and the serial port needs to be hooked in there too. @@ -163,16 +141,18 @@ void *__map_device_page(void *cookie, uintptr_t paddr, size_t size, void platsupport_undo_serial_setup(void) { /* Re-initialise some structures. */ - setup_status = NOT_INITIALIZED; - if (device_cap) { + ctx.setup_status = NOT_INITIALIZED; +#ifndef USE_DEBUG_PUTCHAR + if (ctx.device_cap) { cspacepath_t path; - seL4_ARCH_Page_Unmap(device_cap); - vka_cspace_make_path(vka, device_cap, &path); + seL4_ARCH_Page_Unmap(ctx.device_cap); + vka_cspace_make_path(ctx.vka, ctx.device_cap, &path); vka_cnode_delete(&path); - vka_cspace_free(vka, device_cap); - device_cap = 0; - vka = NULL; + vka_cspace_free(ctx.vka, ctx.device_cap); + ctx.device_cap = 0; } + ctx.vka = NULL; +#endif /* not USE_DEBUG_PUTCHAR */ } /* Initialise serial input interrupt. */ @@ -183,12 +163,12 @@ void platsupport_serial_input_init_IRQ(void) int platsupport_serial_setup_io_ops(ps_io_ops_t *io_ops) { int err = 0; - if (setup_status == SETUP_COMPLETE) { + if (ctx.setup_status == SETUP_COMPLETE) { return 0; } err = __plat_serial_init(io_ops); if (!err) { - setup_status = SETUP_COMPLETE; + ctx.setup_status = SETUP_COMPLETE; } return err; } @@ -196,116 +176,121 @@ int platsupport_serial_setup_io_ops(ps_io_ops_t *io_ops) int platsupport_serial_setup_bootinfo_failsafe(void) { int err = 0; - if (setup_status == SETUP_COMPLETE) { + if (ctx.setup_status == SETUP_COMPLETE) { return 0; } - memset(&_simple_mem, 0, sizeof(simple_t)); - memset(&_vka_mem, 0, sizeof(vka_t)); -#if defined(CONFIG_LIB_SEL4_PLAT_SUPPORT_USE_SEL4_DEBUG_PUTCHAR) && defined(CONFIG_DEBUG_BUILD) + +#ifdef USE_DEBUG_PUTCHAR /* only support putchar on a debug kernel */ - setup_status = SETUP_COMPLETE; -#else - setup_status = START_FAILSAFE_SETUP; - simple_default_init_bootinfo(&_simple_mem, platsupport_get_bootinfo()); - simple = &_simple_mem; - vka = &_vka_mem; + ctx.setup_status = SETUP_COMPLETE; +#else /* not USE_DEBUG_PUTCHAR */ + ctx.setup_status = START_FAILSAFE_SETUP; + + simple_t *simple = &(ctx.simple_mem); + memset(simple, 0, sizeof(*simple)); + + simple_default_init_bootinfo(simple, platsupport_get_bootinfo()); + + vka_t *vka = &(ctx.vka_mem); + memset(vka, 0, sizeof(*vka)); simple_make_vka(simple, vka); -#ifdef CONFIG_ARCH_X86 - sel4platsupport_get_io_port_ops(&io_ops.io_port_ops, simple, vka); -#endif - err = platsupport_serial_setup_io_ops(&io_ops); -#endif + + ctx.vspace = NULL; + ctx.vka = vka; + + err = setup_io_ops(simple, vka); +#endif /* [not] USE_DEBUG_PUTCHAR */ return err; } int platsupport_serial_setup_simple( - vspace_t *_vspace __attribute__((unused)), - simple_t *_simple __attribute__((unused)), - vka_t *_vka __attribute__((unused))) + vspace_t *vspace __attribute__((unused)), + simple_t *simple __attribute__((unused)), + vka_t *vka __attribute__((unused))) { int err = 0; - if (setup_status == SETUP_COMPLETE) { + switch (ctx.setup_status) { + case SETUP_COMPLETE: return 0; - } - if (setup_status != NOT_INITIALIZED) { - printf("Trying to initialise a partially initialised serial. Current setup status is %d\n", setup_status); + case NOT_INITIALIZED: + break; /* continue below */ + default: + ZF_LOGE("Trying to initialise a partially initialised serial. Current setup status is %d\n", ctx.setup_status); assert(!"You cannot recover"); return -1; } -#if defined(CONFIG_LIB_SEL4_PLAT_SUPPORT_USE_SEL4_DEBUG_PUTCHAR) && defined(CONFIG_DEBUG_BUILD) + +#ifdef USE_DEBUG_PUTCHAR /* only support putchar on a debug kernel */ - setup_status = SETUP_COMPLETE; -#else + ctx.setup_status = SETUP_COMPLETE; +#else /* not USE_DEBUG_PUTCHAR */ /* start setup */ - setup_status = START_REGULAR_SETUP; - vspace = _vspace; - simple = _simple; - vka = _vka; -#ifdef CONFIG_ARCH_X86 - sel4platsupport_get_io_port_ops(&io_ops.io_port_ops, simple, vka); -#endif - err = platsupport_serial_setup_io_ops(&io_ops); - /* done */ - vspace = NULL; - simple = NULL; - /* Don't reset vka here */ -#endif + ctx.setup_status = START_REGULAR_SETUP; + ctx.vspace = vspace; + ctx.vka = vka; + err = setup_io_ops(simple, vka); /* uses ctx.vka */ +#endif /* [not] USE_DEBUG_PUTCHAR */ return err; } -static void __serial_setup() +/* This function is called if an attempt for serial I/O is done before it has + * been set up. Try to do some best-guess seetup. + */ +static int fallback_serial_setup() { - int started_regular __attribute__((unused)) = 0; - /* this function is called if we attempt to do serial and it isn't setup. - * we now need to handle this somehow */ - switch (setup_status) { - case START_FAILSAFE_SETUP: - /* we're stuck. */ - abort(); - break; - case START_REGULAR_SETUP: - started_regular = 1; + switch (ctx.setup_status) { + + case SETUP_COMPLETE: + return 0; /* we don't except to be called in thi state */ + case NOT_INITIALIZED: + case START_REGULAR_SETUP: + break; /* continue below fir failsafe setup */ + + default: /* includes START_FAILSAFE_SETUP */ + return -1; + } + #ifdef CONFIG_LIB_SEL4_PLAT_SUPPORT_USE_SEL4_DEBUG_PUTCHAR - setup_status = SETUP_COMPLETE; - printf("\nWarning: using printf before serial is set up. This only works as your\n"); - printf("printf is backed by seL4_Debug_PutChar()\n"); - started_regular = 1; -#else - /* attempt failsafe initialization and print something out */ - platsupport_serial_setup_bootinfo_failsafe(); - if (started_regular) { - printf("Regular serial setup failed.\n" - "This message coming to you courtesy of failsafe serial\n" - "Your vspace has been clobbered but we will keep running to get any more error output\n"); - } else { - printf("You attempted to print before initialising the libsel4platsupport serial device!\n"); - while (1); - } -#endif /* CONFIG_LIB_SEL4_PLAT_SUPPORT_USE_SEL4_DEBUG_PUTCHAR */ - break; - case SETUP_COMPLETE: - break; + ctx.setup_status = SETUP_COMPLETE; + ZF_LOGI("using kernel syscalls for char I/O"); +#else /* not CONFIG_LIB_SEL4_PLAT_SUPPORT_USE_SEL4_DEBUG_PUTCHAR */ + /* Attempt failsafe initialization to be able to print something. */ + int err = platsupport_serial_setup_bootinfo_failsafe(); + if (err || (START_REGULAR_SETUP != ctx.setup_status)) { + /* Setup failed, so printing an error may not output anything. */ + ZF_LOGE("You attempted to print before initialising the" + " libsel4platsupport serial device!"); + return -1; } + + /* Setup worked, so this warning will show up. */ + ZF_LOGW("Regular serial setup failed.\n" + "This message coming to you courtesy of failsafe serial.\n" + "Your vspace has been clobbered but we will keep running" + " to get any more error output"); +#endif /* [not] CONFIG_LIB_SEL4_PLAT_SUPPORT_USE_SEL4_DEBUG_PUTCHAR */ + return 0; } -void NO_INLINE #ifdef CONFIG_LIB_SEL4_MUSLC_SYS_ARCH_PUTCHAR_WEAK -WEAK +#define LIBSEL4_IO NO_INLINE WEAK +#else +#define LIBSEL4_IO NO_INLINE #endif -__arch_putchar(int c) + +void LIBSEL4_IO __arch_putchar(int c) { - if (setup_status != SETUP_COMPLETE) { - __serial_setup(); + if (ctx.setup_status != SETUP_COMPLETE) { + if (0 != fallback_serial_setup()) { + abort(); /* ToDo: hopefully this does not print anything */ + UNREACHABLE(); + } } __plat_putchar(c); } -size_t NO_INLINE -#ifdef CONFIG_LIB_SEL4_MUSLC_SYS_ARCH_PUTCHAR_WEAK -WEAK -#endif -__arch_write(char *data, size_t count) +size_t LIBSEL4_IO __arch_write(char *data, size_t count) { for (size_t i = 0; i < count; i++) { __arch_putchar(data[i]); @@ -315,8 +300,11 @@ __arch_write(char *data, size_t count) int __arch_getchar(void) { - if (setup_status != SETUP_COMPLETE) { - __serial_setup(); + if (ctx.setup_status != SETUP_COMPLETE) { + if (0 != fallback_serial_setup()) { + abort(); /* ToDo: hopefully this does not print anything */ + UNREACHABLE(); + } } return __plat_getchar(); }