2373 lines
87 KiB
Python
Executable File
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
|
|
#----------------------------------------------------------------------------
|