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

时间:2022-08-01 02:59:55

The code following is the first part of u-boot to define interrupt vector table, and my question is how every line will be used. I understand the first 2 lines which is the starting point and the first instruction to implement: reset, and we define reset below. But when will we use these instructions below? According to System.map, every instruction has a fixed address, so _fiq is at 0x0000001C, when we want to execute fiq, we will copy this address into pc and then execute,right? But in which way can we jump to this instruction: ldr pc, _fiq? It's realised by hardware or software? Hope I make myself understood correctly.

下面的代码是u-boot定义中断向量表的第一部分,我的问题是如何使用每一行。我理解的前两行是起始点和第一个要实现的指令:重置,我们在下面定义重置。但是我们什么时候会使用下面的说明呢?根据系统。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  

2 个解决方案

#1


9  

If you understand reset then you understand all of them.

如果你理解重置,那么你就理解了所有的重置。

When the processor is reset then hardware sets the pc to 0x0000 and starts executing by fetching the instruction at 0x0000. When an undefined instruction is executed or tries to be executed the hardware responds by setting the pc to 0x0004 and starts executing the instruction at 0x0004. irq interrupt, the hardware finishes the instruction it is executing starts executing the instruction at address 0x0018. and so on.

当处理器重置时,硬件将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

Now of course in addition to changing the pc and starting execution from these addresses. The hardware will save the state of the machine, switch processor modes if necessary and then start executing at the new address from the vector table.

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

Our job as programmers is to build the binary such that the instructions we want to be run for each of these instructions is at the right address. The hardware provides one word, one instruction for each location. Now if you never expect to ever have any of these exceptions, you dont have to have a branch at address zero for example you can just have your program start, there is nothing magic about the memory at these addresses. If you expect to have these exceptions, then you have two choices for instructions that are one word and can jump out of the way of the exception that follows. One is a branch the other is a load pc. There are pros and cons to each.

作为程序员,我们的工作是构建二进制文件,以便我们希望为每个指令运行的指令都位于正确的地址。硬件为每个位置提供一个单词、一条指令。如果你从来没有想过会有任何这些异常,你不需要在地址为0处有一个分支,例如你可以让你的程序启动,这些地址的内存没有什么神奇之处。如果您希望有这些异常,那么对于一个词的指令您有两个选择,可以跳过后面的异常。一个是分支,另一个是加载pc。各有利弊。

#2


5  

When the hardware takes an exception, the program counter (PC) is automatically set to the address of the relevant exception vector and the processor begins executing instructions from that address. When the processor comes out of reset, the PC is automatically set to base+0. An undefined instruction sets the PC to base+4, etc. The base address of the vector table (base) is either 0x00000000, 0xFFFF0000, or VBAR depending on the processor and configuration. Note that this provides limited flexibility in where the vector table gets placed and you'll need to consult the ARM documentation in conjunction with the reference manual for the device that you are using to get the right value to be used.

当硬件发生异常时,程序计数器(PC)将自动设置为相关异常向量的地址,处理器将从该地址开始执行指令。当处理器复位后,PC机自动设置为+0。一个未定义的指令将PC设置为base+4,等等。根据处理器和配置,向量表(base)的基本地址要么是0x00000000、0xff0000,要么是VBAR。注意,这在向量表放置的位置上提供了有限的灵活性,您需要参考ARM文档,同时参考设备的参考手册,以便获得要使用的正确值。

The layout of the table (4 bytes per exception) makes it necessary to immediately branch from the vector to the actual exception handler. The reasons for the LDR PC, label approach are twofold - because a PC-relative branch is limited to (24 << 2) bits (+/-32MB) using B would constrain the layout of the code in memory somewhat; by loading an absolute address the handler can be located anywhere in memory. Secondly it makes it very simple to change exception handlers at runtime, by simply writing a different address to that location, rather than having to assemble and hotpatch a branch instruction.

表的布局(每个异常4个字节)使它必须立即从vector转移到实际的异常处理程序。使用LDR PC、label方法的原因有两个——因为使用B的PC相关分支被限制为(24 < 2)位(+/- 32mb),会在一定程度上限制代码在内存中的布局;通过加载绝对地址,处理程序可以位于内存中的任何位置。其次,在运行时更改异常处理程序非常简单,只需为该位置编写不同的地址,而不必组装和修改分支指令。

There's little value to having a remappable reset vector in this way, however, which is why you tend to see that one implemented as a simple branch to skip over the rest of the vectors to the real entry point code.

然而,以这种方式拥有一个可重映射的重置向量几乎没有任何价值,这就是为什么您倾向于看到一个实现为一个简单分支的向量,以便跳过其余的向量到真正的入口点代码。

#1


9  

If you understand reset then you understand all of them.

如果你理解重置,那么你就理解了所有的重置。

When the processor is reset then hardware sets the pc to 0x0000 and starts executing by fetching the instruction at 0x0000. When an undefined instruction is executed or tries to be executed the hardware responds by setting the pc to 0x0004 and starts executing the instruction at 0x0004. irq interrupt, the hardware finishes the instruction it is executing starts executing the instruction at address 0x0018. and so on.

当处理器重置时,硬件将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

Now of course in addition to changing the pc and starting execution from these addresses. The hardware will save the state of the machine, switch processor modes if necessary and then start executing at the new address from the vector table.

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

Our job as programmers is to build the binary such that the instructions we want to be run for each of these instructions is at the right address. The hardware provides one word, one instruction for each location. Now if you never expect to ever have any of these exceptions, you dont have to have a branch at address zero for example you can just have your program start, there is nothing magic about the memory at these addresses. If you expect to have these exceptions, then you have two choices for instructions that are one word and can jump out of the way of the exception that follows. One is a branch the other is a load pc. There are pros and cons to each.

作为程序员,我们的工作是构建二进制文件,以便我们希望为每个指令运行的指令都位于正确的地址。硬件为每个位置提供一个单词、一条指令。如果你从来没有想过会有任何这些异常,你不需要在地址为0处有一个分支,例如你可以让你的程序启动,这些地址的内存没有什么神奇之处。如果您希望有这些异常,那么对于一个词的指令您有两个选择,可以跳过后面的异常。一个是分支,另一个是加载pc。各有利弊。

#2


5  

When the hardware takes an exception, the program counter (PC) is automatically set to the address of the relevant exception vector and the processor begins executing instructions from that address. When the processor comes out of reset, the PC is automatically set to base+0. An undefined instruction sets the PC to base+4, etc. The base address of the vector table (base) is either 0x00000000, 0xFFFF0000, or VBAR depending on the processor and configuration. Note that this provides limited flexibility in where the vector table gets placed and you'll need to consult the ARM documentation in conjunction with the reference manual for the device that you are using to get the right value to be used.

当硬件发生异常时,程序计数器(PC)将自动设置为相关异常向量的地址,处理器将从该地址开始执行指令。当处理器复位后,PC机自动设置为+0。一个未定义的指令将PC设置为base+4,等等。根据处理器和配置,向量表(base)的基本地址要么是0x00000000、0xff0000,要么是VBAR。注意,这在向量表放置的位置上提供了有限的灵活性,您需要参考ARM文档,同时参考设备的参考手册,以便获得要使用的正确值。

The layout of the table (4 bytes per exception) makes it necessary to immediately branch from the vector to the actual exception handler. The reasons for the LDR PC, label approach are twofold - because a PC-relative branch is limited to (24 << 2) bits (+/-32MB) using B would constrain the layout of the code in memory somewhat; by loading an absolute address the handler can be located anywhere in memory. Secondly it makes it very simple to change exception handlers at runtime, by simply writing a different address to that location, rather than having to assemble and hotpatch a branch instruction.

表的布局(每个异常4个字节)使它必须立即从vector转移到实际的异常处理程序。使用LDR PC、label方法的原因有两个——因为使用B的PC相关分支被限制为(24 < 2)位(+/- 32mb),会在一定程度上限制代码在内存中的布局;通过加载绝对地址,处理程序可以位于内存中的任何位置。其次,在运行时更改异常处理程序非常简单,只需为该位置编写不同的地址,而不必组装和修改分支指令。

There's little value to having a remappable reset vector in this way, however, which is why you tend to see that one implemented as a simple branch to skip over the rest of the vectors to the real entry point code.

然而,以这种方式拥有一个可重映射的重置向量几乎没有任何价值,这就是为什么您倾向于看到一个实现为一个简单分支的向量,以便跳过其余的向量到真正的入口点代码。