本篇的重點是講解裝置和驅動的啟動流程,裝置和驅動的流程是整個核心啟動的核心,也是工作中最常面對的問題。出於知識點的系統性考慮,在進入主題之前我們先看下整個 Linux 在 ARM 中的啟動流程如何。
Uboot 的啟動流程
ARM Linux 的啟動流程大致為:Uboot → Kernel → Root filesystem。Uboot 在上電的時候就拿到 CPU 的控制權,實現了硬體的初始化。具體是怎麼實現的呢?一起來看一下,CPU 的內部集成了小容量的 Sram,而 PC 指標一上電就指向 Sram 的起始地址 0x00000000,所以一上電 Uboot 程式碼就得到了執行。
Uboot 拿到 CPU 使用權就開始做初始化工作,比如關閉看門狗、設定 CPU 執行樣式、設定堆疊、初始化記憶體、網絡卡、nand flash 等,最後把 Linux 核心載入到記憶體中。
- 初始化 RAM
因為核心要在 RAM 中執行,所以在呼叫核心之前必須初始化和設定 RAM,為呼叫核心做好準備。
- 初始化串列埠
核心在啟動過程中可以將資訊透過串列埠輸出,這樣就可以清楚的知道核心啟動資訊。雖然串列埠不是 Uboot 必須要完成的工作,但是透過串列埠可以方便除錯 Uboot 和內核的各種資訊。
- 檢測處理器型別
Uboot 在呼叫核心前需要檢測系統的處理器型別,並將其儲存在某個變數中提供給核心,核心在啟動過程中會根據該處理器的型別呼叫相應的初始化程式。
- 設定核心啟動引數
核心在啟動過程中會根據該啟動引數進行相應的初始化工作。
- 呼叫核心映象
值得註意的是儲存 Uboot 的儲存器不同,Uboot 的執行過程也並不相同,一般來講 Flash 分為 nor Flash 和 nand Flash 兩種:nor Flash 支援晶片內執行(XIP,eXecute In Place),這樣程式碼可以在 Flash 上直接執行而不必複製到 RAM 中去執行。
但是 nand Flash 並不支援 XIP,所以要想執行 nand Flash 上的程式碼,必須先將其複製到 RAM 中去,然後跳到 RAM 中去執行。如果核心存放在 nor Flash 中,那麼可直接跳轉到核心中去執行。但通常由於在 nor Flash 中執行程式碼會有種種限制,而且速度也遠不及 RAM 快,所以一般的嵌入式系統都是將核心覆制到 RAM 中,然後跳轉到 RAM 中去執行。不論哪種情況,在跳到核心執行之前 CPU 的暫存器必須滿足以下條件:r0 = 0,r1 = 處理器型別,r2 = 標記串列在 RAM 中的地址。
Linux 內核的啟動流程(裝置和驅動的載入)
關於 Uboot 的啟動本課程不做詳細介紹,因為本課程的主要內容是核心。在講述核心啟動之前讓我們先瞭解下內核的組成結構:
其中,
(1)vmlinusx 是 ELF 格式的 Object 檔案,這種檔案只是各個原始碼經過連線以後得到的檔案,並不能在 ARM 平臺上執行。
(2)經過 objcopy 這個工具轉換以後,得到了二進位制格式檔案 Image,Image 檔案相比於 vmlinusx 檔案,除了格式不同以外,還被去除了許多註釋和除錯的資訊。
(3)Image 檔案經過壓縮以後得到了 piggy.gz,這個檔案僅僅是 Image 的壓縮版,並無其他不同。
(4)接著編譯生成另外幾個模組檔案 misc.o、big_endian.o
、head.o、head-xscale.o,這幾個檔案組成一個叫 Bootstrap Loader 的元件,又叫引導程式,編譯生成 piggy.o 檔案。
(5)最後 piggy.o 檔案和 Bootstrap Loader 組成一個 Bootable Kernel Image 檔案(可啟動檔案)。
經過上面的分析不難知道 piggy.o 就是核心映象,而剩下的幾個檔案就組成了引導程式。知道了內核的組成結構,Uboot 就是按照內核的組成結構一層一層剝開然後引導內核的:
可以說 start_kernel()
之前的所有工作都是為了將環境準備好,滿足 start_kernel()
的要求,然後由 start_kernel()
開始進行內核的載入:
關於 start_kernl()
函式的內容太多,可以透過紅色回呼函式看出,start_kernel()
函式基本是在回呼很多對應的註冊函式。為了本系列課程的結構性這裡就不展開所有知識點講解,本篇內容接著前一篇裝置樹的內容重點講解下裝置和驅動的匹配過程。
還記得上一篇講到的裝置樹三大作用嗎?
- 平臺標識;
- 執行時配置;
- 裝置資訊集合。
接下來我們就看看核心在啟動的時候是如何尋找裝置,驅動又如何和裝置系結的。
首先在平臺目錄下可以看到有很多平臺描述的檔案,如圖:
有那麼多的平臺,我們到底要執行哪個平臺是首先要考慮的事情。這也是裝置三大功能的第一個功能——平臺標識。
- 裝置樹裡有對裝置根節點的 Compatible 描述,平臺檔案裡有對
__initconst
的描述,如果兩個欄位一致則找到了對應的板級檔案,這樣就透過裝置樹把要用的裝置平臺與其他平臺區分開來了,如圖:
找到平臺後就可以根據回呼函式的指標呼叫該平臺的註冊函式。這裡以飛思卡爾 imx.6dl 平臺為例,回呼的時候會呼叫 imx6q_init_machine()
函式,如下:
這裡補充一個知識點,細心的讀者也許發現了在 Compatible 欄位裡用逗號分隔了兩個字串。板級匹配的時候用的是哪個字串,另外一個字串又是做什麼用?首先後面的欄位 “fsl,imx6dl” 是抽象共用平臺描述符,前面的欄位 “fsl,imx6dl-sabresd” 是通用平臺下的具體平臺描述符,可以理解為母板和子板的區別。在具體的子板檔案中我們可以透過前面的欄位進行裝置資訊的獲取,如圖:
-
接著是執行時配置,讓核心在啟動的時候根據引數設定進行不同的處理。有經驗的讀者清楚在 Uboot 裡也有對 Bootargs 的配置,這裡為什麼多此一舉呢,是為了在 Uboot 中更靈活的對核心啟動進行配置。
-
最後的作用就是裝置資訊集合,這是裝置和驅動匹配的核心,也是工作中面對最多的情況。出於這一作用的內容是工作中經常遇到的重點也是難點,我們專門用一篇內容來詳細講解各級裝置是如何展開的,並且手把手教你如何定製一套自己的開發板全新案例。
朋友會在“發現-看一看”看到你“在看”的內容