裸金属ARM Raspberry Pi + qemu具有浮点除法的奇怪行为

时间:2018-01-07 09:41:03

标签: c arm raspberry-pi2 bare-metal

我目前正在教自己的裸机ARM内核开发,我已经基于详细记录,决定使用Raspberry Pi 2作为目标平台。我目前正在使用qemu模拟设备。 在我的内核调用的函数中,我需要通过函数参数来划分数值常量,并将结果存储为浮点数以供将来计算。 调用此函数会导致qemu脱轨。这是功能本身(设置PL011波特率):

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
};

我发布了一个最小的可验证示例,但几乎任何事情都会引发问题。如果你甚至使用:

void test(uint32_t test_var) {
    float test_div = test_var / 16;
    (void)test_div;    // squash [-Wunused-variable] warnings
    // goes off the rails here
};

你会得到相同的结果。

单步执行gdb中的函数,单步执行float divider...将导致qemu跳出函数并直接进入我的引导加载程序代码中的暂停循环(对于内核主函数返回时)

在gdb中检查info args会显示正确的参数。检查info locals会显示float divider的正确值。检查info stack显示正确的堆栈跟踪和参数。 最初我怀疑sp可能在错误的位置,但由于堆栈跟踪看起来正常,因此不会检出。 (对于裸机)

(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) 

这是从导致不可预测行为的行之前的寄存器转储:

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

我的Makefile是:

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)

正如您所看到的,我正在链接lgcc并使用-mfpu=neon-vfpv4 -mfloat-abi=hard,因此至少gcc应该从lgcc提供自己的浮点除法函数。

有人能指出我正确的方向调试此问题吗? 我怀疑我要么使用不正确的编译器参数而没有为浮点除法加载正确的函数,要么堆栈存在一些问题。

任何人都可以在这里找到任何见解吗?

2 个答案:

答案 0 :(得分:2)

您是否检查过fpu协处理器是否已启用?

在原来的pi1 / pi-zero上我用这个

;@ enable fpu
mrc p15, 0, r0, c1, c0, 2
orr r0,r0,#0x300000 ;@ single precision
orr r0,r0,#0xC00000 ;@ double precision
mcr p15, 0, r0, c1, c0, 2
mov r0,#0x40000000
fmxr fpexc,r0

如果不起作用,最后几行可能会故意崩溃。

不幸的是,你可能在pi2中有armv7或armv8核心,因为有两种变体。我怀疑具体的寄存器和指令可能与上面基于armv6的raspberry pi不同。

答案 1 :(得分:1)

离开轨道,你的意思是你的异常处理程序被调用了吗? 如果是这样,qemu有调试选项,可以帮助查找引发的异常。检查qemu-system-arm -M raspi2 -d help

我们可以从启用int,cpu_reset,unimp,guest_errors开始。