(一)U-Boot啟動過程--詳細版的完全分析
-------------------------------------------------------------------------------------------------------------------------------------------
我們知道,bootloader是系統上電後最初載入執行的程式碼。它提供了處理器上電覆位後最開始需要執行的初始化程式碼。
在PC機上載入程式一般由BIOS開始執行,然後讀取硬碟中位於MBR(Main Boot Record,主引導記錄)中的Bootloader(例如LILO或GRUB),並進一步引導
然而在嵌入式系統中通常沒有像BIOS那樣的韌體程式,因此整個系統的載入啟動就完全由bootloader來完成。它主要的功能是載入與引導核心映像
一個嵌入式的儲存裝置通過通常包括四個分割槽:
第一分割槽:存放的當然是u-boot
第二個分割槽:存放著u-boot要傳給系統核心的引數
第三個分割槽:是系統核心(kernel)
第四個分割槽:則是根檔案系統
如下圖所示:
-------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------
u-boot是一種普遍用於嵌入式系統中的Bootloader。
Bootloader介紹
Bootloader是進行嵌入式開發必然會接觸的一個概念,它是嵌入式學院<嵌入式工程師職業培訓班>二期課程中嵌入式Linux系統開發方面的重要內容。本篇文章主要講解Bootloader的基本概念以及內部原理,這部分內容的掌握將對嵌入式linux系統開發的學習非常有幫助!
Bootloader的定義:Bootloader是在作業系統執行之前執行的一小段程式,通過這一小段程式,我們可以初始化硬體裝置、建立記憶體空間的對映表,從而建立適當的系統軟硬體環境,為最終呼叫作業系統核心做好準備。意思就是說如果我們要想讓一個作業系統在我們的板子上運轉起來,我們就必須首先對我們的板子進行一些基本配置和初始化,然後才可以將作業系統引導進來執行。具體在Bootloader中完成了哪些操作我們會在後面分析到,這裡我們先來回憶一下PC的體系結構:PC機中的引導載入程式是由BIOS和位於硬碟MBR中的OS Boot Loader(比如LILO和GRUB等)一起組成的,BIOS在完成硬體檢測和資源分配後,將硬碟MBR中的Boot Loader讀到系統的RAM中,然後將控制權交給OS Boot Loader。Boot Loader的主要執行任務就是將核心映象從硬碟上讀到RAM中,然後跳轉到核心的入口點去執行,即開始啟動作業系統。在嵌入式系統中,通常並沒有像BIOS那樣的韌體程式(注:有的嵌入式cpu也會內嵌一段短小的啟動程式),因此整個系統的載入啟動任務就完全由Boot Loader來完成。比如在一個基於ARM7TDMI core的嵌入式系統中,系統在上電或復位時通常都從地址0x00000000處開始執行,而在這個地址處安排的通常就是系統的Boot Loader程式。(先想一下,通用PC和嵌入式系統為何會在此處存在如此的差異呢?)
Bootloader是基於特定硬體平臺來實現的,因此幾乎不可能為所有的嵌入式系統建立一個通用的Bootloader,不同的處理器架構都有不同的Bootloader,Bootloader不但依賴於cpu的體系結構,還依賴於嵌入式系統板級裝置的配置。對於2塊不同的板子而言,即使他們使用的是相同的處理器,要想讓執行在一塊板子上的Bootloader程式也能執行在另一塊板子上,一般也需要修改Bootloader的源程式。
Bootloader的啟動方式
Bootloader的啟動方式主要有網路啟動方式、磁碟啟動方式和Flash啟動方式。
1、網路啟動方式
圖1 Bootloader網路啟動方式示意圖
如圖1所示,裡面主機和目標板,他們中間通過網路來連線,首先目標板的DHCP/BIOS通過BOOTP服務來為Bootloader分配IP地址,配置網路引數,這樣才能支援網路傳輸功能。我們使用的u-boot可以直接設定網路引數,因此這裡就不用使用DHCP的方式動態分配IP了。接下來目標板的Bootloader通過TFTP服務將核心映像下載到目標板上,然後通過網路檔案系統來建立主機與目標板之間的檔案通訊過程,之後的系統更新通常也是使用Boot Loader的這種工作模式。工作於這種模式下的Boot Loader通常都會向它的終端使用者提供一個簡單的命令列介面。
2、磁碟啟動方式
這種方式主要是用在臺式機和伺服器上的,這些計算機都使用BIOS引導,並且使用磁碟作為儲存介質,這裡面兩個重要的用來啟動linux的有LILO和GRUB,這裡就不再具體說明了。
3、Flash啟動方式
這是我們最常用的方式。Flash有NOR Flash和NAND Flash兩種。NOR Flash可以支援隨機訪問,所以程式碼可以直接在Flash上執行,Bootloader一般是儲存在Flash晶片上的。另外Flash上還儲存著引數、核心映像和檔案系統。這種啟動方式與網路啟動方式之間的不同之處就在於,在網路啟動方式中,核心映像和檔案系統首先是放在主機上的,然後經過網路傳輸下載進目標板的,而這種啟動方式中核心映像和檔案系統則直接是放在Flash中的,這兩點在我們u-boot的使用過程中都用到了。
U-boot的定義
U-boot,全稱Universal Boot Loader,是由DENX小組的開發的遵循GPL條款的開放原始碼專案,它的主要功能是完成硬體裝置初始化、作業系統程式碼搬運,並提供一個控制檯及一個指令集在作業系統執行前操控硬體裝置。U-boot之所以這麼通用,原因是他具有很多特點:開放原始碼、支援多種嵌入式作業系統核心、支援多種處理器系列、較高的穩定性、高度靈活的功能設定、豐富的裝置驅動原始碼以及較為豐富的開發除錯文件與強大的網路技術支援。另外u-boot對作業系統和產品研發提供了靈活豐富的支援,主要表現在:可以引導壓縮或非壓縮系統核心,可以靈活設定/傳遞多個關鍵引數給作業系統,適合系統在不同開發階段的除錯要求與產品釋出,支援多種檔案系統,支援多種目標板環境引數儲存介質,採用CRC32校驗,可校驗核心及映象檔案是否完好,提供多種控制檯介面,使使用者可以在不需要ICE的情況下通過串列埠/乙太網/USB等介面下載資料並燒錄到儲存裝置中去(這個功能在實際的產品中是很實用的,尤其是在軟體現場升級的時候),以及提供豐富的裝置驅動等。
u-boot原始碼的目錄結構
1、board中存放於開發板相關的配置檔案,每一個開發板都以子資料夾的形式出現。
2、Commom資料夾實現u-boot行下支援的命令,每一個命令對應一個檔案。
3、cpu中存放特定cpu架構相關的目錄,每一款cpu架構都對應了一個子目錄。
4、Doc是文件目錄,有u-boot非常完善的文件。
5、Drivers中是u-boot支援的各種裝置的驅動程式。
6、Fs是支援的檔案系統,其中最常用的是JFFS2檔案系統。
7、Include資料夾是u-boot使用的標頭檔案,還有各種硬體平臺支援的彙編檔案,系統配置檔案和檔案系統支援的檔案。
8、Net是與網路協議相關的程式碼,bootp協議、TFTP協議、NFS檔案系統得實現。
9、Tooles是生成U-boot的工具。
對u-boot的目錄有了一些瞭解後,分析啟動程式碼的過程就方便多了,其中比較重要的目錄就是/board、/cpu、/drivers和/include目錄,如果想實現u-boot在一個平臺上的移植,就要對這些目錄進行深入的分析。
-------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------
什麼是《編譯地址》?什麼是《執行地址》?
(一)編譯地址: 32位的處理器,它的每一條指令是4個位元組,以4個位元組儲存順序,進行順序執行,CPU是順序執行的,只要沒發生什麼跳轉,它會順序進行執行行, 編譯器會對每一條指令分配一個編譯地址,這是編譯器分配的,在編譯過程中分配的地址,我們稱之為編譯地址。
(二)執行地址:是指程式指令真正執行的地址,是由使用者指定的,使用者將執行地址燒錄到哪裡,哪裡就是執行的地址。
比如有一個指令的編譯地址是0x5,實際執行的地址是0x200,如果使用者將指令燒到0x200上,那麼這條指令的執行地址就是0x200,
當編譯地址和執行地址不同的時候會出現什麼結果?結果是不能跳轉,編譯後會產生跳轉地址,如果實際地址和編譯後產生的地址不相等,那麼就不能跳轉。
C語言編譯地址:都希望把編譯地址和實際執行地址放在一起的,但是彙編程式碼因為不需要做C語言到彙編的轉換,可以認為的去寫地址,所以直接寫的就是他的執行地址這就是為什麼任何bootloader剛開始會有一段彙編程式碼,因為起始程式碼編譯地址和實際地址不相等,這段程式碼和彙編無關,跳轉用的執行地址。
編譯地址和執行地址如何來算呢?
1. 假如有兩個編譯地址a=0x10,b=0x7,b的執行地址是0x300,那麼a的執行地址就是b的執行地址加上兩者編譯地址的差值,a-b=0x10-0x7=0x3,
a的執行地址就是0x300+0x3=0x303。
2. 假設uboot上兩條指令的編譯地址為a=0x33000007和b=0x33000001,這兩條指令都落在bank6上,現在要計算出他們對應的執行地址,要找出執行地址的始地址,這個是由使用者燒錄進去的,假設執行地址的首地址是0x0,則a的執行地址為0x7,b為0x1,就是這樣算出來的。
為什麼要分配編譯地址?這樣做有什麼好處,有什麼作用?
比如在函式a中定義了函式b,當執行到函式b時要進行指令跳轉,要跳轉到b函式所對應的起始地址上去,編譯時,編譯器給每條指令都分配了編譯地址,如果編譯器已經給分配了地址就可以直接進行跳轉,查詢b函式跳轉指令所對應的表,進行直接跳轉,因為有個編譯地址和指令對應的一個表,如果沒有分配,編譯器就查詢不到這個跳轉地址,要進行計算,非常麻煩。
什麼是《相對地址》?
以NOR Flash為例,NOR Falsh是對映到bank0上面,SDRAM是對映到bank6上面,uboot和核心最終是在SDRAM上面執行,最開始我們是從Nor Flash的零地址開始往後燒錄,uboot中至少有一段程式碼編譯地址和執行地址是不一樣的,編譯uboot或核心時,都會將編譯地址放入到SDRAM中,他們最終都會在SDRAM中執行,剛開始uboot在Nor
Flash中執行,執行地址是一個低端地址,是bank0中的一個地址,但編譯地址是bank6中的地址,這樣就會導致絕對跳轉指令執行的失敗,所以就引出了相對地址的概念。
那麼什麼是相對地址呢?
至少在bank0中uboot這段程式碼要知道不能用b+編譯地址這樣的方法去跳轉指令,因為這段程式碼的編譯地址和執行地址不一樣,那如何去做呢?
要去計算這個指令執行的真實地址,計算出來後再做跳轉,應該是b+執行地址,不能出現b+編譯地址,而是b+執行地址,而執行地址是算出來的。
_TEXT_BASE:
.word TEXT_BASE //0x33F80000,在board/config.mk中
這段話表示,使用者告訴編譯器編譯地址的起始地址
-------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------
大多數 Boot Loader 都包含兩種不同的操作模式:"啟動載入"模式和"下載"模式,這種區別僅對於開發人員才有意義。
但從終端使用者的角度看,Boot Loader 的作用就是:用來載入作業系統,而並不存在所謂的啟動載入模式與下載工作模式的區別。
(一)啟動載入(Boot loading)模式:這種模式也稱為"自主"(Autonomous)模式。
也即 Boot Loader 從目標機上的某個固態儲存裝置上將作業系統載入到 RAM 中執行,整個過程並沒有使用者的介入。
這種模式是 Boot Loader 的正常工作模式,因此在嵌入式產品釋出的時侯,Boot Loader 顯然必須工作在這種模式下。
(二)下載(Downloading)模式:在這種模式下,目標機上的 Boot Loader 將通過串列埠連線或網路連線等通訊手段從主機(Host)下載檔案,比如:下載核心映像和根檔案系統映像等。從主機下載的檔案通常首先被 Boot Loader儲存到目標機的RAM 中,然後再被 BootLoader寫到目標機上的FLASH類固態儲存裝置中。Boot
Loader 的這種模式通常在第一次安裝核心與根檔案系統時被使用;此外,以後的系統更新也會使用 Boot Loader 的這種工作模式。工作於這種模式下的 Boot Loader 通常都會向它的終端使用者提供一個簡單的命令列介面。這種工作模式通常在第一次安裝核心與跟檔案系統時使用。或者在系統更新時使用。進行嵌入式系統除錯時一般也讓bootloader工作在這一模式下。
UBoot 這樣功能強大的 Boot Loader 同時支援這兩種工作模式,而且允許使用者在這兩種工作模式之間進行切換。
大多數 bootloader 都分為階段 1(stage1)和階段 2(stage2)兩大部分,uboot 也不例外。依賴於 CPU 體系結構的程式碼(如 CPU 初始化程式碼等)通常都放在階段 1 中且通常用匯編語言實現,而階段 2 則通常用 C 語言來實現,這樣可以實現複雜的功能,而且有更好的可讀性和移植性。
-------------------------------------------------------------------------------------------------------------------------------------------
第一、大概總結性得的分析
系統啟動的入口點。既然我們現在要分析u-boot的啟動過程,就必須先找到u-boot最先實現的是哪些程式碼,最先完成的是哪些任務。
另一方面一個可執行的image必須有一個入口點,並且只能有一個全域性入口點,所以要通知編譯器這個入口在哪裡。由此我們可以找到程式的入口點是在/board/lpc2210/u-boot.lds中指定的,其中ENTRY(_start)說明程式從_start開始執行,而他指向的是cpu/arm7tdmi/start.o檔案。
因為我們用的是ARM7TDMI的cpu架構,在復位後從地址0x00000000取它的第一條指令,所以我們將Flash對映到這個地址上,
這樣在系統加電後,cpu將首先執行u-boot程式。u-boot的啟動過程是多階段實現的,分了兩個階段。
依賴於cpu體系結構的程式碼(如裝置初始化程式碼等)通常都放在stage1中,而且通常都是用匯編語言來實現,以達到短小精悍的目的。
而stage2則通常是用C語言來實現的,這樣可以實現複雜的功能,而且程式碼具有更好的可讀性和可移植性。
下面我們先詳細分析下stage1中的程式碼,如圖2所示:
圖2 Start.s程式流程
程式碼真正開始是在_start,設定異常向量表,這樣在cpu發生異常時就跳轉到/cpu/arm7tdmi/interrupts中去執行相應得中斷程式碼。
在interrupts檔案中大部分的異常程式碼都沒有實現具體的功能,只是列印一些異常訊息,其中關鍵的是reset中斷程式碼,跳到reset入口地址。
reset復位入口之前有一些段的宣告。
1.在reset中,首先是將cpu設定為svc32模式下,並遮蔽所有irq和fiq。
2.在u-boot中除了定時器使用了中斷外,其他的基本上都不需要使用中斷,比如串列埠通訊和網路等通訊等,在u-boot中只要完成一些簡單的通訊就可以了,所以在這裡遮蔽掉了所有的中斷響應。
3.初始化外部匯流排。這部分首先設定了I/O口功能,包括串列埠、網路介面等的設定,其他I/O口都設定為GPIO。然後設定BCFG0~BCFG3,即外部匯流排控制器。這裡bank0對應Flash,設定為16位寬度,匯流排速度設為最慢,以實現穩定的操作;Bank1對應DRAM,設定和Flash相同;Bank2對應RTL8019。
4.接下來是cpu關鍵設定,包括系統重對映(告訴處理器在系統發生中斷的時候到外部儲存器中去讀取中斷向量表)和系統頻率。
5.lowlevel_init,設定RAM的時序,並將中斷控制器清零。這些部分和特定的平臺有關,但大致的流程都是一樣的。
下面就是程式碼的搬移階段了。為了獲得更快的執行速度,
通常把stage2載入到RAM空間中來執行,因此必須為載入Boot Loader的stage2準備好一段可用的RAM空間範圍。空間大小最好是memory page大小(通常是4KB)的倍數
一般而言,1M的RAM空間已經足夠了。
flash中儲存的u-boot可執行檔案中,程式碼段、資料段以及BSS段都是首尾相連儲存的,
所以在計算搬移大小的時候就是利用了用BSS段的首地址減去程式碼的首地址,這樣算出來的就是實際使用的空間。
程式用一個迴圈將程式碼搬移到0x81180000,即RAM底端1M空間用來儲存程式碼。
然後程式繼續將中斷向量表搬到RAM的頂端。由於stage2通常是C語言執行程式碼,所以還要建立堆疊去。
在堆疊區之前還要將malloc分配的空間以及全域性資料所需的空間空下來,他們的大小是由巨集定義給出的,可以在相應位置修改。
基本記憶體分佈圖:
圖3 搬移後記憶體分佈情況圖
下來是u-boot啟動的第二個階段,是用c程式碼寫的,
這部分是一些相對變化不大的部分,我們針對不同的板子改變它呼叫的一些初始化函式,並且通過設定一些巨集定義來改變初始化的流程,
所以這些程式碼在移植的過程中並不需要修改,也是錯誤相對較少出現的檔案。
在檔案的開始先是定義了一個函式指標陣列,通過這個陣列,程式通過一個迴圈來按順序進行常規的初始化,並在其後通過一些巨集定義來初始化一些特定的裝置。
在最後程式進入一個迴圈,main_loop。這個迴圈接收使用者輸入的命令,以設定引數或者進行啟動引導。
本篇文章將分析重點放在了前面的start.s上,是因為這部分無論在移植還是在除錯過程中都是最容易出問題的地方,要解決問題就需要程式設計師對程式碼進行修改,所以在這裡簡單介紹了一下start.s的基本流程,希望能對大家有所幫助
-------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------
第二、程式碼分析
2.2 階段 1 介紹
uboot 的 stage1 程式碼通常放在 start.s 檔案中,它用匯編語言寫成,其主要程式碼部分如下:
2.2.1 定義入口
由於一個可執行的 Image 必須有一個入口點,並且只能有一個全域性入口,通常這個入口放在 ROM(Flash)的 0x0
地址,因此,必須通知編譯器以使其知道這個入口,該工作可通過修改聯結器指令碼來完成。
1. board/crane2410/uboot.lds: ENTRY(_start) ==> cpu/arm920t/start.S: .globl _start
2. uboot 程式碼區(TEXT_BASE = 0x33F80000)定義在 board/crane2410/config.mk
U-Boot啟動核心的過程可以分為兩個階段,兩個階段的功能如下:
(1)第一階段的功能
Ø 硬體裝置初始化
Ø 載入U-Boot第二階段程式碼到RAM空間
Ø 設定好棧
Ø 跳轉到第二階段程式碼入口
(2)第二階段的功能
Ø 初始化本階段使用的硬體裝置
Ø 檢測系統記憶體對映
Ø 將核心從Flash讀取到RAM中
Ø 為核心設定啟動引數
Ø 呼叫核心
第一階段對應的檔案是cpu/arm920t/start.S和board/samsung/mini2440/lowlevel_init.S。
U-Boot啟動第一階段流程如下:
詳細分析
圖 2.1 U-Boot啟動第一階段流程
根據cpu/arm920t/u-boot.lds中指定的連線方式:
看一下uboot.lds檔案,在board/smdk2410目錄下面,uboot.lds是告訴編譯器這些段改怎麼劃分,GUN編譯過的段,最基本的三個段是RO,RW,ZI,RO表示只讀,對應於具體的指程式碼段,RW是資料段,ZI是歸零段,就是全域性變數的那段。Uboot程式碼這麼多,如何保證start.s會第一個執行,編譯在最開始呢?就是通過uboot.lds連結檔案進行
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000; //起始地址
. = ALIGN(4); //4位元組對齊
.text : //test指程式碼段,上面3行標識是不佔用任何空間的
{
cpu/arm920t/start.o (.text) //這裡把start.o放在第一位就表示把start.s編
譯時放到最開始,這就是為什麼把uboot燒到起始地址上它肯定執行的是start.s
*(.text)
}
. = ALIGN(4); //前面的 “.” 代表當前值,是計算一個當前的值,是計算上
面佔用的整個空間,再加一個單元就表示它現在的位置
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
.got : { *(.got) }
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
__bss_start = .; //bss表示歸零段
.bss : { *(.bss) }
_end = .;
}
第一個連結的是cpu/arm920t/start.o,因此u-boot.bin的入口程式碼在cpu/arm920t/start.o中,其原始碼在cpu/arm920t/start.S中。下面我們來分析cpu/arm920t/start.S的執行。
1. 硬體裝置初始化
(1)設定異常向量
下面程式碼是系統啟動後U-boot上電後執行的第一段程式碼,它是什麼意思?
u-boot對應的第一階段程式碼放在cpu/arm920t/start.S檔案中,入口程式碼如下:.
globl _startglobal /*宣告一個符號可被其它檔案引用,相當於聲明瞭一個全域性變數,.globl與.global相同*/
_start: b start_code /* 復位 */b是不帶返回的跳轉(bl是帶返回的跳轉),意思是無條件直接跳轉到start_code標號出執行程式
ldr pc, _undefined_instruction /* 未定義指令向量 l---dr相當於mov操作*/
ldr pc, _software_interrupt /* 軟體中斷向量 */
ldr pc, _prefetch_abort /* 預取指令異常向量 */
ldr pc, _data_abort /* 資料操作異常向量 */
ldr pc, _not_used /* 未使用 */
ldr pc, _irq /* irq中斷向量 */
ldr pc, _fiq /* fiq中斷向量 */
/* 中斷向量表入口地址 */
_undefined_instruction: .word undefined_instruction /*就是在當前地址,即_undefined_instruction 處存放 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偽操作用於分配一段字記憶體單元(分配的單元都是字對齊的),並用偽操作中的expr初始化
.balignl 16,0xdeadbeef
他們是系統定義的異常,一上電程式跳轉到start_code異常處執行相應的彙編指令,下面定義出的都是不同的異常,比如軟體發生軟中斷時,CPU就會去執行軟中斷的指令,這些異常中斷在CUP中地址是從0開始,每個異常佔4個位元組
ldr pc, _undefined_instruction表示把_undefined_instruction存放的數值存放到pc指標上
_undefined_instruction: .word undefined_instruction表示未定義的這個異常是由.word來定義的,它表示定義一個字,一個32位的數
. word後面的數:表示把該標識的編譯地址寫入當前地址,標識是不佔用任何指令的。把標識存放的數值copy到指標pc上面,那麼標識上存放的值是什麼?
是由.word undefined_instruction來指定的,pc就代表你執行程式碼的地址,她就實現了CPU要做一次跳轉時的工作。
以上程式碼設定了ARM異常向量表,各個異常向量介紹如下:
表 2.1 ARM異常向量表
地址 |
異常 |
進入模式 |
描述 |
0x00000000 |
復位 |
管理模式 |
復位電平有效時,產生復位異常,程式跳轉到復位處理程式處執行 |
0x00000004 |
未定義指令 |
未定義模式 |
遇到不能處理的指令時,產生未定義指令異常 |
0x00000008 |
軟體中斷 |
管理模式 |
執行SWI指令產生,用於使用者模式下的程式呼叫特權操作指令 |
0x0000000c |
預存指令 |
中止模式 |
處理器預取指令的地址不存在,或該地址不允許當前指令訪問,產生指令預取中止異常 |
0x00000010 |
資料操作 |
中止模式 |
處理器資料訪問指令的地址不存在,或該地址不允許當前指令訪問時,產生資料中止異常 |
0x00000014 |
未使用 |
未使用 |
未使用 |
0x00000018 |
IRQ |
IRQ |
外部中斷請求有效,且CPSR中的I位為0時,產生IRQ異常 |
0x0000001c |
FIQ |
FIQ |
快速中斷請求引腳有效,且CPSR中的F位為0時,產生FIQ異常 |
在cpu/arm920t/start.S中還有這些異常對應的異常處理程式。當一個異常產生時,CPU根據異常號在異常向量表中找到對應的異常向量,然後執行異常向量處的跳轉指令,CPU就跳轉到對應的異常處理程式執行。
其中復位異常向量的指令“b start_code”決定了U-Boot啟動後將自動跳轉到標號“start_code”處執行。
(2)CPU進入SVC模式
start_code:
/*
* set the cpu to SVC32 mode
*/
mrs r0, cpsr
bic r0, r0, #0x1f /*工作模式位清零 */
orr r0, r0, #0xd3 /*工作模式位設定為“10011”(管理模式),並將中斷禁止位和快中斷禁止位置1 */
msr cpsr, r0
以上程式碼將CPU的工作模式位設定為管理模式,即設定相應的CPSR程式狀態字,並將中斷禁止位和快中斷禁止位置一,從而遮蔽了IRQ和FIQ中斷。
作業系統先註冊一個總的中斷,然後去查是由哪個中斷源產生的中斷,再去查使用者註冊的中斷表,查出來後就去執行使用者定義的使用者中斷處理函式。
(3)設定控制暫存器地址
# if defined(CONFIG_S3C2400) /*關閉看門狗*/
# define pWTCON 0x15300000 /*;看門狗暫存器*/
# define INTMSK 0x14400008 /*;中斷遮蔽暫存器*/
# define CLKDIVN 0x14800014 /*;時鐘分頻暫存器*/
#else /* s3c2410與s3c2440下面4個暫存器地址相同 */
# define pWTCON 0x53000000 /* WATCHDOG控制暫存器地址 */
# define INTMSK 0x4A000008 /* INTMSK暫存器地址 */
# define INTSUBMSK 0x4A00001C /* INTSUBMSK暫存器地址 次級中斷遮蔽暫存器*/
# define CLKDIVN 0x4C000014 /* CLKDIVN暫存器地址 ;時鐘分頻暫存器*/
# endif
對與s3c2440開發板,以上程式碼完成了WATCHDOG,INTMSK,INTSUBMSK,CLKDIVN四個暫存器的地址的設定。各個暫存器地址參見參考文獻[4] 。
(4)關閉看門狗
ldr r0, =pWTCON /*將pwtcon暫存器地址賦給R0*/
mov r1, #0x0 /*r1的內容為0*/
str r1, [r0] /* 看門狗控制器的最低位為0時,看門狗不輸出復位訊號 */
以上程式碼向看門狗控制暫存器寫入0,關閉看門狗。否則在U-Boot啟動過程中,CPU將不斷重啟。
為什麼要關看門狗?
就是防止,不同得兩個以上得CPU,進行喂狗的時間間隔問題:說白了,就是你執行的程式碼如果超出喂狗時間,而你不關狗,就會導致,