加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

Linux 內(nèi)核入口分析 - 嵌入式Linux系統(tǒng)開發(fā) - 嵌入式Linux系統(tǒng)開發(fā)

2021/09/28
348
閱讀需 8 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

從啟動引導(dǎo)程序 bootloader(uboot)跳轉(zhuǎn)到 Linux 內(nèi)核后,Linux 內(nèi)核開始啟動,今天我們分析一下 Linux 內(nèi)核啟動入口。

跳轉(zhuǎn)過去初始化肯定是在匯編文件中,根據(jù)架構(gòu)可以選擇不同的平臺,這里看一下鏈接匯編文件:

linux4.14/arch/arm/kernel/vmlinux.lds.S

這里可以看到鏈接時候 Linux 入口是 stext 段,這里是啟動引導(dǎo)程序跳轉(zhuǎn)過來的第一段Linux 代碼:

Linux入口地址

我們先看一下入口地址的確定,同一文件。

SECTIONS {  /*   * XXX: The linker does not define how output sections are   * assigned to input sections when there are multiple statements   * matching the same input section name.  There is no documented   * order of matching.   *   * unwind exit sections must be discarded before the rest of the   * unwind sections get included.   */  /DISCARD/ : {   *(.ARM.exidx.exit.text)   *(.ARM.extab.exit.text)   ARM_CPU_DISCARD(*(.ARM.exidx.cpuexit.text))   ARM_CPU_DISCARD(*(.ARM.extab.cpuexit.text))   ARM_EXIT_DISCARD(EXIT_TEXT)   ARM_EXIT_DISCARD(EXIT_DATA)   EXIT_CALL #ifndef CONFIG_MMU   *(.text.fixup)   *(__ex_table) #endif #ifndef CONFIG_SMP_ON_UP   *(.alt.smp.init) #endif   *(.discard)   *(.discard.*)  }  . = PAGE_OFFSET + TEXT_OFFSET;  .head.text : {   _text = .;   HEAD_TEXT  }

這個 SECTIONS 比較長,只放一部分。在這里有個比較重要的東西:

. = PAGE_OFFSET + TEXT_OFFSET;

這一句表示了 Linux 系統(tǒng)真正的啟動地址。

PAGE_OFFSET 是 Linux 內(nèi)核空間的虛擬起始地址,定義在:

linux4.14/arch/arm64/include/asm/memory.h

 

注意,這里的地址都很重要,很多地方會用到。當(dāng)然,這里的地址可能會隨著 Linux 內(nèi)核版本的不同和硬件的不同,會變化。這里沒有一個具體的數(shù),因?yàn)?VA_BITS 中的數(shù)字是可選的,大家可以根據(jù)自己的平臺算一下。

TEXT_OFFSET 定義在:

linux4.14/arch/arm/Makefile 中:

 

這個值一般是 0x00008000 ,算出 PAGE_OFFSET 后加上這個值就是 Linux 內(nèi)核的起始地址。

修改這個偏移量就可以使Linux內(nèi)核拷貝到不同的地址,自己修改注意內(nèi)存對齊。

stext 段

從上面的ENTRY(stext)可以知道,一開始是運(yùn)行stext段,這個段內(nèi)的代碼是 start_kernel 函數(shù)前匯編環(huán)境的初始化。

linux4.14/arch/arm64/kernel/head.S

preserve_boot_args 保存 bootloader 傳遞過來的參數(shù)。

el2_setup 是設(shè)置 Linux 啟動模式是 EL2。Linux 有 EL0、EL1、EL2、EL3 四種異常啟動模式,這里設(shè)置一開始是 EL2,EL2 支持虛擬內(nèi)存技術(shù),然后注釋說明后面又退回 EL1,在 EL1 啟動 kernel。EL3 一般是只在安全模式使用。

set_cpu_boot_mode_flag 保存上面 cpu 的啟動模式。

__create_page_tables 創(chuàng)建頁表。

__cpu_setup 初始化CPU,這里主要是初始化和 MMU 內(nèi)存相關(guān)的 CPU 部分。

__primary_switch 這里會進(jìn)行跳轉(zhuǎn)。

在同一個文件中,會跳轉(zhuǎn)到這里,739 行開啟了MMU。然后最重要的是跳轉(zhuǎn)到

__primary_switched 函數(shù)。先把 __primary_switched 地址放到 x8 寄存器中,再跳轉(zhuǎn)到 x8,也就是跳轉(zhuǎn)到 __primary_switched。

接下來分析 __primary_switched 函數(shù):

 

 

324-327  初始化了 init 進(jìn)程的內(nèi)存信息,開辟了內(nèi)存空間。

329-334 設(shè)置了向量表。

336-340 保存了FDT,也就是 flat device tree 。

342-348 清除了BSS 段,我們知道一般是內(nèi)存四區(qū):堆區(qū)、棧區(qū)、全局區(qū)、代碼區(qū)。其中全局區(qū)可以再分為 data 段和 BSS 段,BSS 段存儲了未初始化的變量,這里將BSS段進(jìn)行清零操作,否則內(nèi)存中的值是不確定的,這是一個傳統(tǒng)操作。

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

研究生在讀,熟悉硬件、STM32單片機(jī)、嵌入式Linux。已收獲小米、聯(lián)發(fā)科、浙江大華、上能電氣、英威騰、匯川技術(shù)、格力、富士康等大廠offer。在這里分享求職經(jīng)驗(yàn)、嵌入式學(xué)習(xí)規(guī)劃、考研、嵌入式Linux技術(shù)文章等。