ARM引导加载程序:中断向量表理解

14

以下代码是u-boot中定义中断向量表的第一部分,我的问题是每行代码的用途。我理解前两行代码是开始点和实现第一条指令:reset,并且我们在下面定义了reset。但是这些指令何时会被使用呢?根据System.map,每个指令都有一个固定的地址,因此_fiq位于0x0000001C,当我们想要执行fiq时,我们会将该地址复制到pc中然后执行,对吗?但是我们如何跳转到这条指令:ldr pc,_fiq?这是通过硬件还是软件实现的呢?希望我表达得清楚。

>.globl _start  
>_start:b         reset  
>       ldr       pc, _undefined_instruction  
>       ldr       pc, _software_interrupt  
>       ldr       pc, _prefetch_abort  
>       ldr       pc, _data_abort  
>       ldr       pc, _not_used  
>       ldr       pc, _irq  
>       ldr       pc, _fiq  

>_undefined_instruction: .word undefined_instruction  
>_software_interrupt:    .word software_interrupt  
>_prefetch_abort:        .word prefetch_abort  
>_data_abort:            .word data_abort  
>_not_used:              .word not_used  
>_irq:                   .word irq  
>_fiq:                   .word fiq  

进一步思考:我们首先在这里准备每个中断,然后从“启动”或“复位”计算机开始,这些也被定义为中断。然后我们继续重置例程。以下中断仅是未来硬件/软件中断的准备,对吗? - user2122968
ldr pc, _fiq 被翻译为 ldr pc, [pc, #_fiq-offset]。这样,*_fiq* 地址处的值被加载到 PC 寄存器中。也就是说,在地址 _fiq 处放置一个函数指针。一开始它将是 NULL;稍后可以由其他人填充。在 u-boot 中,默认情况下不启用中断。请参见:在 ARM 上启用 u-boot 中断 - artless noise
2个回答

12

如果您了解复位,则您就理解它们全部。

当处理器被复位时,硬件会将PC设置为0x0000,并通过获取0x0000处的指令开始执行。当执行未定义的指令或试图执行未定义的指令时,硬件会将PC设置为0x0004,并开始执行0x0004处的指令。irq中断,硬件完成正在执行的指令,开始执行地址为0x0018的指令,依此类推。

00000000 <_start>:
   0:   ea00000d    b   3c <reset>
   4:   e59ff014    ldr pc, [pc, #20]   ; 20 <_undefined_instruction>
   8:   e59ff014    ldr pc, [pc, #20]   ; 24 <_software_interrupt>
   c:   e59ff014    ldr pc, [pc, #20]   ; 28 <_prefetch_abort>
  10:   e59ff014    ldr pc, [pc, #20]   ; 2c <_data_abort>
  14:   e59ff014    ldr pc, [pc, #20]   ; 30 <_not_used>
  18:   e59ff014    ldr pc, [pc, #20]   ; 34 <_irq>
  1c:   e59ff014    ldr pc, [pc, #20]   ; 38 <_fiq>

00000020 <_undefined_instruction>:
  20:   00000000    andeq   r0, r0, r0

00000024 <_software_interrupt>:
  24:   00000000    andeq   r0, r0, r0

00000028 <_prefetch_abort>:
  28:   00000000    andeq   r0, r0, r0

0000002c <_data_abort>:
  2c:   00000000    andeq   r0, r0, r0

00000030 <_not_used>:
  30:   00000000    andeq   r0, r0, r0

00000034 <_irq>:
  34:   00000000    andeq   r0, r0, r0

00000038 <_fiq>:
  38:   00000000    andeq   r0, r0, r0

当然,除了更改电脑并从这些地址开始执行之外,硬件还将保存机器状态,必要时切换处理器模式,然后从向量表中的新地址开始执行。

作为程序员,我们的工作是构建二进制文件,使得我们想要在每个指令上运行的指令位于正确的地址。硬件为每个位置提供一个字,一条指令。现在,如果您从不希望有任何这些异常,例如您可以让程序直接启动,而无需在零地址处进行分支,这些地址的内存并没有任何神奇之处。如果您希望具有这些异常,则对于一个字并且能够跳出随后的异常的指令,您有两个选择:是分支还是加载pc。对于每种方法都有利弊。


根据我在规格说明中看到的内容,向量表位于0x0处,初始SP值驻留在其中。如果PC在复位时设置为0x0,处理器如何解释这个值?它总是一个无效指令吗? - pbn
3
你混淆了Cortex-M和普通的ARM核心。普通的ARM核心类似于上面所说的,在异常表中执行地址处的指令。而Cortex-M核心有一个向量表,其中零向量是堆栈指针的加载值,偏移量为4的复位向量则用于在复位时从向量表加载堆栈指针,然后开始执行复位向量地址处的代码。 - old_timer

7
硬件发生异常时,程序计数器(PC)会自动设置为相关异常向量的地址,并从该地址开始执行指令。当处理器退出重置状态时,PC会自动设置为 base+0 。未定义的指令将PC设置为 base+4 等等。向量表的基地址(base)取决于处理器和配置,可以是0x00000000、0xFFFF0000或VBAR。请注意,这提供了有限的灵活性,需要与使用的设备的参考手册一起查阅ARM文档以获取正确的值。
表格布局(每个异常4字节)使得必须立即从向量跳转到实际的异常处理程序。使用LDR PC,label方法的原因有两个 - 因为相对于PC的分支被限制为(24 << 2)位(+/-32MB),使用B将在一定程度上限制内存中代码的布局;通过加载绝对地址,处理程序可以位于内存中的任何位置。其次,它使得在运行时非常简单地更改异常处理程序,只需将不同的地址写入该位置,而不必组装和热修补分支指令。
以这种方式具有可重新映射的重置向量的价值很小,这就是你看到一个简单的分支来跳过其余向量到实际入口点代码的原因。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接