




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、1 引言在專用的嵌入式板子運(yùn)行 GNU/Linux 系統(tǒng)已經(jīng)變得越來越流行。一個(gè)嵌入式 Linux 系統(tǒng)從軟件的角度看通??梢苑譃樗膫€(gè)層次:1. 引導(dǎo)加載程序。固化在固件(firmware)中的 boot 代碼,也就是 Boot Loader,它的啟動(dòng)通常分為兩個(gè)階段。2.Linux 內(nèi)核。特定于嵌入式板子的定制內(nèi)核以及內(nèi)核的啟動(dòng)參數(shù)。3.文件系統(tǒng)。包括根文件系統(tǒng)和建立于 Flash 內(nèi)存設(shè)備之上文件系統(tǒng),root fs。4.用戶應(yīng)用程序。特定于用戶的應(yīng)用程序。有時(shí)在用戶應(yīng)用程序和內(nèi)核層之間可能還會(huì)包括一個(gè)嵌入式圖形用戶界面。常用的嵌入式 GUI 有:MicroWindows 和 MiniGU
2、I 等。引導(dǎo)加載程序是系統(tǒng)加電后運(yùn)行的第一段軟件代碼。回憶一下 PC 的體系結(jié)構(gòu)我們可以知道,PC 機(jī)中的引導(dǎo)加載程序由 BIOS(其本質(zhì)就是一段固件程序)和位于硬盤 MBR 中的 OS Boot Loader(比如,LILO 和 GRUB 等)一起組成。BIOS 在完成硬件檢測(cè)和資源分配后,將硬盤 MBR 中的 Boot Loader 讀到系統(tǒng)的 RAM 中,然后將控制權(quán)交給 OS Boot Loader。Boot Loader 的主要運(yùn)行任務(wù)就是將內(nèi)核映象從硬盤上讀到 RAM 中,然后跳轉(zhuǎn)到內(nèi)核的入口點(diǎn)去運(yùn)行,也即開始啟動(dòng)操作系統(tǒng)。而在嵌入式系統(tǒng)中,通常并沒有像 BIOS 那樣的固件程序(
3、注,有的嵌入式 CPU 也會(huì)內(nèi)嵌一段短小的啟動(dòng)程序),因此整個(gè)系統(tǒng)的加載啟動(dòng)任務(wù)就完全由 Boot Loader 來完成。比如在一個(gè)基于 ARM7TDMI core 的嵌入式系統(tǒng)中,系統(tǒng)在上電或復(fù)位時(shí)通常都從地址 0x 處開始執(zhí)行,而在這個(gè)地址處安排的通常就是系統(tǒng)的 Boot Loader 程序。2 bootloader簡介應(yīng)用程序文件系統(tǒng)操作系統(tǒng)內(nèi)核BootLoader簡單地說,Boot Loader (引導(dǎo)加載程序)就是在操作系統(tǒng)內(nèi)核運(yùn)行之前運(yùn)行的一段小程序,它的作用就是加載操作系統(tǒng),它是系統(tǒng)加電后運(yùn)行的第一段軟件代碼。通過這段代碼實(shí)現(xiàn)硬件的初始化,建立內(nèi)存空間的映射圖,為操作系統(tǒng)內(nèi)核準(zhǔn)備
4、好硬件環(huán)境并引導(dǎo)內(nèi)核的啟動(dòng)。如上圖所示的那樣在設(shè)備的啟動(dòng)過程中bootloader位于最底層,首先被運(yùn)行來引導(dǎo)操作系統(tǒng)運(yùn)行,很容易可以看出 bootloader是底層程序所以它的實(shí)現(xiàn)嚴(yán)重地依賴于硬件,特別是在嵌入式世界。因此,在嵌入式世界里建立一個(gè)通用的BootLoader幾乎是不可能的。盡管如此,一些功能強(qiáng)大、支持硬件環(huán)境較多的BootLoader也被廣大的使用者和愛好者所支持,從而形成了一些被廣泛認(rèn)可的、較為通用的的bootloader實(shí)現(xiàn)。2.1 Boot Loader 所支持的 CPU 和嵌入式板每種不同的 CPU 體系結(jié)構(gòu)都有不同的 Boot Loader。有些 Boot Loade
5、r 也支持多種體系結(jié)構(gòu)的 CPU,比如 U-Boot 就同時(shí)支持 ARM 體系結(jié)構(gòu)和MIPS 體系結(jié)構(gòu)。除了依賴于 CPU 的體系結(jié)構(gòu)外,Boot Loader 實(shí)際上也依賴于具體的嵌入式板級(jí)設(shè)備的配置。這也就是說,對(duì)于兩塊不同的嵌入式板而言,即使它們是基于同一種 CPU 而構(gòu)建的,要想讓運(yùn)行在一塊板子上的 Boot Loader 程序也能運(yùn)行在另一塊板子上,通常也都需要修改 Boot Loader 的源程序。2.2 Boot Loader 的安裝媒介(Installation Medium)系統(tǒng)加電或復(fù)位后,所有的 CPU 通常都從某個(gè)由 CPU 制造商預(yù)先安排的地址上取指令。比如,基于 A
6、RM7TDMI core 的 CPU 在復(fù)位時(shí)通常都從地址 0x 取它的第一條指令。而基于 CPU 構(gòu)建的嵌入式系統(tǒng)通常都有某種類型的固態(tài)存儲(chǔ)設(shè)備(比如:ROM、EEPROM 或 FLASH 等)被映射到這個(gè)預(yù)先安排的地址上。因此在系統(tǒng)加電后,CPU 將首先執(zhí)行 Boot Loader 程序。下圖1就是一個(gè)同時(shí)裝有 Boot Loader、內(nèi)核的啟動(dòng)參數(shù)、內(nèi)核映像和根文件系統(tǒng)映像的固態(tài)存儲(chǔ)設(shè)備的典型空間分配結(jié)構(gòu)圖:圖1 固態(tài)存儲(chǔ)設(shè)備的典型空間分配結(jié)構(gòu)2.3 Boot Loader 的啟動(dòng)過程:單階段(Single Stage)/多階段(Multi-Stage)通常多階段的 Boot Loade
7、r 能提供更為復(fù)雜的功能,以及更好的可移植性。從固態(tài)存儲(chǔ)設(shè)備上啟動(dòng)的 Boot Loader 大多都是 2 階段的啟動(dòng)過程,也即啟動(dòng)過程可以分為 stage 1 和 stage 2 兩部分。而至于在 stage 1 和 stage 2 具體完成哪些任務(wù)將在下面討論。2.4 Boot Loader 的操作模式 (Operation Mode)大多數(shù) Boot Loader 都包含兩種不同的操作模式:啟動(dòng)加載模式和下載模式,這種區(qū)別僅對(duì)于開發(fā)人員才有意義。但從最終用戶的角度看,Boot Loader 的作用就是用來加載操作系統(tǒng),而并不存在所謂的啟動(dòng)加載模式與下載工作模式的區(qū)別。啟動(dòng)加載(Boot
8、loading)模式:這種模式也稱為自主(Autonomous)模式。也即 Boot Loader 從目標(biāo)機(jī)上的某個(gè)固態(tài)存儲(chǔ)設(shè)備上將操作系統(tǒng)加載到 RAM 中運(yùn)行,整個(gè)過程并沒有用戶的介入。這種模式是 Boot Loader 的正常工作模式,因此在嵌入式產(chǎn)品發(fā)布的時(shí)侯,Boot Loader 顯然必須工作在這種模式下。下載(Downloading)模式:在這種模式下,目標(biāo)機(jī)上的 Boot Loader 將通過串口連接或網(wǎng)絡(luò)連接等通信手段從主機(jī)(Host)下載文件,比如:下載內(nèi)核映像和根文件系統(tǒng)映像等。從主機(jī)下載的文件通常首先被 Boot Loader 保存到目標(biāo)機(jī)的 RAM 中,然后再被 Bo
9、ot Loader 寫到目標(biāo)機(jī)上的FLASH 類固態(tài)存儲(chǔ)設(shè)備中。Boot Loader 的這種模式通常在第一次安裝內(nèi)核與根文件系統(tǒng)時(shí)被使用;此外,以后的系統(tǒng)更新也會(huì)使用 Boot Loader 的這種工作模式。工作于這種模式下的 Boot Loader 通常都會(huì)向它的終端用戶提供一個(gè)簡單的菜單界面或命令行接口來接收要執(zhí)行的操作。像 Blob 或 U-Boot 等這樣功能強(qiáng)大的 Boot Loader 通常同時(shí)支持這兩種工作模式,而且允許用戶在這兩種工作模式之間進(jìn)行切換。比如,Blob 在啟動(dòng)時(shí)處于正常的啟動(dòng)加載模式,但是它會(huì)延時(shí) 10 秒等待終端用戶按下任意鍵而將 blob 切換到下載模式。如
10、果在 10 秒內(nèi)沒有用戶按鍵,則 blob 繼續(xù)啟動(dòng) Linux 內(nèi)核。2.5 常見的Boot LoaderU-BOOT:U-Boot是Das U-Boot的簡稱,其含義是Universal Boot Loader,是遵循GPL條款的開放源碼項(xiàng)目。uboot是一個(gè)龐大的公開源碼的軟件。它支持一些系列的arm體系,包含常見的外設(shè)的驅(qū)動(dòng),是一個(gè)功能強(qiáng)大的板極支持包。vivi:vivi是韓國mizi 公司開發(fā)的bootloader, 適用于ARM9處理器。 Vivi也有兩種工作模式:啟動(dòng)加載模式和下載模式。啟動(dòng)加載模式可以在一段時(shí)間后(這個(gè)時(shí)間可更改)自行啟動(dòng)linux內(nèi)核,這是vivi的默認(rèn)模式。
11、如果修改或更新需要進(jìn)入下載模式,在下載模式下,vivi為用戶提供一個(gè)命令行接口通過接口可以使用vivi提供的一些命令,來實(shí)現(xiàn)flash的燒寫、管理、操作mtd分區(qū)信息、啟動(dòng)系統(tǒng)等功能。2.6 U-BOOT的目錄結(jié)構(gòu)目錄說明board和開發(fā)板相關(guān)的文件,每一個(gè)開發(fā)板都以一個(gè)子目錄出現(xiàn)在當(dāng)前目錄中,比如:smdk2410。該子目錄中存放于開發(fā)板相關(guān)的配置文件,如makefile和U-Boot.lds。其中包含SDRAM初始化代碼、Flash底層驅(qū)動(dòng)、板級(jí)初始化文件。config.mk定義了TEXT_BASE是代碼在內(nèi)存的真實(shí)地址common與體系結(jié)構(gòu)無關(guān)的文件,實(shí)現(xiàn)各種命令的C文件。該文件主要實(shí)現(xiàn)
12、uboot命令行下支持的命令,每一條命令都對(duì)應(yīng)一個(gè)文件。例如bootm命令對(duì)應(yīng)就是cmd_bootm.c。cpu與特定CPU架構(gòu)相關(guān)目錄,每一款uboot下支持的CPU在該目錄下對(duì)應(yīng)一個(gè)子目錄,比如arm920t。每個(gè)CPU子目錄中都包括cpu.c和interrupt.c、start.S。cpu.c初始化CPU、設(shè)置指令Cache和數(shù)據(jù)Cache等interrupt.c設(shè)置系統(tǒng)的各種中斷和異常start.S是U-boot啟動(dòng)時(shí)執(zhí)行的第一個(gè)文件,它主要做早期系統(tǒng)初始化,代碼重定向和設(shè)置系統(tǒng)堆棧diskDisk分區(qū)處理代碼,對(duì)磁盤的支持doc文檔目錄,uboot有非常完整的文檔driversUbo
13、ot支持的設(shè)備驅(qū)動(dòng)程序都放在該目錄,例如各種網(wǎng)卡、支持CFI的Flash、串口、USB等fs支持的文件系統(tǒng),目前支持cramfs、fat、fdos、jffs2和registerfsinclude頭文件,還有對(duì)各種硬件平臺(tái)支持的匯編文件,系統(tǒng)配置文件和對(duì)文件系統(tǒng)支持的文件等。該目錄下的configs目錄有與開發(fā)板相關(guān)的配置文件,如smdk2410.h。該目錄下的asm目錄有與CPU體系結(jié)構(gòu)相關(guān)的頭文件,比如arm對(duì)應(yīng)的就是與網(wǎng)絡(luò)協(xié)議棧相關(guān)的代碼,BOOTP協(xié)議、TFTP協(xié)議、RARP和NFS文件系統(tǒng)的實(shí)現(xiàn)等lib_xxx與ARM體系結(jié)構(gòu)相關(guān)的庫文件。如與arm相關(guān)的庫放在l
14、ib_arm中tools生成uboot的工具,如mkimage,crc等等3 Boot Loader 的主要任務(wù)與典型結(jié)構(gòu)框架從操作系統(tǒng)的角度看,Boot Loader 的總目標(biāo)就是正確地調(diào)用內(nèi)核來執(zhí)行。另外,由于 Boot Loader 的實(shí)現(xiàn)依賴于 CPU 的體系結(jié)構(gòu),因此大多數(shù) Boot Loader 都分為 stage1 和 stage2 兩大部分。依賴于 CPU 體系結(jié)構(gòu)的代碼,比如設(shè)備初始化代碼等,通常都放在 stage1 中,而且通常都用匯編語言來實(shí)現(xiàn),以達(dá)到短小精悍的目的。而 stage2 則通常用C語言來實(shí)現(xiàn),這樣可以實(shí)現(xiàn)給復(fù)雜的功能,而且代碼會(huì)具有更好的可讀性和可移植性。以
15、u-boot為例,它啟動(dòng)過程的兩個(gè)階段(stage) 如下:第一階段(stage 1) cpu/arm920t/start.S依賴于CPU體系結(jié)構(gòu)的代碼(如設(shè)備初始化代碼等),一般用匯編語言來實(shí)現(xiàn)。主要進(jìn)行以下方面的設(shè)置:設(shè)置ARM進(jìn)入SVC模式、禁止IRQ和FIQ、關(guān)閉看門狗、屏蔽所有中斷。設(shè)置時(shí)鐘(FCLK,HCLK,PCLK)、清空I/D cache、清空TLB、禁止MMU和cache、配置內(nèi)存控制器、為搬運(yùn)代碼做準(zhǔn)備、搬移uboot映像到RAM中(使用copy_loop實(shí)現(xiàn))、分配堆棧、清空bss段(使用clbss_l實(shí)現(xiàn))。最后通過ldr pc, _start_armboot跳轉(zhuǎn)到第
16、二階段。第二階段(stage 2) lib_arm/board.c該階段主要都是用語言來實(shí)現(xiàn)。start_armboot()進(jìn)行一系列初始化(cpu, 板卡,中斷,串口,控制臺(tái)等),開啟I/D cache。初始化FLASH,根據(jù)系統(tǒng)配置執(zhí)行其他初始化操作。打印LOG,使能中斷,獲取環(huán)境變量,初始化網(wǎng)卡。最后進(jìn)入main_loop()函數(shù)。綜上所述,可簡單的歸納兩個(gè)階段的功能如下:第一階段的功能: 硬件設(shè)備初始化 加載U-Boot第二階段代碼到RAM空間 設(shè)置好棧 跳轉(zhuǎn)到第二階段代碼入口第二階段的功能: 初始化本階段使用的硬件設(shè)備 檢測(cè)系統(tǒng)內(nèi)存映射 將內(nèi)核從Flash讀取到RAM中 為內(nèi)核設(shè)置啟
17、動(dòng)參數(shù) 調(diào)用內(nèi)核U-Boot啟動(dòng)第一階段流程如下:3.1 u-boot 的 stage1詳細(xì)分析uboot的第一階段設(shè)計(jì)的非常巧妙,幾乎都是用匯編語言實(shí)現(xiàn)的。首先我們來看一下它的鏈接腳本(u-boot-1.1.6boardsmdk2410u-boot.lds),通過它我們可以知道它整個(gè)程序的各個(gè)段是怎么存放的。它定義了整個(gè)程序編譯之后的連接過程,決定了一個(gè)可執(zhí)行程序的各個(gè)段的存儲(chǔ)位置。/* 指定輸出可執(zhí)行文件是elf 格式,32 位ARM 指令,小端 */OUTPUT_FORMAT(elf32-littlearm, elf32-littlearm, elf32-littlearm)/* 指定輸
18、出可執(zhí)行文件的平臺(tái)架構(gòu)為ARM架構(gòu) */OUTPUT_ARCH(arm)/* 指定輸出可執(zhí)行文件的起始代碼段為_start */ENTRY(_start)SECTIONS. = 0x;/入口地址. = ALIGN(4);/四字節(jié)對(duì)齊.text :/代碼段,上面3行標(biāo)識(shí)是不占任何空間的cpu/arm920t/start.o(.text)/這里將start.o放在第一位就表示把start.s編譯時(shí)放在最開始,也就是uboot啟動(dòng)是最先執(zhí)行start.S*(.text)/所有的其他程序的代碼段以四字節(jié)對(duì)齊放在它后面. = ALIGN(4);/前面的“.”表示當(dāng)前值.rodata : *(.rodat
19、a) /只讀數(shù)據(jù)段. = ALIGN(4);.data : *(.data) /指定讀/寫數(shù)據(jù)段. = ALIGN(4);.got : *(.got) /指定got段,got段式是uboot自定義的一個(gè)段,非標(biāo)準(zhǔn)段. = .;_u_boot_cmd_start = .;/把_u_boot_cmd_start賦值為當(dāng)前位置,即起始位置.u_boot_cmd : *(.u_boot_cmd) /指定u_boot_cmd段,uboot把所有的uboot命令放在該段_u_boot_cmd_end = .;/把 _u_boot_cmd_end賦值為當(dāng)前位置,即結(jié)束位置. = ALIGN(4);_bss_
20、start = .;/_bss_start賦值為當(dāng)前位置,即bss段得開始位置.bss : *(.bss) _end = .;/把_end賦值為當(dāng)前位置,即bss段得結(jié)束地址從上面這段代碼我們可以看出uboot運(yùn)行的第一個(gè)程序是cpu/arm920t/start.S里面的第一個(gè)段_start。我們查看start.S的源碼。3.1.1硬件設(shè)備初始化(1)設(shè)置異常向量cpu/arm920t/start.S開頭有如下的代碼:/global用于聲明一個(gè)符號(hào)可被其他文檔引用,相當(dāng)于聲明了一個(gè)全局變量,.globl 和.global 相同。/該部分為處理器的異常處理向量表。地址范圍為0x 0x,剛好8 條
21、指令。.globl _start/* u-boot啟動(dòng)入口 */_start:breset/* 復(fù)位*/ldrpc, _undefined_instruction/* 未定義指令向量 */ldrpc, _software_interrupt/* 軟件中斷向量 */ldrpc, _prefetch_abort/* 預(yù)取指令異常向量 */ldrpc, _data_abort/* 數(shù)據(jù)操作異常向量 */ldrpc, _not_used/* 未使用 */ldrpc, _irq/* irq中斷向量 */ldrpc, _fiq/* fiq中斷向量 */* 中斷向量表入口地址 */.word 偽操作用于分配
22、一段字內(nèi)存單元(分配的單元都是字對(duì)齊的),并用偽操作中的expr 初始化。.long 和.int 作用與之相同。_undefined_instruction:.word undefined_instruction_software_interrupt:.word software_interrupt_prefetch_abort:.word prefetch_abort_data_abort:.word data_abort_not_used:.word not_used_irq:.word irq_fiq:.word fiq.balignl 16,0xdeadbeef以上代碼設(shè)置了ARM異常
23、向量表,各個(gè)異常向量介紹如下:地址異常進(jìn)入模式描述0x復(fù)位管理模式復(fù)位電平有效時(shí),產(chǎn)生復(fù)位異常,程序跳轉(zhuǎn)到復(fù)位處理程序處執(zhí)行0x未定義指令未定義模式遇到不能處理的指令時(shí)產(chǎn)生未定義指令異常0x軟件中斷管理模式執(zhí)行SWI指令產(chǎn)生,用于用戶模式下的程序調(diào)用特權(quán)操作指令0xc預(yù)存指令中止模式處理器預(yù)取指令的地址不存在,或該地址不允許當(dāng)前指令訪問,產(chǎn)生指令預(yù)取中止異常0x數(shù)據(jù)操作中止模式處理器數(shù)據(jù)訪問指令的地址不存在或該地址不允許當(dāng)前指令訪問時(shí),產(chǎn)生數(shù)據(jù)中止異常0x未使用未使用未使用0xIRQIRQ外部中斷請(qǐng)求有效,且CPSR中的I位為0時(shí),產(chǎn)生IRQ異常0xcFIQFIQ快速中斷請(qǐng)求引腳有效,且CPS
24、R中的F位為0時(shí),產(chǎn)生FIQ異常在cpu/arm920t/start.S中還有這些異常對(duì)應(yīng)的異常處理程序。當(dāng)一個(gè)異常產(chǎn)生時(shí),CPU根據(jù)異常號(hào)在異常向量表中找到對(duì)應(yīng)的異常向量,然后執(zhí)行異常向量處的跳轉(zhuǎn)指令,CPU就跳轉(zhuǎn)到對(duì)應(yīng)的異常處理程序執(zhí)行。其中復(fù)位異常向量的指令“b reset”決定了U-Boot啟動(dòng)后將自動(dòng)跳轉(zhuǎn)到標(biāo)號(hào)reset處執(zhí)行。許多人都認(rèn)為_start的值是0x,為什么是這個(gè)地址呢? 因?yàn)檫B接腳本上指定了。真的是這樣嗎?我們來看看我們編譯好之后,在u-boot目錄下有個(gè)System.map,這里面有各個(gè)變量的值,其中會(huì)告訴你,_start的值為:0x33f80000。注意,這里有兩個(gè)
25、地址:編譯地址和運(yùn)行地址。什么是編譯地址?什么是運(yùn)行地址? 32 位的處理器,它的每一條指令是4個(gè)字節(jié),以4個(gè)字節(jié)存儲(chǔ)順序,進(jìn)行順序執(zhí)行,CPU是順序執(zhí)行的,只要沒發(fā)生什么跳轉(zhuǎn),它會(huì)順序進(jìn)行執(zhí)行,編譯器會(huì)對(duì)每一條指令分配一個(gè)編譯地址,這是編譯器分配的,在編譯過程中分配的地址,我們稱之為編譯地址。運(yùn)行地址是指,程序指令真正運(yùn)行的地址,是由用戶指定的,用戶將運(yùn)行地址燒錄到哪里,哪里就是運(yùn)行的地址。編譯地址和運(yùn)行地址如何來算呢?假如有兩個(gè)編譯地址a=0x10,b=0x7,b的運(yùn)行地址是0x300 ,那么a的運(yùn)行地址就是b的運(yùn)行地址加上兩者編譯地址的差值,a-b=0x10-0x7=0x3,a的運(yùn)行地址
26、就是0x300+0x3=0x303。(2)CPU進(jìn)入SVC模式start_code:/* * set the cpu to SVC32 mode */mrsr0, cpsrbicr0, r0, #0x1f/*工作模式位清零 */orrr0, r0, #0xd3/*工作模式位設(shè)置為“10011”(管理模式),并將中斷禁止位和快中斷禁止位置1 */msrcpsr, r0以上代碼將CPU的工作模式位設(shè)置為管理模式,并將中斷禁止位和快中斷禁止位置一,從而屏蔽了IRQ和FIQ中斷。(3)設(shè)置控制寄存器地址#if defined(CONFIG_S3C2400)#define pWTCON0x#define
27、 INTMSK0x#define CLKDIVN0x#else/* s3c2410與s3c2440下面4個(gè)寄存器地址相同 */#define pWTCON0x/* WATCHDOG控制寄存器地址*/#define INTMSK0x4A/* INTMSK寄存器地址 */#define INTSUBMSK0x4A00001C/* INTSUBMSK寄存器地址 */#define CLKDIVN0x4C /* CLKDIVN寄存器地址 */# endif對(duì)于s3c2440開發(fā)板,以上代碼完成了WATCHDOG,INTMSK,INTSUBMSK,CLKDIVN四個(gè)寄存器的地址的設(shè)置。(4)關(guān)閉看門狗l
28、drr0, =pWTCONmovr1, #0x0strr1, r0/* 看門狗控制器的最低位為0時(shí),看門狗不輸出復(fù)位信號(hào) */以上代碼向看門狗控制寄存器寫入0,關(guān)閉看門狗。為什么需要關(guān)閉看門狗呢?這里有個(gè)喂狗的過程,所謂的喂狗是每隔一段時(shí)間給某個(gè)寄存器置位而已,在實(shí)際中會(huì)專門啟動(dòng)一個(gè)線程或進(jìn)程會(huì)專門喂狗,當(dāng)上層軟件出現(xiàn)故障時(shí)就會(huì)停止喂狗,停止喂狗之后,cpu會(huì)自動(dòng)復(fù)位,一般都在外部專門有一個(gè)看門狗,做一個(gè)外部的電路,不在cpu內(nèi)部使用看門狗,否則在U-Boot啟動(dòng)過程中,CPU將不斷重啟。(5)屏蔽中斷/* * mask all IRQs by setting all bits in the
29、INTMR - default */movr1, #0xffffffff/* 某位被置1則對(duì)應(yīng)的中斷被屏蔽 */ldrr0, =INTMSKstrr1, r0INTMSK是主中斷屏蔽寄存器,每一位對(duì)應(yīng)SRCPND(中斷源引腳寄存器)中的一位,表明SRCPND相應(yīng)位代表的中斷請(qǐng)求是否被CPU所處理。INTMSK寄存器是一個(gè)32位的寄存器,每位對(duì)應(yīng)一個(gè)中斷,向其中寫入0xffffffff就將INTMSK寄存器全部位置1,從而屏蔽對(duì)應(yīng)的中斷。為什么要關(guān)閉中斷呢?中斷處理(ldr pc .)是將代碼的編譯地址放在了指針上,而這段時(shí)間內(nèi)還沒有搬移代碼,所以不能進(jìn)行跳轉(zhuǎn)。#if defined(CONFI
30、G_S3C2440) ldr r1, =0x7fff ldr r0, =INTSUBMSK str r1, r0#endifINTSUBMSK每一位對(duì)應(yīng)SUBSRCPND中的一位,表明SUBSRCPND相應(yīng)位代表的中斷請(qǐng)求是否被CPU所處理。INTSUBMSK寄存器是一個(gè)32位的寄存器,但是只使用了低15位。向其中寫入0x7fff就是將INTSUBMSK寄存器全部有效位(低15位)置1,從而屏蔽對(duì)應(yīng)的中斷。(6)設(shè)置MPLLCON,UPLLCON, CLKDIVN#if defined(CONFIG_S3C2440)#define MPLLCON 0x4C#define UPLLCON 0x4
31、C ldr r0, =CLKDIVN mov r1, #5 str r1, r0 ldr r0, =MPLLCON ldr r1, =0x7F021 str r1, r0 ldr r0, =UPLLCON ldr r1, =0x38022 str r1, r0#else/* FCLK:HCLK:PCLK = 1:2:4 */* default FCLK is 120 MHz ! */ldrr0, =CLKDIVNmovr1, #3strr1, r0#endifCPU上電幾毫秒后,晶振輸出穩(wěn)定,F(xiàn)CLK=Fin(晶振頻率),CPU開始執(zhí)行指令。但實(shí)際上,F(xiàn)CLK可以高于Fin,為了提高系統(tǒng)時(shí)鐘,
32、需要用軟件來啟用PLL。這就需要設(shè)置CLKDIVN,MPLLCON,UPLLCON這3個(gè)寄存器。CLKDIVN寄存器用于設(shè)置FCLK,HCLK,PCLK三者間的比例如下:CLKDIVN位說明初始值HDIVN2:100 : HCLK = FCLK/1.01 : HCLK = FCLK/2.10 : HCLK = FCLK/4 (當(dāng) CAMDIVN9 = 0 時(shí))HCLK= FCLK/8 (當(dāng) CAMDIVN9 = 1 時(shí))11 : HCLK = FCLK/3 (當(dāng) CAMDIVN8 = 0 時(shí))HCLK = FCLK/6 (當(dāng) CAMDIVN8 = 1時(shí))00PDIVN00: PCLK = HC
33、LK/1 1: PCLK = HCLK/20設(shè)置CLKDIVN為5,就將HDIVN設(shè)置為二進(jìn)制的10,由于CAMDIVN9沒有被改變過,取默認(rèn)值0,因此HCLK = FCLK/4。PDIVN被設(shè)置為1,因此PCLK= HCLK/2。因此分頻比FCLK:HCLK:PCLK = 1:4:8 。MPLLCON寄存器用于設(shè)置FCLK與Fin的倍數(shù)。MPLLCON的位19:12稱為MDIV,位9:4稱為PDIV,位1:0稱為SDIV。對(duì)于S3C2440,F(xiàn)CLK與Fin的關(guān)系如下面公式:MPLLCON與UPLLCON通常設(shè)置如下:輸入頻率輸出頻率MDIVPDIVSDIV12.0000MHz48.00 M
34、Hz56(0x38)2212.0000MHz405.00 MHz127(0x7f)21當(dāng)s3c2440系統(tǒng)主頻設(shè)置為405MHZ,USB時(shí)鐘頻率設(shè)置為48MHZ時(shí),系統(tǒng)可以穩(wěn)定運(yùn)行,因此設(shè)置MPLLCON與UPLLCON為:MPLLCON=(0x7f12) | (0x024) | (0x01) = 0x7f021UPLLCON=(0x3812) | (0x024) | (0x02) = 0x38022(7)關(guān)閉MMU,cache接著往下看:#ifndef CONFIG_SKIP_LOWLEVEL_INITblcpu_init_crit#endifcpu_init_crit這段代碼在U-Boot
35、正常啟動(dòng)時(shí)才需要執(zhí)行,若將U-Boot從RAM中啟動(dòng)則應(yīng)該注釋掉這段代碼。下面分析一下cpu_init_crit到底做了什么:#ifndef CONFIG_SKIP_LOWLEVEL_INITcpu_init_crit: /* * 使數(shù)據(jù)cache與指令cache無效 */ */ movr0, #0 mcrp15, 0, r0, c7, c7, 0/* 向c7寫入0將使ICache與DCache無效*/ mcrp15, 0, r0, c8, c7, 0/* 向c8寫入0將使TLB失效 */ /* * disable MMU stuff and caches */ mrcp15, 0, r0,
36、c1, c0, 0/* 讀出控制寄存器到r0中 */ bicr0, r0, #0x clear bits 13, 9:8 (-V- -RS) bicr0, r0, #0x clear bits 7, 2:0 (B- -CAM) orrr0, r0, #0x set bit 2 (A) Align orrr0, r0, #0x set bit 12 (I) I-Cache mcrp15, 0, r0, c1, c0, 0/* 保存r0到控制寄存器 */ /* * before relocating, we have to setup RAM timing * because memory tim
37、ing is board-dependend, you will * find a lowlevel_init.S in your board directory. */ movip, lr bllowlevel_init movlr, ipmovpc, lr#endif /* CONFIG_SKIP_LOWLEVEL_INIT */代碼中的c0,c1,c7,c8都是ARM920T的協(xié)處理器CP15的寄存器。其中c7是cache控制寄存器,c8是TLB控制寄存器。將0寫入c7、c8,使Cache,TLB內(nèi)容無效。通過修改CP15的c1寄存器來關(guān)閉了MMU。為什么要關(guān)閉catch 和MMU 呢?
38、catch 和MMU 是做什么用的? catch 是cpu內(nèi)部的一個(gè)2級(jí)緩存,她的作用是將常用的數(shù)據(jù)和指令放在cpu內(nèi)部,MMU是用來做虛實(shí)地址轉(zhuǎn)換用的,我們的目的是設(shè)置控制的寄存器,寄存器都是實(shí)地址,如果既要開啟MMU又要做虛實(shí)地址轉(zhuǎn)換的話,中間還多一步,先要把實(shí)地址轉(zhuǎn)換成虛地址,然后再做設(shè)置,但對(duì)uboot 而言就是起到一個(gè)簡單的初始化的作用和引導(dǎo)操作系統(tǒng),如果開啟MMU 的話,很麻煩,也沒必要,所以關(guān)閉MMU. 說到catch 就必須提到一個(gè)關(guān)鍵字Volatile,以后在設(shè)置寄存器時(shí)會(huì)經(jīng)常遇到,他的本質(zhì)是告訴編譯器不要對(duì)我的代碼進(jìn)行優(yōu)化,優(yōu)化的過程是將常用的代碼取出來放到catch中,它
39、沒有從實(shí)際的物理地址去取,它直接從cpu的緩存中去取,但常用的代碼就是為了感知一些常用變量的變化,所以在這種情況下要用Volatile關(guān)鍵字告訴編譯器不要做優(yōu)化,每次從實(shí)際的物理地址中去取指令。(8)初始化RAM控制寄存器其中的bl lowlevel_init用于初始化各個(gè)bank,完成了內(nèi)存初始化的工作,由于內(nèi)存初始化是依賴于開發(fā)板的,因此lowlevel_init的代碼一般放在board下面相應(yīng)的目錄中。對(duì)于s3c2440,lowlevel_init在board/smdk2440/lowlevel_init.S中定義如下:#define BWSCON0x/* 13個(gè)存儲(chǔ)控制器的開始地址 *
40、/ _TEXT_BASE: .wordTEXT_BASE.globl lowlevel_initlowlevel_init: /* memory control configuration */ /* make r0 relative the current location so that it */ /* reads SMRDATA out of FLASH rather than memory ! */ ldr r0, =SMRDATA ldrr1, _TEXT_BASE subr0, r0, r1/* SMRDATA減 _TEXT_BASE就是13個(gè)寄存器的偏移地址 */ ldrr1,
41、 =BWSCON/* Bus Width Status Controller */ add r2, r0, #13*40: ldr r3, r0, #4/*將13個(gè)寄存器的值逐一賦值給對(duì)應(yīng)的寄存器*/ str r3, r1, #4 cmp r2, r0 bne 0b /* everything is fine now */ movpc, lr .ltorg /* the literal pools origin */SMRDATA:/* 下面是13個(gè)寄存器的值 */ .word .word lowlevel_init的作用就是將SMRDATA開始的13個(gè)值復(fù)制給開始地址BWSCON的13個(gè)寄存
42、器,從而完成了存儲(chǔ)控制器的設(shè)置。(9)復(fù)制U-Boot第二階段代碼到RAMrelocate: adrr0, _start/* r0 - current position of code */ ldrr1, _TEXT_BASE/* test if we run from flash or RAM */* 判斷U-Boot是否是下載到RAM中運(yùn)行,若是,則不用再復(fù)制到RAM中了,這種情況通常在調(diào)試U-Boot時(shí)才發(fā)生 */ cmp r0, r1/*_start等于_TEXT_BASE說明是下載到RAM中運(yùn)行 */ beqstack_setup ldrr2, _armboot_start ldrr
43、3, _bss_start subr2, r3, r2/* r2 - size of armboot */ addr2, r0, r2/* r2 - source end address */ /* 搬運(yùn)U-Boot自身到RAM中*/copy_loop: ldmiar0!,r3-r10/* 從地址為r0的NOR Flash中讀入8個(gè)字的數(shù)據(jù) */ stmiar1!,r3-r10/* 將r3至r10寄存器的數(shù)據(jù)復(fù)制給地址為r1的內(nèi)存 */ cmpr0, r2/* until source end addreee r2 */ blecopy_loop終于到重點(diǎn)部分了:代碼重定向(拷貝stage2到
44、RAM中),拷貝時(shí)要確定兩點(diǎn):(1) stage2的可執(zhí)行映象在固態(tài)存儲(chǔ)設(shè)備的存放起始地址和終止地址;(2) RAM空間的起始地址。下面我們來看看它到底是怎么重定向的:adr r0,_startadr偽指令,匯編器會(huì)將執(zhí)行到_start時(shí)PC的值放到r0中。所以此時(shí)r0中保存的不是編譯地址,而是運(yùn)行地址。假如U-boot 是從RAM 開始運(yùn)行,則從adr,r0,_start 得到的地址信息為r0=_start=_TEXT_BASE=TEXT_BASE=0x33f80000;假如U-boot 從Flash 開始運(yùn)行,即從處理器對(duì)應(yīng)的地址運(yùn)行,則r0=0x。而這里r0=0。ldr r1,_TEXT
45、_BASE這條匯編指令的意思是把_TEXT_BASE的值作為地址,把這個(gè)地址的內(nèi)容賦給r1,從下面可以知道:_TEXT_BASE里面存儲(chǔ)的內(nèi)容是TEXT_BASE,我們通過查看board/smdk2410/config.mk發(fā)現(xiàn)TEXT_BASE的值為0x33f80000,所以r1的值就是0x33f80000cmp r0,r1將r0和r1做比較,此時(shí)r0 = 0x,r1 = 0x33f80000,顯然不相等,那么執(zhí)行的就是下面的匯編指令:ldr r2, _armboot_start由此可以知道r2的值是_start,通過ldr將標(biāo)號(hào)的編譯地址放到r2中,也就是0x33f80000,即代碼段的起
46、始地址。ldr r3, _bss_start有此可知,r3就是_bss_start的值。由u-boot.lds的鏈接腳本可以知道,r3的值是整個(gè)代碼得結(jié)尾.sub r2,r3,r2這條指令的意思是r2 = r3 -r2,即r2 = 代碼結(jié)束 - 代碼開始,這樣得到的是r2 = 整個(gè)代碼的大小。add r2,r0,r2這條指令的意思是r2 = r0 + r2,即 r2 = 代碼開始 + 代碼大小,這樣得到的是r2 = falsh 里面代碼的結(jié)尾,此時(shí)我們得到r0 = flash 代碼的起始位置,r1 = 0x33f80000(sdram :0x 0x)將flash里面的代碼拷貝到sdram里面了
47、。(10)設(shè)置堆棧/* 設(shè)置堆棧 */stack_setup:ldrr0, _TEXT_BASE/* upper 128 KiB: relocated uboot */subr0, r0, #CONFIG_SYS_MALLOC_LEN/* malloc area */subr0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* 跳過全局?jǐn)?shù)據(jù)區(qū) */#ifdef CONFIG_USE_IRQsubr0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)#endifsubsp, r0, #12/* leave 3 words for
48、 abort-stack */只要將sp指針指向一段沒有被使用的內(nèi)存就完成棧的設(shè)置了。U-Boot內(nèi)存使用情況如下圖所示:(11)清除BSS段clear_bss:ldrr0, _bss_start/* BSS段開始地址,在u-boot.lds中指定*/ldrr1, _bss_end/* BSS段結(jié)束地址,在u-boot.lds中指定*/movr2, #0xclbss_l:strr2, r0/* 將bss段清零*/addr0, r0, #4cmp r0, r1bleclbss_l初始值為0,無初始值的全局變量,靜態(tài)變量將自動(dòng)被放在BSS段。應(yīng)該將這些變量的初始值賦為0,否則這些變量的初始值將是一
49、個(gè)隨機(jī)的值,若有些程序直接使用這些沒有初始化的變量將引起未知的后果。(12)跳轉(zhuǎn)到第二階段代碼入口ldrpc, _start_armboot_start_armboot:.word start_armboot跳轉(zhuǎn)到第二階段代碼入口start_armboot處。3.2 Boot Loader 的 stage2詳細(xì)分析start_armboot函數(shù)在lib_arm/board.c中定義,是U-Boot第二階段代碼的入口。U-Boot啟動(dòng)第二階段流程如下:正如前面所說,stage2 的代碼通常用 C 語言來實(shí)現(xiàn),以便于實(shí)現(xiàn)更復(fù)雜的功能和取得更好的代碼可讀性和可移植性。在分析start_armboot
50、函數(shù)前先來看看一些重要的數(shù)據(jù)結(jié)構(gòu):(1)gd_t結(jié)構(gòu)體U-Boot使用了一個(gè)結(jié)構(gòu)體gd_t來存儲(chǔ)全局?jǐn)?shù)據(jù)區(qū)的數(shù)據(jù),這個(gè)結(jié)構(gòu)體在include/asm-arm/global_data.h中定義如下:typedefstructglobal_databd_t*bd;/* 與板子相關(guān)的結(jié)構(gòu)體,見下面 */unsigned longflags;/* 選項(xiàng) */unsigned longbaudrate;/* 波特率 */unsigned longhave_console;/* serial_init() was called */unsigned longenv_addr;/* Address of E
51、nvironment struct */unsigned longenv_valid;/* Checksum of Environment valid? */unsigned longfb_base;/* base address of frame buffer */void*jt;/* jump table */gd_t;U-Boot使用了一個(gè)存儲(chǔ)在寄存器中的指針gd來記錄全局?jǐn)?shù)據(jù)區(qū)的地址:#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm (r8)DECLARE_GLOBAL_DATA_PTR定義一個(gè)gd_t全局?jǐn)?shù)據(jù)
52、結(jié)構(gòu)的指針,這個(gè)指針存放在指定的寄存器r8中。這個(gè)聲明也避免編譯器把r8分配給其它的變量。任何想要訪問全局?jǐn)?shù)據(jù)區(qū)的代碼,只要代碼開頭加入“DECLARE_GLOBAL_DATA_PTR”一行代碼,然后就可以使用gd指針來訪問全局?jǐn)?shù)據(jù)區(qū)了。根據(jù)U-Boot內(nèi)存使用圖中可以計(jì)算gd的值:gd = TEXT_BASE CONFIG_SYS_MALLOC_LEN sizeof(gd_t) (2)bd_t結(jié)構(gòu)體bd_t在include/asm-arm.u/u-boot.h中定義如下:typedef struct bd_info intbi_baudrate;/* 串口通訊波特率 */ unsigned
53、longbi_ip_addr;/* IP 地址*/ struct environment_s *bi_env;/* 環(huán)境變量開始地址 */ ulong bi_arch_number;/* 開發(fā)板的機(jī)器碼 */ ulong bi_boot_params;/* 內(nèi)核參數(shù)的開始地址 */ struct/* RAM配置信息 */ ulong start;/* 起始地址 */ulong size;/* 長度 */ bi_dramCONFIG_NR_DRAM_BANKS;bd_t;U-Boot啟動(dòng)內(nèi)核時(shí)要給內(nèi)核傳遞參數(shù),這時(shí)就要使用gd_t,bd_t結(jié)構(gòu)體中的信息來設(shè)置標(biāo)記列表。(3)init_sequence數(shù)組U-Boot使用一個(gè)數(shù)組init_sequence(在lib_arm/board.c中)來存儲(chǔ)對(duì)于大多數(shù)開發(fā)板都要執(zhí)行的初始化函數(shù)的函數(shù)指針。init_sequence數(shù)組中有較多的編譯選項(xiàng),去掉編譯選項(xiàng)后init_sequence數(shù)組如下所示:typedef int (init_fnc_t) (void);/* 這是使用typedef定義一個(gè)init
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 閘機(jī)系統(tǒng)施工方案
- 奉賢區(qū)拉森鋼板樁施工方案
- 低碳綠色施工方案
- 酒店會(huì)議室墻布施工方案
- 建筑工地臨時(shí)便道施工方案
- 中 關(guān) 村:威海市惠河路-90 號(hào) 7 幢工業(yè)房房地產(chǎn)抵押估價(jià)報(bào)告
- 恒鑫生活:公司財(cái)務(wù)報(bào)表及審閱報(bào)告(2024年1月-12月)
- 東鵬飲料(集團(tuán))股份有限公司2024年年度報(bào)告摘要
- 超級(jí)難的初三數(shù)學(xué)試卷
- 壓井施工方案
- 德育工作表彰獎(jiǎng)勵(lì)制度
- 工字鋼承重負(fù)荷表xls
- 圍術(shù)期輸血-課件
- 中國飲食文化PPT(第3版)完整全套教學(xué)課件
- 離婚協(xié)議書電子版可打印
- 天水紅石礦業(yè)有限公司水洞溝金礦450td采選項(xiàng)目環(huán)境影響評(píng)價(jià)報(bào)告書
- 部編道德與法治六年級(jí)下冊(cè)第8課《科技發(fā)展 造福人類》優(yōu)秀課件
- 對(duì)氯氰芐、α-異丙基對(duì)氯苯基乙酰氯、鄰氟苯甲酰氯和2-氯-4-甲砜基苯甲酸項(xiàng)目可行性研究報(bào)告書
- 老舊供熱管網(wǎng)改造工程技術(shù)標(biāo)投標(biāo)方案
- 碲化鎘薄膜太陽電池
- 機(jī)械制造工藝學(xué)課程設(shè)計(jì)階梯軸夾具設(shè)計(jì)
評(píng)論
0/150
提交評(píng)論