溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

2.uboot和系統(tǒng)移植-第5部分-2.5.uboot源碼分析1-啟動第一階段

發(fā)布時間:2020-07-20 13:07:06 來源:網(wǎng)絡(luò) 閱讀:544 作者:1069182055 欄目:開發(fā)技術(shù)

第一部分、章節(jié)目錄

2.5.1.start.S引入

2.5.2.start.S解析1

2.5.3.start.S解析2

2.5.4.start.S解析3

2.5.5.start.S解析4

2.5.6.start.S解析5

2.5.7.start.S解析6

2.5.8.start.S解析7

2.5.9.uboot重定位詳解

2.5.10.start.S解析8

2.5.11.start.S解析9

2.5.12.start.S解析10

2.5.13.start.S解析11



第二部分、章節(jié)介紹

2.5.1.start.S引入

本節(jié)首先通過分析鏈接腳本找到start.S文件,然后講解了如何利用SourceInsight找到相應(yīng)文件的技巧。

2.5.2.start.S解析1

本節(jié)開始分析start.S文件,主要分析了文件開頭部分的幾個頭文件包含,通過這些頭文件包含將代碼和前面課程中講到的配置過程關(guān)聯(lián)起來。

2.5.3.start.S解析2

本節(jié)主要分析了uboot從SD/Nand等啟動時的16字節(jié)校驗頭和異常向量表的建立,以及其他一些符號等。

2.5.4.start.S解析3

本節(jié)介紹了復(fù)位后設(shè)置CPU為SVC32模式、cache和mmu設(shè)置、讀取OMpin以判斷啟動介質(zhì)選擇等功能代碼。

2.5.5.start.S解析4

本節(jié)分析了lowlevel_init.S中的起始部分,包括檢測復(fù)位狀態(tài)、IO恢復(fù)、關(guān)看門狗、開發(fā)板供電鎖存等。

2.5.6.start.S解析5

本節(jié)重點解析了判斷當(dāng)前運行地址的代碼片段,這個判斷方法和裸機中講解的方式有所不同。然后簡要分析了匯編初始化時鐘的代碼段。

2.5.7.start.S解析6

本節(jié)重點講解低層初始化中DDR初始化和串口初始化部分的代碼,并且告訴大家uboot啟動時的"OK"是如何打印出來的。

2.5.8.start.S解析7

總結(jié)lowlevel_init函數(shù)中的所有工作,并且返回start.S中繼續(xù)分析并引出了uboot第二階段的重定位部分。

2.5.9.uboot重定位詳解

本節(jié)講解uboot的重定位代碼copy_bl2函數(shù),和裸機中SD卡重定位一節(jié)相對照應(yīng)該是很容易理解的。

2.5.10.start.S解析8

本節(jié)開始講解MMU和虛擬地址映射,主要是虛擬地址映射的基本理論和實現(xiàn)原理、cache的作用等。

2.5.11.start.S解析9

本節(jié)分析啟動代碼中段式頁表建立的過程代碼,通過簡單分析讓大家學(xué)會看頁表表項,從中可以看出內(nèi)存映射關(guān)系。

2.5.12.start.S解析10

本節(jié)完成頁表的講解和總結(jié),清楚明白的讓大家看到X210中虛擬地址映射的圖標(biāo),同時結(jié)束MMU相關(guān)的部分。

2.5.13.start.S解析11

本節(jié)講述start.S中最后的部分,包括清理bss段等,以及最終跳轉(zhuǎn)到uboot第二階段的代碼。本章結(jié)束。


第三部分、隨堂記錄

2.5.1.start.S引入

2.5.1.1、u-boot.lds中找到start.S入口

(1)在C語言中整個項目的入口就是main函數(shù)(這是C語言規(guī)定的),所以譬如說一個有10000個.c文件的項目,第一個要分析的文件就是包含了main函數(shù)的那個文件。

(2)在uboot中因為有匯編階段參與,因此不能直接找main.c。整個程序的入口取決于鏈接腳本中ENTRY聲明的地方。ENTRY(_start)因此_start符號所在的文件就是整個程序的起始文件,_start所在處的代碼就是整個程序的起始代碼。


2.5.1.2、SourceInsight中如何找到文件

(1)當(dāng)前狀況:我們知道在uboot中的1000多個文件中有一個符號叫_start,但是我們不知道這個符號在哪個文件中。這種情況下要查找一個符號在所有項目中文件中的引用,要使用SourceInsight的搜索功能。

(2)利用SI工具搜索到一共7個_start,然后分析搜索出來的7處,發(fā)現(xiàn)有2個是api_example,2個是onenand相關(guān)的,都不是我們要找的。剩下3個都在uboot/cpu/s5pc11x/start.S文件中。

(3)然后進入start.S文件中,發(fā)現(xiàn)57行中就是_start標(biāo)號的定義處,于是乎我們就找到了整個uboot的入口代碼,就是第57行。


2.5.1.3、SI中找文件技巧

(1)以上,找到了start.S文件,下面我們就從start.S文件開始分析uboot第一階段。

(2)在SI中,如果我們知道我們要找的文件的名字,但是我們又不知道他在哪個目錄下,我們要怎樣找到并打開這個文件?方法是在SI中先打開右邊的工程項目管理欄目,然后點擊最左邊那個(這個是以文件為單位來瀏覽的),然后在上面輸入欄中輸入要找的文件的名字。我們在輸入的時候,SI在不斷幫我們進行匹配,即使你不記得文件的全名只是大概記得名字,也能幫助你找到你要找的文件。



2.5.2.start.S解析1

2.5.2.1、不簡單的頭文件包含

(1)#include <config.h>。config.h是在include目錄下的,這個文件不是源碼中本身存在的文件,而是配置過程中自動生成的文件。(詳見mkconfig腳本)。這個文件的內(nèi)容其實是包含了一個頭文件:#include <configs/x210_sd.h>".

(2)經(jīng)過分析后,發(fā)現(xiàn)start.S中包含的第一個頭文件就是:include/configs/x210_sd.h,這個文件是整個uboot移植時的配置文件。這里面是好多宏。因此這個頭文件包含將include/configs/x210_sd.h文件和start.S文件關(guān)聯(lián)了起來。因此之后在分析start.S文件時,主要要考慮的就是x210_sd.h文件。

(3)#include <version.h>。include/version.h中包含了include/version_autogenerated.h,這個頭文件就是配置過程中自動生成的。里面就一行內(nèi)容:#define U_BOOT_VERSION "U-Boot 1.3.4"。這里面定義的宏U_BOOT_VERSION的值是一個字符串,字符串中的版本號信息來自于Makefile中的配置值。這個宏在程序中會被調(diào)用,在uboot啟動過程中會串口打印出uboot的版本號,那個版本號信息就是從這來的。

(4)#include <asm/proc/domain.h>。asm目錄不是uboot中的原生目錄,uboot中本來是沒有這個目錄的。asm目錄是配置時創(chuàng)建的一個符號鏈接,實際指向的是就是asm-arm(詳解上一章節(jié)分析mkconfig腳本時).

(5)經(jīng)過分析后發(fā)現(xiàn),實際文件是:include/asm-arm/proc-armv/domain.h

(6)從這里可以看出之前配置時創(chuàng)建的符號鏈接的作用,如果沒有這些符號鏈接則編譯時根本通不過,因為找不到頭文件。(所以uboot不能在windows的共享文件夾下配置編譯,因為windows中沒有符號鏈接)

思考:為什么start.S不直接包含asm-arm/proc-armv/domain.h,而要用asm/proc/domain.h。這樣的設(shè)計主要是為了可移植性。因為如果直接包含,則start

.S文件和CPU架構(gòu)(和硬件)有關(guān)了,可移植性就差了。譬如我要把uboot移植到mips架構(gòu)下,則start.S源代碼中所有的頭文件包含全部要修改。我們用了符號鏈接之后,則start.S中源代碼不用改,只需要在具體的硬件移植時配置不同,創(chuàng)建的符號鏈接指向的不同,則可以具有可移植性。



2.5.3.start.S解析2

2.5.3.1、啟動代碼的16字節(jié)頭部

(1)裸機中講過,在SD卡啟動/Nand啟動等整個鏡像開頭需要16字節(jié)的校驗頭。(mkv210p_w_picpath.c中就是為了計算這個校驗頭)。我們以前做裸機程序時根本沒考慮這16字節(jié)校驗頭,因為:1、如果我們是usb啟動直接下載的方式啟動的則不需要16字節(jié)校驗頭(irom application note);2、如果是SD卡啟動mkv210p_w_picpath.c中會給原鏡像前加16字節(jié)的校驗頭。

(2)uboot這里start.S中在開頭位置放了16字節(jié)的填充占位,這個占位的16字節(jié)只是保證正式的p_w_picpath的頭部確實有16字節(jié),但是這16字節(jié)的內(nèi)容是不對的,還是需要后面去計算校驗和然后重新填充的。


2.5.3.2、異常向量表的構(gòu)建

(1)異常向量表是硬件決定的,軟件只是參照硬件的設(shè)計來實現(xiàn)它。

(2)異常向量表中每種異常都應(yīng)該被處理,否則真遇到了這種異常就跑飛了。但是我們在uboot中并未非常細致的處理各種異常。

(3)復(fù)位異常處的代碼是:b reset,因此在CPU復(fù)位后真正去執(zhí)行的有效代碼是reset處的代碼,因此reset符號處才是真正的有意義的代碼開始的地方。


2.5.3.3、有點意思的deadbeef

(1).balignl 16,0xdeadbeef. 這一句指令是讓當(dāng)前地址對齊排布,如果當(dāng)前地址不對齊則自動向后走地址直到對齊,并且向后走的那些內(nèi)存要用0xdeadbeef來填充。

(2)0xdeadbeef這是一個十六進制的數(shù)字,這個數(shù)字很有意思,組成這個數(shù)字的十六進制數(shù)全是abcdef之中的字母,而且這8個字母剛好組成了英文的dead beef這兩個單詞,字面意思是壞牛肉。

(3)為什么要對齊訪問?有時候是效率的要求,有時候是硬件的特殊要求。


2.5.3.4、TEXT_BASE等

(1)第100行這個TEXT_BASE就是上個課程中分析Makefile時講到的那個配置階段的TEXT_BASE,其實就是我們鏈接時指定的uboot的鏈接地址。(值就是c3e00000)

(2)源代碼中和配置Makefile中很多變量是可以互相運送的。簡單來說有些符號的值可以從Makefile中傳遞到源代碼中。



2.5.4.start.S解析3

(1)CFG_PHY_UBOOT_BASE 33e00000 uboot在DDR中的物理地址


2.5.4.1、設(shè)置CPU為SVC模式

(1)msr cpsr_c, #0xd3 將CPU設(shè)置為禁止FIQ IRQ,ARM狀態(tài),SVC模式。

(2)其實ARM CPU在復(fù)位時默認就會進入SVC模式,但是這里還是使用軟件將其置為SVC模式。整個uboot工作時CPU一直處于SVC模式。


2.5.4.2、設(shè)置L2、L1cache和MMU

(1)bl disable_l2cache // 禁止L2 cache

(2)bl set_l2cache_auxctrl_cycle // l2 cache相關(guān)初始化

(3)bl enable_l2cache // 使能l2 cache

(4)刷新L1 cache的icache和dcache。

(5)關(guān)閉MMU

總結(jié):上面這5步都是和CPU的cache和mmu有關(guān)的,不用去細看,大概知道即可。


2.5.4.3、識別并暫存啟動介質(zhì)選擇

(1)從哪里啟動是由SoC的OM5:OM0這6個引腳的高低電平?jīng)Q定的。

(2)實際上在210內(nèi)部有一個寄存器(地址是0xE0000004),這個寄存器中的值是硬件根據(jù)OM引腳的設(shè)置而自動設(shè)置值的。這個值反映的就是OM引腳的接法(電平高低),也就是真正的啟動介質(zhì)是誰。

(3)我們代碼中可以通過讀取這個寄存器的值然后判斷其值來確定當(dāng)前選中的啟動介質(zhì)是Nand還是SD還是其他的。

(4)start.S的225-227行執(zhí)行完后,在r2寄存器中存儲了一個數(shù)字,這個數(shù)字等于某個特定值時就表示SD啟動,等于另一個特定值時表示從Nand啟動····

(5)260行中給r3中賦值#BOOT_MMCSD(0x03),這個在SD啟動時實際會被執(zhí)行,因此執(zhí)行完這一段代碼后r3中存儲了0x03,以后備用。


2.5.4.4、設(shè)置棧(SRAM中的棧)并調(diào)用lowlevel_init

(1)284-286行第一次設(shè)置棧。這次設(shè)置棧是在SRAM中設(shè)置的,因為當(dāng)前整個代碼還在SRAM中運行,此時DDR還未被初始化還不能用。棧地址0xd0036000是自己指定的,指定的原則就是這塊空間只給棧用,不會被別人占用。

(2)在調(diào)用函數(shù)前初始化棧,主要原因是在被調(diào)用的函數(shù)內(nèi)還有再次調(diào)用函數(shù),而BL只會將返回地址存儲到LR中,但是我們只有一個LR,所以在第二層調(diào)用函數(shù)前要先將LR入棧,否則函數(shù)返回時第一層的返回地址就丟了。



2.5.5.start.S解析4

(1)使用SourceInsight的Reference功能,找到lowlevel_init函數(shù)真正的地方,是在uboot/board/samsumg/x210/lowlevel_init.S中。

2.5.5.1、檢查復(fù)位狀態(tài)

(1)復(fù)雜CPU允許多種復(fù)位情況。譬如直接冷上電、熱啟動、睡眠(低功耗)狀態(tài)下的喚醒等,這些情況都屬于復(fù)位。所以我們在復(fù)位代碼中要去檢測復(fù)位狀態(tài),來判斷到底是哪種情況。

(2)判斷哪種復(fù)位的意義在于:冷上電時DDR是需要初始化才能用的;而熱啟動或者低功耗狀態(tài)下的復(fù)位則不需要再次初始化DDR。

2.5.5.2、IO狀態(tài)恢復(fù)

(1)這個和上一個和主線啟動代碼都無關(guān),因此不用去管他。


2.5.5.3、關(guān)看門狗

(1)參考裸機中看門狗章節(jié)


2.5.5.4、一些SRAM SROM相關(guān)GPIO設(shè)置

(1)與主線啟動代碼無關(guān),不用管


2.5.5.5、供電鎖存

(1)lowlevel_init.S的第100-104行,開發(fā)板供電鎖存。

總結(jié):在前100行,lowlevel_init.S中并沒有做太多有意義的事情(除了關(guān)看門狗、供電鎖存外),然后下面從110行才開始進行有意義的操作。




2.5.6.start.S解析5

2.5.6.1、判斷當(dāng)前代碼執(zhí)行位置

(1)lowlevel_init.S的110-115行。

(2)這幾行代碼的作用就是判定當(dāng)前代碼執(zhí)行的位置在SRAM中還是在DDR中。為什么要做這個判定?原因1:BL1(uboot的前一部分)在SRAM中有一份,在DDR中也有一份,因此如果是冷啟動那么當(dāng)前代碼應(yīng)該是在SRAM中運行的BL1,如果是低功耗狀態(tài)的復(fù)位這時候應(yīng)該就是在DDR中運行的。原因2:我們判定當(dāng)前運行代碼的地址是有用的,可以指導(dǎo)后面代碼的運行。譬如在lowlevel_init.S中判定當(dāng)前代碼的運行地址,就是為了確定要不要執(zhí)行時鐘初始化和初始化DDR的代碼。如果當(dāng)前代碼是在SRAM中,說明冷啟動,那么時鐘和DDR都需要初始化;如果當(dāng)前代碼是在DDR中,那么說明是熱啟動則時鐘和DDR都不用再次初始化。

(2)bic r1, pc, r0 這句代碼的意義是:將pc的值中的某些bit位清0,剩下一些特殊的bit位賦值給r1(r0中為1的那些位清零)相等于:r1 = pc & ~(ff000fff)

ldr r2, _TEXT_BASE 加載鏈接地址到r2,然后將r2的相應(yīng)位清0剩下特定位。

(3)最后比較r1和r2.

總結(jié):這一段代碼是通過讀取當(dāng)前運行地址和鏈接地址,然后處理兩個地址后對比是否相等,來判定當(dāng)前運行是在SRAM中(不相等)還是DDR中(相等)。從而決定是否跳過下面的時鐘和DDR初始化。


2.5.6.2、system_clock_init

(1)使用SI搜索功能,確定這個函數(shù)就在當(dāng)前文件的205行,一直到第385行。這個初始化時鐘的過程和裸機中初始化的過程一樣的,只是更加完整而且是用匯編代碼寫的。

(2)在x210_sd.h中300行到428行,都是和時鐘相關(guān)的配置值。這些宏定義就決定了210的時鐘配置是多少。也就是說代碼在lowlevel_init.S中都寫好了,但是代碼的設(shè)置值都被宏定義在x210_sd.h中了。因此,如果移植時需要更改CPU的時鐘設(shè)置,根本不需要動代碼,只需要在x210_sd.h中更改配置值即可。



2.5.7.start.S解析6

2.5.7.1、mem_ctrl_asm_init

(1)該函數(shù)用來初始化DDR

(2)函數(shù)位置在uboot/cpu/s5pc11x/s5pc110/cpu_init.S文件中。

(3)該函數(shù)和裸機中初始化DDR代碼是一樣的。實際上裸機中初始化DDR的代碼就是從這里抄的。配置值也可以從這里抄,但是當(dāng)時我自己根據(jù)理解+抄襲整出來的一份。

(4)配置值中其他配置值參考裸機中的解釋即可明白,有一個和裸機中講的不一樣。DMC0_MEMCONFIG_0,在裸機中配置值為0x20E01323;在uboot中配置為0x30F01313.這個配置不同就導(dǎo)致結(jié)果不同。

在 裸機中DMC0的256MB內(nèi)存地址范圍是0x20000000-0x2FFFFFFF; 

在uboot中DMC0的256MB內(nèi)存地址范圍為0x30000000-0x3FFFFFFF。

(5)之前在裸機中時配置為2開頭的地址,當(dāng)時并沒有說可以配置為3開頭。從分析九鼎移植的uboot可以看出:DMC0上允許的地址范圍是20000000-3FFFFFFF(一共是512MB),而我們實際只接了256MB物理內(nèi)存,SoC允許我們給這256MB挑選地址范圍。

(6)總結(jié)一下:在uboot中,可用的物理地址范圍為:0x30000000-0x4FFFFFFF。一共512MB,其中30000000-3FFFFFFF為DMC0,40000000-4FFFFFFF為DMC1。

(7)我們需要的內(nèi)存配置值在x210_sd.h的438行到468行之間。分析的時候要注意條件編譯的條件,配置頭文件中考慮了不同時鐘配置下的內(nèi)存配置值,這個的主要目的是讓不同時鐘需求的客戶都能找到合適自己的內(nèi)存配置值。

(8)在uboot中DMC0和DMC1都工作了,所以在裸機中只要把uboot中的配置值和配置代碼全部移植過去,應(yīng)該是能夠讓DMC0和DMC1都工作的。


2.5.7.2、uart_asm_init

(1)這個函數(shù)用來初始化串口

(2)初始化完了后通過串口發(fā)送了一個'O'


2.5.7.3、tzpc_init

(1)trust zone初始化,沒搞過,不管


2.5.7.4、pop {pc}以返回

(1)返回前通過串口打印'K'


分析;lowlevel_init.S執(zhí)行完如果沒錯那么就會串口打印出"OK"字樣。這應(yīng)該是我們uboot中看到的最早的輸出信息。



2.5.8.start.S解析7

總結(jié)回顧:lowlevel_init.S中總共做了哪些事情:

檢查復(fù)位狀態(tài)、IO恢復(fù)、關(guān)看門狗、開發(fā)板供電鎖存、時鐘初始化、DDR初始化、串口初始化并打印'O'、tzpc初始化、打印'K'。

其中值得關(guān)注的:關(guān)看門狗、開發(fā)板供電鎖存、時鐘初始化、DDR初始化、打印"OK"

2.5.8.1、再次設(shè)置棧(DDR中的棧)

(1)再次開發(fā)板供電鎖存。第一,做2次是不會錯的;第二,做2次則第2次無意義;做代碼移植時有一個古怪謹慎保守策略就是盡量添加代碼而不要刪除代碼。

(2)之前在調(diào)用lowlevel_init程序前設(shè)置過1次棧(start.S 284-287行),那時候因為DDR尚未初始化,因此程序執(zhí)行都是在SRAM中,所以在SRAM中分配了一部分內(nèi)存作為棧。本次因為DDR已經(jīng)被初始化了,因此要把棧挪移到DDR中,所以要重新設(shè)置棧,這是第二次(start.S 297-299行);這里實際設(shè)置的棧的地址是33E00000,剛好在uboot的代碼段的下面緊挨著。

(3)為什么要再次設(shè)置棧?DDR已經(jīng)初始化了,已經(jīng)有大片內(nèi)存可以用了,沒必要再把棧放在SRAM中可憐兮兮的了;原來SRAM中內(nèi)存大小空間有限,棧放在那里要注意不能使用過多的棧否則棧會溢出,我們及時將棧遷移到DDR中也是為了盡可能避免棧使用時候的小心翼翼。

感慨:uboot的啟動階段主要技巧就在于小范圍內(nèi)有限條件下的輾轉(zhuǎn)騰挪。


2.5.8.2、再次判斷當(dāng)前地址以決定是否重定位

(1)再次用相同的代碼判斷運行地址是在SRAM中還是DDR中,不過本次判斷的目的不同(上次判斷是為了決定是否要執(zhí)行初始化時鐘和DDR的代碼)這次判斷是為了決定是否進行uboot的relocate。

(2)冷啟動時當(dāng)前情況是uboot的前一部分(16kb或者8kb)開機自動從SD卡加載到SRAM中正在運行,uboot的第二部分(其實第二部分是整個uboot)還躺在SD卡的某個扇區(qū)開頭的N個扇區(qū)中。此時uboot的第一階段已經(jīng)即將結(jié)束了(第一階段該做的事基本做完了),結(jié)束之前要把第二部分加載到DDR中鏈接地址處(33e00000),這個加載過程就叫重定位。



2.5.9.uboot重定位詳解

(1)D0037488這個內(nèi)存地址在SRAM中,這個地址中的值是被硬件自動設(shè)置的。硬件根據(jù)我們實際電路中SD卡在哪個通道中,會將這個地址中的值設(shè)置為相應(yīng)的數(shù)字。譬如我們從SD0通道啟動時,這個值為EB000000;從SD2通道啟動時,這個值為EB200000

(2)我們在start.S的260行確定了從MMCSD啟動,然后又在278行將#BOOT_MMCSD寫入了INF_REG3寄存器中存儲著。然后又在322行讀出來,再和#BOOT_MMCSD去比較,確定是從MMCSD啟動。最終跳轉(zhuǎn)到mmcsd_boot函數(shù)中去執(zhí)行重定位動作。

(3)真正的重定位是通過調(diào)用movi_bl2_copy函數(shù)完成的,在uboot/cpu/s5pc11x/movi.c中。是一個C語言的函數(shù)

(4)copy_bl2(2, MOVI_BL2_POS, MOVI_BL2_BLKCNT,

CFG_PHY_UBOOT_BASE, 0);

分析參數(shù):2表示通道2;MOVI_BL2_POS是uboot的第二部分在SD卡中的開始扇區(qū),這個扇區(qū)數(shù)字必須和燒錄uboot時燒錄的位置相同;MOVI_BL2_BLKCNT是uboot的長度占用的扇區(qū)數(shù);CFG_PHY_UBOOT_BASE是重定位時將uboot的第二部分復(fù)制到DDR中的起始地址(33E00000).



2.5.10.start.S解析8

2.5.10.1、什么是虛擬地址、物理地址

(1)物理地址就是物理設(shè)備設(shè)計生產(chǎn)時賦予的地址。像裸機中使用的寄存器的地址就是CPU設(shè)計時指定的,這個就是物理地址。物理地址是硬件編碼的,是設(shè)計生產(chǎn)時確定好的,一旦確定了就不能改了。

(2)一個事實就是:寄存器的物理地址是無法通過編程修改的,是多少就是多少,只能通過查詢數(shù)據(jù)手冊獲得并操作。壞處就是不夠靈活。一個解決方案就是使用虛擬地址。

(3)虛擬地址意思就是在我們軟件操作和硬件被操作之間增加一個層次,叫做虛擬地址映射層。有了虛擬地址映射后,軟件操作只需要給虛擬地址,硬件操作還是用原來的物理地址,映射層建立一個虛擬地址到物理地址的映射表。當(dāng)我們軟件運行的時候,軟件中使用的虛擬地址在映射表中查詢得到對應(yīng)的物理地址再發(fā)給硬件去執(zhí)行(虛擬地址到物理地址的映射是不可能通過軟件來實現(xiàn)的)。


2.5.10.2、MMU單元的作用

(1)MMU就是memory management unit,內(nèi)存管理單元。MMU實際上是SOC中一個硬件單元,它的主要功能就是實現(xiàn)虛擬地址到物理地址的映射。

(2)MMU單片在CP15協(xié)處理器中進行控制,也就是說要操控MMU進行虛擬地址映射,方法就是對cp15協(xié)處理器的寄存器進行編程。


2.5.10.3、地址映射的額外收益1:訪問控制

(1)訪問控制就是:在管理上對內(nèi)存進行分塊,然后每塊進行獨立的虛擬地址映射,然后在每一塊的映射關(guān)系中同時還實現(xiàn)了訪問控制(對該塊可讀、可寫、只讀、只寫、不可訪問等控制)

(2)回想在C語言中編程中經(jīng)常會出現(xiàn)一個錯誤:Segmentation fault。實際上這個段錯誤就和MMU實現(xiàn)的訪問控制有關(guān)。當(dāng)前程序只能操作自己有權(quán)操作的地址范圍(若干個內(nèi)存塊),如果當(dāng)前程序指針出錯訪問了不該訪問的內(nèi)存塊則就會觸發(fā)段錯誤。


2.5.10.4、地址映射的額外收益2:cache

(1)cache的工作和虛擬地址映射有關(guān)系。

(2)cache是快速緩存,意思就是比CPU慢但是比DDR塊。CPU嫌DDR太慢了,于是乎把一些DDR中常用的內(nèi)容事先讀取緩存在cache中,然后CPU每次需要找東西時先在cache中找。如果cache中有就直接用cache中的;如果cache中沒有才會去DDR中尋找。


參考閱讀:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=22891521&id=2109284



2.5.11.start.S解析9

2.5.11.1、使能域訪問(cp15的c3寄存器)

(1)cp15協(xié)處理器內(nèi)部有c0到c15共16個寄存器,這些寄存器每一個都有自己的作用。我們通過mrc和mcr指令來訪問這些寄存器。所謂的操作cp協(xié)處理器其實就是操作cp15的這些寄存器。

(2)c3寄存器在mmu中的作用是控制域訪問。域訪問是和MMU的訪問控制有關(guān)的。


2.5.11.2、設(shè)置TTB(cp15的c2寄存器)

(1)TTB就是translation table base,轉(zhuǎn)換表基地址。首先要明白什么是TT(translation table轉(zhuǎn)換表),TTB其實就是轉(zhuǎn)換表的基地址。

(2)轉(zhuǎn)換表是建立一套虛擬地址映射的關(guān)鍵。轉(zhuǎn)換表分2部分,表索引和表項。表索引對應(yīng)虛擬地址,表項對應(yīng)物理地址。一對表索引和表項構(gòu)成一個轉(zhuǎn)換表單元,能夠?qū)σ粋€內(nèi)存塊進行虛擬地址轉(zhuǎn)換。(映射中基本規(guī)定中規(guī)定了內(nèi)存映射和管理是以塊為單位的,至于塊有多大,要看你的MMU的支持和你自己的選擇。在ARM中支持3種塊大小,細表1KB、粗表4KB、段1MB)。真正的轉(zhuǎn)換表就是由若干個轉(zhuǎn)換表單元構(gòu)成的,每個單元負責(zé)1個內(nèi)存塊,總體的轉(zhuǎn)換表負責(zé)整個內(nèi)存空間(0-4G)的映射。

(3)整個建立虛擬地址映射的主要工作就是建立這張轉(zhuǎn)換表

(4)轉(zhuǎn)換表放置在內(nèi)存中的,放置時要求起始地址在內(nèi)存中要xx位對齊。轉(zhuǎn)換表不需要軟件去干涉使用,而是將基地址TTB設(shè)置到cp15的c2寄存器中,然后MMU工作時會自動去查轉(zhuǎn)換表。


2.5.11.3、使能MMU單元(cp15的c1寄存器)

(1)cp15的c1寄存器的bit0控制MMU的開關(guān)。只要將這一個bit置1即可開啟MMU。開啟MMU之后上層軟件層的地址就必須經(jīng)過TT的轉(zhuǎn)換才能發(fā)給下層物理層去執(zhí)行。


2.5.11.4、找到映射表待分析

(1)通過符號查找,確定轉(zhuǎn)換表在lowlevel_init.S文件的593行。


2.5.11.5、S5PV210的2種虛擬地址管理



2.5.12.start.S解析10

2.5.12.1、宏FL_SECTION_ENTRY

2.5.12.2、頁表項各bit位含義

2.5.12.3、段式頁表詳解

2.5.12.4、實驗操作驗證

2.5.12.5、總結(jié):關(guān)于MMU和虛擬地址映射的學(xué)習(xí)


宏觀上理解轉(zhuǎn)換表:整個轉(zhuǎn)換表可以看作是一個int類型的數(shù)組,數(shù)組中的一個元素就是一個表索引和表項的單元。數(shù)組中的元素值就是表項,這個元素的數(shù)組下標(biāo)就是表索引。

ARM的段式映射中長度為1MB,因此一個映射單元只能管1MB內(nèi)存,那我們整個4G范圍內(nèi)需要4G/1MB=4096個映射單元,也就是說這個數(shù)組的元素個數(shù)是4096.實際上我們做的時候并沒有依次單個處理這4096個單元,而是把4096個分成幾部分,然后每部分用for循環(huán)做相同的處理。



2.5.13.start.S解析11

2.5.13.1、再次設(shè)置棧

(1)第三次設(shè)置棧。這次設(shè)置棧還是在DDR中,之前雖然已經(jīng)在DDR中設(shè)置過一次棧了,但是本次設(shè)置棧的目的是將棧放在比較合適(安全,緊湊而不浪費內(nèi)存)的地方。

(2)我們實際將棧設(shè)置在uboot起始地址上方2MB處,這樣安全的棧空間是:2MB-uboot大小-0x1000=1.8MB左右。這個空間既沒有太浪費內(nèi)存,又足夠安全。


2.5.13.2、清理bss

(1)清理bss段代碼和裸機中講的一樣。注意表示bss段的開頭和結(jié)尾地址的符號是從鏈接腳本u-boot.lds得來的。


2.5.13.3、ldr pc, _start_armboot

(1)start_armboot是uboot/lib_arm/board.c中,這是一個C語言實現(xiàn)的函數(shù)。這個函數(shù)就是uboot的第二階段。這句代碼的作用就是將uboot第二階段執(zhí)行的函數(shù)的地址傳給pc,實際上就是使用一個遠跳轉(zhuǎn)直接跳轉(zhuǎn)到DDR中的第二階段開始地址處。

(2)遠跳轉(zhuǎn)的含義就是這句話加載的地址和當(dāng)前運行地址無關(guān),而和鏈接地址有關(guān)。因此這個遠跳轉(zhuǎn)可以實現(xiàn)從SRAM中的第一階段跳轉(zhuǎn)到DDR中的第二階段。

(3)這里這個遠跳轉(zhuǎn)就是uboot第一階段和第二階段的分界線。


2.5.13.4、總結(jié):uboot的第一階段做了哪些工作

(1)構(gòu)建異常向量表

(2)設(shè)置CPU為SVC模式

(3)關(guān)看門狗

(4)開發(fā)板供電置鎖

(5)時鐘初始化

(6)DDR初始化

(7)串口初始化并打印"OK"

(8)重定位

(9)建立映射表并開啟MMU

(10)跳轉(zhuǎn)到第二階段


向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI