#!/usr/bin/env python2 #=============================================================================== # # MBN TOOLS # # GENERAL DESCRIPTION # Contains all MBN Utilities for image generation # # Copyright (c) 2016, 2018, The Linux Foundation. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # * Neither the name of The Linux Foundation nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # #------------------------------------------------------------------------------- # EDIT HISTORY FOR FILE # # This section contains comments describing changes made to the module. # Notice that changes are listed in reverse chronological order. # # when who what, where, why # -------- --- --------------------------------------------------------- # 05/21/18 rissha Added support for extended MBNV6 and Add support for hashing elf segments with SHA384 # 03/22/18 thiru Added support for extended MBNV5. # 06/06/13 yliong CR 497042: Signed and encrypted image is corrupted. MRC features. # 03/18/13 dhaval Add support for hashing elf segments with SHA256 and # sync up to mpss, adsp mbn-tools # 01/14/13 kedara Remove dependency on .builds, cust.h, targ.h files # 08/30/12 kedara Add virtual block suppport # 02/24/12 dh Add ssd side effect file names # 07/08/11 aus Added support for image_id in SBL image header as required by PBL # Sahara mode # 10/20/11 dxiang Clean up #=============================================================================== import stat import csv import itertools import struct import os import shutil import hashlib #---------------------------------------------------------------------------- # GLOBAL VARIABLES BEGIN #---------------------------------------------------------------------------- PAD_BYTE_1 = 255 # Padding byte 1s PAD_BYTE_0 = 0 # Padding byte 0s SHA256_SIGNATURE_SIZE = 256 # Support SHA256 MAX_NUM_ROOT_CERTS = 4 # Maximum number of OEM root certificates MI_BOOT_SBL_HDR_SIZE = 80 # sizeof(sbl_header) BOOT_HEADER_LENGTH = 20 # Boot Header Number of Elements SBL_HEADER_LENGTH = 20 # SBL Header Number of Elements MAX_PHDR_COUNT = 100 # Maximum allowable program headers CERT_CHAIN_ONEROOT_MAXSIZE = 6*1024 # Default Cert Chain Max Size for one root VIRTUAL_BLOCK_SIZE = 131072 # Virtual block size for MCs insertion in SBL1 if ENABLE_VIRTUAL_BLK ON MAGIC_COOKIE_LENGTH = 12 # Length of magic Cookie inserted per VIRTUAL_BLOCK_SIZE MIN_IMAGE_SIZE_WITH_PAD = 256*1024 # Minimum image size for sbl1 Nand based OTA feature SBL_AARCH64 = 0xF # Indicate that SBL is a Aarch64 image SBL_AARCH32 = 0x0 # Indicate that SBL is a Aarch32 image # Magic numbers filled in for boot headers FLASH_CODE_WORD = 0x844BDCD1 UNIFIED_BOOT_COOKIE_MAGIC_NUMBER = 0x33836685 MAGIC_NUM = 0x73D71034 AUTODETECT_PAGE_SIZE_MAGIC_NUM = 0x7D0B435A AUTODETECT_PAGE_SIZE_MAGIC_NUM64 = 0x7D0B5436 AUTODETECT_PAGE_SIZE_MAGIC_NUM128 = 0x7D0B6577 SBL_VIRTUAL_BLOCK_MAGIC_NUM = 0xD48B54C6 # ELF Definitions ELF_HDR_COMMON_SIZE = 24 ELF32_HDR_SIZE = 52 ELF32_PHDR_SIZE = 32 ELF64_HDR_SIZE = 64 ELF64_PHDR_SIZE = 56 ELFINFO_MAG0_INDEX = 0 ELFINFO_MAG1_INDEX = 1 ELFINFO_MAG2_INDEX = 2 ELFINFO_MAG3_INDEX = 3 ELFINFO_MAG0 = '\x7f' ELFINFO_MAG1 = 'E' ELFINFO_MAG2 = 'L' ELFINFO_MAG3 = 'F' ELFINFO_CLASS_INDEX = 4 ELFINFO_CLASS_32 = '\x01' ELFINFO_CLASS_64 = '\x02' ELFINFO_VERSION_INDEX = 6 ELFINFO_VERSION_CURRENT = '\x01' ELF_BLOCK_ALIGN = 0x1000 ALIGNVALUE_1MB = 0x100000 ALIGNVALUE_4MB = 0x400000 ELFINFO_DATA2LSB = '\x01' ELFINFO_EXEC_ETYPE = '\x02\x00' ELFINFO_ARM_MACHINETYPE = '\x28\x00' ELFINFO_VERSION_EV_CURRENT = '\x01\x00\x00\x00' ELFINFO_SHOFF = 0x00 ELFINFO_PHNUM = '\x01\x00' ELFINFO_RESERVED = 0x00 # ELF Program Header Types NULL_TYPE = 0x0 LOAD_TYPE = 0x1 DYNAMIC_TYPE = 0x2 INTERP_TYPE = 0x3 NOTE_TYPE = 0x4 SHLIB_TYPE = 0x5 PHDR_TYPE = 0x6 TLS_TYPE = 0x7 """ The eight bits between 20 and 27 in the p_flags field in ELF program headers is not used by the standard ELF format. We use this byte to hold OS and processor specific fields as recommended by ARM. The bits in this byte are defined as follows: Pool Indx Segment type Access type Page/non page bits in p_flags /-----27-----/----26-24-------/---- 23-21----/------20-------/ After parsing segment description strings in the SCL file, the appropriate segment flag values are chosen from the follow definitions. The mask defined below is then used to update the existing p_flags field in the program headers with the updated values. """ # Mask for bits 20-27 to parse program header p_flags MI_PBT_FLAGS_MASK = 0x0FF00000 # Helper defines to help parse ELF program headers MI_PBT_FLAG_SEGMENT_TYPE_MASK = 0x07000000 MI_PBT_FLAG_SEGMENT_TYPE_SHIFT = 0x18 MI_PBT_FLAG_PAGE_MODE_MASK = 0x00100000 MI_PBT_FLAG_PAGE_MODE_SHIFT = 0x14 MI_PBT_FLAG_ACCESS_TYPE_MASK = 0x00E00000 MI_PBT_FLAG_ACCESS_TYPE_SHIFT = 0x15 MI_PBT_FLAG_POOL_INDEX_MASK = 0x08000000 MI_PBT_FLAG_POOL_INDEX_SHIFT = 0x1B # Segment Type MI_PBT_L4_SEGMENT = 0x0 MI_PBT_AMSS_SEGMENT = 0x1 MI_PBT_HASH_SEGMENT = 0x2 MI_PBT_BOOT_SEGMENT = 0x3 MI_PBT_L4BSP_SEGMENT = 0x4 MI_PBT_SWAPPED_SEGMENT = 0x5 MI_PBT_XBL_SEC_SEGMENT = 0x5 MI_PBT_SWAP_POOL_SEGMENT = 0x6 MI_PBT_PHDR_SEGMENT = 0x7 # Page/Non-Page Type MI_PBT_NON_PAGED_SEGMENT = 0x0 MI_PBT_PAGED_SEGMENT = 0x1 # Access Type MI_PBT_RW_SEGMENT = 0x0 MI_PBT_RO_SEGMENT = 0x1 MI_PBT_ZI_SEGMENT = 0x2 MI_PBT_NOTUSED_SEGMENT = 0x3 MI_PBT_SHARED_SEGMENT = 0x4 MI_PBT_RWE_SEGMENT = 0x7 # ELF Segment Flag Definitions MI_PBT_ELF_AMSS_NON_PAGED_RO_SEGMENT = 0x01200000 MI_PBT_ELF_AMSS_PAGED_RO_SEGMENT = 0x01300000 MI_PBT_ELF_SWAP_POOL_NON_PAGED_ZI_SEGMENT_INDEX0 = 0x06400000 MI_PBT_ELF_SWAPPED_PAGED_RO_SEGMENT_INDEX0 = 0x05300000 MI_PBT_ELF_SWAP_POOL_NON_PAGED_ZI_SEGMENT_INDEX1 = 0x0E400000 MI_PBT_ELF_SWAPPED_PAGED_RO_SEGMENT_INDEX1 = 0x0D300000 MI_PBT_ELF_AMSS_NON_PAGED_ZI_SEGMENT = 0x01400000 MI_PBT_ELF_AMSS_PAGED_ZI_SEGMENT = 0x01500000 MI_PBT_ELF_AMSS_NON_PAGED_RW_SEGMENT = 0x01000000 MI_PBT_ELF_AMSS_PAGED_RW_SEGMENT = 0x01100000 MI_PBT_ELF_AMSS_NON_PAGED_NOTUSED_SEGMENT = 0x01600000 MI_PBT_ELF_AMSS_PAGED_NOTUSED_SEGMENT = 0x01700000 MI_PBT_ELF_AMSS_NON_PAGED_SHARED_SEGMENT = 0x01800000 MI_PBT_ELF_AMSS_PAGED_SHARED_SEGMENT = 0x01900000 MI_PBT_ELF_HASH_SEGMENT = 0x02200000 MI_PBT_ELF_BOOT_SEGMENT = 0x03200000 MI_PBT_ELF_PHDR_SEGMENT = 0x07000000 MI_PBT_ELF_NON_PAGED_L4BSP_SEGMENT = 0x04000000 MI_PBT_ELF_PAGED_L4BSP_SEGMENT = 0x04100000 MI_PBT_ELF_AMSS_RELOCATABLE_IMAGE = 0x8000000 # New definitions for EOS demap paging requirement # Bit 20 (0b) Bit 24-26(000): Non Paged = 0x0000_0000 # Bit 20 (1b) Bit 24-26(000): Locked Paged = 0x0010_0000 # Bit 20 (1b) Bit 24-26(001): Unlocked Paged = 0x0110_0000 # Bit 20 (0b) Bit 24-26(011): non secure = 0x0310_0000 MI_PBT_ELF_RESIDENT_SEGMENT = 0x00000000 MI_PBT_ELF_PAGED_LOCKED_SEGMENT = 0x00100000 MI_PBT_ELF_PAGED_UNLOCKED_SEGMENT = 0x01100000 MI_PBT_ELF_UNSECURE_SEGMENT = 0x03100000 #---------------------------------------------------------------------------- # GLOBAL VARIABLES END #---------------------------------------------------------------------------- #---------------------------------------------------------------------------- # CLASS DEFINITIONS BEGIN #---------------------------------------------------------------------------- #---------------------------------------------------------------------------- # OS Type ID Class #---------------------------------------------------------------------------- class OSType: BMP_BOOT_OS = 0 WM_BOOT_OS = 1 ANDROID_BOOT_OS = 2 CHROME_BOOT_OS = 3 SYMBIAN_BOOT_OS = 4 LINUX_BOOT_OS = 5 #---------------------------------------------------------------------------- # Image Type ID Class - These values must be kept consistent with mibib.h #---------------------------------------------------------------------------- class ImageType: NONE_IMG = 0 OEM_SBL_IMG = 1 AMSS_IMG = 2 QCSBL_IMG = 3 HASH_IMG = 4 APPSBL_IMG = 5 APPS_IMG = 6 HOSTDL_IMG = 7 DSP1_IMG = 8 FSBL_IMG = 9 DBL_IMG = 10 OSBL_IMG = 11 DSP2_IMG = 12 EHOSTDL_IMG = 13 NANDPRG_IMG = 14 NORPRG_IMG = 15 RAMFS1_IMG = 16 RAMFS2_IMG = 17 ADSP_Q5_IMG = 18 APPS_KERNEL_IMG = 19 BACKUP_RAMFS_IMG = 20 SBL1_IMG = 21 SBL2_IMG = 22 RPM_IMG = 23 SBL3_IMG = 24 TZ_IMG = 25 PSI_IMG = 32 #---------------------------------------------------------------------------- # Global Image Type Table # Format of the look-up table: # KEY - IMAGE_TYPE string as passed into mbn_builder.py # VALUE - [Specific ImageType ID enum, Template key string, MBN Type] #---------------------------------------------------------------------------- image_id_table = { 'appsbl': [ImageType.APPSBL_IMG, 'APPSBL_IMG', 'bin'], 'dbl': [ImageType.DBL_IMG, 'DBL_IMG', 'bin'], 'osbl': [ImageType.OSBL_IMG, 'OSBL_IMG', 'bin'], 'amss': [ImageType.AMSS_IMG, 'AMSS_IMG', 'elf'], 'amss_mbn': [ImageType.HASH_IMG, 'HASH_IMG', 'elf'], 'apps': [ImageType.APPS_IMG, 'APPS_IMG', 'bin'], 'hostdl': [ImageType.HOSTDL_IMG, 'HOSTDL_IMG', 'bin'], 'ehostdl': [ImageType.EHOSTDL_IMG, 'EHOSTDL_IMG', 'bin'], 'emmcbld': [ImageType.EHOSTDL_IMG, 'EMMCBLD_IMG', 'bin'], 'qdsp6fw': [ImageType.DSP1_IMG, 'DSP1_IMG', 'elf'], 'qdsp6sw': [ImageType.DSP2_IMG, 'DSP2_IMG', 'elf'], 'qdsp5': [ImageType.ADSP_Q5_IMG, 'ADSP_Q5_IMG', 'bin'], 'tz': [ImageType.TZ_IMG, 'TZ_IMG', 'elf'], 'tz_rumi': [ImageType.TZ_IMG, 'TZ_IMG', 'elf'], 'tz_virtio': [ImageType.TZ_IMG, 'TZ_IMG', 'elf'], 'tzbsp_no_xpu': [ImageType.TZ_IMG, 'TZ_IMG', 'elf'], 'tzbsp_with_test': [ImageType.TZ_IMG, 'TZ_IMG', 'elf'], 'rpm': [ImageType.RPM_IMG, 'RPM_IMG', 'elf'], 'sbl1': [ImageType.SBL1_IMG, 'SBL1_IMG', 'bin'], 'sbl2': [ImageType.SBL2_IMG, 'SBL2_IMG', 'bin'], 'sbl3': [ImageType.SBL3_IMG, 'SBL3_IMG', 'bin'], 'efs1': [ImageType.RAMFS1_IMG, 'RAMFS1_IMG', 'bin'], 'efs2': [ImageType.RAMFS2_IMG, 'RAMFS2_IMG', 'bin'], 'pmic': [ImageType.PSI_IMG, 'PSI_IMG', 'elf'], # DO NOT add any additional image information } #---------------------------------------------------------------------------- # Header Class Notes: # In order to properly read and write the header structures as binary data, # the Python Struct library is used to align and package up the header objects # All Struct objects are initialized by a special string with the following # notation. These structure objects are then used to decode binary data in order # to fill out the appropriate class in Python, or they are used to package up # the Python class so that we may write the binary data out. #---------------------------------------------------------------------------- """ Format | C Type | Python Type | Standard Size ----------------------------------------------------- 1) 'X's | char * | string | 'X' bytes 2) H | unsigned short | integer | 2 bytes 3) I | unsigned int | integer | 4 bytes """ #---------------------------------------------------------------------------- # ELF Header Class #---------------------------------------------------------------------------- class Elf_Ehdr_common: # Structure object to align and package the ELF Header s = struct.Struct('16sHHI') def __init__(self, data): unpacked_data = (Elf_Ehdr_common.s).unpack(data) self.unpacked_data = unpacked_data self.e_ident = unpacked_data[0] self.e_type = unpacked_data[1] self.e_machine = unpacked_data[2] self.e_version = unpacked_data[3] def printValues(self): print "ATTRIBUTE / VALUE" for attr, value in self.__dict__.iteritems(): print attr, value #---------------------------------------------------------------------------- # ELF Header Class #---------------------------------------------------------------------------- class Elf32_Ehdr: # Structure object to align and package the ELF Header s = struct.Struct('16sHHIIIIIHHHHHH') def __init__(self, data): unpacked_data = (Elf32_Ehdr.s).unpack(data) self.unpacked_data = unpacked_data self.e_ident = unpacked_data[0] self.e_type = unpacked_data[1] self.e_machine = unpacked_data[2] self.e_version = unpacked_data[3] self.e_entry = unpacked_data[4] self.e_phoff = unpacked_data[5] self.e_shoff = unpacked_data[6] self.e_flags = unpacked_data[7] self.e_ehsize = unpacked_data[8] self.e_phentsize = unpacked_data[9] self.e_phnum = unpacked_data[10] self.e_shentsize = unpacked_data[11] self.e_shnum = unpacked_data[12] self.e_shstrndx = unpacked_data[13] def printValues(self): print "ATTRIBUTE / VALUE" for attr, value in self.__dict__.iteritems(): print attr, value def getPackedData(self): values = [self.e_ident, self.e_type, self.e_machine, self.e_version, self.e_entry, self.e_phoff, self.e_shoff, self.e_flags, self.e_ehsize, self.e_phentsize, self.e_phnum, self.e_shentsize, self.e_shnum, self.e_shstrndx ] return (Elf32_Ehdr.s).pack(*values) #---------------------------------------------------------------------------- # ELF Program Header Class #---------------------------------------------------------------------------- class Elf32_Phdr: # Structure object to align and package the ELF Program Header s = struct.Struct('I' * 8) def __init__(self, data): unpacked_data = (Elf32_Phdr.s).unpack(data) self.unpacked_data = unpacked_data self.p_type = unpacked_data[0] self.p_offset = unpacked_data[1] self.p_vaddr = unpacked_data[2] self.p_paddr = unpacked_data[3] self.p_filesz = unpacked_data[4] self.p_memsz = unpacked_data[5] self.p_flags = unpacked_data[6] self.p_align = unpacked_data[7] def printValues(self): print "ATTRIBUTE / VALUE" for attr, value in self.__dict__.iteritems(): print attr, value def getPackedData(self): values = [self.p_type, self.p_offset, self.p_vaddr, self.p_paddr, self.p_filesz, self.p_memsz, self.p_flags, self.p_align ] return (Elf32_Phdr.s).pack(*values) #---------------------------------------------------------------------------- # ELF Header Class #---------------------------------------------------------------------------- class Elf64_Ehdr: # Structure object to align and package the ELF Header s = struct.Struct('16sHHIQQQIHHHHHH') def __init__(self, data): unpacked_data = (Elf64_Ehdr.s).unpack(data) self.unpacked_data = unpacked_data self.e_ident = unpacked_data[0] self.e_type = unpacked_data[1] self.e_machine = unpacked_data[2] self.e_version = unpacked_data[3] self.e_entry = unpacked_data[4] self.e_phoff = unpacked_data[5] self.e_shoff = unpacked_data[6] self.e_flags = unpacked_data[7] self.e_ehsize = unpacked_data[8] self.e_phentsize = unpacked_data[9] self.e_phnum = unpacked_data[10] self.e_shentsize = unpacked_data[11] self.e_shnum = unpacked_data[12] self.e_shstrndx = unpacked_data[13] def printValues(self): print "ATTRIBUTE / VALUE" for attr, value in self.__dict__.iteritems(): print attr, value def getPackedData(self): values = [self.e_ident, self.e_type, self.e_machine, self.e_version, self.e_entry, self.e_phoff, self.e_shoff, self.e_flags, self.e_ehsize, self.e_phentsize, self.e_phnum, self.e_shentsize, self.e_shnum, self.e_shstrndx ] return (Elf64_Ehdr.s).pack(*values) #---------------------------------------------------------------------------- # ELF Program Header Class #---------------------------------------------------------------------------- class Elf64_Phdr: # Structure object to align and package the ELF Program Header s = struct.Struct('IIQQQQQQ') def __init__(self, data): unpacked_data = (Elf64_Phdr.s).unpack(data) self.unpacked_data = unpacked_data self.p_type = unpacked_data[0] self.p_flags = unpacked_data[1] self.p_offset = unpacked_data[2] self.p_vaddr = unpacked_data[3] self.p_paddr = unpacked_data[4] self.p_filesz = unpacked_data[5] self.p_memsz = unpacked_data[6] self.p_align = unpacked_data[7] def printValues(self): print "ATTRIBUTE / VALUE" for attr, value in self.__dict__.iteritems(): print attr, value def getPackedData(self): values = [self.p_type, self.p_flags, self.p_offset, self.p_vaddr, self.p_paddr, self.p_filesz, self.p_memsz, self.p_align ] return (Elf64_Phdr.s).pack(*values) #---------------------------------------------------------------------------- # ELF Segment Information Class #---------------------------------------------------------------------------- class SegmentInfo: def __init__(self): self.flag = 0 def printValues(self): print 'Flag: ' + str(self.flag) #---------------------------------------------------------------------------- # Regular Boot Header Class #---------------------------------------------------------------------------- class Boot_Hdr: def __init__(self, init_val): self.image_id = ImageType.NONE_IMG self.flash_parti_ver = 3 self.image_src = init_val self.image_dest_ptr = init_val self.image_size = init_val self.code_size = init_val self.sig_ptr = init_val self.sig_size = init_val self.cert_chain_ptr = init_val self.cert_chain_size = init_val self.magic_number1 = init_val self.version = init_val self.OS_type = init_val self.boot_apps_parti_entry = init_val self.boot_apps_size_entry = init_val self.boot_apps_ram_loc = init_val self.reserved_ptr = init_val self.reserved_1 = init_val self.reserved_2 = init_val self.reserved_3 = init_val def getLength(self): return BOOT_HEADER_LENGTH def writePackedData(self, target, write_full_hdr): values = [self.image_id, self.flash_parti_ver, self.image_src, self.image_dest_ptr, self.image_size, self.code_size , self.sig_ptr, self.sig_size, self.cert_chain_ptr, self.cert_chain_size, self.magic_number1, self.version, self.OS_type, self.boot_apps_parti_entry, self.boot_apps_size_entry, self.boot_apps_ram_loc, self.reserved_ptr, self.reserved_1, self.reserved_2, self.reserved_3 ] if self.flash_parti_ver >= 6: values.insert(10, self.metadata_size_qti) values.insert(11, self.metadata_size) if self.image_dest_ptr >= 0x100000000: values[3] = 0xFFFFFFFF if self.cert_chain_ptr >= 0x100000000: values[6] = 0xFFFFFFFF if self.sig_ptr >= 0x100000000: values[8] = 0xFFFFFFFF # Write 10 entries(40B) or 20 entries(80B) of boot header if write_full_hdr is False: if self.flash_parti_ver >= 6: s = struct.Struct('I'* 12) values = values[:12] else: s = struct.Struct('I'* 10) values = values[:10] else: s = struct.Struct('I' * self.getLength()) packed_data = s.pack(*values) fp = OPEN(target,'wb') fp.write(packed_data) fp.close() return s.size #---------------------------------------------------------------------------- # SBL Boot Header Class #---------------------------------------------------------------------------- class Sbl_Hdr: def __init__(self, init_val): self.codeword = init_val self.magic = init_val self.image_id = init_val self.reserved_1 = init_val self.reserved_2 = init_val self.image_src = init_val self.image_dest_ptr = init_val self.image_size = init_val self.code_size = init_val self.sig_ptr = init_val self.sig_size = init_val self.cert_chain_ptr = init_val self.cert_chain_size = init_val self.oem_root_cert_sel = init_val self.oem_num_root_certs = init_val self.booting_image_config = init_val self.reserved_6 = init_val self.reserved_7 = init_val self.reserved_8 = init_val self.reserved_9 = init_val def getLength(self): return SBL_HEADER_LENGTH def writePackedData(self, target): values = [self.codeword, self.magic, self.image_id, self.reserved_1, self.reserved_2, self.image_src, self.image_dest_ptr, self.image_size, self.code_size, self.sig_ptr, self.sig_size, self.cert_chain_ptr, self.cert_chain_size, self.oem_root_cert_sel, self.oem_num_root_certs, self.booting_image_config, self.reserved_6, self.reserved_7, self.reserved_8, self.reserved_9 ] s = struct.Struct('I' * self.getLength()) packed_data = s.pack(*values) fp = OPEN(target,'wb') fp.write(packed_data) fp.close() return s.size #---------------------------------------------------------------------------- # CLASS DEFINITIONS END #---------------------------------------------------------------------------- #------------------------------------------------------------------------------ # Hooks for Scons #------------------------------------------------------------------------------ def exists(env): return env.Detect('mbn_tools') def generate(env): #---------------------------------------------------------------------------- # Generate Global Dictionary #---------------------------------------------------------------------------- generate_global_dict(env) #---------------------------------------------------------------------------- # Assign Build Configurable Values #---------------------------------------------------------------------------- init_build_vars(env) #---------------------------------------------------------------------------- # Add Methods to Environment #---------------------------------------------------------------------------- env.AddMethod(filter_dictionary, "FilterDictionary") env.AddMethod(image_auth, "ImageAuth") env.AddMethod(image_header, "ImageHeader") env.AddMethod(pboot_gen_elf, "PBootGenElf") env.AddMethod(pboot_add_hash, "PBootAddHash") env.AddMethod(modify_elf_flags, "ModifyElfFlags") env.AddMethod(generate_code_hash, "GenerateCodeHash") env.AddMethod(insert_SBL1_magicCookie, "InsertSBLMagicCookie") env.AddMethod(modify_relocatable_flags, "ModifyRelocatableFlags") #---------------------------------------------------------------------------- # Load Encryption Tools and Methods if required #---------------------------------------------------------------------------- if 'USES_ENCRYPT_MBN' in env: # Add Encryption Tools to environment env.Tool('pil_encrypt', toolpath = ['${BUILD_ROOT}/core/securemsm/ssd/tools/pil_encrypt']) env.AddMethod(get_ssd_se_fname, "GetSSDSideEffectFileName") env.AddMethod(encrypt_elf_segments, "EncryptElfSegments") env.AddMethod(generate_meta_data, "GenerateMetaData") env.AddMethod(encrypt_mbn, "EncryptMBN") return None #---------------------------------------------------------------------------- # BOOT TOOLS BEGIN #---------------------------------------------------------------------------- #---------------------------------------------------------------------------- # generate_meta_data #---------------------------------------------------------------------------- def generate_meta_data(env, meta_out_file_name, add_magic_num = False): ''' Make call to SSD API to return buffer filled with XML header information. The XML header which we write contains information regarding the algorithms being used along with specific key values which are to be used for encrpytion. ''' xml_header = env.SSDGetMetaData(add_magic_num) # Initialize xml_target_file = open(meta_out_file_name,'wb') xml_header_size = len(xml_header) # Write XML buffer into target file xml_target_file.write(xml_header) # Pad if necessary to the maximum size if xml_header_size <= XML_HEADER_MAXSIZE: bytes_to_pad = XML_HEADER_MAXSIZE - xml_header_size pad_file(xml_target_file, bytes_to_pad, PAD_BYTE_1) xml_target_file.close() else: xml_target_file.close() raise RuntimeError, "XML Size too large: " + str(xml_header_size) #---------------------------------------------------------------------------- # encrypt_mbn #---------------------------------------------------------------------------- def encrypt_mbn(env, mbn_in_file_name, mbn_out_file_name): # Open Files mbn_in_fp = OPEN(mbn_in_file_name, "rb") mbn_out_fp = OPEN(mbn_out_file_name, "wb+") # encrypt the input file content and write to output file mbn_file_size = os.path.getsize(mbn_in_file_name) file_buff = mbn_in_fp.read(mbn_file_size) encrypted_buf = env.SSDEncryptSegment(0, file_buff, mbn_file_size) mbn_out_fp.write(encrypted_buf) # Close Files mbn_in_fp.close() mbn_out_fp.close() # Clean up encryption files env.SSDDeInit() #---------------------------------------------------------------------------- # get_ssd_se_fname #---------------------------------------------------------------------------- def get_ssd_se_fname(env): return env.SSDGetSideEffectFileName() #---------------------------------------------------------------------------- # encrypt_elf_segments #---------------------------------------------------------------------------- def encrypt_elf_segments(env, elf_in_file_name, elf_out_file_name): # Open Files elf_in_fp = OPEN(elf_in_file_name, "rb") elf_out_fp = OPEN(elf_out_file_name, "wb+") # Initialize [elf_header, phdr_table] = preprocess_elf_file(elf_in_file_name) encrypted_seg_counter = 0 # Copy input file to output file shutil.copyfileobj(elf_in_fp, elf_out_fp, os.path.getsize(elf_in_file_name)) # Begin ELF segment encryption for i in range(elf_header.e_phnum): curr_phdr = phdr_table[i] # Only encrypt segments of LOAD_TYPE. Do not encrypt the hash segment. if curr_phdr.p_type == LOAD_TYPE and \ MI_PBT_SEGMENT_TYPE_VALUE(curr_phdr.p_flags) != MI_PBT_HASH_SEGMENT: # Read full segment into buffer elf_in_fp.seek(curr_phdr.p_offset) data_len = curr_phdr.p_filesz file_buff = elf_in_fp.read(data_len) # Call encryption routine on buffer encrypted_buf = env.SSDEncryptSegment(encrypted_seg_counter, file_buff, data_len) encrypted_seg_counter += 1 # Write encrypted segment into output file in same location elf_out_fp.seek(curr_phdr.p_offset) elf_out_fp.write(encrypted_buf) # Close Files elf_in_fp.close() elf_out_fp.close() # Clean up encryption files env.SSDDeInit() #---------------------------------------------------------------------------- # Converts integer to bytes. If length after conversion # is smaller than given length of byte string, returned value is right-filled # with 0x00 bytes. Use Little-endian byte order. #---------------------------------------------------------------------------- def convert_int_to_byte_string(n, l): return b''.join([chr((n >> ((l - i - 1) * 8)) % 256) for i in xrange(l)][::-1]) #---------------------------------------------------------------------------- # Create default elf header #---------------------------------------------------------------------------- def create_elf_header( output_file_name, image_dest, image_size, is_elf_64_bit = False): if (output_file_name is None): raise RuntimeError, "Requires a ELF header file" # Create a elf header and program header # Write the headers to the output file elf_fp = file(output_file_name, "wb") if (is_elf_64_bit is True): # ELf header elf_fp.write(ELFINFO_MAG0) elf_fp.write(ELFINFO_MAG1) elf_fp.write(ELFINFO_MAG2) elf_fp.write(ELFINFO_MAG3) elf_fp.write(ELFINFO_CLASS_64) elf_fp.write(ELFINFO_DATA2LSB) elf_fp.write(ELFINFO_VERSION_CURRENT) elf_fp.write(''.rjust(9, chr(ELFINFO_RESERVED))) elf_fp.write(ELFINFO_EXEC_ETYPE) elf_fp.write(ELFINFO_ARM_MACHINETYPE) elf_fp.write(ELFINFO_VERSION_EV_CURRENT) elf_fp.write(convert_int_to_byte_string(image_dest, 8)) elf_fp.write(convert_int_to_byte_string(ELF64_HDR_SIZE, 8)) elf_fp.write(convert_int_to_byte_string(ELFINFO_SHOFF, 8)) elf_fp.write(''.rjust(4, chr(ELFINFO_RESERVED))) elf_fp.write(convert_int_to_byte_string(ELF64_HDR_SIZE, 2)) elf_fp.write(convert_int_to_byte_string(ELF64_PHDR_SIZE, 2)) elf_fp.write(ELFINFO_PHNUM) elf_fp.write(''.rjust(6, chr(ELFINFO_RESERVED))) # Program Header elf_fp.write(convert_int_to_byte_string(LOAD_TYPE, 4)) elf_fp.write(convert_int_to_byte_string(MI_PBT_RWE_SEGMENT, 4)) elf_fp.write(convert_int_to_byte_string(ELF64_HDR_SIZE+ELF64_PHDR_SIZE, 8)) elf_fp.write(convert_int_to_byte_string(image_dest, 8)) elf_fp.write(convert_int_to_byte_string(image_dest, 8)) elf_fp.write(convert_int_to_byte_string(image_size, 8)) elf_fp.write(convert_int_to_byte_string(image_size, 8)) elf_fp.write(convert_int_to_byte_string(ELF_BLOCK_ALIGN, 8)) else: # ELf header elf_fp.write(ELFINFO_MAG0) elf_fp.write(ELFINFO_MAG1) elf_fp.write(ELFINFO_MAG2) elf_fp.write(ELFINFO_MAG3) elf_fp.write(ELFINFO_CLASS_32) elf_fp.write(ELFINFO_DATA2LSB) elf_fp.write(ELFINFO_VERSION_CURRENT) elf_fp.write(''.rjust(9, chr(ELFINFO_RESERVED))) elf_fp.write(ELFINFO_EXEC_ETYPE) elf_fp.write(ELFINFO_ARM_MACHINETYPE) elf_fp.write(ELFINFO_VERSION_EV_CURRENT) elf_fp.write(convert_int_to_byte_string(image_dest, 4)) elf_fp.write(convert_int_to_byte_string(ELF32_HDR_SIZE, 4)) elf_fp.write(convert_int_to_byte_string(ELFINFO_SHOFF, 4)) elf_fp.write(''.rjust(4, chr(ELFINFO_RESERVED))) elf_fp.write(convert_int_to_byte_string(ELF32_HDR_SIZE, 2)) elf_fp.write(convert_int_to_byte_string(ELF32_PHDR_SIZE, 2)) elf_fp.write(ELFINFO_PHNUM) elf_fp.write(''.rjust(6, chr(ELFINFO_RESERVED))) # Program Header elf_fp.write(convert_int_to_byte_string(LOAD_TYPE, 4)) elf_fp.write(convert_int_to_byte_string(ELF32_HDR_SIZE+ELF32_PHDR_SIZE, 4)) elf_fp.write(convert_int_to_byte_string(image_dest, 4)) elf_fp.write(convert_int_to_byte_string(image_dest, 4)) elf_fp.write(convert_int_to_byte_string(image_size, 4)) elf_fp.write(convert_int_to_byte_string(image_size, 4)) elf_fp.write(convert_int_to_byte_string(MI_PBT_RWE_SEGMENT, 4)) elf_fp.write(convert_int_to_byte_string(ELF_BLOCK_ALIGN, 4)) elf_fp.close() return 0 #---------------------------------------------------------------------------- # image_header #---------------------------------------------------------------------------- def image_header(env, gen_dict, code_file_name, output_file_name, secure_type, header_format = 'reg', requires_preamble = False, preamble_file_name = None, elf_file_name = None, write_full_hdr = False, in_code_size = None, cert_chain_size_in = CERT_CHAIN_ONEROOT_MAXSIZE, num_of_pages = None, header_version = None): # Preliminary checks if (requires_preamble is True) and (preamble_file_name is None): raise RuntimeError, "Image Header requires a preamble file" if (gen_dict['IMAGE_KEY_MBN_TYPE'] == 'elf') and (elf_file_name is None): raise RuntimeError, "ELF Image Headers require an elf file" if (in_code_size is None) and (os.path.exists(code_file_name) is False): raise RuntimeError, "Code size unavailable, and input file does not exist" # Initialize if in_code_size is not None: code_size = in_code_size else: code_size = os.path.getsize(code_file_name) image_dest = 0 image_source = 0 # If secure build, set signature and cert chain sizes if secure_type == 'secure': signature_size = SHA256_SIGNATURE_SIZE cert_chain_size = cert_chain_size_in image_size = code_size + cert_chain_size + signature_size if (image_size % 4) != 0: image_size += (4 - (image_size % 4)) else: signature_size = 0 cert_chain_size = 0 image_size = code_size if header_version: assert header_version in [3, 5, 6], 'Not a valid MBN header version' # For ELF or hashed images, image destination will be determined from an ELF input file if gen_dict['IMAGE_KEY_MBN_TYPE'] == 'elf': image_dest = get_hash_address(elf_file_name) + (header_size(header_version)) elif gen_dict['IMAGE_KEY_MBN_TYPE'] == 'bin': image_dest = gen_dict['IMAGE_KEY_IMAGE_DEST'] image_source = gen_dict['IMAGE_KEY_IMAGE_SOURCE'] # Build the header based on format specified if header_format == 'sbl': boot_sbl_header = Sbl_Hdr(init_val = int('0xFFFFFFFF',16)) boot_sbl_header.codeword = FLASH_CODE_WORD boot_sbl_header.magic = MAGIC_NUM boot_sbl_header.image_id = gen_dict['IMAGE_KEY_IMAGE_ID'] boot_sbl_header.image_src = MI_BOOT_SBL_HDR_SIZE boot_sbl_header.image_dest_ptr = image_dest boot_sbl_header.image_size = image_size boot_sbl_header.code_size = code_size boot_sbl_header.sig_ptr = image_dest + code_size boot_sbl_header.sig_size = signature_size boot_sbl_header.cert_chain_ptr = image_dest + code_size + signature_size boot_sbl_header.cert_chain_size = cert_chain_size boot_sbl_header.oem_root_cert_sel = gen_dict['IMAGE_KEY_OEM_ROOT_CERT_SEL'] boot_sbl_header.oem_num_root_certs = gen_dict['IMAGE_KEY_OEM_NUM_ROOT_CERTS'] if 'USES_SBL_FOR_AARCH64' in env: boot_sbl_header.booting_image_config = SBL_AARCH64 elif 'USES_SBL_FOR_AARCH632' in env: boot_sbl_header.booting_image_config = SBL_AARCH32 # If preamble is required, output the preamble file and update the boot_sbl_header if requires_preamble is True: boot_sbl_header = image_preamble(gen_dict, preamble_file_name, boot_sbl_header, num_of_pages) # Package up the header and write to output file boot_sbl_header.writePackedData(target = output_file_name) elif header_format == 'reg': boot_header = Boot_Hdr(init_val = int('0x0',16)) boot_header.image_id = gen_dict['IMAGE_KEY_IMAGE_ID'] boot_header.image_src = image_source boot_header.image_dest_ptr = image_dest boot_header.image_size = image_size boot_header.code_size = code_size boot_header.sig_ptr = image_dest + code_size boot_header.sig_size = signature_size boot_header.cert_chain_ptr = image_dest + code_size + signature_size boot_header.cert_chain_size = cert_chain_size boot_header.flash_parti_ver = header_version # version if header_version >= 5: boot_header.image_src = 0 # sig_size_qc boot_header.image_dest_ptr = 0 # cert_chain_size_qc if header_version >= 6: boot_header.metadata_size_qti = 0 # qti_metadata size boot_header.metadata_size = 0 # oem_metadata size # If preamble is required, output the preamble file and update the boot_header if requires_preamble is True: boot_header = image_preamble(gen_dict, preamble_file_name, boot_header, num_of_pages) # Package up the header and write to output file boot_header.writePackedData(target = output_file_name, write_full_hdr = write_full_hdr) else: raise RuntimeError, "Header format not supported: " + str(header_format) return 0 #---------------------------------------------------------------------------- # pboot_gen_elf #---------------------------------------------------------------------------- def pboot_gen_elf(env, elf_in_file_name, hash_out_file_name, elf_out_file_name, secure_type = 'non_secure', hash_seg_max_size = None, last_phys_addr = None, append_xml_hdr = False, is_sha256_algo = True, cert_chain_size_in = CERT_CHAIN_ONEROOT_MAXSIZE, header_version = None): sha_algo = 'SHA1' if is_sha256_algo: sha_algo = 'SHA256' if header_version >= 6: sha_algo = 'SHA384' image_header_size = header_size(header_version) if (sha_algo == 'SHA384'): mi_prog_boot_digest_size = 48 elif sha_algo == 'SHA256': mi_prog_boot_digest_size = 32 else: mi_prog_boot_digest_size = 20 # Open Files elf_in_fp = OPEN(elf_in_file_name, "rb") hash_out_fp = OPEN(hash_out_file_name, "wb+") if elf_out_file_name is not None: elf_out_fp = OPEN(elf_out_file_name, "wb+") # Initialize [elf_header, phdr_table] = preprocess_elf_file(elf_in_file_name) num_phdrs = elf_header.e_phnum phdr_total_size = num_phdrs * elf_header.e_phentsize phdr_size = elf_header.e_phentsize hashtable_size = 0 hashtable_shift = 0 if elf_header.e_ident[ELFINFO_CLASS_INDEX] == ELFINFO_CLASS_64: new_phdr = Elf64_Phdr('\0' * ELF64_PHDR_SIZE) elf_header_size = ELF64_HDR_SIZE is_elf64 = True else: new_phdr = Elf32_Phdr('\0' * ELF32_PHDR_SIZE) elf_header_size = ELF32_HDR_SIZE is_elf64 = False hash = '\0' * mi_prog_boot_digest_size phdr_start = 0 bytes_to_pad = 0 hash_seg_end = 0 # Process program headers if an output elf is specified if elf_out_file_name is not None: # Assert limit on number of program headers in input ELF if num_phdrs > MAX_PHDR_COUNT: raise RuntimeError, "Input ELF has exceeded maximum number of program headers" # Create new program header for the ELF Header + Program Headers new_phdr.p_type = NULL_TYPE new_phdr.p_flags = MI_PBT_ELF_PHDR_SEGMENT # If hash table program header is not found, make sure to include it elf_header.e_phnum += 2 # Create an empty hash entry for PHDR_TYPE hash_out_fp.write('\0' * mi_prog_boot_digest_size) hashtable_size += mi_prog_boot_digest_size # Create an empty hash entry for the hash segment itself hash_out_fp.write('\0' * mi_prog_boot_digest_size) hashtable_size += mi_prog_boot_digest_size # Begin hash table generation for i in range(num_phdrs): curr_phdr = phdr_table[i] if (MI_PBT_PAGE_MODE_VALUE(curr_phdr.p_flags) == MI_PBT_PAGED_SEGMENT): seg_offset = curr_phdr.p_offset seg_size = curr_phdr.p_filesz hash_size = 0 # Check if the vaddr is page aligned off = curr_phdr.p_vaddr & (ELF_BLOCK_ALIGN - 1) if int(off) is not 0: seg_size -= (ELF_BLOCK_ALIGN - off) seg_offset += (ELF_BLOCK_ALIGN - off) # Seg_size should be page aligned if (seg_size & (ELF_BLOCK_ALIGN - 1)) > 0: raise RuntimeError, "seg_size: " + hex(seg_size) + " is not ELF page aligned!" off = seg_offset + seg_size while seg_offset < off: if seg_offset < ELF_BLOCK_ALIGN: hash_size = seg_offset else: hash_size = ELF_BLOCK_ALIGN elf_in_fp.seek(seg_offset) fbuf = elf_in_fp.read(hash_size) if MI_PBT_CHECK_FLAG_TYPE(curr_phdr.p_flags) is True: hash = generate_hash(fbuf, sha_algo) else: hash = '\0' * mi_prog_boot_digest_size # Write hash to file hash_out_fp.write(hash) hashtable_size += mi_prog_boot_digest_size seg_offset += ELF_BLOCK_ALIGN # Copy the hash entry for all that are PAGED segments and those that are not the PHDR type. This is for # backward tool compatibility where some images are generated using older exe tools. elif((MI_PBT_PAGE_MODE_VALUE(curr_phdr.p_flags) == MI_PBT_NON_PAGED_SEGMENT) and (curr_phdr.p_type is not PHDR_TYPE)): # Read full hash entry into buffer elf_in_fp.seek(curr_phdr.p_offset) data_len = curr_phdr.p_filesz file_buff = elf_in_fp.read(data_len) if (MI_PBT_CHECK_FLAG_TYPE(curr_phdr.p_flags) is True) and (data_len > 0): hash = generate_hash(file_buff, sha_algo) else: hash = '\0' * mi_prog_boot_digest_size # Write hash to file hash_out_fp.write(hash) hashtable_size += mi_prog_boot_digest_size # End hash table generation # Generate the rest of the ELF output file if specified if elf_out_file_name is not None: # Preempt hash table size if necessary if secure_type == 'secure': hashtable_size += (SHA256_SIGNATURE_SIZE + cert_chain_size_in) if append_xml_hdr is True: hashtable_size += XML_HEADER_MAXSIZE # Initialize the hash table program header [hash_Phdr, pad_hash_segment, hash_tbl_end_addr, hash_tbl_offset] = \ initialize_hash_phdr(elf_in_file_name, hashtable_size, image_header_size, ELF_BLOCK_ALIGN, is_elf64) # Check if hash segment max size parameter was passed if (hash_seg_max_size is not None): # Error checking for hash segment size validity if hashtable_size > hash_seg_max_size: raise RuntimeError, "Hash table exceeds maximum hash segment size: " + hex(hash_seg_max_size) if (hash_seg_max_size & (ELF_BLOCK_ALIGN-1)) is not 0: raise RuntimeError, "Hash segment size passed is not ELF Block Aligned: " + hex(hash_seg_max_size) # Check if hash physical address parameter was passed if last_phys_addr is not None: hash_Phdr.p_vaddr = last_phys_addr hash_Phdr.p_paddr = last_phys_addr # Check if hash segment max size was passed if hash_seg_max_size is not None: hash_Phdr.p_memsz = hash_seg_max_size # Determine the end of the hash segment, make sure it's block aligned bytes_to_pad = ELF_BLOCK_ALIGN - pad_hash_segment hash_seg_end = hash_tbl_end_addr + bytes_to_pad # Check if a shifting is required to accommodate for the hash segment. # Get the minimum offset by going through the program headers. # Note that the program headers in the input file do not contain # the dummy program header for ELF + Program header, and the # program header for the hashtable. min_offset = phdr_table[0].p_offset for i in range(num_phdrs): curr_phdr = phdr_table[i] if curr_phdr.p_offset < min_offset: min_offset = curr_phdr.p_offset if min_offset < hash_seg_end: hashtable_shift = hash_seg_end - min_offset # Move program headers to after ELF header phdr_start = elf_header_size # We copy over no section headers so assign these values to 0 in ELF Header elf_header.e_shnum = 0 elf_header.e_shstrndx = 0 elf_header.e_shoff = 0 # Output remaining ELF segments for i in range(num_phdrs): # Increment the file offset before writing to the destination file curr_phdr = phdr_table[i] # We do not copy over program headers of PHDR type, decrement the program # header count and continue the loop if curr_phdr.p_type is PHDR_TYPE: elf_header.e_phnum -= 1 continue src_offset = curr_phdr.p_offset # Copy the ELF segment file_copy_offset(elf_in_fp, src_offset, elf_out_fp, curr_phdr.p_offset + hashtable_shift, curr_phdr.p_filesz) # Output remaining program headers and ELF segments elf_header.e_phoff = phdr_start # Output new program headers which we have generated elf_out_fp.seek(phdr_start) new_phdr.p_filesz = elf_header_size + (elf_header.e_phnum * phdr_size) elf_out_fp.write(new_phdr.getPackedData()) elf_out_fp.write(hash_Phdr.getPackedData()) phdr_start += (2 * phdr_size) # Increment the file offset before writing to the destination file for i in range(num_phdrs): curr_phdr = phdr_table[i] if curr_phdr.p_type is PHDR_TYPE: continue curr_phdr.p_offset += hashtable_shift # Copy the program header elf_out_fp.seek(phdr_start) elf_out_fp.write(curr_phdr.getPackedData()) # Update phdr_start phdr_start += phdr_size # Finally, copy the new ELF header to the destination file elf_out_fp.seek(0) elf_out_fp.write(elf_header.getPackedData()) # Recalculate hash of ELF + program headers and output to hash output file elf_out_fp.seek(0) # Read the elf header elfhdr_buff = elf_out_fp.read(elf_header_size) # Seek to the program header offset listed in elf header. elf_out_fp.seek(elf_header.e_phoff) # Read the program header and compute hash proghdr_buff = elf_out_fp.read(elf_header.e_phnum * phdr_size) hash = generate_hash(elfhdr_buff + proghdr_buff, sha_algo) # Write hash to file as first hash table entry hash_out_fp.seek(0) hash_out_fp.write(hash) # Close files elf_in_fp.close() hash_out_fp.close() if elf_out_file_name is not None: elf_out_fp.close() return 0 #---------------------------------------------------------------------------- # pboot_add_hash #---------------------------------------------------------------------------- def pboot_add_hash(env, elf_in_file_name, hash_tbl_file_name, elf_out_file_name): # Open files elf_in_fp = OPEN(elf_in_file_name, "rb") hash_tbl_fp = OPEN(hash_tbl_file_name, "rb") elf_out_fp = OPEN(elf_out_file_name, "wb+") # Initialize [elf_header, phdr_table] = preprocess_elf_file(elf_in_file_name) hash_size = os.path.getsize(hash_tbl_file_name) hash_segment_found = False # Attempt to find the location of the hash program header for i in range(elf_header.e_phnum): curr_phdr = phdr_table[i] if curr_phdr.p_flags == MI_PBT_ELF_HASH_SEGMENT: hash_segment_found = True break if hash_segment_found is True: # Copy input file to output file shutil.copyfileobj(elf_in_fp, elf_out_fp, os.path.getsize(elf_in_file_name)) # Update ELF to insert hash table at corresponding file offset hash_hdr_offset = curr_phdr.p_offset file_copy_offset(hash_tbl_fp, 0, elf_out_fp, hash_hdr_offset, hash_size) else: raise RuntimeError, "Hash segment program header not found in file " + elf_in_file_name # Close files elf_in_fp.close() hash_tbl_fp.close() elf_out_fp.close() return 0 #---------------------------------------------------------------------------- # image_auth #---------------------------------------------------------------------------- def image_auth(env, *args): if len(args) < 7 or len(args) > 8: raise RuntimeError, "Usage Invalid" # Initialize File Names binary_in = args[0] signature = args[1] attestation_cert = args[2] attestation_ca_cert = args[3] root_cert = args[4] cert_chain_out = args[5] signed_image_out = args[6] if len(args) == 8: cert_size_max_in = args[7] else: cert_size_max_in = CERT_CHAIN_ONEROOT_MAXSIZE # Creating list of certificates to support creation of certificate chains # of lenth 1, 2, or 3 certificates cert_list = [] num_certs = 0 if (os.path.exists(attestation_cert)): cert_list.append(attestation_cert) num_certs = num_certs + 1 if (os.path.exists(attestation_ca_cert)): cert_list.append(attestation_ca_cert) num_certs = num_certs + 1 if (os.path.exists(root_cert)): cert_list.append(root_cert) num_certs = num_certs + 1 if (num_certs == 0): raise RuntimeError, "Missing file(s) required for signing.\n" # Create the Certificate Chain concat_files (cert_chain_out, cert_list) # Pad to ensure Certificate Chain Size is CERT_CHAIN_MAX_SIZE cert_size = os.path.getsize(cert_chain_out) if cert_size <= cert_size_max_in: bytes_to_pad = cert_size_max_in - cert_size cert_fp = OPEN(cert_chain_out,'ab') pad_file(cert_fp, bytes_to_pad, PAD_BYTE_1) cert_fp.close() else: raise RuntimeError, "Certificate Size too large: " + str(cert_size) # Create the Final Signed Image File concat_files (signed_image_out, [binary_in, signature, cert_chain_out]) return 0 #---------------------------------------------------------------------------- # modify_relocatable_flags #---------------------------------------------------------------------------- def modify_relocatable_flags(env, output_elf ): # Offset into program header where the p_flags field is stored phdr_align_flag_offset = 28 phdr_reloc_flag_offset = 24 # Initialize [elf_header, phdr_table] = preprocess_elf_file(output_elf) if elf_header.e_ident[ELFINFO_CLASS_INDEX] == ELFINFO_CLASS_64: curr_phdr = Elf64_Phdr('\0' * ELF64_PHDR_SIZE) elf_header_size = ELF64_HDR_SIZE is_elf64 = True else: curr_phdr = Elf32_Phdr('\0' * ELF32_PHDR_SIZE) elf_header_size = ELF32_HDR_SIZE is_elf64 = False # Open files elf_in_fp = OPEN(output_elf, "r+") # Go to the start of the p_flag entry in the first program header file_offset_align_flag = elf_header.e_phoff + phdr_align_flag_offset # Change the align field in the program header in the ELF file elf_in_fp.seek(file_offset_align_flag) curr_phdr = phdr_table[0] #default alignment value is 1MB unless otherwise specified if 'USES_RELOC_ALIGN_VALUE_4MB' in env: alignment_value = ALIGNVALUE_4MB else: alignment_value = ALIGNVALUE_1MB #create new alignment value new_align = (curr_phdr.p_align & 0) | alignment_value # Create structure to package new flag field s = struct.Struct('I') new_flag_bytes = s.pack(new_align) # Write the new flag value and incr ement offset elf_in_fp.write(new_flag_bytes) # Go to the start of the p_flag entry in the first program header file_offset_reloc_flag = elf_header.e_phoff + phdr_reloc_flag_offset # Change each program header flag in the ELF file with relocatable flag for i in range(elf_header.e_phnum): # Seek to correct location and create new p_flag value elf_in_fp.seek(file_offset_reloc_flag) curr_phdr = phdr_table[i] new_flag = (curr_phdr.p_flags & ~MI_PBT_FLAGS_MASK) | (MI_PBT_ELF_AMSS_RELOCATABLE_IMAGE) # Create structure to package new flag field s = struct.Struct('I') new_flag_bytes = s.pack(new_flag) # Write the new flag value and increment offset elf_in_fp.write(new_flag_bytes) file_offset_reloc_flag += elf_header.e_phentsize # Close files elf_in_fp.close() return 0 #---------------------------------------------------------------------------- # modify_elf_flags #---------------------------------------------------------------------------- def modify_elf_flags(env, elf_in_file_name, scl_file_name): # Initialize [elf_header, phdr_table] = preprocess_elf_file(elf_in_file_name) segment_list = readSCL(scl_file_name, env['GLOBAL_DICT']) if elf_header.e_ident[ELFINFO_CLASS_INDEX] == ELFINFO_CLASS_64: curr_phdr = Elf64_Phdr('\0' * ELF64_PHDR_SIZE) # Offset into program header where the p_flags field is stored phdr_flag_off = 4 else: curr_phdr = Elf32_Phdr('\0' * ELF32_PHDR_SIZE) # Offset into program header where the p_flags field is stored phdr_flag_off = 24 # Open files elf_in_fp = OPEN(elf_in_file_name, "r+") # Check for corresponding number of segments if len(segment_list) is not elf_header.e_phnum: raise RuntimeError, 'SCL file and ELF file have different number of segments!' # Go to the start of the p_flag entry in the first program header file_offset = elf_header.e_phoff + phdr_flag_off # Change each program header flag in the ELF file based off the SCL file for i in range(elf_header.e_phnum): # Seek to correct location and create new p_flag value elf_in_fp.seek(file_offset) curr_phdr = phdr_table[i] new_flag = (curr_phdr.p_flags & ~MI_PBT_FLAGS_MASK) | (segment_list[i].flag) # Create structure to package new flag field s = struct.Struct('I') new_flag_bytes = s.pack(new_flag) # Write the new flag value and increment offset elf_in_fp.write(new_flag_bytes) file_offset += elf_header.e_phentsize # Close files elf_in_fp.close() return 0 #---------------------------------------------------------------------------- # generate_code_hash #---------------------------------------------------------------------------- def generate_code_hash(env, elf_in_file_name): # Initialize [elf_header, phdr_table] = preprocess_elf_file(elf_in_file_name) if elf_header.e_ident[ELFINFO_CLASS_INDEX] == ELFINFO_CLASS_64: curr_phdr = Elf64_Phdr('\0' * ELF64_PHDR_SIZE) # Offset into program header where the p_flags field is stored phdr_flag_off = 4 else: curr_phdr = Elf32_Phdr('\0' * ELF32_PHDR_SIZE) # Offset into program header where the p_flags field is stored phdr_flag_off = 24 # Open files elf_in_fp = OPEN(elf_in_file_name, "rb+") # Go to the start of the p_flag entry in the first program header file_offset = elf_header.e_phoff + phdr_flag_off # XXX Get these from env? DP_CODE_ALIGN = 0x100 DP_PAGE_SIZE = 4096 DP_HASH_SIZE = 32 # SHA-256 DP_HASH_MAGIC = 0xC0DEDEC0 PH_PERM_RW = 0x06 PH_PERM_RX = 0x05 PH_PERM_RO = 0x04 PH_PERM_MASK = 0x07 page_size = DP_PAGE_SIZE hash_size = DP_HASH_SIZE # First identify the hash segment. It is the first RW section. # Its Align should be 8, and its size a multiple of DP_HASH_SIZE; hash_seg_idx = -1 for i in range(elf_header.e_phnum): curr_phdr = phdr_table[i] if (curr_phdr.p_align == 8 and (curr_phdr.p_flags & PH_PERM_MASK) == PH_PERM_RW and curr_phdr.p_filesz != 0 and (curr_phdr.p_filesz % DP_HASH_SIZE) == 0): hash_seg_idx = i # Validate the contents of the hash segment. It should be # filled with DP_HASH_MAGIC elf_in_fp.seek(curr_phdr.p_offset) hash_data = ""; while (len(hash_data) < curr_phdr.p_filesz): hash_data = hash_data + elf_in_fp.read(curr_phdr.p_filesz - len(hash_data)) hash_data = struct.unpack("I" * (curr_phdr.p_filesz / 4), hash_data) for v in hash_data[:]: if (v != DP_HASH_MAGIC): hash_seg_idx = -1 break; if (hash_seg_idx != -1): break if (hash_seg_idx == -1): # return if there is no hash segment. return 0 hash_phdr = phdr_table[hash_seg_idx] # Now find the code segment for the hashes. Look for matching number of pages code_seg_idx = -1 code_seg_pages = hash_phdr.p_filesz / DP_HASH_SIZE for i in range(elf_header.e_phnum): curr_phdr = phdr_table[i] curr_pages = (curr_phdr.p_filesz + DP_PAGE_SIZE - 1) / DP_PAGE_SIZE if (curr_phdr.p_align == DP_CODE_ALIGN and (curr_phdr.p_flags & PH_PERM_MASK) == PH_PERM_RX and curr_pages == code_seg_pages): if (code_seg_idx != -1): raise RuntimeError, 'Multiple code segments match for: ' + code_seg_pages + ' pages' code_seg_idx = i if (code_seg_idx == -1): raise RuntimeError, 'No matching code segment found' code_phdr = phdr_table[code_seg_idx] # Now hash the pages in the code segment hashes = [] elf_in_fp.seek(code_phdr.p_offset) bytes_left = code_phdr.p_filesz; while (bytes_left > 0): bytes_in_page = min(bytes_left, DP_PAGE_SIZE) page = ""; while (len(page) < bytes_in_page): page = page + elf_in_fp.read(bytes_in_page - len(page)) if (len(page) < DP_PAGE_SIZE): page = page + (struct.pack('b', 0) * (DP_PAGE_SIZE - len(page))) hashes = hashes + [generate_hash(page, 'SHA256')] bytes_left -= bytes_in_page # And write them to the hash segment elf_in_fp.seek(hash_phdr.p_offset) for h in hashes[:]: elf_in_fp.write(h) # Close files elf_in_fp.close() return 0 #---------------------------------------------------------------------------- # BOOT TOOLS END #---------------------------------------------------------------------------- #---------------------------------------------------------------------------- # HELPER FUNCTIONS BEGIN #---------------------------------------------------------------------------- #---------------------------------------------------------------------------- # Create a list to hold all segment information from an input SCL file #---------------------------------------------------------------------------- def readSCL(filename, global_dict): scl_fp = OPEN(filename,'r') # Initialize file_data = scl_fp.readlines() num_lines = len(file_data) current_line = '' previous_line = '' strip_chars = '(){}[]' i = 0 bracket_counter = 0 seg_list = [] # Parse through all lines while i < num_lines: # Save the last line read previous_line = current_line current_line = file_data[i] # Look for the symbol '{' for the line to read. # Use bracket counter to skip nested '{ }' if ('{' in current_line): if bracket_counter is 0: # Create a new SegmentInfo class and set up tokens new_scl_entry = SegmentInfo() previous_line = previous_line.strip() tokens = previous_line.split(' ') # Check that at least two tokens were parsed # Token 1: Segment Name # Token 2: Start Address -- not used in MBN tools if len(tokens) < 2: raise RuntimeError, 'SCL Segment Syntax malformed: ' + previous_line # Get the segment flags corresponding to the segment name description new_scl_entry.flag = getSegmentFlag(tokens[0].strip(strip_chars)) seg_list.append(new_scl_entry) bracket_counter += 1 elif '}' in current_line: bracket_counter -= 1 i+=1 scl_fp.close() return seg_list #---------------------------------------------------------------------------- # Given a string parsed from a SCL file, returns the ELF segment flags #---------------------------------------------------------------------------- def getSegmentFlag(seg_info): ret_val = None # Define string values for various types of segments RO = "RO" RW = "RW" ZI = "ZI" PAGEABLE = "PAGED" NOTPAGEABLE = "NOTPAGED" SWAPABLE = "SWAPPED" SWAP_POOL = "SWAP_POOL" RESERVED = "RESERVED" HASHTBL = "HASH" SHARED = "SHARED" NOTUSED = "NOTUSED" BOOT_SEGMENT = "BOOT_SEGMENT" CODE = "CODE" L4BSP = "L4BSP" POOL_INDEX_0 = "INDEX_0" POOL_INDEX_1 = "INDEX_1" # New definitions for EOS demand paging NONPAGE = "NONPAGE" PAGEUNLOCKED = "PAGEUNLOCKED" PAGELOCKED = "PAGELOCKED" UNSECURE = "UNSECURE" if seg_info is None or len(seg_info) is 0: raise RuntimeError, 'Invalid segment information passed: ' + seg_info # Conditional checks and assignments of the corresponding segment flag values if NOTPAGEABLE in seg_info: if RO in seg_info: ret_val = MI_PBT_ELF_AMSS_NON_PAGED_RO_SEGMENT elif CODE in seg_info: ret_val = MI_PBT_ELF_AMSS_NON_PAGED_RO_SEGMENT elif ZI in seg_info: if SWAP_POOL in seg_info: if POOL_INDEX_0 in seg_info: ret_val = MI_PBT_ELF_SWAP_POOL_NON_PAGED_ZI_SEGMENT_INDEX0 else: ret_val = MI_PBT_ELF_SWAP_POOL_NON_PAGED_ZI_SEGMENT_INDEX1 else: ret_val = MI_PBT_ELF_AMSS_NON_PAGED_ZI_SEGMENT elif NOTUSED in seg_info: ret_val = MI_PBT_ELF_AMSS_NON_PAGED_NOTUSED_SEGMENT elif SHARED in seg_info: ret_val = MI_PBT_ELF_AMSS_NON_PAGED_SHARED_SEGMENT elif HASHTBL in seg_info: ret_val = MI_PBT_ELF_HASH_SEGMENT elif BOOT_SEGMENT in seg_info: ret_val = MI_PBT_ELF_BOOT_SEGMENT elif L4BSP in seg_info: ret_val = MI_PBT_ELF_NON_PAGED_L4BSP_SEGMENT else: ret_val = MI_PBT_ELF_AMSS_NON_PAGED_RW_SEGMENT elif PAGEABLE in seg_info: if RO in seg_info or CODE in seg_info: if SWAPABLE in seg_info: if POOL_INDEX_0 in seg_info: ret_val = MI_PBT_ELF_SWAPPED_PAGED_RO_SEGMENT_INDEX0 else: ret_val = MI_PBT_ELF_SWAPPED_PAGED_RO_SEGMENT_INDEX1 else: ret_val = MI_PBT_ELF_AMSS_PAGED_RO_SEGMENT elif ZI in seg_info: ret_val = MI_PBT_ELF_AMSS_PAGED_ZI_SEGMENT elif NOTUSED in seg_info: ret_val = MI_PBT_ELF_AMSS_PAGED_NOTUSED_SEGMENT elif SHARED in seg_info: ret_val = MI_PBT_ELF_AMSS_PAGED_SHARED_SEGMENT elif L4BSP in seg_info: ret_val = MI_PBT_ELF_PAGED_L4BSP_SEGMENT else: ret_val = MI_PBT_ELF_AMSS_PAGED_RW_SEGMENT elif PAGELOCKED in seg_info: ret_val = MI_PBT_ELF_PAGED_LOCKED_SEGMENT elif PAGEUNLOCKED in seg_info: ret_val = MI_PBT_ELF_PAGED_UNLOCKED_SEGMENT elif NONPAGE in seg_info: ret_val = MI_PBT_ELF_RESIDENT_SEGMENT elif UNSECURE in seg_info: ret_val = MI_PBT_ELF_UNSECURE_SEGMENT else: raise RuntimeError, 'The segment name is wrongly defined in the SCL file: ' + seg_info return ret_val #---------------------------------------------------------------------------- # Pad a file with specific number of bytes # Note: Assumes the fp is seeked to the correct location of padding #---------------------------------------------------------------------------- def pad_file(fp, num_bytes, value): if num_bytes < 0: raise RuntimeError, "Number of bytes to pad must be greater than zero" while num_bytes > 0: fp.write('%c' % value) num_bytes -= 1 return #---------------------------------------------------------------------------- # Concatenates the files listed in 'sources' in order and writes to 'target' #---------------------------------------------------------------------------- def concat_files (target, sources): if type(sources) is not list: sources = [sources] target_file = OPEN(target,'wb') for fname in sources: file = OPEN(fname,'rb') while True: bin_data = file.read(65536) if not bin_data: break target_file.write(bin_data) file.close() target_file.close() #---------------------------------------------------------------------------- # Parse build configurable values and assign to global variables for tools #---------------------------------------------------------------------------- def init_build_vars(env): # Maximum size of Certificate Chain used in Secure Boot global CERT_CHAIN_ONEROOT_MAXSIZE CERT_CHAIN_ONEROOT_MAXSIZE = get_dict_value(env['GLOBAL_DICT'], 'CERT_CHAIN_MAXSIZE', (6*1024)) # Maximum size of the XML Header used in encrypted ELF images global XML_HEADER_MAXSIZE XML_HEADER_MAXSIZE = get_dict_value(env['GLOBAL_DICT'], 'XML_HEADER_MAXSIZE', (2*1024)) #---------------------------------------------------------------------------- # Generates the global dictionary and add to the environment #---------------------------------------------------------------------------- def generate_global_dict(env): # Get file names for 'cust' and 'targ' auto-generated files inside 'build/ms' cust_h = env.subst('CUST${BUILD_ID}.H').lower() targ_h = env.subst('TARG${BUILD_ID}.H').lower() cust_file_name = str(env.FindFile(cust_h, "${INC_ROOT}/build/ms")) targ_file_name = str(env.FindFile(targ_h, "${INC_ROOT}/build/ms")) # Check that files are present if (os.path.exists(cust_file_name) is True) and \ (os.path.exists(targ_file_name) is True): # Populate the dictionary from the auto-generated files global_dict = populate_dictionary(targ_file_name, cust_file_name) else: global_dict = {} # Add the dictionary to the environment env.Replace(GLOBAL_DICT = global_dict) #---------------------------------------------------------------------------- # Populate the dictionary from a list of input files #---------------------------------------------------------------------------- def populate_dictionary(*args): if len(args) < 1: raise RuntimeError, "At least 1 file must be specified as an input" global_dict = {} Fields = ["Define", "Key", "Value"] # For each input file for i in range(len(args)): template_file_path = args[i] instream = OPEN(template_file_path, 'r') # Tokenize each line with a white space values = csv.DictReader(instream, Fields, delimiter=" ") for values in itertools.izip(values): new_entry = values[0] # Verify the parsed tokens if (new_entry['Define'] == '#define') and \ (new_entry['Key'] != None) and \ (new_entry['Value'] != None): new_key = new_entry['Key'].strip() new_value = new_entry['Value'].strip() # If value pair is empty string, assume feature definition is true if new_value == '': new_value = 'yes' # Check for and handle text replacements as we parse if global_dict is not None and len(global_dict.keys()) > 0: for key in global_dict: new_value = new_value.replace(key, str(global_dict.get(key))) # Attempt to evaluate value try: new_value = eval(new_value) # Catch exceptions and do not evaluate except: pass # Add to global dictionary global_dict[new_key] = new_value instream.close() return global_dict #---------------------------------------------------------------------------- # Filter out a generic dictionary from the global dictionary #---------------------------------------------------------------------------- def filter_dictionary(env, global_dict, **kwargs): # Check for Image Type # If IMAGE_TYPE parameter is not provided, raise error if not kwargs.has_key('IMAGE_TYPE'): raise RuntimeError, "IMAGE_TYPE must be defined to use FilterDictionary." else: image_type = kwargs.get('IMAGE_TYPE') if type(image_type) is not str: raise RuntimeError, "IMAGE_TYPE must be of string type." # Check for Flash Type # If FLASH_TYPE parameter is not provided, default to 'nand' if not kwargs.has_key('FLASH_TYPE'): flash_type = 'nand' else: flash_type = kwargs.get('FLASH_TYPE') if type(flash_type) is not str: raise RuntimeError, "FLASH_TYPE must be of string type. " # Check for MBN Type # If MBN_TYPE parameter is not provided, default to 'elf' if not kwargs.has_key('MBN_TYPE'): mbn_type = 'elf' else: mbn_type = kwargs.get('MBN_TYPE') if mbn_type != 'elf' and mbn_type != 'bin': raise RuntimeError, "MBN_TYPE currently not supported: " + mbn_type # Check for Image ID # If IMAGE_ID parameter is not provided, default to ID 0 if not kwargs.has_key('IMAGE_ID'): image_id = ImageType.NONE_IMG else: image_id = kwargs.get('IMAGE_ID') if type(image_id) is not int: raise RuntimeError, "IMAGE_ID must be of integer type." # Initialize gen_dict = {} image_dest = 0 image_source = 0 # Check for image_type if image_type not in image_id_table: id = image_id id_match_str = image_type.upper() + "_IMG" id_mbn_type = mbn_type else: id = image_id_table[image_type][0] id_match_str = image_id_table[image_type][1] id_mbn_type = image_id_table[image_type][2] # Handle MBN Type and assign image destination address if id_mbn_type is 'elf': pass elif id_mbn_type is 'bin': template_key_match = 'IMAGE_KEY_' + id_match_str + "_DEST_ADDR" if template_key_match in global_dict: image_dest = global_dict[template_key_match] else: raise RuntimeError, "Builds file does not have IMAGE_KEY pair for: " + image_type else: raise RuntimeError, "MBN_TYPE currently not supported: " + mbn_type # Assign generic dictionary key/value pairs gen_dict['IMAGE_KEY_IMAGE_ID'] = id gen_dict['IMAGE_KEY_IMAGE_DEST'] = image_dest gen_dict['IMAGE_KEY_IMAGE_SOURCE'] = image_source gen_dict['IMAGE_KEY_FLASH_TYPE'] = flash_type gen_dict['IMAGE_KEY_MBN_TYPE'] = id_mbn_type gen_dict['IMAGE_KEY_ID_MATCH_STR'] = id_match_str gen_dict['IMAGE_KEY_FLASH_AUTO_DETECT_MAX_PAGE'] = \ get_dict_value(global_dict,'FLASH_AUTO_DETECT_MAX_PAGE', 8192) gen_dict['IMAGE_KEY_FLASH_AUTO_DETECT_MIN_PAGE'] = \ get_dict_value(global_dict,'FLASH_AUTO_DETECT_MIN_PAGE', 2048) gen_dict['IMAGE_KEY_MAX_SIZE_OF_VERIFY_BUFFER'] = \ get_dict_value(global_dict,'MAX_SIZE_OF_VERIFY_BUFFER', 8192) gen_dict['IMAGE_KEY_BOOT_SMALL_PREAMBLE'] = \ get_dict_value(global_dict,'BOOT_SMALL_PREAMBLE', 1) # Get OEM root certificate select and number oem_root_cert_sel = get_dict_value(global_dict,'OEM_ROOT_CERT_SEL', 1) oem_num_root_certs = get_dict_value(global_dict,'OEM_NUM_ROOT_CERTS', 1) # Error checking for OEM configurable values if oem_root_cert_sel in range(1, MAX_NUM_ROOT_CERTS + 1) and \ oem_num_root_certs in range(1, MAX_NUM_ROOT_CERTS + 1) and \ oem_root_cert_sel <= oem_num_root_certs: gen_dict['IMAGE_KEY_OEM_ROOT_CERT_SEL'] = oem_root_cert_sel gen_dict['IMAGE_KEY_OEM_NUM_ROOT_CERTS'] = oem_num_root_certs else: raise RuntimeError, "Invalid OEM root certificate configuration values" # Assign additional dictionary key/values pair as needed by tools. return gen_dict #---------------------------------------------------------------------------- # Get index value from dictionary if exists, otherwise return default #---------------------------------------------------------------------------- def get_dict_value(dict, key_string, default): key = 'IMAGE_KEY_' + key_string if key in dict: return dict[key] else: return default #---------------------------------------------------------------------------- # Preprocess an ELF file and return the ELF Header Object and an # array of ELF Program Header Objects #---------------------------------------------------------------------------- def preprocess_elf_file(elf_file_name): # Initialize elf_fp = OPEN(elf_file_name, 'rb') elf_header = Elf_Ehdr_common(elf_fp.read(ELF_HDR_COMMON_SIZE)) if verify_elf_header(elf_header) is False: raise RuntimeError, "ELF file failed verification: " + elf_file_name elf_fp.seek(0) if elf_header.e_ident[ELFINFO_CLASS_INDEX] == ELFINFO_CLASS_64: elf_header = Elf64_Ehdr(elf_fp.read(ELF64_HDR_SIZE)) else: elf_header = Elf32_Ehdr(elf_fp.read(ELF32_HDR_SIZE)) phdr_table = [] # Verify ELF header information if verify_elf_header(elf_header) is False: raise RuntimeError, "ELF file failed verification: " + elf_file_name # Get program header size phdr_size = elf_header.e_phentsize # Find the program header offset file_offset = elf_header.e_phoff elf_fp.seek(file_offset) # Read in the program headers for i in range(elf_header.e_phnum): if elf_header.e_ident[ELFINFO_CLASS_INDEX] == ELFINFO_CLASS_64: phdr_table.append(Elf64_Phdr(elf_fp.read(phdr_size))) else: phdr_table.append(Elf32_Phdr(elf_fp.read(phdr_size))) elf_fp.close() return [elf_header, phdr_table] #---------------------------------------------------------------------------- # Get the hash table address from an input ELF file #---------------------------------------------------------------------------- def get_hash_address(elf_file_name): [elf_header, phdr_table] = preprocess_elf_file(elf_file_name) last_paddr = 0 last_paddr_segment = 0 # Find the segment with the largest physical address. # Hash segment's physical address will be immediately after this segment. for i in range(elf_header.e_phnum): curr_phdr = phdr_table[i] if curr_phdr.p_paddr > last_paddr: # Skip the demand paging segment as it would be outside the physical RAM location if MI_PBT_SEGMENT_TYPE_VALUE(curr_phdr.p_flags) != MI_PBT_XBL_SEC_SEGMENT: last_paddr = curr_phdr.p_paddr; last_paddr_segment = i; max_phdr = phdr_table[last_paddr_segment] ret_val = (((max_phdr.p_paddr + max_phdr.p_memsz - 1) & \ ~(ELF_BLOCK_ALIGN-1)) + ELF_BLOCK_ALIGN) return ret_val #---------------------------------------------------------------------------- # Verify ELF header contents from an input ELF file #---------------------------------------------------------------------------- def verify_elf_header(elf_header): if (elf_header.e_ident[ELFINFO_MAG0_INDEX] != ELFINFO_MAG0) or \ (elf_header.e_ident[ELFINFO_MAG1_INDEX] != ELFINFO_MAG1) or \ (elf_header.e_ident[ELFINFO_MAG2_INDEX] != ELFINFO_MAG2) or \ (elf_header.e_ident[ELFINFO_MAG3_INDEX] != ELFINFO_MAG3) or \ ((elf_header.e_ident[ELFINFO_CLASS_INDEX] != ELFINFO_CLASS_64) and \ (elf_header.e_ident[ELFINFO_CLASS_INDEX] != ELFINFO_CLASS_32)) or \ (elf_header.e_ident[ELFINFO_VERSION_INDEX] != ELFINFO_VERSION_CURRENT): return False else: return True #---------------------------------------------------------------------------- # Perform file copy given offsets and the number of bytes to copy #---------------------------------------------------------------------------- def file_copy_offset(in_fp, in_off, out_fp, out_off, num_bytes): in_fp.seek(in_off) read_in = in_fp.read(num_bytes) out_fp.seek(out_off) out_fp.write(read_in) return num_bytes #---------------------------------------------------------------------------- # sha1/sha256 hash routine wrapper #---------------------------------------------------------------------------- def header_size(header_version): if header_version >= 6: return 48 else: return 40 #---------------------------------------------------------------------------- # sha1/sha256 hash routine wrapper #---------------------------------------------------------------------------- def generate_hash(in_buf, sha_algo): # Initialize a SHA1 object from the Python hash library if sha_algo == 'SHA384': m = hashlib.sha384() elif sha_algo == 'SHA256': m = hashlib.sha256() else: m = hashlib.sha1() # Set the input buffer and return the output digest m.update(in_buf) return m.digest() #---------------------------------------------------------------------------- # Initialize the hash program header. #---------------------------------------------------------------------------- def initialize_hash_phdr(elf_in_file_name, hash_tbl_size, hdr_size, hdr_offset, is_elf64): # Set hash header offset to page size boundary. Hash table will be # located at first segment of elf image. hash_hdr_size = hdr_size hash_hdr_offset = hdr_offset hash_tbl_offset = hash_hdr_offset + hash_hdr_size hash_tbl_end_addr = hash_tbl_offset + hash_tbl_size; pad_hash_segment = (hash_tbl_end_addr) & (ELF_BLOCK_ALIGN-1) # Update the hash table program header if is_elf64 is True: hash_Phdr = Elf64_Phdr('\0'*ELF64_PHDR_SIZE) else: hash_Phdr = Elf32_Phdr('\0'*ELF32_PHDR_SIZE) hash_Phdr.p_flags = MI_PBT_ELF_HASH_SEGMENT hash_Phdr.p_align = ELF_BLOCK_ALIGN hash_Phdr.p_offset = hash_hdr_offset hash_Phdr.p_memsz = hash_hdr_size + hash_tbl_size + (ELF_BLOCK_ALIGN - pad_hash_segment) hash_Phdr.p_filesz = hash_hdr_size + hash_tbl_size hash_Phdr.p_type = NULL_TYPE hash_Phdr.p_vaddr = get_hash_address(elf_in_file_name) hash_Phdr.p_paddr = hash_Phdr.p_vaddr return [hash_Phdr, pad_hash_segment, hash_tbl_end_addr, hash_tbl_offset] #---------------------------------------------------------------------------- # image_preamble #---------------------------------------------------------------------------- def image_preamble(gen_dict, preamble_file_name, boot_sbl_header, num_of_pages=None): # Generate the preamble file preamble_fp = OPEN(preamble_file_name, 'wb') # Initialize max_size_verify = gen_dict['IMAGE_KEY_MAX_SIZE_OF_VERIFY_BUFFER'] flash_max_page = gen_dict['IMAGE_KEY_FLASH_AUTO_DETECT_MAX_PAGE'] flash_min_page = gen_dict['IMAGE_KEY_FLASH_AUTO_DETECT_MIN_PAGE'] autodetectpage = [int('0xFFFFFFFF',16)] * max_size_verify # The first three entries in the preamble must include the following values autodetectpage[0] = FLASH_CODE_WORD autodetectpage[1] = MAGIC_NUM if (num_of_pages == 64): autodetectpage[2] = AUTODETECT_PAGE_SIZE_MAGIC_NUM64 elif (num_of_pages == 128): autodetectpage[2] = AUTODETECT_PAGE_SIZE_MAGIC_NUM128 else: autodetectpage[2] = AUTODETECT_PAGE_SIZE_MAGIC_NUM # Package the list into binary data to be written to the preamble s = struct.Struct('I' * max_size_verify) packed_data = s.pack(*autodetectpage) # Output preamble pages based on maximum/minimum page size support for i in range(flash_max_page/flash_min_page): preamble_fp.write(packed_data[:flash_min_page]) # Determine appropriate amount of padding for the preamble and # update the boot_sbl_header accordingly if gen_dict['IMAGE_KEY_BOOT_SMALL_PREAMBLE'] == 1: boot_sbl_header.image_src += (flash_max_page + flash_min_page) amount_to_write = flash_min_page else: boot_sbl_header.image_src += flash_max_page * 2 amount_to_write = flash_max_page pad_file(preamble_fp, amount_to_write, PAD_BYTE_1) preamble_fp.close() return boot_sbl_header #---------------------------------------------------------------------------- # Helper functions to parse ELF program headers #---------------------------------------------------------------------------- def MI_PBT_SEGMENT_TYPE_VALUE(x): return ( ((x) & MI_PBT_FLAG_SEGMENT_TYPE_MASK) >> MI_PBT_FLAG_SEGMENT_TYPE_SHIFT ) def MI_PBT_PAGE_MODE_VALUE(x): return ( ((x) & MI_PBT_FLAG_PAGE_MODE_MASK) >> MI_PBT_FLAG_PAGE_MODE_SHIFT ) def MI_PBT_ACCESS_TYPE_VALUE(x): return ( ((x) & MI_PBT_FLAG_ACCESS_TYPE_MASK) >> MI_PBT_FLAG_ACCESS_TYPE_SHIFT ) def MI_PBT_CHECK_FLAG_TYPE(x): return (MI_PBT_SEGMENT_TYPE_VALUE(x) != MI_PBT_HASH_SEGMENT) and \ (MI_PBT_ACCESS_TYPE_VALUE(x) != MI_PBT_NOTUSED_SEGMENT) and \ (MI_PBT_ACCESS_TYPE_VALUE(x) != MI_PBT_SHARED_SEGMENT) #---------------------------------------------------------------------------- # Helper functions to open a file and return a valid file object #---------------------------------------------------------------------------- def OPEN(file_name, mode): try: fp = open(file_name, mode) except IOError: raise RuntimeError, "The file could not be opened: " + file_name # File open has succeeded with the given mode, return the file object return fp #---------------------------------------------------------------------------- # Helper functions to insert MCs in SBL1(Badger) if ENABLE_VIRTUAL_BLK is ON #---------------------------------------------------------------------------- def insert_SBL1_magicCookie (env, target): file = open(target, "rb") #read the file contents filedata = file.read() length = len(filedata) file.close() if (length <= VIRTUAL_BLOCK_SIZE): return None else: #remove the previous file os.remove(target) #generate new file for appending target data + required MCs file = open(target, "ab") while length > VIRTUAL_BLOCK_SIZE: filedata_till_128kb = filedata[0:VIRTUAL_BLOCK_SIZE] filedata_after_128kb = filedata[VIRTUAL_BLOCK_SIZE:length] a = str(hex(FLASH_CODE_WORD)) mc1 = chr(int(a[8:10],16)) + chr(int(a[6:8],16)) + chr(int(a[4:6],16)) + chr(int(a[2:4],16)) b = str(hex(MAGIC_NUM)) mc2 = chr(int(b[8:10],16)) + chr(int(b[6:8],16)) + chr(int(b[4:6],16)) + chr(int(b[2:4],16)) c = str(hex(SBL_VIRTUAL_BLOCK_MAGIC_NUM)) mc3 = chr(int(c[8:10],16)) + chr(int(c[6:8],16)) + chr(int(c[4:6],16)) + chr(int(c[2:4],16)) MC_inserted_data = filedata_till_128kb + mc1 + mc2 + mc3 file.write(MC_inserted_data) filedata = filedata_after_128kb length = len(filedata) #copy the leftover data (<128KB) in output file if length > 0: file.write(filedata) #close the final output file file.close() # MC_insertion code end #---------------------------------------------------------------------------- # Helper functions to remove MCs in SBL1(Badger) #---------------------------------------------------------------------------- def remove_SBL1_magicCookie (env, target, dest): file = open(target, "rb") #read the file contents filedata = file.read() length = len(filedata) file.close() #generate new file for appending target data + required MCs file = open(dest, "ab") while length > VIRTUAL_BLOCK_SIZE: filedata_till_128kb = filedata[0:VIRTUAL_BLOCK_SIZE] # skipped 12 byte of Virtual Block Magic Cookie Header filedata_after_128kb = filedata[VIRTUAL_BLOCK_SIZE+MAGIC_COOKIE_LENGTH:length] file.write(filedata_till_128kb) filedata = filedata_after_128kb length = len(filedata) #copy the leftover data (<128KB) in output file if length > 0: file.write(filedata) #close the final output file file.close() # MC_removal code end #---------------------------------------------------------------------------- # Helper functions to pad SBL1 image # min_size defaults to 256k # If page_size or num_of_pages is set to 0, the variable is unset #---------------------------------------------------------------------------- def pad_SBL1_image (env, target, min_size_with_pad=MIN_IMAGE_SIZE_WITH_PAD, page_size=0, num_of_pages=0): file = open(target, "rb") #read the file contents filedata = file.read() length = len(filedata) file.close() multiple = 1 alignment = page_size * num_of_pages if (length > alignment and alignment > 0): import math multiple = math.ceil(length/float(alignment)) final_image_size = max(min_size_with_pad, multiple * alignment) if length < final_image_size: sbl1_fp = open(target, 'ab') pad_file (sbl1_fp, (final_image_size-length), PAD_BYTE_0) sbl1_fp.close() # SBL1 pad code end #---------------------------------------------------------------------------- # HELPER FUNCTIONS END #----------------------------------------------------------------------------