Bare-metal ARM + qemu - issue with floating point division
Posted: Sun Jan 07, 2018 6:10 am
My apologies in advance that this is a cross-post from Stackoverflow, but my problem isn't getting much attention there so I'll see if anyone here has any insight!
If anyone would rather earn points on SO, the original question is here.
I'm currently teaching myself bare-metal ARM kernel development, I've settled on using the Raspberry Pi 2 as a target platform on the basis of being well documented. I'm currently emulating the device using qemu.
In a function called by my kernel I'm required to divide a numerical constant by a function argument and store the result as a floating point number for future calculations. Calling this function causes qemu to go off the rails. Here's the function itself ( setting PL011 baud rate ):
( Forgive for saying "go off the rails", but it's actually a little hard to see what qemu is doing, it lands in a non-functional state. Checking where 'pc' currently is in gdb shows that it's at adress 0x8, which is my temporary 'halt' routine for when the kernel 'main' returns. If this is accurate it means that it's broken out of the function, gone down the whole stack frame and returned to the bootstrap code. )
I'd post a minimal verifiable example, but just about anything will trigger the issue. If you even use:
You'll get the same result.
Stepping through the function in gdb, stepping past "float divider..." will cause qemu to jump out of the function and head straight to the halt loop in my bootloader code ( for when the kernel main returns )
Checking "info args" in gdb shows the correct arguments. Checking "info locals" will show the correct value for float divider. Checking "info stack" shows the correct stack trace and arguments. Initially I suspected 'sp' might be in the wrong place, but that doesn't check out since the stack trace looks normal enough.
Here's the register dump from right before the line that causes the unpredictable behavior:
My Makefile is:
As you can see I'm linking against lgcc, and using -mfpu=neon-vfpv4 -mfloat-abi=hard, so at very least gcc should supply it's own floating point division functions from lgcc.
Can anyone point me in the right direction for debugging this issue? I suspect I'm either using the incorrect compiler arguments and not loading the correct function for floating-point division, or there's some issue with the stack.
Can anyone shed any insight here?
Even if someone could point me in the right direction for how to debug the issue, or where to look I'd really appreciate it! Thank you.
If anyone would rather earn points on SO, the original question is here.
I'm currently teaching myself bare-metal ARM kernel development, I've settled on using the Raspberry Pi 2 as a target platform on the basis of being well documented. I'm currently emulating the device using qemu.
In a function called by my kernel I'm required to divide a numerical constant by a function argument and store the result as a floating point number for future calculations. Calling this function causes qemu to go off the rails. Here's the function itself ( setting PL011 baud rate ):
Code: Select all
void pl011_set_baud_rate(pl011_uart_t *uart, uint32_t baud_rate) {
float divider = PL011_UART_CLOCK / (16.0f * baud_rate);
uint16_t integer_divider = (uint16_t)divider;
uint8_t fractional_divider = ((divider - integer_divider) * 64) + 0.5;
mmio_write(uart->IBRD, integer_divider); // Integer baud rate divider
mmio_write(uart->FBRD, fractional_divider); // Fractional baud rate divider
};
I'd post a minimal verifiable example, but just about anything will trigger the issue. If you even use:
Code: Select all
void test(uint32_t test_var) {
float test_div = test_var / 16;
(void)test_div; // squash [-Wunused-variable] warnings
// goes off the rails here
};
Stepping through the function in gdb, stepping past "float divider..." will cause qemu to jump out of the function and head straight to the halt loop in my bootloader code ( for when the kernel main returns )
Checking "info args" in gdb shows the correct arguments. Checking "info locals" will show the correct value for float divider. Checking "info stack" shows the correct stack trace and arguments. Initially I suspected 'sp' might be in the wrong place, but that doesn't check out since the stack trace looks normal enough.
Code: Select all
(gdb) info stack
#0 pl011_set_baud_rate (uart=0x3f201000, baud_rate=115200) at kernel/uart/pl011.c:23
#1 0x0000837c in pl011_init (uart=0x3f201000) at kernel/uart/pl011.c:49
#2 0x0000806c in uart_init () at kernel/uart/uart.c:12
#3 0x00008030 in kernel_init (r0=0, r1=0, atags=0) at kernel/boot/start.c:10
#4 0x00008008 in _start () at kernel/boot/boot.S:6
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb)
Code: Select all
r0 0x3f201000 1059065856
r1 0x1c200 115200
r2 0x7ff 2047
r3 0x0 0
r4 0x0 0
r5 0x0 0
r6 0x0 0
r7 0x0 0
r8 0x0 0
r9 0x0 0
r10 0x0 0
r11 0x7fcc 32716
r12 0x0 0
sp 0x7fb0 0x7fb0
lr 0x837c 33660
pc 0x8248 0x8248 <pl011_set_baud_rate+20>
cpsr 0x600001d3 1610613203
Code: Select all
INCLUDES=include
INCLUDE_PARAMS=$(foreach d, $(INCLUDES), -I$d)
CC=arm-none-eabi-gcc
C_SOURCES:=kernel/boot/start.c kernel/uart/uart.c kernel/uart/pl011.c
AS_SOURCES:=kernel/boot/boot.S
SOURCES=$(C_SOURCES)
SOURCES+=$(AS_SOURCES)
OBJECTS=
OBJECTS+=$(C_SOURCES:.c=.o)
OBJECTS+=$(AS_SOURCES:.S=.o)
CFLAGS=-std=gnu99 -Wall -Wextra -fpic -ffreestanding -mcpu=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard
LDFLAGS=-ffreestanding -nostdlib
LIBS=-lgcc
DEBUG_FLAGS=
BINARY=kernel.bin
.PHONY: all clean debug
all: $(BINARY)
debug: DEBUG_FLAGS += -ggdb
debug: $(BINARY)
$(BINARY): $(OBJECTS)
$(CC) -T linker.ld $(LDFLAGS) $(LIBS) $(OBJECTS) -o $(BINARY)
%.o: %.c
$(CC) $(INCLUDE_PARAMS) $(CFLAGS) $(DEBUG_FLAGS) -c $< -o $@
%.o: %.S
$(CC) $(INCLUDE_PARAMS) $(CFLAGS) $(DEBUG_FLAGS) -c $< -o $@
clean:
rm $(BINARY) $(OBJECTS)
Can anyone point me in the right direction for debugging this issue? I suspect I'm either using the incorrect compiler arguments and not loading the correct function for floating-point division, or there's some issue with the stack.
Can anyone shed any insight here?
Even if someone could point me in the right direction for how to debug the issue, or where to look I'd really appreciate it! Thank you.