coreboot-kgpe-d16/util/qualcomm/mbn_tools.py

2373 lines
87 KiB
Python
Executable File

#!/usr/bin/env python3
#===============================================================================
#
# MBN TOOLS
#
# GENERAL DESCRIPTION
# Contains all MBN Utilities for image generation
#
# 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<bid>.h, targ<bid>.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 = 127 # 0x7F
ELFINFO_MAG1 = 69 # E
ELFINFO_MAG2 = 76 # L
ELFINFO_MAG3 = 70 # F
ELFINFO_CLASS_INDEX = 4
ELFINFO_CLASS_32 = 1
ELFINFO_CLASS_64 = 2
ELFINFO_VERSION_INDEX = 6
ELFINFO_VERSION_CURRENT = 1
ELF_BLOCK_ALIGN = 0x1000
ALIGNVALUE_1MB = 0x100000
ALIGNVALUE_4MB = 0x400000
ELFINFO_DATA2LSB = b'\x01'
ELFINFO_EXEC_ETYPE = b'\x02\x00'
ELFINFO_ARM_MACHINETYPE = b'\x28\x00'
ELFINFO_VERSION_EV_CURRENT = b'\x01\x00\x00\x00'
ELFINFO_SHOFF = 0x00
ELFINFO_PHNUM = b'\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__.items():
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__.items():
print(attr, value)
def getPackedData(self):
if type(self.e_ident) == str:
packvalue = bytes(self.e_ident, 'utf-8')
else:
packvalue = self.e_ident
values = [packvalue,
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__.items():
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__.items():
print(attr, value)
def getPackedData(self):
if type(self.e_ident) == str:
packvalue = bytes(self.e_ident, 'utf-8')
else:
packvalue = self.e_ident
values = [packvalue,
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__.items():
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(b'\0' * ELF64_PHDR_SIZE)
elf_header_size = ELF64_HDR_SIZE
is_elf64 = True
else:
new_phdr = Elf32_Phdr(b'\0' * ELF32_PHDR_SIZE)
elf_header_size = ELF32_HDR_SIZE
is_elf64 = False
hash = b'\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(b'\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(b'\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 = b'\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 = b'\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):
print("MAG0[{:d}]\n".format((elf_header.e_ident[ELFINFO_MAG0_INDEX])))
return False
if (elf_header.e_ident[ELFINFO_MAG1_INDEX] != ELFINFO_MAG1):
print("MAG1[{:d}]\n".format((elf_header.e_ident[ELFINFO_MAG1_INDEX])))
return False
if (elf_header.e_ident[ELFINFO_MAG2_INDEX] != ELFINFO_MAG2):
print("MAG2[{:d}]\n".format((elf_header.e_ident[ELFINFO_MAG2_INDEX])))
return False
if (elf_header.e_ident[ELFINFO_MAG3_INDEX] != ELFINFO_MAG3):
print("MAG3[{:d}]\n".format((elf_header.e_ident[ELFINFO_MAG3_INDEX])))
return False
if ((elf_header.e_ident[ELFINFO_CLASS_INDEX] != ELFINFO_CLASS_64) and \
(elf_header.e_ident[ELFINFO_CLASS_INDEX] != ELFINFO_CLASS_32)):
print("ELFINFO_CLASS_INDEX[{:d}]\n".format((elf_header.e_ident[ELFINFO_CLASS_INDEX])))
return False
if (elf_header.e_ident[ELFINFO_VERSION_INDEX] != ELFINFO_VERSION_CURRENT):
print("ELFINFO_VERSION_INDEX[{:d}]\n".format((elf_header.e_ident[ELFINFO_VERSION_INDEX])))
return False
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(b'\0'*ELF64_PHDR_SIZE)
else:
hash_Phdr = Elf32_Phdr(b'\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
#----------------------------------------------------------------------------