diff --git a/src/platforms/stm32/usbuart.c b/src/platforms/stm32/usbuart.c index 0c390c5a..8b08d9fa 100644 --- a/src/platforms/stm32/usbuart.c +++ b/src/platforms/stm32/usbuart.c @@ -580,59 +580,80 @@ void USBUSART_DMA_RXTX_ISR(void) #endif #ifdef ENABLE_DEBUG -enum { - RDI_SYS_OPEN = 0x01, - RDI_SYS_WRITE = 0x05, - RDI_SYS_ISTTY = 0x09, -}; - -int rdi_write(int fn, const char *buf, size_t len) +/* + * newlib defines _write as a weak link'd function for user code to override. + * + * This function forms the root of the implementation of a variety of functions + * that can write to stdout/stderr, including printf(). + * + * The result of this function is the number of bytes written. + */ +/* NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */ +int _write(const int file, const void *const ptr, const size_t len) { - (void)fn; -#if defined(PLATFORM_HAS_DEBUG) + (void)file; +#ifdef PLATFORM_HAS_DEBUG if (debug_bmp) - return len - usbuart_debug_write(buf, len); -#else - (void)buf; - (void)len; + return usbuart_debug_write(ptr, len); #endif - return 0; + return len; } -struct ex_frame { - union { - int syscall; - int retval; - }; - const int *params; - uint32_t r2, r3, r12, lr, pc; +/* + * newlib defines isatty as a weak link'd function for user code to override. + * + * The result of this function is always 'true'. + */ +int isatty(const int file) +{ + (void)file; + return true; +} + +enum { + RDI_SYS_OPEN = 0x01, }; -void debug_monitor_handler_c(struct ex_frame *sp) +typedef struct ex_frame { + uint32_t r0; + const uint32_t *params; + uint32_t r2; + uint32_t r3; + uint32_t r12; + uintptr_t lr; + uintptr_t return_address; +} ex_frame_s; + +void debug_monitor_handler(void) __attribute__((used)) __attribute__((naked)); + +/* + * This implements the other half of the newlib syscall puzzle. + * When newlib is built for ARM, various calls that do file IO + * such as printf end up calling [_swiwrite](https://github.com/mirror/newlib-cygwin/blob/master/newlib/libc/sys/arm/syscalls.c#L317) + * and other similar low-level implementation functions. These + * generate `swi` instructions for the "RDI Monitor" and that lands us.. here. + * + * The RDI calling convention sticks the file number in r0, the buffer pointer in r1, and length in r2. + * ARMv7-M's SWI (SVC) instruction then takes all that and maps it into an exception frame on the stack. + */ +void debug_monitor_handler(void) { - /* Return to after breakpoint instruction */ - sp->pc += 2; + ex_frame_s *frame; + __asm__( + "mov %[frame], sp" : + [frame] "=r" (frame) + ); + + /* Make sure to return to the instruction after the SWI/BKPT */ + frame->return_address += 2U; - switch (sp->syscall) { + switch (frame->r0) { case RDI_SYS_OPEN: - sp->retval = 1; - break; - case RDI_SYS_WRITE: - sp->retval = rdi_write(sp->params[0], (void*)sp->params[1], sp->params[2]); - break; - case RDI_SYS_ISTTY: - sp->retval = 1; + frame->r0 = 1; break; default: - sp->retval = -1; + frame->r0 = UINT32_MAX; } - + __asm__("bx lr"); } - -__asm__(".globl debug_monitor_handler\n" - ".thumb_func\n" - "debug_monitor_handler: \n" - " mov r0, sp\n" - " b debug_monitor_handler_c\n"); - #endif