ARM基础——START.S的解释

目录

 

前言

1.  设置CPU模式

1.1. globl 

1.2. _start

1.3. ldr

1.4. .word

1.5. .balignl

1.6. _TEXT_BASE _armboot_start

1.7. _bss_start _bss_end

1.8. IRQ_STACK_START FIQ_STACK_START


前言

对于uboot的start.S,主要做的事情就是系统的各个方面的初始化。

从大的方面分,可以分成这几个部分:

  • 设置CPU模式
  • 关闭看门狗
  • 关闭中断
  • 设置堆栈sp指针
  • 清除bss段
  • 异常中断处理
     

1.  设置CPU模式

1.1. globl
 

#include <common.h>
#include <config.h>

/*
 *************************************************************************
 *
 * Jump vector table as in table 3.1 in [1]
 *
 *************************************************************************
 */


.globl _start

globl是个关键字,对应含义为:

意思很简单,就是相当于C语言中的Extern,声明此变量。
u-boot.lds文件里有:

ENTRY(_start)

即指定入口为_start,而由下面的_start的含义可以得知,_start就是整个start.S的最开始,即整个uboot的代码的开始。

1.2. _start
 

_start : b reset

_start后面加上一个冒号’:’,表示其是一个标号Label,类似于C语言goto后面的标号。而同时,_start的值,也就是这个代码的位置了,此处即为代码的最开始,相对的0的位置。而此处最开始的相对的0位置,在程序开始运行的时候,如果是从NorFlash启动,那么其地址是0,

_stat=0

如果是重新relocate代码之后,就是我们定义的值了,即,在config.mk中的:
TEXT_BASE = 0x33D00000

表示是代码段的基地址,即
_start=TEXT_BASE=0x33D00000

而_start标号后面的:
b reset

就是跳转到对应的标号为reset的位置。

1.3. ldr

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

ldr命令的语法为:
LDR{条件} 目的寄存器,<存储器地址>

LDR指令用于从存储器中将一个32位的字数据传送到目的寄存器中。该指令通常用于从存储器中读取32位的字数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。该指令在程序设计中比较常用,且寻址方式灵活多样,请读者认真掌握。

指令示例:

  • LDR R0,[R1] ;将存储器地址为R1的字数据读入寄存器R0。
  • LDR R0,[R1,R2] ;将存储器地址为R1+R2的字数据读入寄存器R0。
  • LDR R0,[R1,#8] ;将存储器地址为R1+8的字数据读入寄存器R0。
  • LDR R0,[R1,R2]! ;将存储器地址为R1+R2的字数据读入寄存器R0,并将新地址R1+R2写入R1。
  • LDR R0,[R1,#8]! ;将存储器地址为R1+8的字数据读入寄存器R0,并将新地址R1+8写入R1。
  • LDR R0,[R1],R2 ;将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2写入R1。
  • LDR R0,[R1,R2,LSL#2]! ;将存储器地址为R1+R2×4的字数据读入寄存器R0,并将新地址R1+R2×4写入R1
  • LDRR0,[R1],R2,LSL#2 ;将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2×4写入R1。

ARM是RISC结构,数据从内存到CPU之间的移动只能通过L/S指令来完成,也就是ldr/str指令。
比如想把数据从内存中某处读取到寄存器中,只能使用ldr

比如:
ldr r0, 0x12345678

就是把0x12345678这个地址中的值存放到r0中。

上面那些ldr的作用,以第一个_undefined_instruction为例,就是将地址为_undefined_instruction中的一个word的值,赋值给pc。

1.4. .word

_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
  • word .word expr {,expr}… 分配一段字内存单元,并用expr初始化字内存单元(32bit)

所以上面的含义,以_undefined_instruction为例,就是,此处分配了一个word=32bit=4字节的地址空间,里面存放的值是undefined_instruction。

而此处_undefined_instruction也就是该地址空间的地址了。用C语言来表达就是:

_undefined_instruction = &undefined_instruction 或 *_undefined_instruction = undefined_instruction

在后面的代码,我们可以看到,undefined_instruction也是一个标号,即一个地址值,对应着就是在发生“未定义指令”的时候,系统所要去执行的代码。

(其他几个对应的“软件中断”,“预取指错误”,“数据错误”,“未定义”,“(普通)中断”,“快速中断”,也是同样的做法,跳转到对应的位置执行对应的代码。)
所以:

ldr pc, 标号1
......
标号1:.word 标号2
......
标号2:
......(具体要执行的代码)

的意思就是,将地址为标号1中内容载入到pc,而地址为标号1中的内容,正好装的是标号2。
用C语言表达其实很简单:
PC = *(标号1) = 标号2
对PC赋值,即是实现代码跳转,所以整个这段汇编代码的意思就是:
跳转到标号2的位置,执行对应的代码。

1.5. .balignl

	.balignl 16,0xdeadbeef

balignl这个标号的语法及含义:

所以意思就是,接下来的代码,都要16字节对齐,不足之处,用0xdeadbeef填充。此处0xdeadbeef本身没有真正的意义,但是很明显,字面上的意思是,(坏)死的牛肉。

虽然其本身没有实际意义,但是其是在十六进制下,能表示出来的,为数不多的,可读的单词之一了。

另外一个相对常见的是:0xbadc0de,意思是bad code,坏的代码,注意其中的o是0,因为十六进制中是没有o的。
这些“单词”,相对的作用是,使得读代码的人,以及在查看程序运行结果时,容易看懂,便于引起注意。

1.6. _TEXT_BASE _armboot_start

/*
 *************************************************************************
 *
 * Startup Code (called from the ARM reset exception vector)
 *
 * do important init only if we don't start from memory!
 * relocate armboot to ram
 * setup stack
 * jump to second stage
 *
 *************************************************************************
 */

_TEXT_BASE:
	.word	TEXT_BASE

.globl _armboot_start
_armboot_start:
	.word _start
  • _TEXT_BASE是一个标号地址,此地址中是一个word类型的变量,变量名是TEXT_BASE,此值见名知意,是text的base,即代码的基地址,可以在config.mk中找到其定义:

TEXT_BASE = 0x33D00000

  • .globl _armboot_start此含义可用C语言表示为:

*(_armboot_start) = _start

1.7. _bss_start _bss_end

/*
 * These are defined in the board-specific linker script.
 */
.globl _bss_start
_bss_start:
	.word __bss_start

.globl _bss_end
_bss_end:
	.word _end

关于_bss_start和_bss_end都只是两个标号,对应着此处的地址。

而两个地址里面分别存放的值是__bss_start和_end,这两个的值,根据注释所说,是定义在开发板相关的链接脚本里面的,我们此处的开发板相关的链接脚本是:
u-boot.lds

其中可以找到__bss_start和_end的定义:

__bss_start = .;
.bss : { *(.bss) }
_end = .;

而关于_bss_start和_bss_end定义为.glogl即全局变量,是因为uboot的其他源码中要用到这两个变量,详情请自己去搜索源码。

1.8. IRQ_STACK_START FIQ_STACK_START

#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
	.word	0x0badc0de

/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
	.word 0x0badc0de
#endif

同上,IRQ_STACK_START和FIQ_STACK_START,也是在cpu_init中用到了。

不过此处,是只有当定义了宏CONFIG_USE_IRQ的时候,才用到这两个变量,其含义也很明显,只有用到了中断IRQ,才会用到中断的堆栈,才有中端堆栈的起始地址。

快速中断FIQ,同理。

1.9 几个汇编指令

	reset:
        mrs	r0, cpsr
	bic	r0, r0, #0x1f
	orr	r0, r0, #0xd3
	msr	cpsr,r0

CPSR当前的程序状态寄存器(Current Program Status Register),而 SPSR保存的程序状态寄存器(Saved Program Status Register)。

1.9.1  MRS指令

MRS指令的格式为:

                                                        MRS{条件} 通用寄存器,程序状态寄存器(CPSR或SPSR)

MRS指令用于将程序状态寄存器的内容传送到通用寄存器中。该指令一般用在以下两种情况:

  • 当需要改变程序状态寄存器的内容时,可用MRS将程序状态寄存器的内容读入通用寄存器,修改后再写回程序状态寄存器。
  • 当在异常处理或进程切换时,需要保存程序状态寄存器的值,可先用该指令读出程序状态寄存器的值,然后保存。

指令示例:
MRS R0,CPSR ;传送CPSR的内容到R0
MRS R0,SPSR ;传送SPSR的内容到R0”
所以,上述汇编代码含义为,将CPSR的值赋给R0寄存器

1.9.2 bic指令

BIC指令的格式为:

                                                             BIC{条件}{S} 目的寄存器,操作数1,操作数2

BIC指令用于清除操作数1的某些位,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。操作数2为32位的掩码,如果在掩码中设置了某一位,则清除这一位。未设置的掩码位保持不变。

而0x1f=11111b

所以,此行代码的含义就是,清除r0的bit[4:0]位

1.9.3 ORR指令

ORR指令的格式为:

                                                           ORR{条件}{S} 目的寄存器,操作数1,操作数2

ORR指令用于在两个操作数上进行逻辑或运算,并把结果放置到目的寄存器中。

操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于设置操作数1的某些位。
指令示例:

ORR R0,R0,#3 ; 该指令设置R0的0、1位,其余位保持不变。

所以此行汇编代码的含义为:而0xd3=1101 0011, 将r0与0xd3算数或运算,然后将结果给r0,即把r0的bit[7:6]和bit[4]和bit[1:0]置为1。

1.9.4 MSR指令

MSR指令的格式为:

                               MSR{条件} 程序状态寄存器(CPSR或SPSR)_<域>,操作数

MSR指令用于将操作数的内容传送到程序状态寄存器的特定域中。其中,操作数可以为通用寄存器或立即数。<域>用于设置程序状态寄存器中需要操作的位,32位的程序状态寄存器可分为4个域:

  • 位[31:24]为条件标志位域,用f表示;
  • 位[23:16]为状态位域,用s表示;
  • 位[15:8]为扩展位域,用x表示;
  • 位[7:0]为控制位域,用c表示;

该指令通常用于恢复或改变程序状态寄存器的内容,在使用时,一般要在MSR指令中指明将要操作的域。
指令示例:

MSR CPSR,R0 ;传送R0的内容到CPSR

MSR SPSR,R0 ;传送R0的内容到SPSR

MSR CPSR_c,R0 ;传送R0的内容到SPSR,但仅仅修改CPSR中的控制位域

此行汇编代码含义为,将r0的值赋给CPSR

所以,上面四行汇编代码的含义就很清楚了。先是把CPSR的值放到r0寄存器中,然后清除bit[4:0],然后再或上0xd3=1101 0011b。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章