我目前正在教自己的裸机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
提供自己的浮点除法函数。
有人能指出我正确的方向调试此问题吗? 我怀疑我要么使用不正确的编译器参数而没有为浮点除法加载正确的函数,要么堆栈存在一些问题。
任何人都可以在这里找到任何见解吗?
答案 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
开始。