1. 程式人生 > >《2.uboot和系統移植-第5部分-2.5.uboot原始碼分析1-啟動第一階段》

《2.uboot和系統移植-第5部分-2.5.uboot原始碼分析1-啟動第一階段》

《2.uboot和系統移植-第5部分-2.5.uboot原始碼分析1-啟動第一階段》

第一部分、章節目錄
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

第二部分、章節介紹
2.5.1.start.S引入
本節首先通過分析連結指令碼找到start.S檔案,然後講解了如何利用SourceInsight找到相應檔案的技巧。
2.5.2.start.S解析1
本節開始分析start.S檔案,主要分析了檔案開頭部分的幾個標頭檔案包含,通過這些標頭檔案包含將程式碼和前面課程中講到的配置過程關聯起來。
2.5.3.start.S解析2
本節主要分析了uboot從SD/Nand等啟動時的16位元組校驗頭和異常向量表的建立,以及其他一些符號等。
2.5.4.start.S解析3
本節介紹了復位後設置CPU為SVC32模式、cache和mmu設定、讀取OMpin以判斷啟動介質選擇等功能程式碼。
2.5.5.start.S解析4
本節分析了lowlevel_init.S中的起始部分,包括檢測復位狀態、IO恢復、關看門狗、開發板供電鎖存等。
2.5.6.start.S解析5
本節重點解析了判斷當前執行地址的程式碼片段,這個判斷方法和裸機中講解的方式有所不同。然後簡要分析了彙編初始化時鐘的程式碼段。
2.5.7.start.S解析6
本節重點講解低層初始化中DDR初始化和串列埠初始化部分的程式碼,並且告訴大家uboot啟動時的"OK"是如何打印出來的。
2.5.8.start.S解析7
總結lowlevel_init函式中的所有工作,並且返回start.S中繼續分析並引出了uboot第二階段的重定位部分。
2.5.9.uboot重定位詳解
本節講解uboot的重定位程式碼copy_bl2函式,和裸機中SD卡重定位一節相對照應該是很容易理解的。
2.5.10.start.S解析8
本節開始講解MMU和虛擬地址對映,主要是虛擬地址對映的基本理論和實現原理、cache的作用等。
2.5.11.start.S解析9
本節分析啟動程式碼中段式頁表建立的過程程式碼,通過簡單分析讓大家學會看頁表表項,從中可以看出記憶體對映關係。
2.5.12.start.S解析10
本節完成頁表的講解和總結,清楚明白的讓大家看到X210中虛擬地址對映的圖示,同時結束MMU相關的部分。
2.5.13.start.S解析11
本節講述start.S中最後的部分,包括清理bss段等,以及最終跳轉到uboot第二階段的程式碼。本章結束。

第三部分、隨堂記錄
2.5.1.start.S引入
2.5.1.1、u-boot.lds中找到start.S入口
(1)在C語言中整個專案的入口就是main函式(這是C語言規定的),所以譬如說一個有10000個.c檔案的專案,第一個要分析的檔案就是包含了main函式的那個檔案。
(2)在uboot中因為有彙編階段參與,因此不能直接找main.c。整個程式的入口取決於連結指令碼中ENTRY宣告的地方。ENTRY(_start)因此_start符號所在的檔案就是整個程式的起始檔案,_start所在處的程式碼就是整個程式的起始程式碼。

2.5.1.2、SourceInsight中如何找到檔案
(1)當前狀況:我們知道在uboot中的1000多個檔案中有一個符號叫_start,但是我們不知道這個符號在哪個檔案中。這種情況下要查詢一個符號在所有專案中檔案中的引用,要使用SourceInsight的搜尋功能。
(2)利用SI工具搜尋到一共7個_start,然後分析搜尋出來的7處,發現有2個是api_example,2個是onenand相關的,都不是我們要找的。剩下3個都在uboot/cpu/s5pc11x/start.S檔案中。
(3)然後進入start.S檔案中,發現57行中就是_start標號的定義處,於是乎我們就找到了整個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指令碼)。這個檔案的內容其實是包含了一個頭檔案:#include <configs/x210_sd.h>".
(2)經過分析後,發現start.S中包含的第一個標頭檔案就是:include/configs/x210_sd.h,這個檔案是整個uboot移植時的配置檔案。這裡面是好多巨集。因此這個標頭檔案包含將include/configs/x210_sd.h檔案和start.S檔案關聯了起來。因此之後在分析start.S檔案時,主要要考慮的就是x210_sd.h檔案。
(3)#include <version.h>。include/version.h中包含了include/version_autogenerated.h,這個標頭檔案就是配置過程中自動生成的。裡面就一行內容:#define U_BOOT_VERSION “U-Boot 1.3.4”。這裡面定義的巨集U_BOOT_VERSION的值是一個字串,字串中的版本號資訊來自於Makefile中的配置值。這個巨集在程式中會被呼叫,在uboot啟動過程中會串列埠打印出uboot的版本號,那個版本號資訊就是從這來的。
(4)#include <asm/proc/domain.h>。asm目錄不是uboot中的原生目錄,uboot中本來是沒有這個目錄的。asm目錄是配置時建立的一個符號連結,實際指向的是就是asm-arm(詳解上一章節分析mkconfig指令碼時).
(5)經過分析後發現,實際檔案是:include/asm-arm/proc-armv/domain.h
(6)從這裡可以看出之前配置時建立的符號連結的作用,如果沒有這些符號連結則編譯時根本通不過,因為找不到標頭檔案。(所以uboot不能在windows的共享資料夾下配置編譯,因為windows中沒有符號連結)
思考:為什麼start.S不直接包含asm-arm/proc-armv/domain.h,而要用asm/proc/domain.h。這樣的設計主要是為了可移植性。因為如果直接包含,則start
.S檔案和CPU架構(和硬體)有關了,可移植性就差了。譬如我要把uboot移植到mips架構下,則start.S原始碼中所有的標頭檔案包含全部要修改。我們用了符號連結之後,則start.S中原始碼不用改,只需要在具體的硬體移植時配置不同,建立的符號連結指向的不同,則可以具有可移植性。

2.5.3.start.S解析2
2.5.3.1、啟動程式碼的16位元組頭部
(1)裸機中講過,在SD卡啟動/Nand啟動等整個映象開頭需要16位元組的校驗頭。(mkv210image.c中就是為了計算這個校驗頭)。我們以前做裸機程式時根本沒考慮這16位元組校驗頭,因為:1、如果我們是usb啟動直接下載的方式啟動的則不需要16位元組校驗頭(irom application note);2、如果是SD卡啟動mkv210image.c中會給原映象前加16位元組的校驗頭。
(2)uboot這裡start.S中在開頭位置放了16位元組的填充佔位,這個佔位的16位元組只是保證正式的image的頭部確實有16位元組,但是這16位元組的內容是不對的,還是需要後面去計算校驗和然後重新填充的。

2.5.3.2、異常向量表的構建
(1)異常向量表是硬體決定的,軟體只是參照硬體的設計來實現它。
(2)異常向量表中每種異常都應該被處理,否則真遇到了這種異常就跑飛了。但是我們在uboot中並未非常細緻的處理各種異常。
(3)復位異常處的程式碼是:b reset,因此在CPU復位後真正去執行的有效程式碼是reset處的程式碼,因此reset符號處才是真正的有意義的程式碼開始的地方。

2.5.3.3、有點意思的deadbeef
(1).balignl 16,0xdeadbeef. 這一句指令是讓當前地址對齊排布,如果當前地址不對齊則自動向後走地址直到對齊,並且向後走的那些記憶體要用0xdeadbeef來填充。
(2)0xdeadbeef這是一個十六進位制的數字,這個數字很有意思,組成這個數字的十六進位制數全是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、設定CPU為SVC模式
(1)msr cpsr_c, #0xd3 將CPU設定為禁止FIQ IRQ,ARM狀態,SVC模式。
(2)其實ARM CPU在復位時預設就會進入SVC模式,但是這裡還是使用軟體將其置為SVC模式。整個uboot工作時CPU一直處於SVC模式。

2.5.4.2、設定L2、L1cache和MMU
(1)bl disable_l2cache // 禁止L2 cache
(2)bl set_l2cache_auxctrl_cycle // l2 cache相關初始化
(3)bl enable_l2cache // 使能l2 cache
(4)重新整理L1 cache的icache和dcache。
(5)關閉MMU
總結:上面這5步都是和CPU的cache和mmu有關的,不用去細看,大概知道即可。

2.5.4.3、識別並暫存啟動介質選擇
(1)從哪裡啟動是由SoC的OM5:OM0這6個引腳的高低電平決定的。
(2)實際上在210內部有一個暫存器(地址是0xE0000004),這個暫存器中的值是硬體根據OM引腳的設定而自動設定值的。這個值反映的就是OM引腳的接法(電平高低),也就是真正的啟動介質是誰。
(3)我們程式碼中可以通過讀取這個暫存器的值然後判斷其值來確定當前選中的啟動介質是Nand還是SD還是其他的。
(4)start.S的225-227行執行完後,在r2暫存器中儲存了一個數字,這個數字等於某個特定值時就表示SD啟動,等於另一個特定值時表示從Nand啟動····
(5)260行中給r3中賦值#BOOT_MMCSD(0x03),這個在SD啟動時實際會被執行,因此執行完這一段程式碼後r3中儲存了0x03,以後備用。

2.5.4.4、設定棧(SRAM中的棧)並呼叫lowlevel_init
(1)284-286行第一次設定棧。這次設定棧是在SRAM中設定的,因為當前整個程式碼還在SRAM中執行,此時DDR還未被初始化還不能用。棧地址0xd0036000是自己指定的,指定的原則就是這塊空間只給棧用,不會被別人佔用。
(2)在呼叫函式前初始化棧,主要原因是在被呼叫的函式內還有再次呼叫函式,而BL只會將返回地址儲存到LR中,但是我們只有一個LR,所以在第二層呼叫函式前要先將LR入棧,否則函式返回時第一層的返回地址就丟了。

2.5.5.start.S解析4
(1)使用SourceInsight的Reference功能,找到lowlevel_init函式真正的地方,是在uboot/board/samsumg/x210/lowlevel_init.S中。
2.5.5.1、檢查復位狀態
(1)複雜CPU允許多種復位情況。譬如直接冷上電、熱啟動、睡眠(低功耗)狀態下的喚醒等,這些情況都屬於復位。所以我們在復位程式碼中要去檢測復位狀態,來判斷到底是哪種情況。
(2)判斷哪種復位的意義在於:冷上電時DDR是需要初始化才能用的;而熱啟動或者低功耗狀態下的復位則不需要再次初始化DDR。
2.5.5.2、IO狀態恢復
(1)這個和上一個和主線啟動程式碼都無關,因此不用去管他。

2.5.5.3、關看門狗
(1)參考裸機中看門狗章節

2.5.5.4、一些SRAM SROM相關GPIO設定
(1)與主線啟動程式碼無關,不用管

2.5.5.5、供電鎖存
(1)lowlevel_init.S的第100-104行,開發板供電鎖存。
總結:在前100行,lowlevel_init.S中並沒有做太多有意義的事情(除了關看門狗、供電鎖存外),然後下面從110行才開始進行有意義的操作。

2.5.6.start.S解析5
2.5.6.1、判斷當前程式碼執行位置
(1)lowlevel_init.S的110-115行。
(2)這幾行程式碼的作用就是判定當前程式碼執行的位置在SRAM中還是在DDR中。為什麼要做這個判定?原因1:BL1(uboot的前一部分)在SRAM中有一份,在DDR中也有一份,因此如果是冷啟動那麼當前程式碼應該是在SRAM中執行的BL1,如果是低功耗狀態的復位這時候應該就是在DDR中執行的。原因2:我們判定當前執行程式碼的地址是有用的,可以指導後面程式碼的執行。譬如在lowlevel_init.S中判定當前程式碼的執行地址,就是為了確定要不要執行時鐘初始化和初始化DDR的程式碼。如果當前程式碼是在SRAM中,說明冷啟動,那麼時鐘和DDR都需要初始化;如果當前程式碼是在DDR中,那麼說明是熱啟動則時鐘和DDR都不用再次初始化。
(2)bic r1, pc, r0 這句程式碼的意義是:將pc的值中的某些bit位清0,剩下一些特殊的bit位賦值給r1(r0中為1的那些位清零)相等於:r1 = pc & ~(ff000fff)
ldr r2, _TEXT_BASE 載入連結地址到r2,然後將r2的相應位清0剩下特定位。
(3)最後比較r1和r2.
總結:這一段程式碼是通過讀取當前執行地址和連結地址,然後處理兩個地址後對比是否相等,來判定當前執行是在SRAM中(不相等)還是DDR中(相等)。從而決定是否跳過下面的時鐘和DDR初始化。

2.5.6.2、system_clock_init
(1)使用SI搜尋功能,確定這個函式就在當前檔案的205行,一直到第385行。這個初始化時鐘的過程和裸機中初始化的過程一樣的,只是更加完整而且是用匯編程式碼寫的。
(2)在x210_sd.h中300行到428行,都是和時鐘相關的配置值。這些巨集定義就決定了210的時鐘配置是多少。也就是說程式碼在lowlevel_init.S中都寫好了,但是程式碼的設定值都被巨集定義在x210_sd.h中了。因此,如果移植時需要更改CPU的時鐘設定,根本不需要動程式碼,只需要在x210_sd.h中更改配置值即可。

2.5.7.start.S解析6
2.5.7.1、mem_ctrl_asm_init
(1)該函式用來初始化DDR
(2)函式位置在uboot/cpu/s5pc11x/s5pc110/cpu_init.S檔案中。
(3)該函式和裸機中初始化DDR程式碼是一樣的。實際上裸機中初始化DDR的程式碼就是從這裡抄的。配置值也可以從這裡抄,但是當時我自己根據理解+抄襲整出來的一份。
(4)配置值中其他配置值參考裸機中的解釋即可明白,有一個和裸機中講的不一樣。DMC0_MEMCONFIG_0,在裸機中配置值為0x20E01323;在uboot中配置為0x30F01313.這個配置不同就導致結果不同。
在 裸機中DMC0的256MB記憶體地址範圍是0x20000000-0x2FFFFFFF;
在uboot中DMC0的256MB記憶體地址範圍為0x30000000-0x3FFFFFFF。
(5)之前在裸機中時配置為2開頭的地址,當時並沒有說可以配置為3開頭。從分析九鼎移植的uboot可以看出:DMC0上允許的地址範圍是20000000-3FFFFFFF(一共是512MB),而我們實際只接了256MB實體記憶體,SoC允許我們給這256MB挑選地址範圍。
(6)總結一下:在uboot中,可用的實體地址範圍為:0x30000000-0x4FFFFFFF。一共512MB,其中30000000-3FFFFFFF為DMC0,40000000-4FFFFFFF為DMC1。
(7)我們需要的記憶體配置值在x210_sd.h的438行到468行之間。分析的時候要注意條件編譯的條件,配置標頭檔案中考慮了不同時鐘配置下的記憶體配置值,這個的主要目的是讓不同時鐘需求的客戶都能找到合適自己的記憶體配置值。
(8)在uboot中DMC0和DMC1都工作了,所以在裸機中只要把uboot中的配置值和配置程式碼全部移植過去,應該是能夠讓DMC0和DMC1都工作的。

2.5.7.2、uart_asm_init
(1)這個函式用來初始化串列埠
(2)初始化完了後通過串列埠傳送了一個’O’

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

2.5.7.4、pop {pc}以返回
(1)返回前通過串列埠列印’K’

分析;lowlevel_init.S執行完如果沒錯那麼就會串列埠打印出"OK"字樣。這應該是我們uboot中看到的最早的輸出資訊。

2.5.8.start.S解析7
總結回顧:lowlevel_init.S中總共做了哪些事情:
檢查復位狀態、IO恢復、關看門狗、開發板供電鎖存、時鐘初始化、DDR初始化、串列埠初始化並列印’O’、tzpc初始化、列印’K’。
其中值得關注的:關看門狗、開發板供電鎖存、時鐘初始化、DDR初始化、列印"OK"
2.5.8.1、再次設定棧(DDR中的棧)
(1)再次開發板供電鎖存。第一,做2次是不會錯的;第二,做2次則第2次無意義;做程式碼移植時有一個古怪謹慎保守策略就是儘量新增程式碼而不要刪除程式碼。
(2)之前在呼叫lowlevel_init程式前設定過1次棧(start.S 284-287行),那時候因為DDR尚未初始化,因此程式執行都是在SRAM中,所以在SRAM中分配了一部分記憶體作為棧。本次因為DDR已經被初始化了,因此要把棧挪移到DDR中,所以要重新設定棧,這是第二次(start.S 297-299行);這裡實際設定的棧的地址是33E00000,剛好在uboot的程式碼段的下面緊挨著。
(3)為什麼要再次設定棧?DDR已經初始化了,已經有大片記憶體可以用了,沒必要再把棧放在SRAM中可憐兮兮的了;原來SRAM中記憶體大小空間有限,棧放在那裡要注意不能使用過多的棧否則棧會溢位,我們及時將棧遷移到DDR中也是為了儘可能避免棧使用時候的小心翼翼。
感慨:uboot的啟動階段主要技巧就在於小範圍內有限條件下的輾轉騰挪。

2.5.8.2、再次判斷當前地址以決定是否重定位
(1)再次用相同的程式碼判斷執行地址是在SRAM中還是DDR中,不過本次判斷的目的不同(上次判斷是為了決定是否要執行初始化時鐘和DDR的程式碼)這次判斷是為了決定是否進行uboot的relocate。
(2)冷啟動時當前情況是uboot的前一部分(16kb或者8kb)開機自動從SD卡載入到SRAM中正在執行,uboot的第二部分(其實第二部分是整個uboot)還躺在SD卡的某個扇區開頭的N個扇區中。此時uboot的第一階段已經即將結束了(第一階段該做的事基本做完了),結束之前要把第二部分載入到DDR中連結地址處(33e00000),這個載入過程就叫重定位。

2.5.9.uboot重定位詳解
(1)D0037488這個記憶體地址在SRAM中,這個地址中的值是被硬體自動設定的。硬體根據我們實際電路中SD卡在哪個通道中,會將這個地址中的值設定為相應的數字。譬如我們從SD0通道啟動時,這個值為EB000000;從SD2通道啟動時,這個值為EB200000
(2)我們在start.S的260行確定了從MMCSD啟動,然後又在278行將#BOOT_MMCSD寫入了INF_REG3暫存器中儲存著。然後又在322行讀出來,再和#BOOT_MMCSD去比較,確定是從MMCSD啟動。最終跳轉到mmcsd_boot函式中去執行重定位動作。
(3)真正的重定位是通過呼叫movi_bl2_copy函式完成的,在uboot/cpu/s5pc11x/movi.c中。是一個C語言的函式
(4)copy_bl2(2, MOVI_BL2_POS, MOVI_BL2_BLKCNT,
CFG_PHY_UBOOT_BASE, 0);
分析引數:2表示通道2;MOVI_BL2_POS是uboot的第二部分在SD卡中的開始扇區,這個扇區數字必須和燒錄uboot時燒錄的位置相同;MOVI_BL2_BLKCNT是uboot的長度佔用的扇區數;CFG_PHY_UBOOT_BASE是重定位時將uboot的第二部分複製到DDR中的起始地址(33E00000).

2.5.10.start.S解析8
2.5.10.1、什麼是虛擬地址、實體地址
(1)實體地址就是物理裝置設計生產時賦予的地址。像裸機中使用的暫存器的地址就是CPU設計時指定的,這個就是實體地址。實體地址是硬體編碼的,是設計生產時確定好的,一旦確定了就不能改了。
(2)一個事實就是:暫存器的實體地址是無法通過程式設計修改的,是多少就是多少,只能通過查詢資料手冊獲得並操作。壞處就是不夠靈活。一個解決方案就是使用虛擬地址。
(3)虛擬地址意思就是在我們軟體操作和硬體被操作之間增加一個層次,叫做虛擬地址對映層。有了虛擬地址對映後,軟體操作只需要給虛擬地址,硬體操作還是用原來的實體地址,對映層建立一個虛擬地址到實體地址的對映表。當我們軟體執行的時候,軟體中使用的虛擬地址在對映表中查詢得到對應的實體地址再發給硬體去執行(虛擬地址到實體地址的對映是不可能通過軟體來實現的)。

2.5.10.2、MMU單元的作用
(1)MMU就是memory management unit,記憶體管理單元。MMU實際上是SOC中一個硬體單元,它的主要功能就是實現虛擬地址到實體地址的對映。
(2)MMU單片在CP15協處理器中進行控制,也就是說要操控MMU進行虛擬地址對映,方法就是對cp15協處理器的暫存器進行程式設計。

2.5.10.3、地址對映的額外收益1:訪問控制
(1)訪問控制就是:在管理上對記憶體進行分塊,然後每塊進行獨立的虛擬地址對映,然後在每一塊的對映關係中同時還實現了訪問控制(對該塊可讀、可寫、只讀、只寫、不可訪問等控制)
(2)回想在C語言中程式設計中經常會出現一個錯誤:Segmentation fault。實際上這個段錯誤就和MMU實現的訪問控制有關。當前程式只能操作自己有權操作的地址範圍(若干個記憶體塊),如果當前程式指標出錯訪問了不該訪問的記憶體塊則就會觸發段錯誤。

2.5.10.4、地址對映的額外收益2:cache
(1)cache的工作和虛擬地址對映有關係。
(2)cache是快速快取,意思就是比CPU慢但是比DDR塊。CPU嫌DDR太慢了,於是乎把一些DDR中常用的內容事先讀取快取在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協處理器內部有c0到c15共16個暫存器,這些暫存器每一個都有自己的作用。我們通過mrc和mcr指令來訪問這些暫存器。所謂的操作cp協處理器其實就是操作cp15的這些暫存器。
(2)c3暫存器在mmu中的作用是控制域訪問。域訪問是和MMU的訪問控制有關的。

2.5.11.2、設定TTB(cp15的c2暫存器)
(1)TTB就是translation table base,轉換表基地址。首先要明白什麼是TT(translation table轉換表),TTB其實就是轉換表的基地址。
(2)轉換表是建立一套虛擬地址對映的關鍵。轉換表分2部分,表索引和表項。表索引對應虛擬地址,表項對應實體地址。一對錶索引和表項構成一個轉換表單元,能夠對一個記憶體塊進行虛擬地址轉換。(對映中基本規定中規定了記憶體對映和管理是以塊為單位的,至於塊有多大,要看你的MMU的支援和你自己的選擇。在ARM中支援3種塊大小,細表1KB、粗表4KB、段1MB)。真正的轉換表就是由若干個轉換表單元構成的,每個單元負責1個記憶體塊,總體的轉換表負責整個記憶體空間(0-4G)的對映。
(3)整個建立虛擬地址對映的主要工作就是建立這張轉換表
(4)轉換表放置在記憶體中的,放置時要求起始地址在記憶體中要xx位對齊。轉換表不需要軟體去幹涉使用,而是將基地址TTB設定到cp15的c2暫存器中,然後MMU工作時會自動去查轉換表。

2.5.11.3、使能MMU單元(cp15的c1暫存器)
(1)cp15的c1暫存器的bit0控制MMU的開關。只要將這一個bit置1即可開啟MMU。開啟MMU之後上層軟體層的地址就必須經過TT的轉換才能發給下層物理層去執行。

2.5.11.4、找到對映表待分析
(1)通過符號查詢,確定轉換表在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、總結:關於MMU和虛擬地址對映的學習

巨集觀上理解轉換表:整個轉換表可以看作是一個int型別的陣列,陣列中的一個元素就是一個表索引和表項的單元。陣列中的元素值就是表項,這個元素的陣列下標就是表索引。
ARM的段式對映中長度為1MB,因此一個對映單元只能管1MB記憶體,那我們整個4G範圍內需要4G/1MB=4096個對映單元,也就是說這個陣列的元素個數是4096.實際上我們做的時候並沒有依次單個處理這4096個單元,而是把4096個分成幾部分,然後每部分用for迴圈做相同的處理。

2.5.13.start.S解析11
2.5.13.1、再次設定棧
(1)第三次設定棧。這次設定棧還是在DDR中,之前雖然已經在DDR中設定過一次棧了,但是本次設定棧的目的是將棧放在比較合適(安全,緊湊而不浪費記憶體)的地方。
(2)我們實際將棧設定在uboot起始地址上方2MB處,這樣安全的棧空間是:2MB-uboot大小-0x1000=1.8MB左右。這個空間既沒有太浪費記憶體,又足夠安全。

2.5.13.2、清理bss
(1)清理bss段程式碼和裸機中講的一樣。注意表示bss段的開頭和結尾地址的符號是從連結指令碼u-boot.lds得來的。

2.5.13.3、ldr pc, _start_armboot
(1)start_armboot是uboot/lib_arm/board.c中,這是一個C語言實現的函式。這個函式就是uboot的第二階段。這句程式碼的作用就是將uboot第二階段執行的函式的地址傳給pc,實際上就是使用一個遠跳轉直接跳轉到DDR中的第二階段開始地址處。
(2)遠跳轉的含義就是這句話載入的地址和當前執行地址無關,而和連結地址有關。因此這個遠跳轉可以實現從SRAM中的第一階段跳轉到DDR中的第二階段。
(3)這裡這個遠跳轉就是uboot第一階段和第二階段的分界線。

2.5.13.4、總結:uboot的第一階段做了哪些工作
(1)構建異常向量表
(2)設定CPU為SVC模式
(3)關看門狗
(4)開發板供電置鎖
(5)時鐘初始化
(6)DDR初始化
(7)串列埠初始化並列印"OK"
(8)重定位
(9)建立對映表並開啟MMU
(10)跳轉到第二階段