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。
 

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