1. 程式人生 > >Linux kernel啟動流程第一階段

Linux kernel啟動流程第一階段

head.S:kernel第一階段入口

來自:http://blog.csdn.net/ooonebook/article/details/52710290

1safe_svcmode_maskallr9;關閉普通中斷、快速中斷,使能SVC模式

實現程式碼:arch/arm/include/asm/assembler.h

註解:來自http://blog.csdn.net/crosskernel/article/details/21091819

從Hyper態返回SVC態
//reg—暫存暫存器
.macro safe_svcmode_maskall reg:req
#if __LINUX_ARM_ARCH__ >= 6
    //讀取cpsr到暫存暫存器reg
mrs \reg , cpsr

/*以下兩條指令區分當前cpsr是否處在HYP_MODE,

  *若處在HYP_MODE模式,標誌位清零

  */
eor \reg, \reg, #HYP_MODE
tst \reg, #MODE_MASK
bic \reg , \reg , #MODE_MASK
    //在暫存暫存器裡存放SVC控制位
orr \reg , \reg , #PSR_I_BIT | PSR_F_BIT | SVC_MODE
THUMB( orr  \reg , \reg , #PSR_T_BIT )
    /*若當前暫存器處於hyper模式,返回SVC模式走一個杜撰的HVC異常返回。*/
bne 1f
orr \reg, \reg, #PSR_A_BIT
    /*設定返回到SVC態的地址是標號2,即該巨集呼叫的後一條指令。*/ 
adr lr, BSYM(2f)
    //退出hyper後的cpsr暫存器
msr spsr_cxsf, \reg
    //放到ELR_hyp中
__MSR_ELR_HYP(14)
    //返回到SVC態
__ERET
    //當前處理器不在hyper狀態,強制切換到SVC態
1: msr  cpsr_c, \reg
2:

2proccessor info的獲取
cpu idprocinfo是一一對應的關係,所以可以通過cpu id來獲取到對應的procinfo結構體。
procinfo使用proc_info_list結構體,用來說明一個cpu的資訊,包括這個cpuID號,對應的核心資料對映區的MMU標識等等。
注意:proc_info_list結構體中存在MMU標識,也就是我們需要在開啟MMU之前需要先獲取procinfo的原因,因為開啟MMU之前需要配置臨時核心頁表,而配置臨時核心頁表需要這裡的MMU標識來進行設定。
對應程式碼:
1mrc p15, 0, r9, c0, c0      @ get processor id

解釋:arm體系將CPUID(處理器識別符號,主識別符號)存放在協處理器cp15c0暫存器中。

2bl__lookup_processor_type   @r5 = procinfor9 = cupid

arch/arm/kernel/head-common.S 通過比較各個CPU的procinfo中的cpu id的值來查詢對應的procinfo結構體。若不支援當前CPU,則r5=0

3)判斷r5,若為0,則列印錯誤。

3地址的一次轉換

  @ 在呼叫__enable_mmu前使用的都是實體地址,而核心卻是以虛擬地址連線的,這裡進行一次轉換

#ifndef CONFIG_XIP_KERNEL
adr     r3,2f             @
r3= 第124行程式碼的實體地址
ldmia   r3, {r4,r8}         @r4= 第124行程式碼的虛似地址,r8=PAGE_OFFSET
sub     r4, r3,r4     @ (PHYS_OFFSET - PAGE_OFFSET)即實體地址與虛似地址差值
add     r8, r8,r4     @ PHYS_OFFSET r8=PAGE_OFFSET對應的實體地址
#else
ldr     r8,=PLAT_PHYS_OFFSET    @ RAM的起始實體地址,值為0x30000000

L124               2:     .long   .   @ "."號表示當前這行程式碼編譯連線後的虛似地址
L125                       .long   PAGE_OFFSET

4、驗證dtb    bl __vet_atags

實現程式碼:arch/arm/kernel/head-common.S

TIPS:

1dtb裡面存放了各種硬體資訊,如果dtb有問題,會導致後續開機過程中讀取的裝置資訊有問題而導致無法開機。

2、在生成dtb的時候會在頭部上新增一個幻數magic,而驗證dtb是否合法主要也就是看這個dtbmagic是否和預期的值一致。其中magic是固定值:0xd00dfeed(大端)或者0xedfe0dd0(小端)我們只要提取待驗證dtb的地址(Uboot傳入的r2)上的資料的前四個位元組,與0xd00dfeed(大端)或者0xedfe0dd0(小端)進行比較,如果匹配的話,就說明對應待驗證dtb就是一個合法的dtb

程式碼註解:

Ldr r5, [r2, #0]    @獲取前4個位元組放在r5

Ldrr6, =OF_DT_MAGIC @獲取magic放在r6

5bl__create_page_tables(建立臨時核心頁表====>開啟MMU)

實現程式碼:arch/arm/kernel/head.S

TIPS:

1MMUMemory ManageUnion):

將線性地址(虛擬地址)對映為實體地址(RAM地址);

提供硬體機制的記憶體訪問授權;

根據頁表找到對應關係以及許可權;

2為了開啟MMU,核心需要建立一個臨時核心頁表,用於kenrel啟動過程中的開啟MMU的過渡階段。並且,使用的是段式管理的方法

程式碼註解:

1)獲取核心頁表的起始地址

pgtbl     r4, r8                @ page table address

pgtbl 巨集用於通過DRAM實體地址來獲取頁表的實體地址。前面我們已經知道r8用於存放DRAM的起始實體地址,r4則是要存放計算得到的頁表實體地址。 
pgtbl
巨集如下:

arch/arm/kernel/head.S
    .macro    pgtbl, rd, phys
    add    \rd, \phys, #TEXT_OFFSET
    sub    \rd, \rd, #PG_DIR_SIZE
    .endm

kernel在放在DRAM上偏移TEXT_OFFSET的位置上。linux規定將TEXT_OFFSET之前的PG_DIR_SIZE大小的空間用作臨時頁表。所以計算方式如下: 
kernel
起始地址=DRAM起始實體地址+TEXT_OFFSET=0x20008000核心頁表地址=kernel起始地址-PG_DIR_SIZE=0x20004000=>PG_DIR_SIZE=0x4000(16K)所以程式碼換算成如下計算: 
\rd(r4) = phys(r8) +TEXT_OFFSET 
\rd(r4) = \rd(r4) -PG_DIR_SIZE
 

2)清零頁表

str    r3, [r0], #4      

@ r0(臨時核心頁表實體地址)指向的暫存器上開始寫入0值,每16個位元組一個迴圈

   str    r3, [r0], #4

   str    r3, [r0], #4

   str    r3, [r0], #4

    teq   r0, r6    

每16個位元組一迴圈可以減少teq的頻率,同時不會出現清零越界(因:頁表大小為16K)

3)設定MMU的標識並存放到r7暫存器

ldr    r7,[r10, #PROCINFO_MM_MMUFLAGS]@ mm_mmuflags

PROCINFO_MM_MMUFLAGS對應如下

DEFINE(PROCINFO_MM_MMUFLAGS,    offsetof(struct proc_info_list, __cpu_mm_mmu_flags));

4)建立對映表

未懂

6b  __enable_mmu(呼叫平臺特定的__CPU_flush函式(在結構體proc_info_list中),使能MMU)

程式碼:

ldr r13, =__mmap_switched       @ address to jump to after

@ mmu has been enabled

@ __mmap_switched函式的地址放在暫存器

@ __mmap_switched實現了開啟MMU之後跳轉到start_kernel

    adr    lr, BSYM(1f)              @ return (PIC) address

@簡單說,就是儲存了呼叫子程式後返回的指令地址

    movr8, r4              @ set TTBR1 to swapper_pg_dir

@ 把臨時核心頁表的地址放在r8暫存器中

    ldrr12, [r10, #PROCINFO_INITFUNC]

@ cpu對應procinfo中的__cpu_flush存放到r12暫存器中

@ __cpu_flush成員存放的是cpu對應架構的setup函式的地址,在結構體proc_info_list中

@ 對於s5pv210來說,這個值就是__v7_setup的連線地址。

    addr12, r12, r10

    retr12

@ 這裡實現為跳轉到__v7_setup的實體地址上,也就是呼叫__v7_setup

1:  b   __enable_mmu

@ 跳轉到__enable_mmu

__enable_mmu:

·        需要先將頁表實體地址寫入到cp15c2暫存器中

·        需要在cp15c3暫存器中寫入位域相應的許可權

·        配置cp15c1暫存器,用來控制MMU的相應功能

·        跳轉到__turn_mmu_on

__turn_mmu_on:真正的開啟MMU的函式,開啟MMU後CPU會把所有地址都當做虛擬地址處理。

注:__turn_mmu_on中會執行命令:

mov r3, r13

ret r3

之前在head.S中有指令“ldr r13, =__mmap_switched”,因此會直接跳轉去執行__mmap_switched,該函式負責跳轉至核心

7、跳轉至核心

準備階段:

·        資料段的準備

·        堆疊段的準備

·        一些後續會訪問到的變數的設定(CPU IDmachine id&dtb、當前程序堆疊指標)

·        當前程序堆疊指標的設定

程式碼:

__mmap_switched_data結構體:

__mmap_switched_data:

    .long   __data_loc          @ r4

    .long   _sdata              @ r5

    .long   __bss_start         @ r6

    .long   _end                @ r7

    .long   processor_id            @ r4

    .long   __machine_arch_type     @ r5

    .long   __atags_pointer         @ r6

#ifdefCONFIG_CPU_CP15

    .long   cr_alignment            @ r7

#else

    .long0               @ r7

#endif

    .long   init_thread_union +THREAD_START_SP @ sp

    .size  __mmap_switched_data, . - __mmap_switched_data

__mmap_switched:

    adrr3, __mmap_switched_data

@ __mmap_switched_data的地址載入到r3

   ldmia   r3!, {r4, r5, r6, r7}

@ __mmap_switched_data(r3)上的值分別載入到r4r5r6r7暫存器中,__mmap_switched_data前面說明了

@ 經過上述動作,r4r5r6r7暫存器分別存放了如下值

@ r4 -> __data_loc:資料段儲存地址

@ r5 -> _sdata:資料段起始地址

@ r6 -> __bss_start:堆疊段起始地址

@ r7 -> _end:堆疊段結束地址

    cmpr4, r5              @ Copy data segment if needed

1:  cmpne   r5, r6

   ldrne   fp, [r4], #4

   strne   fp, [r5], #4

    bne 1b

@ 判斷資料段儲存地址(r4)和資料段起始地址(r5)

@ 如果不一樣的話需要搬移到資料段起始地址(r5)上。

    movfp, #0              @ Clear BSS (and zero fp)

1:  cmp r6, r7

   strcc   fp, [r6],#4

    bcc 1b

@ 清空堆疊段。

@ 從堆疊段起始地址(r6)開始寫入0,一直寫到地址為堆疊段結束地址(r7)

 ARM(  ldmia   r3, {r4, r5, r6, r7, sp})

 THUMB(ldmia   r3, {r4, r5, r6, r7}    )

 THUMB(ldr sp, [r3, #16]       )

@ 繼續將__mmap_switched_data(r3)上的值分別載入到r4r5r6r7sp暫存器中,注意是前面r3已經載入過一部分了,地址和__mmap_switched_data已經不一樣了

@ 經過上述動作,r4r5r6r7暫存器分別存放了如下值

@ r4 -> processor_id變數地址:其內容是cpu處理器ID

@ r5 -> __machine_arch_type變數地址:其內容是machine id

@ r6 -> __atags_pointer變數地址:其內容是dtb的地址

@ r7 -> cr_alignment變數地址:其內容是cp15c1的暫存器的值

@ sp-> init_thread_union + THREAD_START_SP,設定了當前程序的堆疊

    strr9, [r4]            @ Save processor ID

@ cpu處理器id(r9)放到processor_id變數中([r4])

    strr1, [r5]            @ Save machine type

@ mechine id(r1)存放到__machine_arch_type變數中([r5])

    strr2, [r6]            @ Save atags pointer

@ dtb的地址指標(r2)存放到__atags_pointer變數中([r6])

    cmpr7, #0

   strne   r0, [r7]            @ Save control register values

@ cp15c1的暫存器的值(r0)存放到cr_alignment變數中([r7])

   b   start_kernel

@ 跳轉到start_kernel中,也就是啟動流程的第二階段。

ENDPROC(__mmap_switched)

Kernel啟動流程中的Tips:

1、 Kernel一般會存在於儲存裝置上,比如FLASH\EMMC\SDCARD. 因此,需要先將kernel映象載入到RAM的位置上,CPU才可以去訪問到kernel

但是注意,載入的位置是有要求的,一般是載入到物理RAM偏移0x8000的位置,也就是要在前面預留出32KRAMkernel會從載入的位置上開始解壓,而kernel前面的32K空閒RAM中,16K作為boot params,16K作為臨時頁表

2、 Arch/arm/kernel/head.S(kernel的入口函式)

3、 bootloader需要通過設定PC指標到kernel的入口程式碼處(也就是kernel的載入位置)來實現kernel的跳轉。

Kernel的硬體要求如下(解釋了bootloader為何去那樣初始化硬體):

MMU =off MMU用來處理實體地址到虛擬記憶體地址的對映,因此需要軟體上需要先配置其對映表(也就是後續文章會說明的頁表)。MMU關閉的情況下,CPU定址的地址都是實體地址,也就是不需要經過轉化直接訪問相應的硬體。一旦開啟之後,CPU定址的所有地址都是虛擬地址,都會經過MMU對映到真正的實體地址上,即使你在程式碼中訪問的是一個實體地址,也會被當作虛擬記憶體地址使用。而對映表是由kernel自己建立的,因此,在建立對映表之前kernel訪問的地址都是實體地址,所以必須保證MMU是關閉狀態。

 D-cache= off 
CACHE
CPU和記憶體之間的高速緩衝儲存器,又分成資料緩衝器D-cache和指令緩衝器I-cache資料Cache一定要關閉,否則可能kernel剛啟動的過程中,去取資料的時候,從Cache裡面取,而這時候RAM中資料還沒有Cache過來,導致資料預取異常博主的理解是,假設開啟MMU之前,cache上存了一個項地址0x20000000、資料0xffff0000”,開啟MMU之後,讀取0x20000000地址上(虛擬地址)資料,但是此時會直接從cache中讀到項地址0x20000000、資料0xffff0000”,但實際上對應實體地址上的資料並不是這個,所以會導致讀取的資料錯誤。


巨集

位置

預設值

說明

KERNEL_RAM_ADDR

arch/arm/kernel/head.S +26

0xc0008000

kernel在RAM中的虛擬地址

PAGE_OFFSET

include/asm-arm/memeory.h +50

0xc0000000

核心空間的起始虛擬地址

TEXT_OFFSET

arch/arm/Makefile +131

0x00008000

核心在RAM中起始位置相對於

RAM起始地址的偏移

TEXTADDR

arch/arm/kernel/head.S +49 

0xc0008000 

kernel的起始虛擬地址


PHYS_OFFSET

include/asm-arm/arch- *** /memory.h

平臺相關

RAM的起始實體地址,對於s3c2410來說在include/asm-arm/arch-s3c2410/memory.h下定義,值為0x30000000(ram接在片選6上)

相關推薦

Linux kernel啟動流程第一階段

head.S:kernel第一階段入口 來自:http://blog.csdn.net/ooonebook/article/details/52710290 1、safe_svcmode_maskallr9;關閉普通中斷、快速中斷,使能SVC模式 實現程式碼:arch/a

ARM linux kernel啟動流程 head.S(一)

1. kernel執行的史前時期和記憶體佈局 在arm平臺下,zImage.bin壓縮映象是由bootloader載入到實體記憶體,然後跳到zImage.bin裡一段程式,它專門於將被壓縮的kernel解壓縮到KERNEL_RAM_PADDR開始的一段記憶體中,接著跳進真

kernel啟動流程第二階段

整個流程基本為從網上大牛的分享結合自己的理解所述,部分引用可能未貼上連結。 Kernel啟動流程中的Tips: 1、 Kernel一般會存在於儲存裝置上,比如FLASH\EMMC\SDCARD. 因此,需要先將kernel映象載入到RAM的位置上,CPU才可以去訪問到k

[kernel 啟動流程] (第二章)第一階段之——設定SVC、關閉中斷

本文是基於arm平臺。例子都是以tiny210(s5pv210 armv7)為基礎的。 [kernel 啟動流程]系列: 建議參考文件: ================================================ 零、說

[kernel 啟動流程] (第五章)第一階段之——臨時核心頁表的建立

本文是基於arm平臺。例子都是以tiny210(s5pv210 armv7)為基礎的。 [kernel 啟動流程]系列: 建議參考文件: ================================================ 零、說明

linuxkernel啟動流程

                       &nb

Linux系統啟動流程

linux系統啟動流程Linux系統啟動流程Linux系統啟動流程圖1、加電自檢 上電自檢POST,主要負責檢測系統外圍關鍵設備(如:CPU、內存、顯卡、I/O、鍵盤鼠標等)是否正常, 自檢完成後從BIOS中設置的系統啟動順序來搜索用於啟動系統的驅動器2、grub引導(1)grub引導第一階段(stag

linux系統啟動流程(CentOS5/6/7為例)

centos 系統一個操作系統要完整啟動起來需要哪些步驟呢?今天以CentOS為例探索一下linux的啟動流程;一、Linux系統的組成--kernel+rootfs(根文件系統)kernel: 內核,進行進程管理、內存管理、網絡管理、驅動程序、文件系統、安全功能等等rootfs: 根文件系統1.內核設計流派

LINUX啟動流程

啟動 分享 src 引導 一個 技術分享 bio 化工 swap分區 (上圖片轉自一位高手所做) 啟動第一步--加載BIOS當你打開計算機電源,計算機會首先加載BIOS信息,BIOS信息是如此的重要,以至於計算機必須在最開始就找到它。這是因為BIOS中包含了CPU

Linux啟動流程

啟動流程前言:當我們了解了系統的啟動過程,那麽系統啟動出問題時,我們就能知道什麽地方出錯了,就能哪裏出錯修哪裏,不需要重裝系統。CentOS6和CentOS7啟動流程類似,不過CentOS6和7在啟動後期不一樣,CentOS6後期是init,CentOS7是systemd,那麽細節之處呢,跟著我一塊看看吧!l

Linux啟動流程

情況 啟動腳本 問題 .com tin 系統環境 通用 bubuko ant 本文轉載自:http://www.ruanyifeng.com/blog/2013/08/linux_boot_process.html 半年前,我寫了《計算機是如何啟動的?》,探討BIO

Linux系統啟動流程之chkconfig

Linux系統啟動流程chkconfig根據用戶的要求,需要在系統正常啟動後自動運行某些腳本。chkconfig xxx on 這個命令就自動在對應的rc2 rc3 rc4的目錄下創建腳本先拿rc2.d來看看這個是rc2.d目錄裏一個文件的內容,chkconfig 2345 57 432345指明了運行級別,

第七篇:Linux系統啟動流程

.com 標誌位 linu http 操作系統 流程 mbr 我們 png 1.bios:是在主板上的一段程序,決定計算機從哪一塊啟動介質中讀操作系統。2.硬盤最小單位是扇區,一個扇區512byte,計算機啟動第一個讀的扇區叫“主引導記錄”(MBR),446B:引導信息 6

Linux 系統啟動流程

認證 don grand 導致 procedure 圖形界面 大小 發行版 內核 內核簡介: kernel功能:進程管理、內存管理、網絡管理、驅動程序、文件系統、安全功能 庫:函數的集合,同時提供調用接口;不能作為程序的執行入口單獨執行,只能被程序調用  過程調用:pr

Linux 伺服器啟動流程詳解

啟動第一步--載入 BIOS 當你開啟計算機電源,計算機會首先載入 BIOS 資訊,BIOS 資訊是如此的重要,以至於計算機必須在最開始就找到它。這是因為 BIOS 中包含了 CPU 的相關資訊、裝置啟動順序信息、硬碟資訊、記憶體資訊、時鐘資訊、PnP 特性等等。在此之後,計算機心裡就有譜了,知道應該去讀取

linux作業系統啟動流程和光碟映象製作

1、簡述linux作業系統啟動流程 POST (加電自檢):自檢主要硬體裝置如:CPU、記憶體、硬碟是否正常,以及輸入輸出裝置是否存在問題等。 BIOS(Boot Sequence):BIOS(基本的輸入輸出系統)裝載在硬體晶片CMOS之上,自檢時會啟動這個程式,並根據CMOS上的配置資訊去讀取其他的硬體資

第七週 簡述linux作業系統啟動流程

目錄   1、簡述linux作業系統啟動流程           centos6啟動流程: centos7啟動流程: 2、簡述grub啟動載入程式配置及命令列介面詳解 grub的版本: grub的三個階段 與

Linux核心啟動流程分析(一)

1. 依據arch/arm/kernel/vmlinux.lds 生成linux核心原始碼根目錄下的vmlinux,這個vmlinux屬於未壓縮,帶除錯資訊、符號表的最初的核心,大小約23MB;  命令:arm-linux-gnu-ld -o vmlinux -T a

Linux系統啟動流程及系統裁剪

一、核心管理簡要理論 1、核心的功能  (1)程序管理  (2)記憶體管理(核心管理程式碼中程式碼量最大的部分)  (3)I/O管理:中斷及中斷處理  (4)檔案系統:ext3,ext4,reiserfs,xfs等。。  (5)驅動程式  (6)安全相關:SELinux

國嵌視訊學習---linux核心啟動流程

一、核心檔案uImage的構成 uImage:Uboot header和zImage zImage:解壓程式碼和壓縮後的vmlinux映象 二、zImage核心的構成 其中解壓程式碼由Head.s和misc.s組成。 三、vmlinux核心構成 1.啟動程式碼部分: