一.預覽
本文主要對IMX6Q的啓動過程進行分析,主要參考文檔來自NXP官方文檔IMX6DQRM.pdf,使用的uboot爲NXP官方提供uboot-imx,使用的開發板爲迅爲iTOP-i.MX6。NXP官方文檔IMX6DQRM.pdf對啓動過程的描述在第8章,本文主要參考第8章。
二.關鍵詞
IVT image vector table
DCD device configuration data
三.啓動過程
imx6q上電覆位後,從芯片內部ROM開始執行。內部ROM包含代碼,可以 引導啓動。這個內部ROM代碼首先會檢查BOOT_MODE[1:0]來確定引導模式,而BOOT_MODE[1:0]狀態由芯片的BOOT_MODE的管腳確定,在iTOP-i.MX6開發板上已經固定爲10,即內部引導。
BOOT_MODE[1:0] Boot Type
00 Boot From Fuses
01 Serial Downloader
10 Internal Boot
11 Reserved
在確定了引導模式之後,接下來就確定引導設備(uboot的存儲位置)。在iTOP-i.MX6開發板上可通過撥碼開關設置引導設備,這裏撥碼開關實際是利用imx6q芯片的管腳來設置引導設備,具體設置方法請看IMX6DQRM.pdf的8.5節。iTOP-i.MX6開發板可以設置從eMMC(板上flash)或SD卡爲引導設備,這裏設置eMMC引導設備。
在確定了引導設備eMMC之後,內部ROM代碼會去eMMC上讀取鏡像,我們的鏡像(uboot,內核,文件系統)都存儲在eMMC上。在eMMC上首先存儲的是uboot。在uboot-imx的源文件有board/freescale/mx6q_topeet/flash_header.S文件(mx6q_topeet對應iTOP-i.MX6開發板,在board/freescale/mx6q_sabresd/flash_header.S也有此文件),再看board/freescale/u-boot.lds,可以發現如下,flash_header.S代碼是存儲在start.S代碼的前面。
.text :
{
/* WARNING - the following is hand-optimized to fit within */
/* the sector layout of our flash chips! XXX FIXME XXX */
board/freescale/mx6q_topeet/flash_header.o (.text.flasheader)
cpu/arm_cortexa8/start.o
board/freescale/mx6q_topeet/libmx6q_topeet.a (.text)
lib_arm/libarm.a (.text)
net/libnet.a (.text)
drivers/mtd/libmtd.a (.text)
drivers/mmc/libmmc.a (.text)
. = DEFINED(env_offset) ? env_offset : .;
common/env_embedded.o(.text)
*(.text)
}
在其他arm開發板上uboot都是從cpu/arm_cortexa8/start.S開始運行,這裏flash_header.S存儲在start.S之前,會在start.S之前運行是怎麼回事呢?
要回答這個問題,我們可以看官方文檔IMX6DQRM.pdf的第8.6節。在內部ROM代碼讀取鏡像時,會先讀取IVT(鏡像向量表)、boot data、DCD(設備配置數據)。flash_header.S內的代碼就是IVT、boot data和DCD。在flash_header.S內先有0x400字節的偏移(這個偏移量是IMX6DQRM.pdf的第8.6.1小節有說明,eMMC啓動是偏移0x400字節),然後是IVT、boot data和DCD的描述。
內部ROM代碼先偏移0x400字節讀取IVT,再根據IVT的內容找到boot data和DCD,實際IVT保存了boot data和DCD的地址。內部ROM代碼讀取並執行上述內容後,知道了uboot鏡像(start.S開始的)的起始地址和長度,對DDR進行了初始化設置,接下來就可以導入uboot鏡像。
整個過程:上電覆位->內部ROM代碼->芯片基本初始化(時鐘,緩存等)->確定引導模式->確定引導設備->從引導設備上偏移一定地址後讀取IVT->在IVT內找到boot data和DCD->根據boot data確定uboot的起始地址和長度->根據DCD設置相關外設,包括DDR內存->導入uboot。
IVT、boot data和DCD的數據結構在官方文檔IMX6DQRM.pdf的第8.6節有詳細說明。
四. flash_header.S註釋
/* 這裏僅有flash_header.S的部分代碼,詳細請看源文件 */
#include <config.h>
#include <asm/arch/mx6.h>
#ifdef CONFIG_FLASH_HEADER
#ifndef CONFIG_FLASH_HEADER_OFFSET
# error "Must define the offset of flash header"
#endif
/* 數據變大端宏定義 */
#define CPU_2_BE_32(l) \
((((l) & 0x000000FF) << 24) | \
(((l) & 0x0000FF00) << 8) | \
(((l) & 0x00FF0000) >> 8) | \
(((l) & 0xFF000000) >> 24))
/* DCD數據寫入宏定義 */
#define MXC_DCD_ITEM(i, addr, val) \
dcd_node_##i: \
.word CPU_2_BE_32(addr) ; \
.word CPU_2_BE_32(val) ; \
.section ".text.flasheader", "x"
b _start
.org CONFIG_FLASH_HEADER_OFFSET /*在eMMC中IVT偏移0x400字節*/
/* IVT 數據結構 */
ivt_header: .word 0x402000D1 /* Tag=0xD1, Len=0x0020, Ver=0x40 */
app_code_jump_v: .word _start /* 從uboot開始執行的第一條指令的絕對地址 */
reserv1: .word 0x0
dcd_ptr: .word dcd_hdr /* DCD的絕對地址 */
boot_data_ptr: .word boot_data /* boot data 的絕對地址 */
self_ptr: .word ivt_header /* IVT 的絕對地址 */
#ifdef CONFIG_SECURE_BOOT
app_code_csf: .word __hab_data /* CSF文件的絕對地址,與安全boot相關,這裏沒有 */
#else
app_code_csf: .word 0x0
#endif
reserv2: .word 0x0
/* Boot data structure */
boot_data: .word TEXT_BASE /* Absolute address of the image */
#ifdef CONFIG_SECURE_BOOT
image_len: .word __hab_data_end - TEXT_BASE + CONFIG_FLASH_HEADER_OFFSET
#else
image_len: .word _end_of_copy - TEXT_BASE + CONFIG_FLASH_HEADER_OFFSET
#endif
plugin: .word 0x0
/* DCD */
#if defined CONFIG_MX6DL_DDR3
#if defined CONFIG_DDR_32BIT
/* DCD(device configuration data) header*/
dcd_hdr: .word 0x40E001D2 /* Tag=0xD2, Len=59*8 + 4 + 4, Ver=0x40 */
/* Write data command */
write_dcd_cmd: .word 0x04DC01CC /* Tag=0xCC, Len=59*8 + 4, Param=0x04 */
/* IOMUX Control (IOMUXC) registers */
MXC_DCD_ITEM(1, IOMUXC_BASE_ADDR + 0x774, 0x000C0000)
......
MXC_DCD_ITEM(26, IOMUXC_BASE_ADDR + 0x47c, 0x00000030)
/* MMDC(Multi-mode DDR controller) register set */
MXC_DCD_ITEM(27, MMDC_P0_BASE_ADDR + 0x800, 0xA1390003)
......
MXC_DCD_ITEM(59, MMDC_P0_BASE_ADDR + 0x01c, 0x00000000)