【发布时间】:2019-10-14 04:37:20
【问题描述】:
我正在 ARM-Cortex A53、SoC BCM2837(换句话说,Raspberry PI 3)上进行裸机编程(我正在开发内核)。我实际上正在编写负责处理迷你 UART 的软件(一种 hello world,据 OsDev wiki https://wiki.osdev.org/ARM_RaspberryPi_Tutorial_C 报道)。所以我编写了一组函数来处理迷你 UART,让我们考虑以下问题,因为任何其他函数的问题仍然存在:
void miniUartSendByte(unsigned char byte){
// FIFO can accept at least one byte
while(*AUX_MU_LSR_REG & 0b100000);
// write byte to buffer
*AUX_MU_IO_REG = byte;
return;
}
其中 AUX_MU_* 是 volatile unsigned int* 类型。这是上面代码的反汇编:
1000ac: d10043ff sub sp, sp, #0x10
1000b0: 39003fe0 strb w0, [sp, #15]
1000b4: d503201f nop
1000b8: d28a0a80 mov x0, #0x5054 // #20564
1000bc: f2afc420 movk x0, #0x7e21, lsl #16
1000c0: b9400000 ldr w0, [x0]
1000c4: 121b0000 and w0, w0, #0x20
1000c8: 7100001f cmp w0, #0x0
1000cc: 54ffff61 b.ne 1000b8 <miniUartSendByte+0xc> // b.any
1000d0: d28a0800 mov x0, #0x5040 // #20544
1000d4: f2afc420 movk x0, #0x7e21, lsl #16
1000d8: 39403fe1 ldrb w1, [sp, #15]
1000dc: b9000001 str w1, [x0]
1000e0: d503201f nop
1000e4: 910043ff add sp, sp, #0x10
1000e8: d65f03c0 ret
这是 QEMU 报告的执行情况:
----------------
IN: kernel_main
0x00100050: a9bf7bfd stp x29, x30, [sp, #-0x10]!
0x00100054: 910003fd mov x29, sp
0x00100058: 52800c60 movz w0, #0x63
0x0010005c: 94000012 bl #0x1000a4 // jump to miniUartSendByte
----------------
IN: miniUartSendByte
0x001000a4: d10043ff sub sp, sp, #0x10
0x001000a8: 39003fe0 strb w0, [sp, #0xf]
0x001000ac: d503201f nop
0x001000b0: d28a0a80 movz x0, #0x5054
0x001000b4: f2afc420 movk x0, #0x7e21, lsl #16
0x001000b8: b9400000 ldr w0, [x0]
0x001000bc: 121b0000 and w0, w0, #0x20
0x001000c0: 7100001f cmp w0, #0
0x001000c4: 54ffff61 b.ne #0x1000b0
----------------
IN:
0x00000200: 00000000 .byte 0x00, 0x00, 0x00, 0x00 // ??
可以看到,机器在执行跳转时,收到异常,跳转到地址0x200,这里放置了中断处理程序(注意,没有配置中断处理程序,我还没实现)并在执行无限循环时卡在地址 0x200(不存在中断处理程序时的默认行为)。现在,从 QEMU 我能够捕获异常的类型:
Taking exception 1 [Undefined Instruction]
...from EL3 to EL3
...with ESR 0x0/0x2000000
...with ELR 0x200
...to EL3 PC 0x200 PSTATE 0x3cd
我正在使用以下命令进行编译:
aarch64-elf-gcc -Wall -O0 -ffreestanding -nostdinc -nostdlib -nostartfiles -mcpu=cortex-a53 -g -c ... -o ...
我还尝试查看这是否是“编译器”问题,尝试执行以下完全无用的代码:
void a(){
for(int j=0; j<10; j++);
return;
}
void b(char* string){
for(int i = 0; i<10; i++){
a();
}
return;
}
void kernel_main(){
a();
b("test");
while(1);
return;
}
但是执行没有任何问题... 现在我无法弄清楚出了什么问题。我的意思是,生成该汇编程序的 C 代码没有什么不好的,并且汇编代码中的地址似乎还可以……知道问题出在哪里吗?为什么那个未定义的指令异常?如果需要更多信息,我可以提供更多详细信息
【问题讨论】:
-
你用
unsigned int *AUX_MU_IO_REG写一个字节有什么原因吗?这将写入至少 16 位。结果还会写入什么内容? -
well unisgned int* 指的是AUX_MU_IO_REG映射到的地址寄存器,换句话说要写入AUX_MU_IO_REG我需要写入该内存位置,此外根据BCM2837数据表,即使寄存器是32位宽,只有前8位可以访问,所以我一次只能写一个字节,其余的都被忽略了。此外,我见过的任何代码 sn-p 都使用 volatile unsigned int* 来访问寄存器(例如,看一下问题中的 OsDev 链接,函数 mmio_write() 正是我所做的(除了他们使用 uint32_t 而不是 unsigned int )
-
大概那么处理器是little-endian。
-
好的,根据 ARM 文档 (infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0500e/…) 指令总是小端的。你能解释一下这对执行有何影响吗?我的意思是为什么 miniUartSendByte() 会导致跳转异常,而像 a() 或 b() 这样的函数却不会?此外,如果问题出在寄存器中存储的数据中,我不应该遇到关于 UART 行为的问题(我不知道,我想发送 'c' 但发送的是完全不同的东西)而不是代码执行问题??抱歉,我有点困惑……
-
我尝试在 gcc 中使用 -mlittle-endian 标志进行编译,但结果没有改变。实际上 gcc 已经以小端格式编译(我认为它能够通过查看 -mcpu 参数来检测目标的字节序),实际上如果我使用 -mbig-endian 编译,则返回以下内容“为大端系统编译目标是小端”。我不认为这是问题......
标签: c exception arm kernel bare-metal