天天看點

嵌入式Linux系統啟動過程

嵌入式Linux系統啟動過程

系統啟動過程 

一個嵌入式 Linux 系統從軟體角度看可以分為四個部分:引導加載程式(Bootloader), Linux 核心,檔案系統,應用

程式。  

當系統首次引導時,或系統被重置時,處理器會執行一個位于Flash/ROM中的已知位置處的代碼,Bootloader就是這第

一段代碼。它主要用來初始化處理器及外設,然後調用 Linux 核心。Linux 核心在完成系統的初始化之後需要挂載某個

檔案系統作為根檔案系統(Root Filesystem),然後加載必要的核心子產品,啟動應用程式。這就是嵌入式Linux系統啟

動過程 Linux 引導的整個過程。 

根檔案系統是 Linux 系統的核心組成部分,它可以作為Linux 系統中檔案和資料的存儲區域,通常它還包括系統配置文

件和運作應用軟體所需要的庫。應用程式可以說是嵌入式系統的“靈魂”,它所實作的功能通常就是設計該嵌入式系統所要

達到的目标。如果沒有應用程式的支援,任何硬體上設計精良的嵌入式系統都沒有實用意義。 

從以上分析可以看出 Bootloader在運作過程中雖然具有初始化系統和執行使用者輸入的指令等作用,但它最根本的功能就

是為了啟動 Linux 核心,讓我們進一步分析 Bootloader 和 Linux 核心在嵌入式系統中的關系和作用。 

Bootloader  

1、Bootloader基本概述

Bootloader是嵌入式系統的引導加載程式,它是系統上電後運作的第一段程式,其作用類似于 PC 機上的 BIOS。

Bootloader是依賴于硬體而實作的,特别是在嵌入式領域,為嵌入式系統建立一個通用的Bootloader是很困難的,但為

了能達到啟動Linux 核心的目的,所有的 Bootloader都必須具備以下功能: 

1) 初始化 RAM  

因為 Linux 核心一般都會在 RAM 中運作,是以在調用 Linux 核心之前 Bootloader 必須設定和初始化 RAM,為調用 

Linux核心做好準備。初始化 RAM 的任務包括設定 CPU 的控制寄存器參數,以便能正常使用 RAM 以及檢測RAM 大

小等。 

2) 初始化序列槽端口 

在 Linux 的啟動過程中有着非常重要的作用,它是 Linux核心和使用者互動的方式之一。Linux 在啟動過程中可以将資訊

通過序列槽輸出,這樣便可清楚的了解 Linux 的啟動過程。雖然它并不是 Bootloader 必須要完成的工作,但是通過序列槽

輸出資訊是調試 Bootloader 和Linux 核心的強有力的工具,是以一般的 Bootloader 都會在執行過程中初始化一個串

口作為調試端口。 

3) 檢測處理器類型  

Bootloader在調用 Linux核心前必須檢測系統的處理器類型,并将其儲存到某個常量中提供給 Linux 核心。Linux 核心

在啟動過程中會根據該處理器類型調用相應的初始化程式。 

4) 設定 Linux啟動參數  

Bootloader在執行過程中必須設定和初始化 Linux 的核心啟動參數。  

5) 調用 Linux核心映像  Bootloader完成的最後一項工作便是調用 Linux核心。如果 Linux 核心存放在 Flash 中,并且可直接在上面運作(這

裡的 Flash 指 Nor Flash),那麼可直接跳轉到核心中去執行。但由于在 Flash 中執行代碼會有種種限制,而且速度也

遠不及 RAM 快,是以一般的嵌入式系統都是将 Linux核心拷貝到 RAM 中,然後跳轉到 RAM 中去執行。 

2、Bootloader啟動過程

嵌入式Linux系統通過Bootloader引導,一上電,就要執行Bootloader來初始化系統。在完成對系統的初始化任務之後,

它會将非易失性存儲器(通常是 Flash或 DOC 等)中的Linux 核心拷貝到 RAM 中去,然後跳轉到核心的第一條指

令處繼續執行,進而啟動 Linux 核心。Bootloader 和 Linux 核心有着密不可分的聯系。 

Bootloader多數有兩個階段的啟動過程: 

Stage1: ?

 基本的硬體初始化  ? 為加載stage2準備RAM空間  ? 拷貝核心映像和檔案系統映像到RAM中  ? 設定堆棧指針sp  ?  跳到stage2的入口點 

Stage2: ?

 初始化本階段要使用到的硬體裝置  ?  檢測系統的記憶體映射  ? 加載核心映像和檔案系統映像  ? 設定核心的啟動參數 

嵌入式系統中廣泛采用的非易失性存儲器通常是 Flash,而 Bootloader就位于該存儲器的最前端,是以系統上電或複位

後執行的第一段程式便是 Bootloader。Bootloader在flash中的存儲示意圖如下: 

Bootloader啟動流程圖  

3、Bootloader 的啟動方式

3.1網絡啟動方式 

這種方式的開發闆不需要較大的存儲媒體,跟無盤工作站有點類似,但是使用這種啟動方式之前,需要把Bootloader安

裝到闆上的EPROM或者Flash中。Bootloader通過以太網接口遠端下載下傳Linux核心映像或者檔案系統。Bootloader下

載檔案一般都使用TFTP網絡協定,還可以通過DHCP的方式動态配置IP位址。 

3.2硬碟啟動方式  

傳統的Linux系統運作在桌上型電腦或者伺服器上,這些計算機一般都使用BIOS引導,并使用磁盤作為存儲媒體。Linux傳

統上是LILO (Linux Loader) 引導,後來又出現了GUN的軟體 (Grand Unified Bootloader) 。 這兩種Bootloader廣泛

應用在X86的Linux系統上。 

3.3 Flash啟動方式 

大多數嵌入式系統上都使用Flash存儲媒體。Flash有很多類型,包括NOR Flash、NAND Flash和其它半導體盤。它們

之間的不同在于: NOR Flash 支援晶片内執行(XIP, eXecute In Place),這樣代碼可以在Flash上直接執行而不必

拷貝到RAM中去執行。而NAND Flash并不支援XIP,是以要想執行 NAND Flash 上的代碼,必須先将其拷貝到 RAM

中去,然後跳到 RAM 中去執行。NOR Flash 使用最為普遍。Bootloader一般放在Flash的底端或者頂端,這需要根據

處理器的複位向量來進行設定。可以配置成MTD裝置來通路Flash分區。 

4、Bootloader種類

嵌入式Linux系統已經有各種各樣的Bootloader,種類劃分的方法也不是唯一的,一般可以按照它所支援處理器體系結

構不同進行劃分,如下表: Bootloader

 Mointor描述 X86ARMPowerPC

LILO 否 Linux磁盤引導程式 是 否 否 Grub 否 GNU引導的LILO替代程式 是 否 否 

Loadlin 否 從DOS引導Linux 是 否 否 

ROLO 否 從ROM引導Linux而不需要BIOS是 否 否 

Etherboot 否 通過以太網啟動Linux引導程式 是 否 否 

Linux BIOS否 完全替代BUIS的Linux引導程式 是 否 否 

Blob 否 LART等硬體平台的引導程式 否 是 否 

U-Boot 是 通用引導程式 是 是 是 

RedBoot 是 基于eCos的引導程式 是 是 是 常見嵌入式

Linux的Bootloader有:Blob、Redboot、U-Boot 

Linux核心的啟動過程

Linux 核心有兩種映像:一種是非壓縮核心,叫 Image,另一種是它的壓縮版本,叫zImage。根據核心映像的不同,Linux 

核心的啟動在開始階段也有所不同。ZImage 是 Image經過壓縮形成的,是以它的大小比 Image 小。但為了能使用 

zImage,必須在它的開頭加上解壓縮的代碼,将 ZImage 解壓縮之後才能執行,是以它的執行速度比 Image 要慢。但

考慮到嵌入式系統的存儲空容量一般比較小,采用 zImage 可以占用較少的存儲空間,是以犧牲一點性能上的代價也是

值得的。是以一般的嵌入式系統均采用壓縮核心的方式。 

在 Bootloader将 Linux 核心映像拷貝到 RAM 以後,解壓核心映像和初始化,完成剩餘的與硬體平台相關的初始化工

作,再進行一系列與核心相關的初始化後,調用第一個使用者程序-init 程序并等待使用者程序的執行,這樣整個 Linux 内

核便啟動完畢。在很多情況下,我們可以調用一個簡單的 shell 腳本來啟動必需的嵌入式應用程式。 

···········································································································································································································

u-boot介紹:

u-boot是一種普遍用于嵌入式系統中的Bootloader,Bootloader是在作業系統運作之前執行的一小段程式,通過它,我們可以初始化硬體裝置、建立記憶體空間的映射表,進而建立适當的軟硬體環境,為最終調用作業系統核心做好準備。Boot Loader的主要運作任務就是将核心映象從硬碟上讀到RAM中,然後跳轉到核心的入口點去運作,即開始啟動作業系統。系統在上電或複位時通常都從位址0x00000000處開始執行,而在這個位址處安排的通常就是系統的Boot Loader程式。

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的工具。

其中比較重要的目錄就是/board、/cpu、/drivers和 /include目錄,如果想實作u-boot在一個平台上的移植,就要對這些目錄進行深入的分析。 

u-boot的啟動過程:

系統啟動的入口點。既然我們現在要分析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的啟動過程是多階段實作的,分了兩個階段:

第一階段是用彙編寫的,主要任務是:

1、CPU 自身初始化:包括MMU,Cache,時鐘系統,SDRAM 控制器等的初始化

2、重定位:把自己從非易失性存儲器搬移到 RAM 中

3、配置設定堆棧空間,設定堆棧指針

4、清零 BSS 資料段

5、跳轉到第二階段入口函數 start_armboot()

第二階段是用C寫的,主要任務是:

1、為 U-boot 内部私有資料配置設定存儲空間,并清零

2、依次調用函數指針數組 init_sequence 中定義的函數進行一系列的初始化

3、如果系統支援 NOR Flash,調用flash_init ()和display_flash_config ()初始化并顯示檢測到的器件資訊

4、如果系統支援 LCD 或VFD,調用lcd_setmem()或vfd_setmem()計算幀緩(Framebuffer)大小,然後在BSS 資料段之後為Framebuffer 配置設定空間,初始化gd->fb_base 為Framebuffer 的起始位址

5、調用 mem_malloc_init()進行存儲配置設定系統(類似于C 語言中的堆)的初始化和空間配置設定

6、如果系統支援 NAND Flash,調用nand_init ()進行初始化

7、如果系統支援 DataFlash,調用AT91F_DataflashInit()和dataflash_print_info()進行初始化并顯示檢測到的器件資訊

8、調用 env_relocate ()進行環境變量的重定位,即從Flash 中搬移到RAM 中

9、如果系統支援 VFD,調用drv_vfd_init()進行VFD 裝置初始化

10、從 環 境 變 量 中 讀 取 IP 位址和MAC 位址, 初始化gd->bd-> bi_ip_addr 和gd->bd->bi_enetaddr

11、調用 jumptable_init ()進行跳轉表初始化,跳轉表在global_data 中,具體用途尚不清楚

12、調用 console_init_r()進行控制台初始化

13、如果需要,調用 misc_init_r ()進行雜項初始化

14、調用 enable_interrupts ()打開中斷

15、如果需要,調用board_late_init()進行單闆後期初始化,對于AT91SAM9260EK,主要是以太網初始化

16、進入主循環:根據使用者的選擇啟動 linux,或者進入指令循環執行使用者輸入的指令

這部分是一些相對變化不大的部分,我們針對不同的闆子改變它調用的一些初始化函數,并且通過設定一些宏定義來改變初始化的流程,是以這些代碼在移植的過程中并不需要修改,也是錯誤相對較少出現的檔案。在檔案的開始先是定義了一個函數指針數組,通過這個數組,程式通過一個循環來按順序進行正常的初始化,并在其後通過一些宏定義來初始化一些特定的裝置。在最後程式進入一個循環,main_loop。這個循環接收使用者輸入的指令,以設定參數或者進行啟動引導。

······································································································································································································································