/****************************************************************************** * Copyright (c) 2011 - 2022 Xilinx, Inc. All rights reserved. * SPDX-License-Identifier: MIT ******************************************************************************/ /*****************************************************************************/ /** * * @file image_mover.c * * Move partitions to either DDR to execute or to program FPGA. * It performs partition walk. * *
* MODIFICATION HISTORY:
*
* Ver	Who	Date		Changes
* ----- ---- -------- -------------------------------------------------------
* 1.00a jz	05/24/11	Initial release
* 2.00a jz	06/30/11	Updated partition header defs for 64-byte
*			 			alignment change in data2mem tool
* 2.00a mb	05/25/12	Updated for standalone based bsp FSBL
* 			 			Nand/SD encryption and review comments
* 3.00a np	08/30/12	Added FSBL user hook calls
* 						(before and after bitstream download.)
* 4.00a sgd	02/28/13	Fix for CR#691148 Secure bootmode error in devcfg test
*						Fix for CR#695578 FSBL failed to load standalone 
*						application in secure bootmode
*
* 4.00a sgd	04/23/13	Fix for CR#710128 FSBL failed to load standalone 
*						application in secure bootmode
* 5.00a kc	07/30/13	Fix for CR#724165 Partition Header used by FSBL 
*						is not authenticated
* 						Fix for CR#724166 FSBL doesn�t use PPK authenticated 
*						by Boot ROM for authenticating the Partition images 
* 						Fix for CR#732062 FSBL fails to build if UART not 
*						available 
* 7.00a kc  10/30/13    Fix for CR#755245 FSBL does not load partition
*                       if eMMC has only one partition
* 8.00a kc  01/16/13    Fix for CR#767798  FSBL MD5 Checksum failure
* 						for encrypted images
*						Fix for CR#761895 FSBL should authenticate image
*						only if partition owner was not set to u-boot
* 9.00a kc  04/16/14    Fix for CR#785778  FSBL takes 8 seconds to 
* 						authenticate (RSA) a bitstream on zc706
* 10.00a kc 07/15/14	Fix for CR#804595 Zynq FSBL - Issues with
* 						fallback image offset handling using MD5
* 						Fix for PR#782309 Fallback support for AES
* 						encryption with E-Fuse - Enhancement
* 11.00a ka 10/12/18    Fix for CR#1006294 Zynq FSBL - Zynq FSBL does not check
* 						USE_AES_ONLY eFuse
* 12.0  vns 03/18/22    Fixed CR#1125470 to authenticate the parition header buffer
*                       which is being used instead of one from DDR.
*                       Deleted GetImageHeaderAndSignature() and added
*                       GetNAuthImageHeader()
*
* 
* * @note * A partition is either an executable or a bitstream to program FPGA * ******************************************************************************/ /***************************** Include Files *********************************/ #include "fsbl.h" #include "image_mover.h" #include "xil_printf.h" #include "xreg_cortexa9.h" #include "pcap.h" #include "fsbl_hooks.h" #include "md5.h" #ifdef XPAR_XWDTPS_0_BASEADDR #include "xwdtps.h" #endif #ifdef RSA_SUPPORT #include "rsa.h" #include "xil_cache.h" #include "xilrsa.h" #endif /************************** Constant Definitions *****************************/ /* We are 32-bit machine */ #define MAXIMUM_IMAGE_WORD_LEN 0x40000000 #define MD5_CHECKSUM_SIZE 16 /**************************** Type Definitions *******************************/ /***************** Macros (Inline Functions) Definitions *********************/ /************************** Function Prototypes ******************************/ u32 ValidateParition(u32 StartAddr, u32 Length, u32 ChecksumOffset); u32 GetPartitionChecksum(u32 ChecksumOffset, u8 *Checksum); u32 CalcPartitionChecksum(u32 SourceAddr, u32 DataLength, u8 *Checksum); /************************** Variable Definitions *****************************/ /* * Partition information flags */ u8 EncryptedPartitionFlag; u8 PLPartitionFlag; u8 PSPartitionFlag; u8 SignedPartitionFlag; u8 PartitionChecksumFlag; u8 BitstreamFlag; u8 ApplicationFlag; u32 ExecutionAddress; ImageMoverType MoveImage; /* * Header array */ PartHeader PartitionHeader[MAX_PARTITION_NUMBER]; u32 PartitionCount; u32 FsblLength; #ifdef XPAR_XWDTPS_0_BASEADDR extern XWdtPs Watchdog; /* Instance of WatchDog Timer */ #endif extern u32 Silicon_Version; extern u32 FlashReadBaseAddress; extern u8 LinearBootDeviceFlag; extern XDcfg *DcfgInstPtr; /*****************************************************************************/ /** * * This function * * @param * * @return * * * @note None * ****************************************************************************/ u32 LoadBootImage(void) { u32 RebootStatusRegister = 0; u32 MultiBootReg = 0; u32 ImageStartAddress = 0; u32 PartitionNum; u32 PartitionDataLength; u32 PartitionImageLength; u32 PartitionTotalSize; u32 PartitionExecAddr; u32 PartitionAttr; u32 ExecAddress = 0; u32 PartitionLoadAddr; u32 PartitionStartAddr; u32 PartitionChecksumOffset; u8 ExecAddrFlag = 0 ; u32 Status; PartHeader *HeaderPtr; u32 EfuseStatusRegValue; #ifdef RSA_SUPPORT u8 Hash[SHA_VALBYTES]; u8 *Ac; #endif #ifndef FORCE_USE_AES_EXCLUDE u32 EncOnly; #endif /* * Resetting the Flags */ BitstreamFlag = 0; ApplicationFlag = 0; RebootStatusRegister = Xil_In32(REBOOT_STATUS_REG); fsbl_printf(DEBUG_INFO, "Reboot status register: 0x%08lx\r\n",RebootStatusRegister); if (Silicon_Version == SILICON_VERSION_1) { /* * Clear out fallback mask from previous run * We start from the first partition again */ if ((RebootStatusRegister & FSBL_FAIL_MASK) == FSBL_FAIL_MASK) { fsbl_printf(DEBUG_INFO, "Reboot status shows previous run falls back\r\n"); RebootStatusRegister &= ~(FSBL_FAIL_MASK); Xil_Out32(REBOOT_STATUS_REG, RebootStatusRegister); } /* * Read the image start address */ ImageStartAddress = *(u32 *)BASEADDR_HOLDER; } else { /* * read the multiboot register */ MultiBootReg = XDcfg_ReadReg(DcfgInstPtr->Config.BaseAddr, XDCFG_MULTIBOOT_ADDR_OFFSET); fsbl_printf(DEBUG_INFO,"Multiboot Register: 0x%08lx\r\n",MultiBootReg); /* * Compute the image start address */ ImageStartAddress = (MultiBootReg & PCAP_MBOOT_REG_REBOOT_OFFSET_MASK) * GOLDEN_IMAGE_OFFSET; } fsbl_printf(DEBUG_INFO,"Image Start Address: 0x%08lx\r\n",ImageStartAddress); /* * Get partitions header information */ Status = GetPartitionHeaderInfo(ImageStartAddress); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL, "Partition Header Load Failed\r\n"); OutputStatus(GET_HEADER_INFO_FAIL); FsblFallback(); } /* * RSA is not implemented in 1.0 and 2.0 * silicon */ if ((Silicon_Version != SILICON_VERSION_1) && (Silicon_Version != SILICON_VERSION_2)) { /* * Read Efuse Status Register */ EfuseStatusRegValue = Xil_In32(EFUSE_STATUS_REG); if (EfuseStatusRegValue & EFUSE_STATUS_RSA_ENABLE_MASK) { fsbl_printf(DEBUG_GENERAL,"RSA enabled for Chip\r\n"); #ifdef RSA_SUPPORT /* * Set the Ppk */ SetPpk(); /* * Read image header table, image headers and partition header * with signature */ Status = GetNAuthImageHeader(ImageStartAddress); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL, "Header signature verification Failed\r\n"); OutputStatus(GET_HEADER_INFO_FAIL); FsblFallback(); } fsbl_printf(DEBUG_GENERAL, "Header authentication is Success\r\n"); #else /* * In case user not enabled RSA authentication feature */ fsbl_printf(DEBUG_GENERAL,"RSA_SUPPORT_NOT_ENABLED_FAIL\r\n"); OutputStatus(RSA_SUPPORT_NOT_ENABLED_FAIL); FsblFallback(); #endif } } #ifdef MMC_SUPPORT /* * In case of MMC support * boot image preset in MMC will not have FSBL partition */ PartitionNum = 0; #else /* * First partition header was ignored by FSBL * As it contain FSBL partition information */ PartitionNum = 1; #endif while (PartitionNum < PartitionCount) { fsbl_printf(DEBUG_INFO, "Partition Number: %lu\r\n", PartitionNum); HeaderPtr = &PartitionHeader[PartitionNum]; /* * Print partition header information */ HeaderDump(HeaderPtr); /* * Validate partition header */ Status = ValidateHeader(HeaderPtr); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL, "INVALID_HEADER_FAIL\r\n"); OutputStatus(INVALID_HEADER_FAIL); FsblFallback(); } /* * Load partition header information in to local variables */ PartitionDataLength = HeaderPtr->DataWordLen; PartitionImageLength = HeaderPtr->ImageWordLen; PartitionExecAddr = HeaderPtr->ExecAddr; PartitionAttr = HeaderPtr->PartitionAttr; PartitionLoadAddr = HeaderPtr->LoadAddr; PartitionChecksumOffset = HeaderPtr->CheckSumOffset; PartitionStartAddr = HeaderPtr->PartitionStart; PartitionTotalSize = HeaderPtr->PartitionWordLen; /* * Partition owner should be FSBL to validate the partition */ if ((PartitionAttr & ATTRIBUTE_PARTITION_OWNER_MASK) != ATTRIBUTE_PARTITION_OWNER_FSBL) { /* * if FSBL is not the owner of partition, * skip this partition, continue with next partition */ fsbl_printf(DEBUG_INFO, "Skipping partition %0lx\r\n", PartitionNum); /* * Increment partition number */ PartitionNum++; continue; } if (PartitionAttr & ATTRIBUTE_PL_IMAGE_MASK) { fsbl_printf(DEBUG_INFO, "Bitstream\r\n"); PLPartitionFlag = 1; PSPartitionFlag = 0; BitstreamFlag = 1; if (ApplicationFlag == 1) { #ifdef STDOUT_BASEADDRESS xil_printf("\r\nFSBL Warning !!!" "Bitstream not loaded into PL\r\n"); xil_printf("Partition order invalid\r\n"); #endif break; } } if (PartitionAttr & ATTRIBUTE_PS_IMAGE_MASK) { fsbl_printf(DEBUG_INFO, "Application\r\n"); PSPartitionFlag = 1; PLPartitionFlag = 0; ApplicationFlag = 1; } /* * Encrypted partition will have different value * for Image length and data length */ if (PartitionDataLength != PartitionImageLength) { fsbl_printf(DEBUG_INFO, "Encrypted\r\n"); EncryptedPartitionFlag = 1; } else { EncryptedPartitionFlag = 0; } #ifndef FORCE_USE_AES_EXCLUDE EncOnly = XDcfg_ReadReg(DcfgInstPtr->Config.BaseAddr, XDCFG_STATUS_OFFSET) & XDCFG_STATUS_EFUSE_SEC_EN_MASK; if ((EncOnly != 0) && (EncryptedPartitionFlag == 0)) { fsbl_printf(DEBUG_GENERAL,"EFUSE_SEC_EN bit is set," " Encryption is mandatory\r\n"); OutputStatus(PARTITION_LOAD_FAIL); FsblFallback(); } #endif /* * Check for partition checksum check */ if (PartitionAttr & ATTRIBUTE_CHECKSUM_TYPE_MASK) { PartitionChecksumFlag = 1; } else { PartitionChecksumFlag = 0; } /* * RSA signature check */ if (PartitionAttr & ATTRIBUTE_RSA_PRESENT_MASK) { fsbl_printf(DEBUG_INFO, "RSA Signed\r\n"); SignedPartitionFlag = 1; } else { SignedPartitionFlag = 0; } /* * Load address check * Loop will break when PS load address zero and partition is * un-signed or un-encrypted */ if ((PSPartitionFlag == 1) && (PartitionLoadAddr < DDR_START_ADDR)) { if ((PartitionLoadAddr == 0) && (!((SignedPartitionFlag == 1) || (EncryptedPartitionFlag == 1)))) { break; } else { fsbl_printf(DEBUG_GENERAL, "INVALID_LOAD_ADDRESS_FAIL\r\n"); OutputStatus(INVALID_LOAD_ADDRESS_FAIL); FsblFallback(); } } if (PSPartitionFlag && (PartitionLoadAddr > DDR_END_ADDR)) { fsbl_printf(DEBUG_GENERAL, "INVALID_LOAD_ADDRESS_FAIL\r\n"); OutputStatus(INVALID_LOAD_ADDRESS_FAIL); FsblFallback(); } /* * Load execution address of first PS partition */ if (PSPartitionFlag && (!ExecAddrFlag)) { ExecAddrFlag++; ExecAddress = PartitionExecAddr; } /* * FSBL user hook call before bitstream download */ if (PLPartitionFlag) { Status = FsblHookBeforeBitstreamDload(); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL,"FSBL_BEFORE_BSTREAM_HOOK_FAIL\r\n"); OutputStatus(FSBL_BEFORE_BSTREAM_HOOK_FAIL); FsblFallback(); } } /* * Move partitions from boot device */ Status = PartitionMove(ImageStartAddress, HeaderPtr); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL,"PARTITION_MOVE_FAIL\r\n"); OutputStatus(PARTITION_MOVE_FAIL); FsblFallback(); } if ((SignedPartitionFlag) || (PartitionChecksumFlag)) { if(PLPartitionFlag) { /* * PL partition loaded in to DDR temporary address * for authentication and checksum verification */ PartitionStartAddr = DDR_TEMP_START_ADDR; } else { PartitionStartAddr = PartitionLoadAddr; } if (PartitionChecksumFlag) { /* * Validate the partition data with checksum */ Status = ValidateParition(PartitionStartAddr, (PartitionTotalSize << WORD_LENGTH_SHIFT), ImageStartAddress + (PartitionChecksumOffset << WORD_LENGTH_SHIFT)); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL,"PARTITION_CHECKSUM_FAIL\r\n"); OutputStatus(PARTITION_CHECKSUM_FAIL); FsblFallback(); } fsbl_printf(DEBUG_INFO, "Partition Validation Done\r\n"); } /* * Authentication Partition */ if (SignedPartitionFlag == 1 ) { #ifdef RSA_SUPPORT Xil_DCacheEnable(); sha_256((u8 *)PartitionStartAddr, ((PartitionTotalSize << WORD_LENGTH_SHIFT) - RSA_PARTITION_SIGNATURE_SIZE), Hash); FsblPrintArray(Hash, 32, "Partition Hash Calculated"); Ac = (u8 *)(PartitionStartAddr + (PartitionTotalSize << WORD_LENGTH_SHIFT) - RSA_SIGNATURE_SIZE); Status = AuthenticatePartition((u8*)Ac, Hash); if (Status != XST_SUCCESS) { Xil_DCacheFlush(); Xil_DCacheDisable(); fsbl_printf(DEBUG_GENERAL,"AUTHENTICATION_FAIL\r\n"); OutputStatus(AUTHENTICATION_FAIL); FsblFallback(); } fsbl_printf(DEBUG_INFO,"Authentication Done\r\n"); Xil_DCacheFlush(); Xil_DCacheDisable(); #else /* * In case user not enabled RSA authentication feature */ fsbl_printf(DEBUG_GENERAL,"RSA_SUPPORT_NOT_ENABLED_FAIL\r\n"); OutputStatus(RSA_SUPPORT_NOT_ENABLED_FAIL); FsblFallback(); #endif } /* * Decrypt PS partition */ if (EncryptedPartitionFlag && PSPartitionFlag) { Status = DecryptPartition(PartitionStartAddr, PartitionDataLength, PartitionImageLength); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL,"DECRYPTION_FAIL\r\n"); OutputStatus(DECRYPTION_FAIL); FsblFallback(); } } /* * Load Signed PL partition in Fabric */ if (PLPartitionFlag) { Status = PcapLoadPartition((u32*)PartitionStartAddr, (u32*)PartitionLoadAddr, PartitionImageLength, PartitionDataLength, EncryptedPartitionFlag); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL,"BITSTREAM_DOWNLOAD_FAIL\r\n"); OutputStatus(BITSTREAM_DOWNLOAD_FAIL); FsblFallback(); } } } /* * FSBL user hook call after bitstream download */ if (PLPartitionFlag) { Status = FsblHookAfterBitstreamDload(); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL,"FSBL_AFTER_BSTREAM_HOOK_FAIL\r\n"); OutputStatus(FSBL_AFTER_BSTREAM_HOOK_FAIL); FsblFallback(); } } /* * Increment partition number */ PartitionNum++; } return ExecAddress; } /*****************************************************************************/ /** * * This function loads all partition header information in global array * * @param ImageAddress is the start address of the image * * @return - XST_SUCCESS if Get partition Header information successful * - XST_FAILURE if Get Partition Header information failed * * @note None * ****************************************************************************/ u32 GetPartitionHeaderInfo(u32 ImageBaseAddress) { u32 PartitionHeaderOffset; u32 Status; /* * Get the length of the FSBL from BootHeader */ Status = GetFsblLength(ImageBaseAddress, &FsblLength); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL, "Get Header Start Address Failed\r\n"); return XST_FAILURE; } /* * Get the start address of the partition header table */ Status = GetPartitionHeaderStartAddr(ImageBaseAddress, &PartitionHeaderOffset); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL, "Get Header Start Address Failed\r\n"); return XST_FAILURE; } /* * Header offset on flash */ PartitionHeaderOffset += ImageBaseAddress; fsbl_printf(DEBUG_INFO,"Partition Header Offset:0x%08lx\r\n", PartitionHeaderOffset); /* * Load all partitions header data in to global variable */ Status = LoadPartitionsHeaderInfo(PartitionHeaderOffset, &PartitionHeader[0]); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL, "Header Information Load Failed\r\n"); return XST_FAILURE; } /* * Get partitions count from partitions header information */ PartitionCount = GetPartitionCount(&PartitionHeader[0]); fsbl_printf(DEBUG_INFO, "Partition Count: %lu\r\n", PartitionCount); /* * Partition Count check */ if (PartitionCount >= MAX_PARTITION_NUMBER) { fsbl_printf(DEBUG_GENERAL, "Invalid Partition Count\r\n"); return XST_FAILURE; #ifndef MMC_SUPPORT } else if (PartitionCount <= 1) { fsbl_printf(DEBUG_GENERAL, "There is no partition to load\r\n"); return XST_FAILURE; #endif } return XST_SUCCESS; } /*****************************************************************************/ /** * * This function goes to the partition header of the specified partition * * @param ImageAddress is the start address of the image * * @return Offset Partition header address of the image * * @return - XST_SUCCESS if Get Partition Header start address successful * - XST_FAILURE if Get Partition Header start address failed * * @note None * ****************************************************************************/ u32 GetPartitionHeaderStartAddr(u32 ImageAddress, u32 *Offset) { u32 Status; Status = MoveImage(ImageAddress + IMAGE_PHDR_OFFSET, (u32)Offset, 4); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL,"Move Image failed\r\n"); return XST_FAILURE; } return XST_SUCCESS; } /*****************************************************************************/ /** * * This function goes to the partition header of the specified partition * * @param ImageAddress is the start address of the image * * @return Offset to Image header table address of the image * * @return - XST_SUCCESS if Get Partition Header start address successful * - XST_FAILURE if Get Partition Header start address failed * * @note None * ****************************************************************************/ u32 GetImageHeaderStartAddr(u32 ImageAddress, u32 *Offset) { u32 Status; Status = MoveImage(ImageAddress + IMAGE_HDR_OFFSET, (u32)Offset, 4); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL,"Move Image failed\r\n"); return XST_FAILURE; } return XST_SUCCESS; } /*****************************************************************************/ /** * * This function gets the length of the FSBL * * @param ImageAddress is the start address of the image * * @return FsblLength is the length of the fsbl * * @return - XST_SUCCESS if fsbl length reading is successful * - XST_FAILURE if fsbl length reading failed * * @note None * ****************************************************************************/ u32 GetFsblLength(u32 ImageAddress, u32 *FsblLength) { u32 Status; Status = MoveImage(ImageAddress + IMAGE_TOT_BYTE_LEN_OFFSET, (u32)FsblLength, 4); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL,"Move Image failed reading FsblLength\r\n"); return XST_FAILURE; } return XST_SUCCESS; } #ifdef RSA_SUPPORT /*****************************************************************************/ /** * * This function goes to read the image headers and its signature and authenticates. * the image header Image header consists of image header table, image headers, * partition headers * * @param ImageBaseAddress is the start address of the image header * * @return - XST_SUCCESS if image header authentication is successful * - XST_FAILURE if image header authentication is failed * * @note None * ****************************************************************************/ u32 GetNAuthImageHeader(u32 ImageBaseAddress) { u32 Status; u32 Offset; u8 *HdrTmpPtr = (u8 *) DDR_TEMP_START_ADDR; u32 Size; u8 *Ac; u8 Hash[SHA_VALBYTES]; sha2_context Sha2Instance; /* * Get the start address of the image header table */ Status = GetImageHeaderStartAddr(ImageBaseAddress, &Offset); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL, "Get Header Start Address Failed\r\n"); return XST_FAILURE; } Size = IMAGE_HEADER_TABLE_SIZE + TOTAL_IMAGE_HEADER_SIZE; /* Read image header table and all image headers */ Status = MoveImage(ImageBaseAddress + Offset, (u32)HdrTmpPtr, Size); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL,"Move IHT and IHs failed\r\n"); return XST_FAILURE; } /* Update SHA */ sha2_starts(&Sha2Instance); sha2_update(&Sha2Instance, (u8 *)HdrTmpPtr, Size); sha2_update(&Sha2Instance, (u8 *)&PartitionHeader[0], TOTAL_PARTITION_HEADER_SIZE); /* * Get the start address of the partition header table */ Status = GetPartitionHeaderStartAddr(ImageBaseAddress, &Offset); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL, "Get Header Start Address Failed\r\n"); return XST_FAILURE; } Offset = Offset + TOTAL_PARTITION_HEADER_SIZE; Size = TOTAL_HEADER_SIZE + RSA_SIGNATURE_SIZE - (Size + TOTAL_PARTITION_HEADER_SIZE); /* Read RSA signature */ Status = MoveImage(ImageBaseAddress + Offset, (u32)HdrTmpPtr, Size); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL,"Move image header signature is failed\r\n"); return XST_FAILURE; } sha2_update(&Sha2Instance, (u8 *)(HdrTmpPtr), Size - RSA_PARTITION_SIGNATURE_SIZE); sha2_finish(&Sha2Instance, Hash); FsblPrintArray(Hash, 32,"Header Hash Calculated"); /* Authentication of image header */ Ac = (u8 *)(HdrTmpPtr + 64); Status = AuthenticatePartition((u8 *)Ac, Hash); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL,"Image header authentication is failed\r\n"); return XST_FAILURE; } return XST_SUCCESS; } #endif /*****************************************************************************/ /** * * This function get the header information of the all the partitions and load into * global array * * @param PartHeaderOffset Offset address where the header information present * * @param Header Partition header pointer * * @return - XST_SUCCESS if Load Partitions Header information successful * - XST_FAILURE if Load Partitions Header information failed * * @note None * ****************************************************************************/ u32 LoadPartitionsHeaderInfo(u32 PartHeaderOffset, PartHeader *Header) { u32 Status; Status = MoveImage(PartHeaderOffset, (u32)Header, sizeof(PartHeader)*MAX_PARTITION_NUMBER); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL,"Move Image failed\r\n"); return XST_FAILURE; } return XST_SUCCESS; } /*****************************************************************************/ /** * * This function dumps the partition header. * * @param Header Partition header pointer * * @return None * * @note None * ******************************************************************************/ void HeaderDump(PartHeader *Header) { fsbl_printf(DEBUG_INFO, "Header Dump\r\n"); fsbl_printf(DEBUG_INFO, "Image Word Len: 0x%08lx\r\n", Header->ImageWordLen); fsbl_printf(DEBUG_INFO, "Data Word Len: 0x%08lx\r\n", Header->DataWordLen); fsbl_printf(DEBUG_INFO, "Partition Word Len:0x%08lx\r\n", Header->PartitionWordLen); fsbl_printf(DEBUG_INFO, "Load Addr: 0x%08lx\r\n", Header->LoadAddr); fsbl_printf(DEBUG_INFO, "Exec Addr: 0x%08lx\r\n", Header->ExecAddr); fsbl_printf(DEBUG_INFO, "Partition Start: 0x%08lx\r\n", Header->PartitionStart); fsbl_printf(DEBUG_INFO, "Partition Attr: 0x%08lx\r\n", Header->PartitionAttr); fsbl_printf(DEBUG_INFO, "Partition Checksum Offset: 0x%08lx\r\n", Header->CheckSumOffset); fsbl_printf(DEBUG_INFO, "Section Count: 0x%08lx\r\n", Header->SectionCount); fsbl_printf(DEBUG_INFO, "Checksum: 0x%08lx\r\n", Header->CheckSum); } /******************************************************************************/ /** * * This function calculates the partitions count from header information * * @param Header Partition header pointer * * @return Count Partition count * * @note None * *******************************************************************************/ u32 GetPartitionCount(PartHeader *Header) { u32 Count=0; struct HeaderArray *Hap; for(Count = 0; Count < MAX_PARTITION_NUMBER; Count++) { Hap = (struct HeaderArray *)&Header[Count]; if(IsLastPartition(Hap)!=XST_FAILURE) break; } return Count; } /******************************************************************************/ /** * This function check whether the current partition is the end of partitions * * The partition is the end of the partitions if it looks like this: * 0x00000000 * 0x00000000 * .... * 0x00000000 * 0x00000000 * 0xFFFFFFFF * * @param H is a pointer to struct HeaderArray * * @return * - XST_SUCCESS if it is the last partition * - XST_FAILURE if it is not last partition * ****************************************************************************/ u32 IsLastPartition(struct HeaderArray *H) { int Index; if (H->Fields[PARTITION_HDR_CHECKSUM_WORD_COUNT] != 0xFFFFFFFF) { return XST_FAILURE; } for (Index = 0; Index < PARTITION_HDR_WORD_COUNT - 1; Index++) { if (H->Fields[Index] != 0x0) { return XST_FAILURE; } } return XST_SUCCESS; } /******************************************************************************/ /** * * This function validates the partition header. * * @param Header Partition header pointer * * @return * - XST_FAILURE if bad header. * - XST_SUCCESS if successful. * * @note None * *******************************************************************************/ u32 ValidateHeader(PartHeader *Header) { struct HeaderArray *Hap; Hap = (struct HeaderArray *)Header; /* * If there are no partitions to load, fail */ if (IsEmptyHeader(Hap) == XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL, "IMAGE_HAS_NO_PARTITIONS\r\n"); return XST_FAILURE; } /* * Validate partition header checksum */ if (ValidatePartitionHeaderChecksum(Hap) != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL, "PARTITION_HEADER_CORRUPTION\r\n"); return XST_FAILURE; } /* * Validate partition data size */ if (Header->ImageWordLen > MAXIMUM_IMAGE_WORD_LEN) { fsbl_printf(DEBUG_GENERAL, "INVALID_PARTITION_LENGTH\r\n"); return XST_FAILURE; } return XST_SUCCESS; } /******************************************************************************/ /** * This function check whether the current partition header is empty. * A partition header is considered empty if image word length is 0 and the * last word is 0. * * @param H is a pointer to struct HeaderArray * * @return * - XST_SUCCESS , If the partition header is empty * - XST_FAILURE , If the partition header is NOT empty * * @note Caller is responsible to make sure the address is valid. * * ****************************************************************************/ u32 IsEmptyHeader(struct HeaderArray *H) { int Index; for (Index = 0; Index < PARTITION_HDR_WORD_COUNT; Index++) { if (H->Fields[Index] != 0x0) { return XST_FAILURE; } } return XST_SUCCESS; } /******************************************************************************/ /** * * This function checks the header checksum If the header checksum is not valid * XST_FAILURE is returned. * * @param H is a pointer to struct HeaderArray * * @return * - XST_SUCCESS is header checksum is ok * - XST_FAILURE if the header checksum is not correct * * @note None. * ****************************************************************************/ u32 ValidatePartitionHeaderChecksum(struct HeaderArray *H) { u32 Checksum; u32 Count; Checksum = 0; for (Count = 0; Count < PARTITION_HDR_CHECKSUM_WORD_COUNT; Count++) { /* * Read the word from the header */ Checksum += H->Fields[Count]; } /* * Invert checksum, last bit of error checking */ Checksum ^= 0xFFFFFFFF; /* * Validate the checksum */ if (H->Fields[PARTITION_HDR_CHECKSUM_WORD_COUNT] != Checksum) { fsbl_printf(DEBUG_GENERAL, "Error: Checksum 0x%8.8lx != 0x%8.8lx\r\n", Checksum, H->Fields[PARTITION_HDR_CHECKSUM_WORD_COUNT]); return XST_FAILURE; } return XST_SUCCESS; } /******************************************************************************/ /** * * This function load the partition from boot device * * @param ImageBaseAddress Base address on flash * @param Header Partition header pointer * * @return * - XST_SUCCESS if partition move successful * - XST_FAILURE if check failed move failed * * @note None * *******************************************************************************/ u32 PartitionMove(u32 ImageBaseAddress, PartHeader *Header) { u32 SourceAddr; u32 Status; u8 SecureTransferFlag = 0; u32 LoadAddr; u32 ImageWordLen; u32 DataWordLen; SourceAddr = ImageBaseAddress; SourceAddr += Header->PartitionStart<LoadAddr; ImageWordLen = Header->ImageWordLen; DataWordLen = Header->DataWordLen; /* * Add flash base address for linear boot devices */ if (LinearBootDeviceFlag) { SourceAddr += FlashReadBaseAddress; } /* * Partition encrypted */ if(EncryptedPartitionFlag) { SecureTransferFlag = 1; } /* * For Signed or checksum enabled partition, * Total partition image need to copied to DDR */ if (SignedPartitionFlag || PartitionChecksumFlag) { ImageWordLen = Header->PartitionWordLen; DataWordLen = Header->PartitionWordLen; } /* * Encrypted and Signed PS partition need to be loaded on to DDR * without decryption */ if (PSPartitionFlag && (SignedPartitionFlag || PartitionChecksumFlag) && EncryptedPartitionFlag) { SecureTransferFlag = 0; } /* * CPU is used for data transfer in case of non-linear * boot device */ if (!LinearBootDeviceFlag) { /* * PL partition copied to DDR temporary location */ if (PLPartitionFlag) { LoadAddr = DDR_TEMP_START_ADDR; } Status = MoveImage(SourceAddr, LoadAddr, (ImageWordLen << WORD_LENGTH_SHIFT)); if(Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL, "Move Image Failed\r\n"); return XST_FAILURE; } /* * As image present at load address */ SourceAddr = LoadAddr; } if ((LinearBootDeviceFlag && PLPartitionFlag && (SignedPartitionFlag || PartitionChecksumFlag)) || (LinearBootDeviceFlag && PSPartitionFlag) || ((!LinearBootDeviceFlag) && PSPartitionFlag && SecureTransferFlag)) { /* * PL signed partition copied to DDR temporary location * using non-secure PCAP for linear boot device */ if(PLPartitionFlag){ SecureTransferFlag = 0; LoadAddr = DDR_TEMP_START_ADDR; } /* * Data transfer using PCAP */ Status = PcapDataTransfer((u32*)SourceAddr, (u32*)LoadAddr, ImageWordLen, DataWordLen, SecureTransferFlag); if(Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL, "PCAP Data Transfer Failed\r\n"); return XST_FAILURE; } /* * As image present at load address */ SourceAddr = LoadAddr; } /* * Load Bitstream partition in to fabric only * if checksum and authentication bits are not set */ if (PLPartitionFlag && (!(SignedPartitionFlag || PartitionChecksumFlag))) { Status = PcapLoadPartition((u32*)SourceAddr, (u32*)Header->LoadAddr, Header->ImageWordLen, Header->DataWordLen, EncryptedPartitionFlag); if(Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL, "PCAP Bitstream Download Failed\r\n"); return XST_FAILURE; } } return XST_SUCCESS; } /******************************************************************************/ /** * * This function load the decrypts partition * * @param StartAddr Source start address * @param DataLength Data length in words * @param ImageLength Image length in words * * @return * - XST_SUCCESS if decryption successful * - XST_FAILURE if decryption failed * * @note None * *******************************************************************************/ u32 DecryptPartition(u32 StartAddr, u32 DataLength, u32 ImageLength) { u32 Status; u8 SecureTransferFlag =1; /* * Data transfer using PCAP */ Status = PcapDataTransfer((u32*)StartAddr, (u32*)StartAddr, ImageLength, DataLength, SecureTransferFlag); if (Status != XST_SUCCESS) { fsbl_printf(DEBUG_GENERAL,"PCAP Data Transfer failed \r\n"); return XST_FAILURE; } return XST_SUCCESS; } /******************************************************************************/ /** * * This function Validate Partition Data by using checksum preset in image * * @param Partition header pointer * @param Partition check sum offset * @return * - XST_SUCCESS if partition data is ok * - XST_FAILURE if partition data is corrupted * * @note None * *******************************************************************************/ u32 ValidateParition(u32 StartAddr, u32 Length, u32 ChecksumOffset) { u8 Checksum[MD5_CHECKSUM_SIZE]; u8 CalcChecksum[MD5_CHECKSUM_SIZE]; u32 Status; u32 Index; #ifdef XPAR_XWDTPS_0_BASEADDR /* * Prevent WDT reset */ XWdtPs_RestartWdt(&Watchdog); #endif /* * Get checksum from flash */ Status = GetPartitionChecksum(ChecksumOffset, &Checksum[0]); if(Status != XST_SUCCESS) { return XST_FAILURE; } fsbl_printf(DEBUG_INFO, "Actual checksum\r\n"); for (Index = 0; Index < MD5_CHECKSUM_SIZE; Index++) { fsbl_printf(DEBUG_INFO, "0x%0x ",Checksum[Index]); } fsbl_printf(DEBUG_INFO, "\r\n"); /* * Calculate checksum for the partition */ Status = CalcPartitionChecksum(StartAddr, Length, &CalcChecksum[0]); if(Status != XST_SUCCESS) { return XST_FAILURE; } fsbl_printf(DEBUG_INFO, "Calculated checksum\r\n"); for (Index = 0; Index < MD5_CHECKSUM_SIZE; Index++) { fsbl_printf(DEBUG_INFO, "0x%0x ",CalcChecksum[Index]); } fsbl_printf(DEBUG_INFO, "\r\n"); /* * Compare actual checksum with the calculated checksum */ for (Index = 0; Index < MD5_CHECKSUM_SIZE; Index++) { if(Checksum[Index] != CalcChecksum[Index]) { fsbl_printf(DEBUG_GENERAL, "Error: " "Partition DataChecksum 0x%0x!= 0x%0x\r\n", Checksum[Index], CalcChecksum[Index]); return XST_FAILURE; } } return XST_SUCCESS; } /******************************************************************************/ /** * * This function gets partition checksum from flash * * @param Check sum offset * @param Checksum pointer * @return * - XST_SUCCESS if checksum read success * - XST_FAILURE if unable get checksum * * @note None * *******************************************************************************/ u32 GetPartitionChecksum(u32 ChecksumOffset, u8 *Checksum) { u32 Status; Status = MoveImage(ChecksumOffset, (u32)Checksum, MD5_CHECKSUM_SIZE); if(Status != XST_SUCCESS) { return XST_FAILURE; } return XST_SUCCESS; } /******************************************************************************/ /** * * This function calculates the checksum preset in image * * @param Start address * @param Length of the data * @param Checksum pointer * * @return * - XST_SUCCESS if Checksum calculate successful * - XST_FAILURE if Checksum calculate failed * * @note None * *******************************************************************************/ u32 CalcPartitionChecksum(u32 SourceAddr, u32 DataLength, u8 *Checksum) { /* * Calculate checksum using MD5 algorithm */ md5((u8*)SourceAddr, DataLength, Checksum, 0 ); return XST_SUCCESS; }