I am trying to implement a bare metal application on the raspberry pi and want to hook up stdout to the mini uart for debugging purposes.
I have followed the process outlined here and here
I have created a uart_putc function which seems to work perfectly, allowing me to print messages to my PC's COM port. I then implemented the _write syscall, making it call my uart_putc function for output. This works fine if I pass a single string literal into printf additional literal parameters or any non-literal parameters nothing is printed to the serial port and after a few calls, the application hangs.
Does anyone have any ideas what might be going wrong? Happy to provide further info if needed...
void uart_putc(char c)
{
while(1)
{
if(aux[AUX_MU_LSR]&0x20) break;
led_blink(); // Blink the LED off then on again to
// make sure we aren't stuck in here
}
aux[AUX_MU_IO] = c;
}
...
int _write(int file, char* ptr, int len)
{
int todo;
for (todo = 0; todo < len; todo++)
{
uart_putc(*ptr++);
}
return len;
}
...
while(1)
{
printf("Hello World\r\n"); // Works
}
while(1)
{
printf("Hello %s\r\n", "World"); // This doesn't print anything
// and will hang after about five calls
}
char* s = (char*)malloc(sizeof(char) * 100); // Heap or stack it doesn't matter
strcpy(s, "Hello World\r\n");
while(1)
{
printf(s); // This doesn't print anything
// and will hang after about five calls
}
while(1)
{
for (i = 0; i < 13; i++)
{
uart_putc(s[i]); // Works
}
}
Update
I am using newlib and _write works correctly when called directly. snprintf seems to exhibit the same problem, i.e.
snprintf(s, 100, "hello world\r\n"); // Works
snprintf(s, 100, "hello %s\r\n", "world"); // Doesn't work
My implementation of _sbrk was nicked from the page referenced in my OP
char *heap_end = 0;
caddr_t _sbrk(int incr) {
extern char heap_low; /* Defined by the linker */
extern char heap_top; /* Defined by the linker */
char *prev_heap_end;
if (heap_end == 0)
{
heap_end = &heap_low;
}
prev_heap_end = heap_end;
if (heap_end + incr > &heap_top)
{
/* Heap and stack collision */
return (caddr_t)0;
}
heap_end += incr;
return (caddr_t) prev_heap_end;
}
Linker script
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm",
"elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib");
SECTIONS
{
/* Read-only sections, merged into text segment: */
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x8000)); . = SEGMENT_START("text-segment", 0x8000);
. = 0x8000;
.ro : {
*(.text.startup)
*(.text)
*(.rodata)
}
.rw : {
*(.data)
__bss_start__ = .;
*(.bss)
__bss_end__ = .;
*(COMMON)
}
. = ALIGN(8);
heap_low = .; /* for _sbrk */
. = . + 0x10000; /* 64kB of heap memory */
heap_top = .; /* for _sbrk */
. = . + 0x10000; /* 64kB of stack memory */
stack_top = .; /* for startup.s */
}
start.s
.section ".text.startup"
.global _start
_start:
ldr sp, =stack_top
// The c-startup
b _cstartup
_inf_loop:
b _inf_loop
Update 2
Further experiments involving snprintf:
snprintf(s, 100, "hello world\r\n"); // Works
snprintf(s, 100, "hello %s\r\n", "world"); // Doesn't work
snprintf(s, 100, "hello %d\r\n", 1); // Doesn't work
char s[100];
char t[100];
strcpy(s, "hello world\r\n");
snprintf(t, 100, s); // Doesn't work
int printf ( const char * format, ... );
). – alexander