篇首
最近突發奇想,Xilinx 的集成開發環境已經很好了,很多必要的代碼都直接生成了,這給開發者帶來了巨大便利的同時,也讓人錯過了很多代碼的精彩,可能有很多人用了很多年了,都還無法清楚的理解其中過程。博主準備以FSBL為例,與大家深入探討一番,從而加深對ZYNQ的加載過程的理解,以便大家作出更精彩的設計!
LoadBootImage() 解讀
本文以Zynq7000 FSBL工程代碼為基礎,分析啟動流程核心函數 L o a d B o o t I m a g e ( ) LoadBootImage() LoadBootImage()的執行邏輯與關鍵技術細節。
一、函數調用框架
int LoadBootImage(void) {FsblHookBeforeBitstreamDdr(); // 鉤子函數Status = XFsbl_LoadPartitions(...); // 核心加載FsblHookBeforeHandoff(); // 移交前預處理return Status;
}
二、 函數執行全流程分解
** 函數入口與預處理**
int LoadBootImage(void) {u32 Status = XFSBL_SUCCESS;XTime tStart, tEnd; // 64位計時器(若啟用性能分析)
- 硬件依賴:
- 依賴
psu_init.c
完成的PS端基礎初始化(時鐘、MIO、SLCR鎖等) - DDR物理層已通過
Xil_DDRInit()
完成訓練(psu_ddr_phyinit.c
)
- 依賴
** F s b l H o o k B e f o r e B i t s t r e a m D d r ( ) FsblHookBeforeBitstreamDdr() FsblHookBeforeBitstreamDdr() 鉤子函數**
#ifdef FSBL_PERFXTime_GetTime(&tStart); // 記錄TSC起始值(AXI Timer 0)
#endif
/* 用戶自定義擴展點:可插入DDR重配置代碼 */
- 關鍵寄存器操作:
- DDRC控制:通過
Xil_Out32(0xFD070000, 0x00040010)
設置DDRC_ADDRMAP0
調整地址映射 - OCM重映射:關閉OCM緩存(
SLCR.OCM_CFG
寄存器位3置1)
- DDRC控制:通過
X F s b l L o a d P a r t i t i o n s ( ) XFsbl_LoadPartitions() XFsblL?oadPartitions() 核心加載
階段1:Boot Header解析
XFsblPs_BootHdr Header;
XFsbl_CheckBootHeader(ImageAddr, &Header); // 從QSPI/NAND讀取頭部
- 頭部結構體(
xfsbl_ps_boothdr.h
):typedef struct {u32 ImageID; // 魔數0xAA995566u32 NumPartitions; // 分區總數(含PL比特流+應用)u32 AuthType; // 加密類型:0=None, 1=RSA-2048u32 Checksum; // 頭部的CRC32校驗// ... 其他字段(分區表偏移、證書偏移等) } XFsblPs_BootHdr;
階段2:安全認證(以RSA-2048為例)
XSecure_Sha3Init(&Sha3Instance); // 初始化SHA-3引擎
XSecure_Sha3Update(&Sha3Instance, (u8*)ImageAddr, Header.HashLength);
XSecure_Sha3Final(&Sha3Instance, CalculatedHash); // 計算哈希
XSecure_VerifySignature(CalculatedHash, StoredSignature); // RSA驗簽
- 硬件加速:
- 使用PS內置的CSU模塊(Crypto Subsystem)
- RSA密鑰存儲在eFUSE或BBRAM中(通過
XSecure_GetEfuseKek()
讀取)
階段3:分區加載循環
for (u8 i=0; i<Header.NumPartitions; i++) {XFsblPs_PartitionHdr PartHdr;XFsbl_ReadPartitionHdr(ImageAddr + Offset, &PartHdr);if (PartHdr.Attr & PART_ATTR_PL) { // PL比特流分區XFsbl_LoadPlBitstream(PartHdr.LoadAddr, PartHdr.Size);} else { // PS應用程序分區XFsbl_LoadElf(PartHdr.LoadAddr, PartHdr.Size); // ELF解析}
}
- 關鍵操作細節:
- PL加載:通過DevCfg接口(
XDcfg_CfgInitialize()
)寫入PCAP - ELF加載:解析Program Headers,使用
Xil_Out32()
逐段寫入DDR - 地址對齊:通過
XLAT_FSBL_TABLE
處理非32位對齊訪問(觸發Data Abort時自動轉換)
- PL加載:通過DevCfg接口(
F s b l H o o k B e f o r e H a n d o f f ( ) FsblHookBeforeHandoff() FsblHookBeforeHandoff() 移交前處理
Xil_DCacheFlush(); // 數據緩存刷新(確保DDR數據一致性)
Xil_Out32(CRL_APB_BASE + 0x24, 0x01000F00); // 配置時鐘分頻
- 寄存器詳解:
- CRL_APB (0xFF5E0024): 設置
RPLL_CTRL
分頻系數(CPU=1.3GHz, DDR=1066MHz) - SLCR_UNLOCK (0xF8000008): 寫入
0xDF0D
解鎖保護寄存器
- CRL_APB (0xFF5E0024): 設置
三、關鍵子函數解析
-
F s b l H o o k B e f o r e B i t s t r e a m D d r ( ) FsblHookBeforeBitstreamDdr() FsblHookBeforeBitstreamDdr()
- 作用:DDR初始化前的預處理鉤子
- 執行內容:
#ifdef FSBL_PERF XTime_GetTime(&tStart); // 性能計數器啟動 #endif
-
X F s b l L o a d P a r t i t i o n s ( ) XFsbl_LoadPartitions() XFsblL?oadPartitions()
- 流程分解:
- X F s b l C h e c k B o o t H e a d e r ( ) XFsbl_CheckBootHeader() XFsblC?heckBootHeader()
驗證BIN文件頭結構(含 s i z e o f ( X F s b l P s B o o t H d r ) sizeof(XFsblPs_BootHdr) sizeof(XFsblPsB?ootHdr)) - X F s b l A u t h e n t i c a t i o n ( ) XFsbl_Authentication() XFsblA?uthentication()
執行RSA-2048簽名驗證(通過 X S e c u r e S h a 3 I n i t ( ) XSecure_Sha3Init() XSecureS?ha3Init()等加密API) - 分區加載循環
遍歷分區表加載:for(u8 PartNum=0; PartNum<Header.NumPartitions; PartNum++){XFsbl_LoadPartition(...); // 加載單個分區#ifdef FSBL_DEBUGxil_printf("Partition %d Loaded\r\n", PartNum);#endif }
- X F s b l C h e c k B o o t H e a d e r ( ) XFsbl_CheckBootHeader() XFsblC?heckBootHeader()
- 流程分解:
-
F s b l H o o k B e f o r e H a n d o f f ( ) FsblHookBeforeHandoff() FsblHookBeforeHandoff()
- 執行DDR刷新操作( X i l D C a c h e F l u s h ( ) Xil_DCacheFlush() XilD?CacheFlush())
- 配置時鐘分頻器(通過 X i l O u t 32 ( ) Xil_Out32() XilO?ut32()寫CRL_APB寄存器)
四、核心宏定義
- $FSBL_DEBUG
控制調試輸出(默認關閉) - KaTeX parse error: Double subscript at position 15: XPAR_PSU_DDR_0_?S_AXI_BASEADDR
DDR控制器基地址宏(值 0 x 00100000 0x00100000 0x00100000) - X L A T F S B L T A B L E XLAT_FSBL_TABLE XLATF?SBLT?ABLE
地址轉換表(處理非對齊訪問)
五、執行流程圖
初始化硬件 → 驗證頭部 ↓ ↓ DDR預處理 → 加載分區 ↘ ↓ 移交控制權 \begin{array}{ccc} \text{初始化硬件} & \rightarrow & \text{驗證頭部} \\ \downarrow & & \downarrow \\ \text{DDR預處理} & \rightarrow & \text{加載分區} \\ & \searrow & \downarrow \\ & & \text{移交控制權} \end{array} 初始化硬件↓DDR預處理?→→↘?驗證頭部↓加載分區↓移交控制權?
六、 關鍵數據流與硬件交互
數據加載路徑
QSPI?Flash → AXI?Quad-SPI控制器 OCM緩存 → DMA DDR3 \text{QSPI Flash} \xrightarrow{\text{AXI Quad-SPI控制器}} \text{OCM緩存} \xrightarrow{\text{DMA}} \text{DDR3} QSPI?FlashAXI?Quad-SPI控制器?OCM緩存DMA?DDR3
- 性能優化:
- 啟用DMA傳輸(
XQspiPs_DmaTransfer()
) - 使用線性突發模式(QSPI配置為DDR模式,時鐘速率83MHz)
- 啟用DMA傳輸(
安全認證流程
原始鏡像 → SHA-3/384 哈希值 哈希值 → RSA-2048簽名 驗簽結果 \begin{aligned} &\text{原始鏡像} \xrightarrow{\text{SHA-3/384}} \text{哈希值} \\ &\text{哈希值} \xrightarrow{\text{RSA-2048簽名}} \text{驗簽結果} \end{aligned} ?原始鏡像SHA-3/384?哈希值哈希值RSA-2048簽名?驗簽結果?
- 抗攻擊設計:
- 哈希計算前會清空CSU的密鑰緩存(
XSecure_CsuAesKcvClear()
) - 簽名失敗觸發安全鎖定(通過
XSecure_SetTamperConfig()
)
- 哈希計算前會清空CSU的密鑰緩存(
七、調試與錯誤處理
調試宏啟用
#define FSBL_DEBUG // 啟用調試輸出
- 典型輸出:
XFsbl_Debug: Partition 0 Loaded at 0x00100000 (Size 1MB) XFsbl_Debug: PL Bitstream CRC Check Passed
** 錯誤碼定義**
#define XFSBL_ERROR_BOOTHEADER 0x1000 // 頭部校驗失敗
#define XFSBL_ERROR_AUTHFAIL 0x1001 // RSA驗簽錯誤
#define XFSBL_ERROR_PLLLOCK 0x1002 // 時鐘鎖相環失鎖
- 錯誤處理:
- 記錄錯誤到PMU全局狀態寄存器(
XFsbl_WriteReg(PMU_GLOBAL_GLOB_GEN_STORAGE, errCode)
) - 觸發系統復位(
XFsbl_FallbackReset()
)
- 記錄錯誤到PMU全局狀態寄存器(
**八、 總結 **
L o a d B o o t I m a g e ( ) LoadBootImage() LoadBootImage()作為Zynq7000啟動鏈的核心,其執行涵蓋硬件初始化、安全認證、多階段加載三大模塊。函數首先通過 F s b l H o o k B e f o r e B i t s t r e a m D d r ( ) FsblHookBeforeBitstreamDdr() FsblHookBeforeBitstreamDdr()完成DDR時序微調與性能監控啟動,隨后 X F s b l L o a d P a r t i t i o n s ( ) XFsbl_LoadPartitions() XFsblL?oadPartitions()深度解析Boot Header結構,利用CSU硬件模塊實現RSA-2048/SHA-3安全認證,并依據分區屬性(PL比特流或PS應用)選擇PCAP配置或ELF加載機制。關鍵點包括:通過DevCfg接口的PL動態重配置、基于XLAT表的非對齊地址訪問補償、以及DMA加速的QSPI數據傳輸。移交控制權前,函數會強制刷新數據緩存(確保內存一致性)并通過CRL_APB寄存器組重配時鐘域。調試方面,FSBL_DEBUG宏可實時輸出分區加載狀態,而錯誤處理機制將異常狀態固化至PMU寄存器,為后續故障分析提供關鍵日志。該函數的設計充分體現了Zynq架構中PS-PL協同、硬件安全加速、以及多級啟動鏈的技術特點。
注:具體實現細節需參考對應版本的 f s b l _ h o o k s . c fsbl\_hooks.c fsbl_hooks.c和 x f s b l _ p a r t i t i o n l o a d . c xfsbl\_partition_load.c xfsbl_partitionl?oad.c源碼文件。