diff options
Diffstat (limited to 'util/qualcomm')
-rwxr-xr-x | util/qualcomm/createxbl.py | 733 | ||||
-rwxr-xr-x | util/qualcomm/ipqheader.py | 129 | ||||
-rwxr-xr-x | util/qualcomm/mbn_tools.py | 2324 | ||||
-rwxr-xr-x | util/qualcomm/mbncat.py | 200 | ||||
-rwxr-xr-x | util/qualcomm/qgpt.py | 234 |
5 files changed, 3620 insertions, 0 deletions
diff --git a/util/qualcomm/createxbl.py b/util/qualcomm/createxbl.py new file mode 100755 index 0000000000..4a218544c0 --- /dev/null +++ b/util/qualcomm/createxbl.py @@ -0,0 +1,733 @@ +#!/usr/bin/env python2 +#============================================================================ +# +#/** @file createxbl.py +# +# GENERAL DESCRIPTION +# Concatentates XBL segments into one ELF image +# +# Copyright (c) 2016, 2018, The Linux Foundation. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of The Linux Foundation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +#**/ +# +#---------------------------------------------------------------------------- +# +# EDIT HISTORY FOR FILE +# +# This section contains comments describing changes made to the module. +# Notice that changes are listed in reverse chronological order. +# +# when who what, where, why +# -------- --- ------------------------------------------------------ +# 03/26/18 tv Added -e to enable extended MBNV5 support +# 09/04/15 et Added -x and -d to embed xbl_sec ELF +# 02/11/15 ck Fixed missing elf type check in ZI OOB feature +# 11/04/14 ck Updated calls to mbn_tools functions +# 10/22/14 ck Added -z option to remove out of bounds ZI segments when converting from 64 to 32 +# 10/10/14 ck Added -c option and logic to enable elf type swapping +# 09/12/14 ck Added single file logic +# 08/29/14 ck Added no_hash option +# 08/29/14 ck Refactored to use proper python arguments and cleaned code +# 06/16/14 niting xbl.mbn to xbl.elf +# 05/28/14 niting Initial revision +# +#============================================================================ +from optparse import OptionParser +import os +import sys +import shutil +import mbn_tools + +PAGE_SIZE = 4096 +SEGMENT_ALIGN = 16 +ELF32_HDR_SIZE = 52 +ELF32_PHDR_SIZE = 32 +ELF64_HDR_SIZE = 64 +ELF64_PHDR_SIZE = 56 + + +############################################################################## +# main +############################################################################## +def main(): + parser = OptionParser(usage='usage: %prog [options] arguments') + + parser.add_option("-f", "--first_filepath", + action="store", type="string", dest="elf_inp_file1", + help="First ELF file to merge.") + + parser.add_option("-s", "--second_filepath", + action="store", type="string", dest="elf_inp_file2", + help="Second ELF file to merge.") + + parser.add_option("-x", "--xbl_sec_filepath", + action="store", type="string", dest="elf_inp_xbl_sec", + help="Second ELF file to merge.") + + parser.add_option("-o", "--output_filepath", + action="store", type="string", dest="binary_out", + help="Merged filename and path.") + + parser.add_option("-a", "--first_elf_arch", + action="store", type="string", dest="elf_1_arch", + help="First (and output) ELF file architecture. '32' or '64'") + + parser.add_option("-b", "--second_elf_arch", + action="store", type="string", dest="elf_2_arch", + help="Second ELF file architecture. '32' or '64'") + + parser.add_option("-d", "--xbl_sec_elf_arch", + action="store", type="string", dest="elf_xbl_sec_arch", + help="xbl_sec file architecture. '32' or '64'") + + parser.add_option("-c", "--output_elf_arch", + action="store", type="string", dest="elf_out_arch", + help="Output ELF file architecture. '32' or '64'" + \ + " If not given defaults to first file arch.") + + parser.add_option("-n", "--no_hash", + action="store_true", dest="hash_image", + help="Disables hashing of image after merging.") + + parser.add_option("-z", "--zi_out_of_bounds", + action="store_true", dest="zi_oob", + help="Removes ZI segments that have addresses greater" + \ + " than 32 bits when converting from a 64 to 32 bit ELF") + + + (options, args) = parser.parse_args() + if not options.elf_inp_file1: + parser.error('First ELF filename not given') + + if not options.binary_out: + parser.error('Output filename not given') + + if not options.elf_1_arch: + parser.error('First ELF architecture not given') + + if (not options.elf_1_arch == '64') and (not options.elf_1_arch == '32'): + parser.error('Invalid First ELF architecture given') + + # Only evaluate elf_2_arch if two files are given for merging + if options.elf_inp_file2: + if (not options.elf_2_arch == '64') and (not options.elf_2_arch == '32'): + parser.error('Invalid Second ELF architecture given') + + # Only evaluate elf_xbl_sec_arch if file is given + if options.elf_inp_xbl_sec: + if (not options.elf_xbl_sec_arch == '64') and (not options.elf_xbl_sec_arch == '32'): + parser.error('Invalid xbl_sec ELF architecture given') + + # If output file architecture is given ensure it is either '32' or '64' + if options.elf_out_arch: + if (not options.elf_out_arch == '64') and (not options.elf_out_arch == '32'): + parser.error('Invalid Output ELF architecture given') + + + gen_dict = {} + + elf_inp_file1 = options.elf_inp_file1 + + # It is valid for only one file to be "merged". This essentially just + # strips off the section names. If second file name is not given then + # set elf_inp_file2 to "" + if options.elf_inp_file2: + elf_inp_file2 = options.elf_inp_file2 + else: + elf_inp_file2 = "" + + # Do same for xbl_sec + elf_inp_xbl_sec = options.elf_inp_xbl_sec if options.elf_inp_xbl_sec else "" + + binary_out = options.binary_out + + if options.elf_1_arch == '64': + is_elf1_64_bit = True + else: + is_elf1_64_bit = False + + # If second filename is not given then set is_elf2_64_bit to false so it + # can be passed even though it is not used. + if options.elf_inp_file2: + if options.elf_2_arch == '64': + is_elf2_64_bit = True + else: + is_elf2_64_bit = False + else: + is_elf2_64_bit = False + + if options.elf_inp_xbl_sec: + if options.elf_xbl_sec_arch == '64': + is_elf_xbl_sec_64_bit = True + else: + is_elf_xbl_sec_64_bit = False + else: + is_elf_xbl_sec_64_bit = False + + # If output ELF arch is given then set is_out_elf_64_bit accordingly. + # If not then default to be input1's setting + if options.elf_out_arch: + if options.elf_out_arch == '64': + is_out_elf_64_bit = True + else: + is_out_elf_64_bit = False + else: + is_out_elf_64_bit = is_elf1_64_bit + + + # Store ZI Out of Bounds value + if not options.zi_oob: + zi_oob_enabled = False + else: + zi_oob_enabled = True + + if options.elf_inp_xbl_sec: + is_ext_mbn_v5 = True + else: + is_ext_mbn_v5 = False + + + mbn_type = 'elf' + header_format = 'reg' + gen_dict['IMAGE_KEY_IMAGE_ID'] = mbn_tools.ImageType.APPSBL_IMG + #gen_dict['IMAGE_KEY_IMAGE_SOURCE'] = 0 + #gen_dict['IMAGE_KEY_IMAGE_DEST'] = 0 + gen_dict['IMAGE_KEY_MBN_TYPE'] = mbn_type + image_header_secflag = 'non_secure' + + source_base = os.path.splitext(str(binary_out))[0] + target_base = os.path.splitext(str(binary_out))[0] + merged_elf = source_base + "_merged.elf" + source_elf = source_base + "_nohash.elf" + target_hash = target_base + ".hash" + target_hash_hd = target_base + "_hash.hd" + target_phdr_elf = target_base + "_phdr.pbn" + target_nonsec = target_base + "_combined_hash.mbn" + + + #print "Input file 1:", elf_inp_file1 + #print "Input file 2:", elf_inp_file2 + #print "Output file:", binary_out + + merge_elfs([], + elf_inp_file1, + elf_inp_file2, + elf_inp_xbl_sec, + merged_elf, + is_elf1_64_bit, + is_elf2_64_bit, + is_elf_xbl_sec_64_bit, + is_out_elf_64_bit, + zi_oob_enabled, + is_ext_mbn_v5) + + + # Hash the image if user did not explicitly say not to + if options.hash_image: + # Just copy the merged elf to the final output name + shutil.move(merged_elf, binary_out) + else: + shutil.copy(merged_elf, source_elf) + + # Create hash table + rv = mbn_tools.pboot_gen_elf([], + source_elf, + target_hash, + elf_out_file_name = target_phdr_elf, + secure_type = image_header_secflag) + if rv: + raise RuntimeError, "Failed to run pboot_gen_elf" + + # Create hash table header + rv = mbn_tools.image_header([], + gen_dict, + target_hash, + target_hash_hd, + image_header_secflag, + is_ext_mbn_v5, + elf_file_name = source_elf) + if rv: + raise RuntimeError, "Failed to create image header for hash segment" + + files_to_cat_in_order = [target_hash_hd, target_hash] + mbn_tools.concat_files (target_nonsec, files_to_cat_in_order) + + # Add the hash segment into the ELF + mbn_tools.pboot_add_hash([], + target_phdr_elf, + target_nonsec, + binary_out) + + return + + +############################################################################## +# roundup +############################################################################## +def roundup(x, precision): + return x if x % precision == 0 else (x + precision - (x % precision)) + +############################################################################## +# merge_elfs +############################################################################## +def merge_elfs(env, + elf_in_file_name1, + elf_in_file_name2, + elf_in_file_xbl_sec, + elf_out_file_name, + is_elf1_64_bit, + is_elf2_64_bit, + is_elf_xbl_sec_64_bit, + is_out_elf_64_bit, + zi_oob_enabled, + is_ext_mbn_v5): + + [elf_header1, phdr_table1] = \ + mbn_tools.preprocess_elf_file(elf_in_file_name1) + + # Check to make sure second file path exists before using + if elf_in_file_name2 != "": + [elf_header2, phdr_table2] = \ + mbn_tools.preprocess_elf_file(elf_in_file_name2) + + # Check to make sure xbl_sec file path exists before using + if elf_in_file_xbl_sec != "": + [elf_headerxblsec, phdr_tablexblsec] = \ + mbn_tools.preprocess_elf_file(elf_in_file_xbl_sec) + + # Open Files + elf_in_fp1 = mbn_tools.OPEN(elf_in_file_name1, "rb") + if elf_in_file_name2 != "": + elf_in_fp2 = mbn_tools.OPEN(elf_in_file_name2, "rb") + if elf_in_file_xbl_sec != "": + elf_in_fpxblsec = mbn_tools.OPEN(elf_in_file_xbl_sec, "rb") + + if elf_out_file_name is not None: + elf_out_fp = mbn_tools.OPEN(elf_out_file_name, "wb+") + + + # Calculate the new program header size. This is dependant on the output + # ELF type and number of program headers going into output. + if is_out_elf_64_bit: + phdr_total_size = elf_header1.e_phnum * ELF64_PHDR_SIZE + phdr_total_count = elf_header1.e_phnum + else: + phdr_total_size = elf_header1.e_phnum * ELF32_PHDR_SIZE + phdr_total_count = elf_header1.e_phnum + + + # This logic only applies if two files are to be merged + if elf_in_file_name2 != "": + if is_out_elf_64_bit: + phdr_total_size += elf_header2.e_phnum * ELF64_PHDR_SIZE + phdr_total_count += elf_header2.e_phnum + else: + phdr_total_size += elf_header2.e_phnum * ELF32_PHDR_SIZE + phdr_total_count += elf_header2.e_phnum + + # Account for xbl_sec header if included + if elf_in_file_xbl_sec != "": + phdr_total_count += 1 + if is_out_elf_64_bit: + phdr_total_size += ELF64_PHDR_SIZE + else: + phdr_total_size += ELF32_PHDR_SIZE + + # Create a new ELF header for the output file + if is_out_elf_64_bit: + out_elf_header = mbn_tools.Elf64_Ehdr('\0' * ELF64_HDR_SIZE) + out_elf_header.e_phoff = ELF64_HDR_SIZE + out_elf_header.e_ehsize = ELF64_HDR_SIZE + out_elf_header.e_phentsize = ELF64_PHDR_SIZE + out_elf_header.e_machine = 183 + out_elf_header.e_ident = str('\x7f' + 'E' + 'L' + 'F' + \ + '\x02' + \ + '\x01' + \ + '\x01' + \ + '\x00' + \ + '\x00' + \ + ('\x00' * 7)) + + out_elf_header.e_entry = elf_header1.e_entry + else: + out_elf_header = mbn_tools.Elf32_Ehdr('\0' * ELF32_HDR_SIZE) + out_elf_header.e_phoff = ELF32_HDR_SIZE + out_elf_header.e_ehsize = ELF32_HDR_SIZE + out_elf_header.e_phentsize = ELF32_PHDR_SIZE + out_elf_header.e_machine = 40 + out_elf_header.e_entry = elf_header1.e_entry + out_elf_header.e_ident = str('\x7f' + 'E' + 'L' + 'F' + \ + '\x01' + \ + '\x01' + \ + '\x01' + \ + '\x00' + \ + '\x00' + \ + ('\x00' * 7)) + + # Address needs to be verified that it is not greater than 32 bits + # as it is possible to go from a 64 bit elf to 32. + if (elf_header1.e_entry > 0xFFFFFFFF): + print "ERROR: File 1's entry point is too large to convert." + exit() + out_elf_header.e_entry = elf_header1.e_entry + + # Common header entries + out_elf_header.e_type = 2 + out_elf_header.e_version = 1 + out_elf_header.e_shoff = 0 + out_elf_header.e_flags = 0 + out_elf_header.e_shentsize = 0 + out_elf_header.e_shnum = 0 + out_elf_header.e_shstrndx = 0 + + + # If ZI OOB is enabled then it is possible that a segment could be discarded + # Scan for that instance and handle before setting e_phnum and writing header + # Ensure ELF output is 32 bit + if zi_oob_enabled == True and is_out_elf_64_bit == False: + for i in range(len(phdr_table1)): + if (phdr_table1[i].p_vaddr > 0xFFFFFFFF) or \ + (phdr_table1[i].p_paddr > 0xFFFFFFFF): + if phdr_table1[i].p_filesz == 0: + phdr_total_count = phdr_total_count - 1 + + if elf_in_file_name2 != "": + for i in range(len(phdr_table2)): + if (phdr_table2[i].p_vaddr > 0xFFFFFFFF) or \ + (phdr_table2[i].p_paddr > 0xFFFFFFFF): + if phdr_table2[i].p_filesz == 0: + phdr_total_count = phdr_total_count - 1 + # Do not include xbl_sec in above calculation + # xbl_sec is to be treated as a single blob + + + # Now it is ok to populate the ELF header and write it out + out_elf_header.e_phnum = phdr_total_count + + # write elf header + if is_out_elf_64_bit == False: + elf_out_fp.write(mbn_tools.Elf32_Ehdr.getPackedData(out_elf_header)) + else: + elf_out_fp.write(mbn_tools.Elf64_Ehdr.getPackedData(out_elf_header)) + + phdr_offset = out_elf_header.e_phoff # offset of where to put next phdr + + # offset the start of the segments just after the program headers + segment_offset = roundup(out_elf_header.e_phoff + phdr_total_size, PAGE_SIZE) + + + # Output first elf data + for i in range(elf_header1.e_phnum): + curr_phdr = phdr_table1[i] + + # Copy program header piece by piece to ensure possible conversion success + if is_out_elf_64_bit == True: + # Converting from 32 to 64 elf requires no data size validation + new_phdr = mbn_tools.Elf64_Phdr('\0' * ELF64_PHDR_SIZE) + new_phdr.p_type = curr_phdr.p_type + new_phdr.p_offset = segment_offset + new_phdr.p_vaddr = curr_phdr.p_vaddr + new_phdr.p_paddr = curr_phdr.p_paddr + new_phdr.p_filesz = curr_phdr.p_filesz + new_phdr.p_memsz = curr_phdr.p_memsz + new_phdr.p_flags = curr_phdr.p_flags + new_phdr.p_align = curr_phdr.p_align + else: + # Converting from 64 to 32 elf requires data size validation + # Note that there is an option to discard a segment if it is only ZI + # and its address is greater than 32 bits + new_phdr = mbn_tools.Elf32_Phdr('\0' * ELF32_PHDR_SIZE) + new_phdr.p_type = curr_phdr.p_type + new_phdr.p_offset = segment_offset + + if curr_phdr.p_vaddr > 0xFFFFFFFF: + if (zi_oob_enabled == True) and (curr_phdr.p_filesz == 0): + continue + else: + print "ERROR: File 1 VAddr is too large for conversion." + exit() + new_phdr.p_vaddr = curr_phdr.p_vaddr + + if curr_phdr.p_paddr > 0xFFFFFFFF: + if (zi_oob_enabled == True) and (curr_phdr.p_filesz == 0): + continue + else: + print "ERROR: File 1 PAddr is too large for conversion." + exit() + new_phdr.p_paddr = curr_phdr.p_paddr + + if curr_phdr.p_filesz > 0xFFFFFFFF: + print "ERROR: File 1 Filesz is too large for conversion." + exit() + new_phdr.p_filesz = curr_phdr.p_filesz + + if curr_phdr.p_memsz > 0xFFFFFFFF: + print "ERROR: File 1 Memsz is too large for conversion." + exit() + new_phdr.p_memsz = curr_phdr.p_memsz + + if curr_phdr.p_flags > 0xFFFFFFFF: + print "ERROR: File 1 Flags is too large for conversion." + exit() + new_phdr.p_flags = curr_phdr.p_flags + + if curr_phdr.p_align > 0xFFFFFFFF: + print "ERROR: File 1 Align is too large for conversion." + exit() + new_phdr.p_align = curr_phdr.p_align + + + #print "i=",i + #print "phdr_offset=", phdr_offset + + # update output file location to next phdr location + elf_out_fp.seek(phdr_offset) + # increment phdr_offset to next location + phdr_offset += out_elf_header.e_phentsize + + inp_data_offset = curr_phdr.p_offset # used to read data from input file + +# print "inp_data_offset=" +# print inp_data_offset +# +# print "curr_phdr.p_offset=" +# print curr_phdr.p_offset +# +# print "curr_phdr.p_filesz=" +# print curr_phdr.p_filesz + + # output current phdr + if is_out_elf_64_bit == False: + elf_out_fp.write(mbn_tools.Elf32_Phdr.getPackedData(new_phdr)) + else: + elf_out_fp.write(mbn_tools.Elf64_Phdr.getPackedData(new_phdr)) + + # Copy the ELF segment + bytes_written = mbn_tools.file_copy_offset(elf_in_fp1, + inp_data_offset, + elf_out_fp, + new_phdr.p_offset, + new_phdr.p_filesz) + + # update data segment offset to be aligned after previous segment + segment_offset += roundup(new_phdr.p_filesz, SEGMENT_ALIGN); + elf_in_fp1.close() + + # Output second elf data if applicable + if elf_in_file_name2 != "": + for i in range(elf_header2.e_phnum): + curr_phdr = phdr_table2[i] + + # Copy program header piece by piece to ensure possible conversion success + if is_out_elf_64_bit == True: + # Converting from 32 to 64 elf requires no data size validation + new_phdr = mbn_tools.Elf64_Phdr('\0' * ELF64_PHDR_SIZE) + new_phdr.p_type = curr_phdr.p_type + new_phdr.p_offset = segment_offset + new_phdr.p_vaddr = curr_phdr.p_vaddr + new_phdr.p_paddr = curr_phdr.p_paddr + new_phdr.p_filesz = curr_phdr.p_filesz + new_phdr.p_memsz = curr_phdr.p_memsz + new_phdr.p_flags = curr_phdr.p_flags + new_phdr.p_align = curr_phdr.p_align + else: + # Converting from 64 to 32 elf requires data size validation + # Note that there is an option to discard a segment if it is only ZI + # and its address is greater than 32 bits + new_phdr = mbn_tools.Elf32_Phdr('\0' * ELF32_PHDR_SIZE) + new_phdr.p_type = curr_phdr.p_type + new_phdr.p_offset = segment_offset + + if curr_phdr.p_vaddr > 0xFFFFFFFF: + if (zi_oob_enabled == True) and (curr_phdr.p_filesz == 0): + continue + else: + print "ERROR: File 2 VAddr is too large for conversion." + exit() + new_phdr.p_vaddr = curr_phdr.p_vaddr + + if curr_phdr.p_paddr > 0xFFFFFFFF: + if (zi_oob_enabled == True) and (curr_phdr.p_filesz == 0): + continue + else: + print "ERROR: File 2 PAddr is too large for conversion." + exit() + new_phdr.p_paddr = curr_phdr.p_paddr + + if curr_phdr.p_filesz > 0xFFFFFFFF: + print "ERROR: File 2 Filesz is too large for conversion." + exit() + new_phdr.p_filesz = curr_phdr.p_filesz + + if curr_phdr.p_memsz > 0xFFFFFFFF: + print "ERROR: File 2 Memsz is too large for conversion." + exit() + new_phdr.p_memsz = curr_phdr.p_memsz + + if curr_phdr.p_flags > 0xFFFFFFFF: + print "ERROR: File 2 Flags is too large for conversion." + exit() + new_phdr.p_flags = curr_phdr.p_flags + + if curr_phdr.p_align > 0xFFFFFFFF: + print "ERROR: File 2 Align is too large for conversion." + exit() + new_phdr.p_align = curr_phdr.p_align + + +# print "i=",i +# print "phdr_offset=", phdr_offset + + # update output file location to next phdr location + elf_out_fp.seek(phdr_offset) + # increment phdr_offset to next location + phdr_offset += out_elf_header.e_phentsize + + inp_data_offset = curr_phdr.p_offset # used to read data from input file + +# print "inp_data_offset=" +# print inp_data_offset +# +# print "curr_phdr.p_offset=" +# print curr_phdr.p_offset +# +# print "curr_phdr.p_filesz=" +# print curr_phdr.p_filesz + + # output current phdr + if is_out_elf_64_bit == False: + elf_out_fp.write(mbn_tools.Elf32_Phdr.getPackedData(new_phdr)) + else: + elf_out_fp.write(mbn_tools.Elf64_Phdr.getPackedData(new_phdr)) + + # Copy the ELF segment + bytes_written = mbn_tools.file_copy_offset(elf_in_fp2, + inp_data_offset, + elf_out_fp, + new_phdr.p_offset, + new_phdr.p_filesz) + + # update data segment offset to be aligned after previous segment + segment_offset += roundup(new_phdr.p_filesz, SEGMENT_ALIGN); + elf_in_fp2.close() + + # Embed xbl_sec image if provided + if elf_in_file_xbl_sec != "": + + # Scan pheaders in xbl_sec for segment that contains entry point address + entry_seg_offset = -1 + entry_addr = elf_headerxblsec.e_entry + for i in range(elf_headerxblsec.e_phnum): + phdr = phdr_tablexblsec[i] + max_addr = phdr.p_vaddr + phdr.p_memsz + if phdr.p_vaddr <= entry_addr <= max_addr: + entry_seg_offset = phdr.p_offset + break + if entry_seg_offset == -1: + print "Error: Failed to find entry point in any segment!" + exit() + # magical equation for program header's phys and virt addr + phys_virt_addr = entry_addr - entry_seg_offset + + if is_out_elf_64_bit: + # Converting from 32 to 64 elf requires no data size validation + new_phdr = mbn_tools.Elf64_Phdr('\0' * ELF64_PHDR_SIZE) + new_phdr.p_type = 0x1 + new_phdr.p_offset = segment_offset + new_phdr.p_vaddr = phys_virt_addr + new_phdr.p_paddr = phys_virt_addr + new_phdr.p_filesz = os.path.getsize(elf_in_file_xbl_sec) + new_phdr.p_memsz = new_phdr.p_filesz + if is_ext_mbn_v5 == True: + new_phdr.p_flags = (0x5 | + (mbn_tools.MI_PBT_XBL_SEC_SEGMENT << + mbn_tools.MI_PBT_FLAG_SEGMENT_TYPE_SHIFT)); + else: + new_phdr.p_flags = 0x5 + new_phdr.p_align = 0x1000 + else: + # Converting from 64 to 32 elf requires data size validation + # Don't discard the segment containing xbl_sec, simply error out + # if the address is greater than 32 bits + new_phdr = mbn_tools.Elf32_Phdr('\0' * ELF32_PHDR_SIZE) + new_phdr.p_type = 0x1 # + new_phdr.p_offset = segment_offset + if is_ext_mbn_v5 == True: + new_phdr.p_flags = (0x5 | + (mbn_tools.MI_PBT_XBL_SEC_SEGMENT << + mbn_tools.MI_PBT_FLAG_SEGMENT_TYPE_SHIFT)); + else: + new_phdr.p_flags = 0x5 + new_phdr.p_align = 0x1000 + + if phys_virt_addr > 0xFFFFFFFF: + if zi_oob_enabled == False or curr_phdr.p_filesz != 0: + print "ERROR: File xbl_sec VAddr or PAddr is too big for conversion." + exit() + new_phdr.p_vaddr = phys_virt_addr + new_phdr.p_paddr = phys_virt_addr + + if os.path.getsize(elf_in_file_xbl_sec) > 0xFFFFFFFF: + print "ERROR: File xbl_sec Filesz is too big for conversion." + exit() + new_phdr.p_filesz = os.path.getsize(elf_in_file_xbl_sec) + new_phdr.p_memsz = new_phdr.p_filesz + + + # update output file location to next phdr location + elf_out_fp.seek(phdr_offset) + # increment phdr_offset to next location + phdr_offset += out_elf_header.e_phentsize + # Copy entire xbl_sec file, so start from byte 0 + inp_data_offset = 0 + + # Output xbl_sec's phdr + elf_in_file_xbl_sec + if is_out_elf_64_bit == False: + elf_out_fp.write(mbn_tools.Elf32_Phdr.getPackedData(new_phdr)) + else: + elf_out_fp.write(mbn_tools.Elf64_Phdr.getPackedData(new_phdr)) + + # Copy the ENTIRE xbl_sec image + bytes_written = mbn_tools.file_copy_offset(elf_in_fpxblsec, + inp_data_offset, + elf_out_fp, + new_phdr.p_offset, + new_phdr.p_filesz) + # update data segment offset to be aligned after previous segment + # Not necessary, unless appending more pheaders after this point + segment_offset += roundup(new_phdr.p_filesz, SEGMENT_ALIGN); + + elf_in_fpxblsec.close() + + elf_out_fp.close() + + return 0 + + +main() diff --git a/util/qualcomm/ipqheader.py b/util/qualcomm/ipqheader.py new file mode 100755 index 0000000000..7615146499 --- /dev/null +++ b/util/qualcomm/ipqheader.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2013 The Linux Foundation. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. 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. +# +# 3. Neither the name of the copyright holder 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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. +# + +import os +import struct +import sys + +PROG_NAME = os.path.basename(sys.argv[0]) + +def create_header(base, size): + """Returns a packed MBN header image with the specified base and size. + + @arg base: integer, specifies the image load address in RAM + @arg size: integer, specifies the size of the image + @returns: string, the MBN header + """ + + # SBLs require size to be 4 bytes aligned. + size = (size + 3) & 0xfffffffc + + # We currently do not support appending certificates. Signing GPL + # code might violate the GPL. So U-Boot will never be signed. So + # this is not required for U-Boot. + + header = [ + 0x5, # Type: APPSBL + 0x3, # Version: 3 + 0x0, # Image source pointer + base, # Image destination pointer + size, # Code Size + Cert Size + Signature Size + size, # Code Size + base + size, # Destination + Code Size + 0x0, # Signature Size + base + size, # Destination + Code Size + Signature Size + 0x0, # Cert Size + ] + + header_packed = struct.pack('<10I', *header) + return header_packed + +def mkheader(base_addr, infname, outfname): + """Prepends the image with the MBN header. + + @arg base_addr: integer, specifies the image load address in RAM + @arg infname: string, image filename + @arg outfname: string, output image with header prepended + @raises IOError: if reading/writing input/output file fails + """ + with open(infname, "rb") as infp: + image = infp.read() + insize = len(image) + + if base_addr > 0xFFFFFFFF: + raise ValueError("invalid base address") + + if base_addr + insize > 0xFFFFFFFF: + raise ValueError("invalid destination range") + + header = create_header(base_addr, insize) + with open(outfname, "wb") as outfp: + outfp.write(header) + outfp.write(image) + +def usage(msg=None): + """Print command usage. + + @arg msg: string, error message if any (default: None) + """ + if msg != None: + sys.stderr.write("%s: %s\n" % (PROG_NAME, msg)) + + print "Usage: %s <base-addr> <input-file> <output-file>" % PROG_NAME + + if msg != None: + exit(1) + +def main(): + """Main entry function""" + + if len(sys.argv) != 4: + usage("incorrect number of arguments") + + try: + base_addr = int(sys.argv[1], 0) + infname = sys.argv[2] + outfname = sys.argv[3] + except ValueError as e: + sys.stderr.write("mkheader: invalid base address '%s'\n" % sys.argv[1]) + exit(1) + + try: + mkheader(base_addr, infname, outfname) + except IOError as e: + sys.stderr.write("%s: %s\n" % (PROG_NAME, e)) + exit(1) + except ValueError as e: + sys.stderr.write("%s: %s\n" % (PROG_NAME, e)) + exit(1) + +if __name__ == "__main__": + main() diff --git a/util/qualcomm/mbn_tools.py b/util/qualcomm/mbn_tools.py new file mode 100755 index 0000000000..12dc210cac --- /dev/null +++ b/util/qualcomm/mbn_tools.py @@ -0,0 +1,2324 @@ +#!/usr/bin/env python2 +#=============================================================================== +# +# MBN TOOLS +# +# GENERAL DESCRIPTION +# Contains all MBN Utilities for image generation +# +# Copyright (c) 2016, 2018, The Linux Foundation. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of The Linux Foundation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +#------------------------------------------------------------------------------- +# EDIT HISTORY FOR FILE +# +# This section contains comments describing changes made to the module. +# Notice that changes are listed in reverse chronological order. +# +# when who what, where, why +# -------- --- --------------------------------------------------------- +# 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_IMG_HDR_SIZE = 40 # sizeof(mi_boot_image_header_type) +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 +FLASH_PARTI_VERSION = 3 # Flash Partition Version Number +MAX_PHDR_COUNT = 100 # Maximum allowable program headers +CERT_CHAIN_ONEROOT_MAXSIZE = 6*1024 # Default Cert Chain Max Size for one root +VIRTUAL_BLOCK_SIZE = 131072 # Virtual block size for MCs insertion in SBL1 if ENABLE_VIRTUAL_BLK ON +MAGIC_COOKIE_LENGTH = 12 # Length of magic Cookie inserted per VIRTUAL_BLOCK_SIZE +MIN_IMAGE_SIZE_WITH_PAD = 256*1024 # Minimum image size for sbl1 Nand based OTA feature + +SBL_AARCH64 = 0xF # Indicate that SBL is a Aarch64 image +SBL_AARCH32 = 0x0 # Indicate that SBL is a Aarch32 image + +# Magic numbers filled in for boot headers +FLASH_CODE_WORD = 0x844BDCD1 +UNIFIED_BOOT_COOKIE_MAGIC_NUMBER = 0x33836685 +MAGIC_NUM = 0x73D71034 +AUTODETECT_PAGE_SIZE_MAGIC_NUM = 0x7D0B435A +AUTODETECT_PAGE_SIZE_MAGIC_NUM64 = 0x7D0B5436 +AUTODETECT_PAGE_SIZE_MAGIC_NUM128 = 0x7D0B6577 +SBL_VIRTUAL_BLOCK_MAGIC_NUM = 0xD48B54C6 + +# ELF Definitions +ELF_HDR_COMMON_SIZE = 24 +ELF32_HDR_SIZE = 52 +ELF32_PHDR_SIZE = 32 +ELF64_HDR_SIZE = 64 +ELF64_PHDR_SIZE = 56 +ELFINFO_MAG0_INDEX = 0 +ELFINFO_MAG1_INDEX = 1 +ELFINFO_MAG2_INDEX = 2 +ELFINFO_MAG3_INDEX = 3 +ELFINFO_MAG0 = '\x7f' +ELFINFO_MAG1 = 'E' +ELFINFO_MAG2 = 'L' +ELFINFO_MAG3 = 'F' +ELFINFO_CLASS_INDEX = 4 +ELFINFO_CLASS_32 = '\x01' +ELFINFO_CLASS_64 = '\x02' +ELFINFO_VERSION_INDEX = 6 +ELFINFO_VERSION_CURRENT = '\x01' +ELF_BLOCK_ALIGN = 0x1000 +ALIGNVALUE_1MB = 0x100000 +ALIGNVALUE_4MB = 0x400000 +ELFINFO_DATA2LSB = '\x01' +ELFINFO_EXEC_ETYPE = '\x02\x00' +ELFINFO_ARM_MACHINETYPE = '\x28\x00' +ELFINFO_VERSION_EV_CURRENT = '\x01\x00\x00\x00' +ELFINFO_SHOFF = 0x00 +ELFINFO_PHNUM = '\x01\x00' +ELFINFO_RESERVED = 0x00 + +# ELF Program Header Types +NULL_TYPE = 0x0 +LOAD_TYPE = 0x1 +DYNAMIC_TYPE = 0x2 +INTERP_TYPE = 0x3 +NOTE_TYPE = 0x4 +SHLIB_TYPE = 0x5 +PHDR_TYPE = 0x6 +TLS_TYPE = 0x7 + +""" +The eight bits between 20 and 27 in the p_flags field in ELF program headers +is not used by the standard ELF format. We use this byte to hold OS and processor +specific fields as recommended by ARM. + +The bits in this byte are defined as follows: + + Pool Indx Segment type Access type Page/non page + bits in p_flags /-----27-----/----26-24-------/---- 23-21----/------20-------/ + +After parsing segment description strings in the SCL file, the appropriate segment +flag values are chosen from the follow definitions. The mask defined below is then +used to update the existing p_flags field in the program headers with the updated +values. +""" +# Mask for bits 20-27 to parse program header p_flags +MI_PBT_FLAGS_MASK = 0x0FF00000 + +# Helper defines to help parse ELF program headers +MI_PROG_BOOT_DIGEST_SIZE = 20 +MI_PBT_FLAG_SEGMENT_TYPE_MASK = 0x07000000 +MI_PBT_FLAG_SEGMENT_TYPE_SHIFT = 0x18 +MI_PBT_FLAG_PAGE_MODE_MASK = 0x00100000 +MI_PBT_FLAG_PAGE_MODE_SHIFT = 0x14 +MI_PBT_FLAG_ACCESS_TYPE_MASK = 0x00E00000 +MI_PBT_FLAG_ACCESS_TYPE_SHIFT = 0x15 +MI_PBT_FLAG_POOL_INDEX_MASK = 0x08000000 +MI_PBT_FLAG_POOL_INDEX_SHIFT = 0x1B + +# Segment Type +MI_PBT_L4_SEGMENT = 0x0 +MI_PBT_AMSS_SEGMENT = 0x1 +MI_PBT_HASH_SEGMENT = 0x2 +MI_PBT_BOOT_SEGMENT = 0x3 +MI_PBT_L4BSP_SEGMENT = 0x4 +MI_PBT_SWAPPED_SEGMENT = 0x5 +MI_PBT_XBL_SEC_SEGMENT = 0x5 +MI_PBT_SWAP_POOL_SEGMENT = 0x6 +MI_PBT_PHDR_SEGMENT = 0x7 + +# Page/Non-Page Type +MI_PBT_NON_PAGED_SEGMENT = 0x0 +MI_PBT_PAGED_SEGMENT = 0x1 + +# Access Type +MI_PBT_RW_SEGMENT = 0x0 +MI_PBT_RO_SEGMENT = 0x1 +MI_PBT_ZI_SEGMENT = 0x2 +MI_PBT_NOTUSED_SEGMENT = 0x3 +MI_PBT_SHARED_SEGMENT = 0x4 +MI_PBT_RWE_SEGMENT = 0x7 + +# ELF Segment Flag Definitions +MI_PBT_ELF_AMSS_NON_PAGED_RO_SEGMENT = 0x01200000 +MI_PBT_ELF_AMSS_PAGED_RO_SEGMENT = 0x01300000 +MI_PBT_ELF_SWAP_POOL_NON_PAGED_ZI_SEGMENT_INDEX0 = 0x06400000 +MI_PBT_ELF_SWAPPED_PAGED_RO_SEGMENT_INDEX0 = 0x05300000 +MI_PBT_ELF_SWAP_POOL_NON_PAGED_ZI_SEGMENT_INDEX1 = 0x0E400000 +MI_PBT_ELF_SWAPPED_PAGED_RO_SEGMENT_INDEX1 = 0x0D300000 +MI_PBT_ELF_AMSS_NON_PAGED_ZI_SEGMENT = 0x01400000 +MI_PBT_ELF_AMSS_PAGED_ZI_SEGMENT = 0x01500000 +MI_PBT_ELF_AMSS_NON_PAGED_RW_SEGMENT = 0x01000000 +MI_PBT_ELF_AMSS_PAGED_RW_SEGMENT = 0x01100000 +MI_PBT_ELF_AMSS_NON_PAGED_NOTUSED_SEGMENT = 0x01600000 +MI_PBT_ELF_AMSS_PAGED_NOTUSED_SEGMENT = 0x01700000 +MI_PBT_ELF_AMSS_NON_PAGED_SHARED_SEGMENT = 0x01800000 +MI_PBT_ELF_AMSS_PAGED_SHARED_SEGMENT = 0x01900000 +MI_PBT_ELF_HASH_SEGMENT = 0x02200000 +MI_PBT_ELF_BOOT_SEGMENT = 0x03200000 +MI_PBT_ELF_PHDR_SEGMENT = 0x07000000 +MI_PBT_ELF_NON_PAGED_L4BSP_SEGMENT = 0x04000000 +MI_PBT_ELF_PAGED_L4BSP_SEGMENT = 0x04100000 +MI_PBT_ELF_AMSS_RELOCATABLE_IMAGE = 0x8000000 + +# New definitions for EOS demap paging requirement +# Bit 20 (0b) Bit 24-26(000): Non Paged = 0x0000_0000 +# Bit 20 (1b) Bit 24-26(000): Locked Paged = 0x0010_0000 +# Bit 20 (1b) Bit 24-26(001): Unlocked Paged = 0x0110_0000 +# Bit 20 (0b) Bit 24-26(011): non secure = 0x0310_0000 +MI_PBT_ELF_RESIDENT_SEGMENT = 0x00000000 +MI_PBT_ELF_PAGED_LOCKED_SEGMENT = 0x00100000 +MI_PBT_ELF_PAGED_UNLOCKED_SEGMENT = 0x01100000 +MI_PBT_ELF_UNSECURE_SEGMENT = 0x03100000 +#---------------------------------------------------------------------------- +# GLOBAL VARIABLES END +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# CLASS DEFINITIONS BEGIN +#---------------------------------------------------------------------------- +#---------------------------------------------------------------------------- +# OS Type ID Class +#---------------------------------------------------------------------------- +class OSType: + BMP_BOOT_OS = 0 + WM_BOOT_OS = 1 + ANDROID_BOOT_OS = 2 + CHROME_BOOT_OS = 3 + SYMBIAN_BOOT_OS = 4 + LINUX_BOOT_OS = 5 + +#---------------------------------------------------------------------------- +# Image Type ID Class - These values must be kept consistent with mibib.h +#---------------------------------------------------------------------------- +class ImageType: + NONE_IMG = 0 + OEM_SBL_IMG = 1 + AMSS_IMG = 2 + QCSBL_IMG = 3 + HASH_IMG = 4 + APPSBL_IMG = 5 + APPS_IMG = 6 + HOSTDL_IMG = 7 + DSP1_IMG = 8 + FSBL_IMG = 9 + DBL_IMG = 10 + OSBL_IMG = 11 + DSP2_IMG = 12 + EHOSTDL_IMG = 13 + NANDPRG_IMG = 14 + NORPRG_IMG = 15 + RAMFS1_IMG = 16 + RAMFS2_IMG = 17 + ADSP_Q5_IMG = 18 + APPS_KERNEL_IMG = 19 + BACKUP_RAMFS_IMG = 20 + SBL1_IMG = 21 + SBL2_IMG = 22 + RPM_IMG = 23 + SBL3_IMG = 24 + TZ_IMG = 25 + PSI_IMG = 32 + +#---------------------------------------------------------------------------- +# Global Image Type Table +# Format of the look-up table: +# KEY - IMAGE_TYPE string as passed into mbn_builder.py +# VALUE - [Specific ImageType ID enum, Template key string, MBN Type] +#---------------------------------------------------------------------------- +image_id_table = { + 'appsbl': [ImageType.APPSBL_IMG, 'APPSBL_IMG', 'bin'], + 'dbl': [ImageType.DBL_IMG, 'DBL_IMG', 'bin'], + 'osbl': [ImageType.OSBL_IMG, 'OSBL_IMG', 'bin'], + 'amss': [ImageType.AMSS_IMG, 'AMSS_IMG', 'elf'], + 'amss_mbn': [ImageType.HASH_IMG, 'HASH_IMG', 'elf'], + 'apps': [ImageType.APPS_IMG, 'APPS_IMG', 'bin'], + 'hostdl': [ImageType.HOSTDL_IMG, 'HOSTDL_IMG', 'bin'], + 'ehostdl': [ImageType.EHOSTDL_IMG, 'EHOSTDL_IMG', 'bin'], + 'emmcbld': [ImageType.EHOSTDL_IMG, 'EMMCBLD_IMG', 'bin'], + 'qdsp6fw': [ImageType.DSP1_IMG, 'DSP1_IMG', 'elf'], + 'qdsp6sw': [ImageType.DSP2_IMG, 'DSP2_IMG', 'elf'], + 'qdsp5': [ImageType.ADSP_Q5_IMG, 'ADSP_Q5_IMG', 'bin'], + 'tz': [ImageType.TZ_IMG, 'TZ_IMG', 'elf'], + 'tz_rumi': [ImageType.TZ_IMG, 'TZ_IMG', 'elf'], + 'tz_virtio': [ImageType.TZ_IMG, 'TZ_IMG', 'elf'], + 'tzbsp_no_xpu': [ImageType.TZ_IMG, 'TZ_IMG', 'elf'], + 'tzbsp_with_test': [ImageType.TZ_IMG, 'TZ_IMG', 'elf'], + 'rpm': [ImageType.RPM_IMG, 'RPM_IMG', 'elf'], + 'sbl1': [ImageType.SBL1_IMG, 'SBL1_IMG', 'bin'], + 'sbl2': [ImageType.SBL2_IMG, 'SBL2_IMG', 'bin'], + 'sbl3': [ImageType.SBL3_IMG, 'SBL3_IMG', 'bin'], + 'efs1': [ImageType.RAMFS1_IMG, 'RAMFS1_IMG', 'bin'], + 'efs2': [ImageType.RAMFS2_IMG, 'RAMFS2_IMG', 'bin'], + 'pmic': [ImageType.PSI_IMG, 'PSI_IMG', 'elf'], + # DO NOT add any additional image information +} + +#---------------------------------------------------------------------------- +# Header Class Notes: +# In order to properly read and write the header structures as binary data, +# the Python Struct library is used to align and package up the header objects +# All Struct objects are initialized by a special string with the following +# notation. These structure objects are then used to decode binary data in order +# to fill out the appropriate class in Python, or they are used to package up +# the Python class so that we may write the binary data out. +#---------------------------------------------------------------------------- +""" + Format | C Type | Python Type | Standard Size + ----------------------------------------------------- + 1) 'X's | char * | string | 'X' bytes + 2) H | unsigned short | integer | 2 bytes + 3) I | unsigned int | integer | 4 bytes + +""" + +#---------------------------------------------------------------------------- +# ELF Header Class +#---------------------------------------------------------------------------- +class Elf_Ehdr_common: + # Structure object to align and package the ELF Header + s = struct.Struct('16sHHI') + + def __init__(self, data): + unpacked_data = (Elf_Ehdr_common.s).unpack(data) + self.unpacked_data = unpacked_data + self.e_ident = unpacked_data[0] + self.e_type = unpacked_data[1] + self.e_machine = unpacked_data[2] + self.e_version = unpacked_data[3] + + def printValues(self): + print "ATTRIBUTE / VALUE" + for attr, value in self.__dict__.iteritems(): + print attr, value + + + +#---------------------------------------------------------------------------- +# ELF Header Class +#---------------------------------------------------------------------------- +class Elf32_Ehdr: + # Structure object to align and package the ELF Header + s = struct.Struct('16sHHIIIIIHHHHHH') + + def __init__(self, data): + unpacked_data = (Elf32_Ehdr.s).unpack(data) + self.unpacked_data = unpacked_data + self.e_ident = unpacked_data[0] + self.e_type = unpacked_data[1] + self.e_machine = unpacked_data[2] + self.e_version = unpacked_data[3] + self.e_entry = unpacked_data[4] + self.e_phoff = unpacked_data[5] + self.e_shoff = unpacked_data[6] + self.e_flags = unpacked_data[7] + self.e_ehsize = unpacked_data[8] + self.e_phentsize = unpacked_data[9] + self.e_phnum = unpacked_data[10] + self.e_shentsize = unpacked_data[11] + self.e_shnum = unpacked_data[12] + self.e_shstrndx = unpacked_data[13] + + def printValues(self): + print "ATTRIBUTE / VALUE" + for attr, value in self.__dict__.iteritems(): + print attr, value + + def getPackedData(self): + values = [self.e_ident, + self.e_type, + self.e_machine, + self.e_version, + self.e_entry, + self.e_phoff, + self.e_shoff, + self.e_flags, + self.e_ehsize, + self.e_phentsize, + self.e_phnum, + self.e_shentsize, + self.e_shnum, + self.e_shstrndx + ] + + return (Elf32_Ehdr.s).pack(*values) + +#---------------------------------------------------------------------------- +# ELF Program Header Class +#---------------------------------------------------------------------------- +class Elf32_Phdr: + + # Structure object to align and package the ELF Program Header + s = struct.Struct('I' * 8) + + def __init__(self, data): + unpacked_data = (Elf32_Phdr.s).unpack(data) + self.unpacked_data = unpacked_data + self.p_type = unpacked_data[0] + self.p_offset = unpacked_data[1] + self.p_vaddr = unpacked_data[2] + self.p_paddr = unpacked_data[3] + self.p_filesz = unpacked_data[4] + self.p_memsz = unpacked_data[5] + self.p_flags = unpacked_data[6] + self.p_align = unpacked_data[7] + + def printValues(self): + print "ATTRIBUTE / VALUE" + for attr, value in self.__dict__.iteritems(): + print attr, value + + def getPackedData(self): + values = [self.p_type, + self.p_offset, + self.p_vaddr, + self.p_paddr, + self.p_filesz, + self.p_memsz, + self.p_flags, + self.p_align + ] + + return (Elf32_Phdr.s).pack(*values) + +#---------------------------------------------------------------------------- +# ELF Header Class +#---------------------------------------------------------------------------- +class Elf64_Ehdr: + # Structure object to align and package the ELF Header + s = struct.Struct('16sHHIQQQIHHHHHH') + + def __init__(self, data): + unpacked_data = (Elf64_Ehdr.s).unpack(data) + self.unpacked_data = unpacked_data + self.e_ident = unpacked_data[0] + self.e_type = unpacked_data[1] + self.e_machine = unpacked_data[2] + self.e_version = unpacked_data[3] + self.e_entry = unpacked_data[4] + self.e_phoff = unpacked_data[5] + self.e_shoff = unpacked_data[6] + self.e_flags = unpacked_data[7] + self.e_ehsize = unpacked_data[8] + self.e_phentsize = unpacked_data[9] + self.e_phnum = unpacked_data[10] + self.e_shentsize = unpacked_data[11] + self.e_shnum = unpacked_data[12] + self.e_shstrndx = unpacked_data[13] + + def printValues(self): + print "ATTRIBUTE / VALUE" + for attr, value in self.__dict__.iteritems(): + print attr, value + + def getPackedData(self): + values = [self.e_ident, + self.e_type, + self.e_machine, + self.e_version, + self.e_entry, + self.e_phoff, + self.e_shoff, + self.e_flags, + self.e_ehsize, + self.e_phentsize, + self.e_phnum, + self.e_shentsize, + self.e_shnum, + self.e_shstrndx + ] + + return (Elf64_Ehdr.s).pack(*values) + +#---------------------------------------------------------------------------- +# ELF Program Header Class +#---------------------------------------------------------------------------- +class Elf64_Phdr: + + # Structure object to align and package the ELF Program Header + s = struct.Struct('IIQQQQQQ') + + def __init__(self, data): + unpacked_data = (Elf64_Phdr.s).unpack(data) + self.unpacked_data = unpacked_data + self.p_type = unpacked_data[0] + self.p_flags = unpacked_data[1] + self.p_offset = unpacked_data[2] + self.p_vaddr = unpacked_data[3] + self.p_paddr = unpacked_data[4] + self.p_filesz = unpacked_data[5] + self.p_memsz = unpacked_data[6] + self.p_align = unpacked_data[7] + + def printValues(self): + print "ATTRIBUTE / VALUE" + for attr, value in self.__dict__.iteritems(): + print attr, value + + def getPackedData(self): + values = [self.p_type, + self.p_flags, + self.p_offset, + self.p_vaddr, + self.p_paddr, + self.p_filesz, + self.p_memsz, + self.p_align + ] + + return (Elf64_Phdr.s).pack(*values) + + +#---------------------------------------------------------------------------- +# ELF Segment Information Class +#---------------------------------------------------------------------------- +class SegmentInfo: + def __init__(self): + self.flag = 0 + def printValues(self): + print 'Flag: ' + str(self.flag) + +#---------------------------------------------------------------------------- +# Regular Boot Header Class +#---------------------------------------------------------------------------- +class Boot_Hdr: + def __init__(self, init_val): + self.image_id = ImageType.NONE_IMG + self.flash_parti_ver = FLASH_PARTI_VERSION + 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.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: + 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, + is_ext_mbn_v5, + 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): + + # 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 + + # 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) + MI_BOOT_IMG_HDR_SIZE + 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 + + if is_ext_mbn_v5 == True: + # If platform image integrity check is enabled + boot_header.flash_parti_ver = 5 # version + boot_header.image_src = 0 # sig_size_qc + boot_header.image_dest_ptr = 0 # cert_chain_size_qc + + # 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): + global MI_PROG_BOOT_DIGEST_SIZE + if (is_sha256_algo is True): + MI_PROG_BOOT_DIGEST_SIZE = 32 + else: + MI_PROG_BOOT_DIGEST_SIZE = 20 + + # Open Files + elf_in_fp = OPEN(elf_in_file_name, "rb") + hash_out_fp = OPEN(hash_out_file_name, "wb+") + + if elf_out_file_name is not None: + elf_out_fp = OPEN(elf_out_file_name, "wb+") + + # Initialize + [elf_header, phdr_table] = preprocess_elf_file(elf_in_file_name) + num_phdrs = elf_header.e_phnum + phdr_total_size = num_phdrs * elf_header.e_phentsize + phdr_size = elf_header.e_phentsize + hashtable_size = 0 + hashtable_shift = 0 + + if elf_header.e_ident[ELFINFO_CLASS_INDEX] == ELFINFO_CLASS_64: + new_phdr = Elf64_Phdr('\0' * ELF64_PHDR_SIZE) + elf_header_size = ELF64_HDR_SIZE + is_elf64 = True + else: + new_phdr = Elf32_Phdr('\0' * ELF32_PHDR_SIZE) + elf_header_size = ELF32_HDR_SIZE + is_elf64 = False + + hash = '\0' * MI_PROG_BOOT_DIGEST_SIZE + phdr_start = 0 + bytes_to_pad = 0 + hash_seg_end = 0 + + # Process program headers if an output elf is specified + if elf_out_file_name is not None: + # Assert limit on number of program headers in input ELF + if num_phdrs > MAX_PHDR_COUNT: + raise RuntimeError, "Input ELF has exceeded maximum number of program headers" + + # Create new program header for the ELF Header + Program Headers + new_phdr.p_type = NULL_TYPE + new_phdr.p_flags = MI_PBT_ELF_PHDR_SEGMENT + + # If hash table program header is not found, make sure to include it + elf_header.e_phnum += 2 + + # Create an empty hash entry for PHDR_TYPE + hash_out_fp.write('\0' * MI_PROG_BOOT_DIGEST_SIZE) + hashtable_size += MI_PROG_BOOT_DIGEST_SIZE + + # Create an empty hash entry for the hash segment itself + hash_out_fp.write('\0' * MI_PROG_BOOT_DIGEST_SIZE) + hashtable_size += MI_PROG_BOOT_DIGEST_SIZE + + # Begin hash table generation + for i in range(num_phdrs): + curr_phdr = phdr_table[i] + + if (MI_PBT_PAGE_MODE_VALUE(curr_phdr.p_flags) == MI_PBT_PAGED_SEGMENT): + seg_offset = curr_phdr.p_offset + seg_size = curr_phdr.p_filesz + hash_size = 0 + + # Check if the vaddr is page aligned + off = curr_phdr.p_vaddr & (ELF_BLOCK_ALIGN - 1) + if int(off) is not 0: + seg_size -= (ELF_BLOCK_ALIGN - off) + seg_offset += (ELF_BLOCK_ALIGN - off) + + # Seg_size should be page aligned + if (seg_size & (ELF_BLOCK_ALIGN - 1)) > 0: + raise RuntimeError, "seg_size: " + hex(seg_size) + " is not ELF page aligned!" + + off = seg_offset + seg_size + + while seg_offset < off: + + if seg_offset < ELF_BLOCK_ALIGN: + hash_size = seg_offset + else: + hash_size = ELF_BLOCK_ALIGN + + elf_in_fp.seek(seg_offset) + fbuf = elf_in_fp.read(hash_size) + + if MI_PBT_CHECK_FLAG_TYPE(curr_phdr.p_flags) is True: + hash = generate_hash(fbuf, is_sha256_algo) + else: + hash = '\0' * MI_PROG_BOOT_DIGEST_SIZE + + # Write hash to file + hash_out_fp.write(hash) + + hashtable_size += MI_PROG_BOOT_DIGEST_SIZE + seg_offset += ELF_BLOCK_ALIGN + + # Copy the hash entry for all that are PAGED segments and those that are not the PHDR type. This is for + # backward tool compatibility where some images are generated using older exe tools. + elif((MI_PBT_PAGE_MODE_VALUE(curr_phdr.p_flags) == MI_PBT_NON_PAGED_SEGMENT) and (curr_phdr.p_type is not PHDR_TYPE)): + # Read full hash entry into buffer + elf_in_fp.seek(curr_phdr.p_offset) + data_len = curr_phdr.p_filesz + file_buff = elf_in_fp.read(data_len) + + if (MI_PBT_CHECK_FLAG_TYPE(curr_phdr.p_flags) is True) and (data_len > 0): + hash = generate_hash(file_buff, is_sha256_algo) + else: + hash = '\0' * MI_PROG_BOOT_DIGEST_SIZE + + # Write hash to file + hash_out_fp.write(hash) + + hashtable_size += MI_PROG_BOOT_DIGEST_SIZE + # End hash table generation + + # Generate the rest of the ELF output file if specified + if elf_out_file_name is not None: + + # Preempt hash table size if necessary + if secure_type == 'secure': + hashtable_size += (SHA256_SIGNATURE_SIZE + cert_chain_size_in) + + if append_xml_hdr is True: + hashtable_size += XML_HEADER_MAXSIZE + + # Initialize the hash table program header + [hash_Phdr, pad_hash_segment, hash_tbl_end_addr, hash_tbl_offset] = \ + initialize_hash_phdr(elf_in_file_name, hashtable_size, MI_BOOT_IMG_HDR_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, is_sha256_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, True)] + bytes_left -= bytes_in_page + + # And write them to the hash segment + elf_in_fp.seek(hash_phdr.p_offset) + + for h in hashes[:]: + elf_in_fp.write(h) + + # Close files + elf_in_fp.close() + + return 0 + +#---------------------------------------------------------------------------- +# BOOT TOOLS END +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# HELPER FUNCTIONS BEGIN +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# Create a list to hold all segment information from an input SCL file +#---------------------------------------------------------------------------- +def readSCL(filename, global_dict): + + scl_fp = OPEN(filename,'r') + + # Initialize + file_data = scl_fp.readlines() + num_lines = len(file_data) + current_line = '' + previous_line = '' + strip_chars = '(){}[]' + i = 0 + bracket_counter = 0 + seg_list = [] + + # Parse through all lines + while i < num_lines: + + # Save the last line read + previous_line = current_line + current_line = file_data[i] + + # Look for the symbol '{' for the line to read. + # Use bracket counter to skip nested '{ }' + if ('{' in current_line): + if bracket_counter is 0: + # Create a new SegmentInfo class and set up tokens + new_scl_entry = SegmentInfo() + previous_line = previous_line.strip() + tokens = previous_line.split(' ') + + # Check that at least two tokens were parsed + # Token 1: Segment Name + # Token 2: Start Address -- not used in MBN tools + if len(tokens) < 2: + raise RuntimeError, 'SCL Segment Syntax malformed: ' + previous_line + + # Get the segment flags corresponding to the segment name description + new_scl_entry.flag = getSegmentFlag(tokens[0].strip(strip_chars)) + seg_list.append(new_scl_entry) + + bracket_counter += 1 + elif '}' in current_line: + bracket_counter -= 1 + + i+=1 + + scl_fp.close() + return seg_list + +#---------------------------------------------------------------------------- +# Given a string parsed from a SCL file, returns the ELF segment flags +#---------------------------------------------------------------------------- +def getSegmentFlag(seg_info): + + ret_val = None + + # Define string values for various types of segments + RO = "RO" + RW = "RW" + ZI = "ZI" + PAGEABLE = "PAGED" + NOTPAGEABLE = "NOTPAGED" + SWAPABLE = "SWAPPED" + SWAP_POOL = "SWAP_POOL" + RESERVED = "RESERVED" + HASHTBL = "HASH" + SHARED = "SHARED" + NOTUSED = "NOTUSED" + BOOT_SEGMENT = "BOOT_SEGMENT" + CODE = "CODE" + L4BSP = "L4BSP" + POOL_INDEX_0 = "INDEX_0" + POOL_INDEX_1 = "INDEX_1" + + # New definitions for EOS demand paging + NONPAGE = "NONPAGE" + PAGEUNLOCKED = "PAGEUNLOCKED" + PAGELOCKED = "PAGELOCKED" + UNSECURE = "UNSECURE" + + if seg_info is None or len(seg_info) is 0: + raise RuntimeError, 'Invalid segment information passed: ' + seg_info + + # Conditional checks and assignments of the corresponding segment flag values + if NOTPAGEABLE in seg_info: + if RO in seg_info: + ret_val = MI_PBT_ELF_AMSS_NON_PAGED_RO_SEGMENT + elif CODE in seg_info: + ret_val = MI_PBT_ELF_AMSS_NON_PAGED_RO_SEGMENT + elif ZI in seg_info: + if SWAP_POOL in seg_info: + if POOL_INDEX_0 in seg_info: + ret_val = MI_PBT_ELF_SWAP_POOL_NON_PAGED_ZI_SEGMENT_INDEX0 + else: + ret_val = MI_PBT_ELF_SWAP_POOL_NON_PAGED_ZI_SEGMENT_INDEX1 + else: + ret_val = MI_PBT_ELF_AMSS_NON_PAGED_ZI_SEGMENT + + elif NOTUSED in seg_info: + ret_val = MI_PBT_ELF_AMSS_NON_PAGED_NOTUSED_SEGMENT + + elif SHARED in seg_info: + ret_val = MI_PBT_ELF_AMSS_NON_PAGED_SHARED_SEGMENT + elif HASHTBL in seg_info: + ret_val = MI_PBT_ELF_HASH_SEGMENT + elif BOOT_SEGMENT in seg_info: + ret_val = MI_PBT_ELF_BOOT_SEGMENT + elif L4BSP in seg_info: + ret_val = MI_PBT_ELF_NON_PAGED_L4BSP_SEGMENT + else: + ret_val = MI_PBT_ELF_AMSS_NON_PAGED_RW_SEGMENT + + elif PAGEABLE in seg_info: + if RO in seg_info or CODE in seg_info: + if SWAPABLE in seg_info: + if POOL_INDEX_0 in seg_info: + ret_val = MI_PBT_ELF_SWAPPED_PAGED_RO_SEGMENT_INDEX0 + else: + ret_val = MI_PBT_ELF_SWAPPED_PAGED_RO_SEGMENT_INDEX1 + else: + ret_val = MI_PBT_ELF_AMSS_PAGED_RO_SEGMENT + elif ZI in seg_info: + ret_val = MI_PBT_ELF_AMSS_PAGED_ZI_SEGMENT + + elif NOTUSED in seg_info: + ret_val = MI_PBT_ELF_AMSS_PAGED_NOTUSED_SEGMENT + elif SHARED in seg_info: + ret_val = MI_PBT_ELF_AMSS_PAGED_SHARED_SEGMENT + elif L4BSP in seg_info: + ret_val = MI_PBT_ELF_PAGED_L4BSP_SEGMENT + else: + ret_val = MI_PBT_ELF_AMSS_PAGED_RW_SEGMENT + + elif PAGELOCKED in seg_info: + ret_val = MI_PBT_ELF_PAGED_LOCKED_SEGMENT + elif PAGEUNLOCKED in seg_info: + ret_val = MI_PBT_ELF_PAGED_UNLOCKED_SEGMENT + elif NONPAGE in seg_info: + ret_val = MI_PBT_ELF_RESIDENT_SEGMENT + elif UNSECURE in seg_info: + ret_val = MI_PBT_ELF_UNSECURE_SEGMENT + + else: + raise RuntimeError, 'The segment name is wrongly defined in the SCL file: ' + seg_info + + return ret_val + +#---------------------------------------------------------------------------- +# Pad a file with specific number of bytes +# Note: Assumes the fp is seeked to the correct location of padding +#---------------------------------------------------------------------------- +def pad_file(fp, num_bytes, value): + + if num_bytes < 0: + raise RuntimeError, "Number of bytes to pad must be greater than zero" + + while num_bytes > 0: + fp.write('%c' % value) + num_bytes -= 1 + + return + +#---------------------------------------------------------------------------- +# Concatenates the files listed in 'sources' in order and writes to 'target' +#---------------------------------------------------------------------------- +def concat_files (target, sources): + if type(sources) is not list: + sources = [sources] + + target_file = OPEN(target,'wb') + + for fname in sources: + file = OPEN(fname,'rb') + while True: + bin_data = file.read(65536) + if not bin_data: + break + target_file.write(bin_data) + file.close() + target_file.close() + +#---------------------------------------------------------------------------- +# Parse build configurable values and assign to global variables for tools +#---------------------------------------------------------------------------- +def init_build_vars(env): + + # Maximum size of Certificate Chain used in Secure Boot + global CERT_CHAIN_ONEROOT_MAXSIZE + CERT_CHAIN_ONEROOT_MAXSIZE = get_dict_value(env['GLOBAL_DICT'], 'CERT_CHAIN_MAXSIZE', (6*1024)) + + # Maximum size of the XML Header used in encrypted ELF images + global XML_HEADER_MAXSIZE + XML_HEADER_MAXSIZE = get_dict_value(env['GLOBAL_DICT'], 'XML_HEADER_MAXSIZE', (2*1024)) + +#---------------------------------------------------------------------------- +# Generates the global dictionary and add to the environment +#---------------------------------------------------------------------------- +def generate_global_dict(env): + + # Get file names for 'cust' and 'targ' auto-generated files inside 'build/ms' + cust_h = env.subst('CUST${BUILD_ID}.H').lower() + targ_h = env.subst('TARG${BUILD_ID}.H').lower() + cust_file_name = str(env.FindFile(cust_h, "${INC_ROOT}/build/ms")) + targ_file_name = str(env.FindFile(targ_h, "${INC_ROOT}/build/ms")) + + # Check that files are present + if (os.path.exists(cust_file_name) is True) and \ + (os.path.exists(targ_file_name) is True): + + # Populate the dictionary from the auto-generated files + global_dict = populate_dictionary(targ_file_name, cust_file_name) + else: + global_dict = {} + + # Add the dictionary to the environment + env.Replace(GLOBAL_DICT = global_dict) + +#---------------------------------------------------------------------------- +# Populate the dictionary from a list of input files +#---------------------------------------------------------------------------- +def populate_dictionary(*args): + + if len(args) < 1: + raise RuntimeError, "At least 1 file must be specified as an input" + + global_dict = {} + Fields = ["Define", "Key", "Value"] + + # For each input file + for i in range(len(args)): + + template_file_path = args[i] + instream = OPEN(template_file_path, 'r') + # Tokenize each line with a white space + values = csv.DictReader(instream, Fields, delimiter=" ") + + for values in itertools.izip(values): + new_entry = values[0] + # Verify the parsed tokens + if (new_entry['Define'] == '#define') and \ + (new_entry['Key'] != None) and \ + (new_entry['Value'] != None): + + new_key = new_entry['Key'].strip() + new_value = new_entry['Value'].strip() + + # If value pair is empty string, assume feature definition is true + if new_value == '': + new_value = 'yes' + + # Check for and handle text replacements as we parse + if global_dict is not None and len(global_dict.keys()) > 0: + for key in global_dict: + new_value = new_value.replace(key, str(global_dict.get(key))) + + # Attempt to evaluate value + try: + new_value = eval(new_value) + # Catch exceptions and do not evaluate + except: + pass + + # Add to global dictionary + global_dict[new_key] = new_value + instream.close() + + return global_dict + +#---------------------------------------------------------------------------- +# Filter out a generic dictionary from the global dictionary +#---------------------------------------------------------------------------- +def filter_dictionary(env, global_dict, **kwargs): + + # Check for Image Type + # If IMAGE_TYPE parameter is not provided, raise error + if not kwargs.has_key('IMAGE_TYPE'): + raise RuntimeError, "IMAGE_TYPE must be defined to use FilterDictionary." + else: + image_type = kwargs.get('IMAGE_TYPE') + if type(image_type) is not str: + raise RuntimeError, "IMAGE_TYPE must be of string type." + + # Check for Flash Type + # If FLASH_TYPE parameter is not provided, default to 'nand' + if not kwargs.has_key('FLASH_TYPE'): + flash_type = 'nand' + else: + flash_type = kwargs.get('FLASH_TYPE') + if type(flash_type) is not str: + raise RuntimeError, "FLASH_TYPE must be of string type. " + + # Check for MBN Type + # If MBN_TYPE parameter is not provided, default to 'elf' + if not kwargs.has_key('MBN_TYPE'): + mbn_type = 'elf' + else: + mbn_type = kwargs.get('MBN_TYPE') + if mbn_type != 'elf' and mbn_type != 'bin': + raise RuntimeError, "MBN_TYPE currently not supported: " + mbn_type + + # Check for Image ID + # If IMAGE_ID parameter is not provided, default to ID 0 + if not kwargs.has_key('IMAGE_ID'): + image_id = ImageType.NONE_IMG + else: + image_id = kwargs.get('IMAGE_ID') + if type(image_id) is not int: + raise RuntimeError, "IMAGE_ID must be of integer type." + + # Initialize + gen_dict = {} + image_dest = 0 + image_source = 0 + + # Check for image_type + if image_type not in image_id_table: + id = image_id + id_match_str = image_type.upper() + "_IMG" + id_mbn_type = mbn_type + else: + id = image_id_table[image_type][0] + id_match_str = image_id_table[image_type][1] + id_mbn_type = image_id_table[image_type][2] + + # Handle MBN Type and assign image destination address + if id_mbn_type is 'elf': + pass + elif id_mbn_type is 'bin': + template_key_match = 'IMAGE_KEY_' + id_match_str + "_DEST_ADDR" + if template_key_match in global_dict: + image_dest = global_dict[template_key_match] + else: + raise RuntimeError, "Builds file does not have IMAGE_KEY pair for: " + image_type + else: + raise RuntimeError, "MBN_TYPE currently not supported: " + mbn_type + + # Assign generic dictionary key/value pairs + gen_dict['IMAGE_KEY_IMAGE_ID'] = id + gen_dict['IMAGE_KEY_IMAGE_DEST'] = image_dest + gen_dict['IMAGE_KEY_IMAGE_SOURCE'] = image_source + gen_dict['IMAGE_KEY_FLASH_TYPE'] = flash_type + gen_dict['IMAGE_KEY_MBN_TYPE'] = id_mbn_type + gen_dict['IMAGE_KEY_ID_MATCH_STR'] = id_match_str + gen_dict['IMAGE_KEY_FLASH_AUTO_DETECT_MAX_PAGE'] = \ + get_dict_value(global_dict,'FLASH_AUTO_DETECT_MAX_PAGE', 8192) + gen_dict['IMAGE_KEY_FLASH_AUTO_DETECT_MIN_PAGE'] = \ + get_dict_value(global_dict,'FLASH_AUTO_DETECT_MIN_PAGE', 2048) + gen_dict['IMAGE_KEY_MAX_SIZE_OF_VERIFY_BUFFER'] = \ + get_dict_value(global_dict,'MAX_SIZE_OF_VERIFY_BUFFER', 8192) + gen_dict['IMAGE_KEY_BOOT_SMALL_PREAMBLE'] = \ + get_dict_value(global_dict,'BOOT_SMALL_PREAMBLE', 1) + + # Get OEM root certificate select and number + oem_root_cert_sel = get_dict_value(global_dict,'OEM_ROOT_CERT_SEL', 1) + oem_num_root_certs = get_dict_value(global_dict,'OEM_NUM_ROOT_CERTS', 1) + + # Error checking for OEM configurable values + if oem_root_cert_sel in range(1, MAX_NUM_ROOT_CERTS + 1) and \ + oem_num_root_certs in range(1, MAX_NUM_ROOT_CERTS + 1) and \ + oem_root_cert_sel <= oem_num_root_certs: + + gen_dict['IMAGE_KEY_OEM_ROOT_CERT_SEL'] = oem_root_cert_sel + gen_dict['IMAGE_KEY_OEM_NUM_ROOT_CERTS'] = oem_num_root_certs + + else: + raise RuntimeError, "Invalid OEM root certificate configuration values" + + # Assign additional dictionary key/values pair as needed by tools. + + return gen_dict + + +#---------------------------------------------------------------------------- +# Get index value from dictionary if exists, otherwise return default +#---------------------------------------------------------------------------- +def get_dict_value(dict, key_string, default): + + key = 'IMAGE_KEY_' + key_string + + if key in dict: + return dict[key] + else: + return default + +#---------------------------------------------------------------------------- +# Preprocess an ELF file and return the ELF Header Object and an +# array of ELF Program Header Objects +#---------------------------------------------------------------------------- +def preprocess_elf_file(elf_file_name): + + # Initialize + elf_fp = OPEN(elf_file_name, 'rb') + elf_header = Elf_Ehdr_common(elf_fp.read(ELF_HDR_COMMON_SIZE)) + + if verify_elf_header(elf_header) is False: + raise RuntimeError, "ELF file failed verification: " + elf_file_name + + elf_fp.seek(0) + + if elf_header.e_ident[ELFINFO_CLASS_INDEX] == ELFINFO_CLASS_64: + elf_header = Elf64_Ehdr(elf_fp.read(ELF64_HDR_SIZE)) + else: + elf_header = Elf32_Ehdr(elf_fp.read(ELF32_HDR_SIZE)) + + phdr_table = [] + + # Verify ELF header information + if verify_elf_header(elf_header) is False: + raise RuntimeError, "ELF file failed verification: " + elf_file_name + + # Get program header size + phdr_size = elf_header.e_phentsize + + # Find the program header offset + file_offset = elf_header.e_phoff + elf_fp.seek(file_offset) + + # Read in the program headers + for i in range(elf_header.e_phnum): + if elf_header.e_ident[ELFINFO_CLASS_INDEX] == ELFINFO_CLASS_64: + phdr_table.append(Elf64_Phdr(elf_fp.read(phdr_size))) + else: + phdr_table.append(Elf32_Phdr(elf_fp.read(phdr_size))) + + elf_fp.close() + return [elf_header, phdr_table] + +#---------------------------------------------------------------------------- +# Get the hash table address from an input ELF file +#---------------------------------------------------------------------------- +def get_hash_address(elf_file_name): + + [elf_header, phdr_table] = preprocess_elf_file(elf_file_name) + + last_paddr = 0 + last_paddr_segment = 0 + + # Find the segment with the largest physical address. + # Hash segment's physical address will be immediately after this segment. + for i in range(elf_header.e_phnum): + curr_phdr = phdr_table[i] + if curr_phdr.p_paddr > last_paddr: + # Skip the demand paging segment as it would be outside the physical RAM location + if MI_PBT_SEGMENT_TYPE_VALUE(curr_phdr.p_flags) != MI_PBT_XBL_SEC_SEGMENT: + last_paddr = curr_phdr.p_paddr; + last_paddr_segment = i; + + max_phdr = phdr_table[last_paddr_segment] + + ret_val = (((max_phdr.p_paddr + max_phdr.p_memsz - 1) & \ + ~(ELF_BLOCK_ALIGN-1)) + ELF_BLOCK_ALIGN) + + return ret_val + +#---------------------------------------------------------------------------- +# Verify ELF header contents from an input ELF file +#---------------------------------------------------------------------------- +def verify_elf_header(elf_header): + if (elf_header.e_ident[ELFINFO_MAG0_INDEX] != ELFINFO_MAG0) or \ + (elf_header.e_ident[ELFINFO_MAG1_INDEX] != ELFINFO_MAG1) or \ + (elf_header.e_ident[ELFINFO_MAG2_INDEX] != ELFINFO_MAG2) or \ + (elf_header.e_ident[ELFINFO_MAG3_INDEX] != ELFINFO_MAG3) or \ + ((elf_header.e_ident[ELFINFO_CLASS_INDEX] != ELFINFO_CLASS_64) and \ + (elf_header.e_ident[ELFINFO_CLASS_INDEX] != ELFINFO_CLASS_32)) or \ + (elf_header.e_ident[ELFINFO_VERSION_INDEX] != ELFINFO_VERSION_CURRENT): + + return False + else: + return True + +#---------------------------------------------------------------------------- +# Perform file copy given offsets and the number of bytes to copy +#---------------------------------------------------------------------------- +def file_copy_offset(in_fp, in_off, out_fp, out_off, num_bytes): + in_fp.seek(in_off) + read_in = in_fp.read(num_bytes) + out_fp.seek(out_off) + out_fp.write(read_in) + + return num_bytes + +#---------------------------------------------------------------------------- +# sha1/sha256 hash routine wrapper +#---------------------------------------------------------------------------- +def generate_hash(in_buf, is_sha256_algo): + # Initialize a SHA1 object from the Python hash library + if (is_sha256_algo is True): + m = hashlib.sha256() + else: + m = hashlib.sha1() + + # Set the input buffer and return the output digest + m.update(in_buf) + return m.digest() + +#---------------------------------------------------------------------------- +# Initialize the hash program header. +#---------------------------------------------------------------------------- +def initialize_hash_phdr(elf_in_file_name, hash_tbl_size, hdr_size, hdr_offset, is_elf64): + # Set hash header offset to page size boundary. Hash table will be + # located at first segment of elf image. + hash_hdr_size = hdr_size + hash_hdr_offset = hdr_offset + hash_tbl_offset = hash_hdr_offset + hash_hdr_size + hash_tbl_end_addr = hash_tbl_offset + hash_tbl_size; + pad_hash_segment = (hash_tbl_end_addr) & (ELF_BLOCK_ALIGN-1) + + # Update the hash table program header + if is_elf64 is True: + hash_Phdr = Elf64_Phdr('\0'*ELF64_PHDR_SIZE) + else: + hash_Phdr = Elf32_Phdr('\0'*ELF32_PHDR_SIZE) + hash_Phdr.p_flags = MI_PBT_ELF_HASH_SEGMENT + hash_Phdr.p_align = ELF_BLOCK_ALIGN + hash_Phdr.p_offset = hash_hdr_offset + hash_Phdr.p_memsz = hash_hdr_size + hash_tbl_size + (ELF_BLOCK_ALIGN - pad_hash_segment) + hash_Phdr.p_filesz = hash_hdr_size + hash_tbl_size + hash_Phdr.p_type = NULL_TYPE + hash_Phdr.p_vaddr = get_hash_address(elf_in_file_name) + hash_Phdr.p_paddr = hash_Phdr.p_vaddr + + return [hash_Phdr, pad_hash_segment, hash_tbl_end_addr, hash_tbl_offset] + +#---------------------------------------------------------------------------- +# image_preamble +#---------------------------------------------------------------------------- +def image_preamble(gen_dict, preamble_file_name, boot_sbl_header, num_of_pages=None): + # Generate the preamble file + preamble_fp = OPEN(preamble_file_name, 'wb') + + # Initialize + max_size_verify = gen_dict['IMAGE_KEY_MAX_SIZE_OF_VERIFY_BUFFER'] + flash_max_page = gen_dict['IMAGE_KEY_FLASH_AUTO_DETECT_MAX_PAGE'] + flash_min_page = gen_dict['IMAGE_KEY_FLASH_AUTO_DETECT_MIN_PAGE'] + autodetectpage = [int('0xFFFFFFFF',16)] * max_size_verify + + # The first three entries in the preamble must include the following values + autodetectpage[0] = FLASH_CODE_WORD + autodetectpage[1] = MAGIC_NUM + if (num_of_pages == 64): + autodetectpage[2] = AUTODETECT_PAGE_SIZE_MAGIC_NUM64 + elif (num_of_pages == 128): + autodetectpage[2] = AUTODETECT_PAGE_SIZE_MAGIC_NUM128 + else: + autodetectpage[2] = AUTODETECT_PAGE_SIZE_MAGIC_NUM + + # Package the list into binary data to be written to the preamble + s = struct.Struct('I' * max_size_verify) + packed_data = s.pack(*autodetectpage) + + # Output preamble pages based on maximum/minimum page size support + for i in range(flash_max_page/flash_min_page): + preamble_fp.write(packed_data[:flash_min_page]) + + # Determine appropriate amount of padding for the preamble and + # update the boot_sbl_header accordingly + if gen_dict['IMAGE_KEY_BOOT_SMALL_PREAMBLE'] == 1: + boot_sbl_header.image_src += (flash_max_page + flash_min_page) + amount_to_write = flash_min_page + else: + boot_sbl_header.image_src += flash_max_page * 2 + amount_to_write = flash_max_page + + pad_file(preamble_fp, amount_to_write, PAD_BYTE_1) + preamble_fp.close() + + return boot_sbl_header + +#---------------------------------------------------------------------------- +# Helper functions to parse ELF program headers +#---------------------------------------------------------------------------- +def MI_PBT_SEGMENT_TYPE_VALUE(x): + return ( ((x) & MI_PBT_FLAG_SEGMENT_TYPE_MASK) >> MI_PBT_FLAG_SEGMENT_TYPE_SHIFT ) + +def MI_PBT_PAGE_MODE_VALUE(x): + return ( ((x) & MI_PBT_FLAG_PAGE_MODE_MASK) >> MI_PBT_FLAG_PAGE_MODE_SHIFT ) + +def MI_PBT_ACCESS_TYPE_VALUE(x): + return ( ((x) & MI_PBT_FLAG_ACCESS_TYPE_MASK) >> MI_PBT_FLAG_ACCESS_TYPE_SHIFT ) + +def MI_PBT_CHECK_FLAG_TYPE(x): + return (MI_PBT_SEGMENT_TYPE_VALUE(x) != MI_PBT_HASH_SEGMENT) and \ + (MI_PBT_ACCESS_TYPE_VALUE(x) != MI_PBT_NOTUSED_SEGMENT) and \ + (MI_PBT_ACCESS_TYPE_VALUE(x) != MI_PBT_SHARED_SEGMENT) + + +#---------------------------------------------------------------------------- +# Helper functions to open a file and return a valid file object +#---------------------------------------------------------------------------- +def OPEN(file_name, mode): + try: + fp = open(file_name, mode) + except IOError: + raise RuntimeError, "The file could not be opened: " + file_name + + # File open has succeeded with the given mode, return the file object + return fp + +#---------------------------------------------------------------------------- +# Helper functions to insert MCs in SBL1(Badger) if ENABLE_VIRTUAL_BLK is ON +#---------------------------------------------------------------------------- +def insert_SBL1_magicCookie (env, target): + file = open(target, "rb") + #read the file contents + filedata = file.read() + length = len(filedata) + file.close() + + if (length <= VIRTUAL_BLOCK_SIZE): + return None + else: + #remove the previous file + os.remove(target) + #generate new file for appending target data + required MCs + file = open(target, "ab") + + while length > VIRTUAL_BLOCK_SIZE: + filedata_till_128kb = filedata[0:VIRTUAL_BLOCK_SIZE] + filedata_after_128kb = filedata[VIRTUAL_BLOCK_SIZE:length] + + a = str(hex(FLASH_CODE_WORD)) + mc1 = chr(int(a[8:10],16)) + chr(int(a[6:8],16)) + chr(int(a[4:6],16)) + chr(int(a[2:4],16)) + + b = str(hex(MAGIC_NUM)) + mc2 = chr(int(b[8:10],16)) + chr(int(b[6:8],16)) + chr(int(b[4:6],16)) + chr(int(b[2:4],16)) + + c = str(hex(SBL_VIRTUAL_BLOCK_MAGIC_NUM)) + mc3 = chr(int(c[8:10],16)) + chr(int(c[6:8],16)) + chr(int(c[4:6],16)) + chr(int(c[2:4],16)) + + MC_inserted_data = filedata_till_128kb + mc1 + mc2 + mc3 + file.write(MC_inserted_data) + + filedata = filedata_after_128kb + length = len(filedata) + + #copy the leftover data (<128KB) in output file + if length > 0: + file.write(filedata) + + #close the final output file + file.close() + # MC_insertion code end + +#---------------------------------------------------------------------------- +# Helper functions to remove MCs in SBL1(Badger) +#---------------------------------------------------------------------------- +def remove_SBL1_magicCookie (env, target, dest): + file = open(target, "rb") + #read the file contents + filedata = file.read() + length = len(filedata) + file.close() + + #generate new file for appending target data + required MCs + file = open(dest, "ab") + + while length > VIRTUAL_BLOCK_SIZE: + filedata_till_128kb = filedata[0:VIRTUAL_BLOCK_SIZE] + # skipped 12 byte of Virtual Block Magic Cookie Header + filedata_after_128kb = filedata[VIRTUAL_BLOCK_SIZE+MAGIC_COOKIE_LENGTH:length] + + file.write(filedata_till_128kb) + + filedata = filedata_after_128kb + length = len(filedata) + + #copy the leftover data (<128KB) in output file + if length > 0: + file.write(filedata) + + #close the final output file + file.close() + + # MC_removal code end + +#---------------------------------------------------------------------------- +# Helper functions to pad SBL1 image +# min_size defaults to 256k +# If page_size or num_of_pages is set to 0, the variable is unset +#---------------------------------------------------------------------------- +def pad_SBL1_image (env, target, min_size_with_pad=MIN_IMAGE_SIZE_WITH_PAD, page_size=0, num_of_pages=0): + file = open(target, "rb") + #read the file contents + filedata = file.read() + length = len(filedata) + file.close() + + multiple = 1 + alignment = page_size * num_of_pages + + if (length > alignment and alignment > 0): + import math + multiple = math.ceil(length/float(alignment)) + + final_image_size = max(min_size_with_pad, multiple * alignment) + + if length < final_image_size: + sbl1_fp = open(target, 'ab') + pad_file (sbl1_fp, (final_image_size-length), PAD_BYTE_0) + sbl1_fp.close() + + # SBL1 pad code end +#---------------------------------------------------------------------------- +# HELPER FUNCTIONS END +#---------------------------------------------------------------------------- diff --git a/util/qualcomm/mbncat.py b/util/qualcomm/mbncat.py new file mode 100755 index 0000000000..c4da265f80 --- /dev/null +++ b/util/qualcomm/mbncat.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python2 +# Copyright (c) 2014, The Linux Foundation. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of The Linux Foundation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import struct +import sys +import os + +"""A utility to generate ipq8064 uber SBL.. + +The very first blob (aka 'uber SBL') read out of NOR SPI flash by the IPQ8064 +maskrom is supposed to be a concatenation of up to three binaries: one to run +on the RPM, another one to run on the AP, and the third one - the actual +coreboot bootblock. + +The uber SBL starts with the combined header descriptor of 80 bytes, with the +first two 4 byte words set to certain values, and the total size of the +payload saved at offsets 28 and 32. + +To generate the uber SBL this utility expects two or three input file names in +the command line, the first file including the described header, and the +following one(s) - in QCA MBN format. This allows to create the uber SBL in +one or two invocations. + +The input files are concatenated together aligned at 256 byte boundary offset +from the combined header. See Usage() below for more details. + +The resulting uber SBL file is prepended by the same combined header adjusted +to reflect the new total file size. +""" + +DEFAULT_OUTPUT_FILE_NAME = 'sbl-ro.mbn' + +class NorSbl: + """Object representing the uber SBL.""" + + NOR_SBL1_HEADER = '<II72s' + NOR_SBL1_HEADER_SZ = struct.calcsize(NOR_SBL1_HEADER) + ALIGNMENT = 256 # Make sure this == UBER_SBL_PAD_SIZE + NOR_CODE_WORD = 0x844bdcd1 + MAGIC_NUM = 0x73d71034 + + def __init__(self, sbl1, verbose): + """Initialize the object and verify the first file in the sequence. + + Args: + sbl1: string, the name of the first out of the three input blobs, + must be prepended by the combined header. + verbose: boolean, if True - print debug information on the console. + """ + self.verbose = verbose + self.mbn_file_names = [] + if self.verbose: + print 'Reading ' + sbl1 + + try: + self.sbl1 = open(sbl1, 'rb').read() + except IOError as e: + print 'I/O error({0}): {1}'.format(e.errno, e.strerror) + raise + + (codeword, magic, _) = struct.unpack_from( + self.NOR_SBL1_HEADER, self.sbl1) + + if codeword != self.NOR_CODE_WORD: + print '\n\nError: Unexpected Codeword!' + print 'Codeword : ' + ('0x%x' % self.NOR_CODE_WORD) + \ + ' != ' + ('0x%x' % codeword) + sys.exit(-1) + + if magic != self.MAGIC_NUM: + print '\n\nError: Unexpected Magic!' + print 'Magic : ' + ('0x%x' % self.MAGIC_NUM) + \ + ' != ' + ('0x%x' % magic) + sys.exit(-1) + + def Append(self, src): + """Add a file to the list of files to be concatenated""" + self.mbn_file_names.append(src) + + def PadOutput(self, outfile, size): + """Pad output file to the required alignment. + + Adds 0xff to the passed in file to get its size to the ALIGNMENT + boundary. + + Args: + outfile: file handle of the file to be padded + size: int, current size of the file + + Returns number of bytes in the added padding. + """ + + # Is padding needed? + overflow = size % self.ALIGNMENT + if overflow: + pad_size = self.ALIGNMENT - overflow + pad = '\377' * pad_size + outfile.write(pad) + if self.verbose: + print 'Added %d byte padding' % pad_size + return pad_size + return 0 + + def Create(self, out_file_name): + """Create the uber SBL. + + Concatenate input files with the appropriate padding and update the + combined header to reflect the new blob size. + + Args: + out_file_name: string, name of the file to save the generated uber + SBL in. + """ + outfile = open(out_file_name, 'wb') + total_size = len(self.sbl1) - self.NOR_SBL1_HEADER_SZ + outfile.write(self.sbl1) + + for mbn_file_name in self.mbn_file_names: + total_size += self.PadOutput(outfile, total_size) + mbn_file_data = open(mbn_file_name, 'r').read() + outfile.write(mbn_file_data) + if self.verbose: + print 'Added %s (%d bytes)' % (mbn_file_name, + len(mbn_file_data)) + total_size += len(mbn_file_data) + + outfile.seek(28) + outfile.write(struct.pack('<I', total_size)) + outfile.write(struct.pack('<I', total_size)) + + +def Usage(v): + print '%s: [-v] [-h] [-o Output MBN] sbl1 sbl2 [bootblock]' % ( + os.path.basename(sys.argv[0])) + print + print 'Concatenates up to three mbn files: two SBLs and a coreboot bootblock' + print ' -h This message' + print ' -v verbose' + print ' -o Output file name, (default: %s)\n' % DEFAULT_OUTPUT_FILE_NAME + sys.exit(v) + +def main(): + verbose = 0 + mbn_output = DEFAULT_OUTPUT_FILE_NAME + i = 0 + + while i < (len(sys.argv) - 1): + i += 1 + if (sys.argv[i] == '-h'): + Usage(0) # doesn't return + + if (sys.argv[i] == '-o'): + mbn_output = sys.argv[i + 1] + i += 1 + continue + + if (sys.argv[i] == '-v'): + verbose = 1 + continue + + break + + argv = sys.argv[i:] + if len(argv) < 2 or len(argv) > 3: + Usage(-1) + + nsbl = NorSbl(argv[0], verbose) + + for mbnf in argv[1:]: + nsbl.Append(mbnf) + + nsbl.Create(mbn_output) + +if __name__ == '__main__': + main() diff --git a/util/qualcomm/qgpt.py b/util/qualcomm/qgpt.py new file mode 100755 index 0000000000..51018361e1 --- /dev/null +++ b/util/qualcomm/qgpt.py @@ -0,0 +1,234 @@ +#!/usr/bin/python +#============================================================================ +# +#/** @file qgpt.py +# +# GENERAL DESCRIPTION +# Generates QCom GPT header for wrapping Bootblock +# +# Copyright (c) 2018, The Linux Foundation. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of The Linux Foundation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +#**/ +# + +import os +import math +import random +import re +import struct +import sys +import tempfile + +from binascii import crc32 +from optparse import OptionParser +from types import * + + +def UpdateMBR(options, GPTBlobBuffer): + i = 0x1BE + GPTBlobBuffer[i + 0] = 0x00 # not bootable + GPTBlobBuffer[i + 1] = 0x00 # head + GPTBlobBuffer[i + 2] = 0x01 # sector + GPTBlobBuffer[i + 3] = 0x00 # cylinder + GPTBlobBuffer[i + 4] = 0xEE # type + GPTBlobBuffer[i + 5] = 0xFF # head + GPTBlobBuffer[i + 6] = 0xFF # sector + GPTBlobBuffer[i + 7] = 0xFF # cylinder + GPTBlobBuffer[i + 8:i + 8 + 4] = [0x01, 0x00, 0x00, 0x00] + + GPTBlobBuffer[i + 12:i + 16] = [0x00, 0x0f, 0x00, 0x00] + + # magic byte for MBR partitioning - always at this location regardless of + # options.sector + GPTBlobBuffer[510:512] = [0x55, 0xAA] + return i + + +def UpdatePartitionEntry(options, GPTBlobBuffer): + + i = 2 * options.sector_size + # GUID of Boot Block + GPTBlobBuffer[i:i + 16] = [0x2c, 0xba, 0xa0, 0xde, 0xdd, 0xcb, 0x05, 0x48, + 0xb4, 0xf9, 0xf4, 0x28, 0x25, 0x1c, 0x3e, 0x98] + i += 16 + + #This is to set Unique Partition GUID. Below Hex Value is : 00ChezaBootblock00 + GPTBlobBuffer[i:i + 16] = [0x00, 0x43, 0x68, 0x65, 0x7a, 0x61, 0x42, 0x6f, + 0x6f, 0x74, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x00] + i += 16 + + # LBA of BootBlock Start Content + GPTBlobBuffer[i:i + 8] = [0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + i += 8 + + # End LBA of BootBlock Content + GPTBlobBuffer[i] = options.end_lba & 0xFF + GPTBlobBuffer[i+1] = (options.end_lba>>8) & 0xFF + GPTBlobBuffer[i+2] = (options.end_lba>>16) & 0xFF + GPTBlobBuffer[i+3] = (options.end_lba>>24) & 0xFF + GPTBlobBuffer[i+4] = (options.end_lba>>32) & 0xFF + GPTBlobBuffer[i+5] = (options.end_lba>>40) & 0xFF + GPTBlobBuffer[i+6] = (options.end_lba>>48) & 0xFF + GPTBlobBuffer[i+7] = (options.end_lba>>56) & 0xFF + i += 8 + + # Attributes + GPTBlobBuffer[i:i + 8] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + i += 8 + + # Label + GPTBlobBuffer[i:i + 17] = [0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, + 0x62, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x6b] + + return i + +def UpdateGPTHeader(options, GPTBlobBuffer): + + i = options.sector_size + # Signature and Revision and HeaderSize i.e. "EFI PART" and 00 00 01 00 + # and 5C 00 00 00 + GPTBlobBuffer[i:i + 16] = [0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54, + 0x00, 0x00, 0x01, 0x00, 0x5C, 0x00, 0x00, 0x00] + i += 16 + + # CRC is zeroed out till calculated later + GPTBlobBuffer[i:i + 4] = [0x00, 0x00, 0x00, 0x00] + i += 4 + + # Reserved, set to 0 + GPTBlobBuffer[i:i + 4] = [0x00, 0x00, 0x00, 0x00] + i += 4 + + # Current LBA + GPTBlobBuffer[i:i + 8] = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + i += 8 + + # Backup LBA, No Backup Gpt Used + GPTBlobBuffer[i:i + 8] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + i += 8 + + # First Usuable LBA (qc_sec + bootblock location) + GPTBlobBuffer[i:i + 8] = [0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + i += 8 + + # Last Usuable LBA (qc_sec + bootblock end location) + GPTBlobBuffer[i] = options.end_lba & 0xFF + GPTBlobBuffer[i+1] = (options.end_lba>>8) & 0xFF + GPTBlobBuffer[i+2] = (options.end_lba>>16) & 0xFF + GPTBlobBuffer[i+3] = (options.end_lba>>24) & 0xFF + GPTBlobBuffer[i+4] = (options.end_lba>>32) & 0xFF + GPTBlobBuffer[i+5] = (options.end_lba>>40) & 0xFF + GPTBlobBuffer[i+6] = (options.end_lba>>48) & 0xFF + GPTBlobBuffer[i+7] = (options.end_lba>>56) & 0xFF + i += 8 + + # GUID + GPTBlobBuffer[i:i + 16] = [0x32,0x1B,0x10,0x98,0xE2,0xBB,0xF2,0x4B, + 0xA0,0x6E,0x2B,0xB3,0x3D,0x00,0x0C,0x20] + i += 16 + + # Partition Table Entry LBA + GPTBlobBuffer[i:i + 8] = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + i += 8 + + # Number of Partition Entries + GPTBlobBuffer[i:i + 4] = [0x01, 0x00, 0x00, 0x00] + i += 4 + + # Size of One Partition Entry + GPTBlobBuffer[i:i + 4] = [0x80, 0x00, 0x00, 0x00] + i += 4 + + # CRC of Partition Entry + + PartEntry = GPTBlobBuffer[options.sector_size*2:options.sector_size*2 + 128] + CalcEntryCRC = crc32(''.join(struct.pack("B", x) for x in PartEntry)) + + GPTBlobBuffer[i] = CalcEntryCRC & 0xFF + GPTBlobBuffer[i+1] = (CalcEntryCRC>>8) & 0xFF + GPTBlobBuffer[i+2] = (CalcEntryCRC>>16) & 0xFF + GPTBlobBuffer[i+3] = (CalcEntryCRC>>24) & 0xFF + i += 4 + + # CRC of Partition Table Header + GPTHeader = GPTBlobBuffer[options.sector_size:options.sector_size + 92] + CalcEntryCRC = crc32(''.join(struct.pack("B", x) for x in GPTHeader)) + i = options.sector_size + 16 + + GPTBlobBuffer[i] = CalcEntryCRC & 0xFF + GPTBlobBuffer[i+1] = (CalcEntryCRC>>8) & 0xFF + GPTBlobBuffer[i+2] = (CalcEntryCRC>>16) & 0xFF + GPTBlobBuffer[i+3] = (CalcEntryCRC>>24) & 0xFF + + return i + + +if __name__ == '__main__': + usage = 'usage: %prog [OPTIONS] INFILE OUTFILE\n\n' + \ + 'Packages IMAGE in a GPT format.' + parser = OptionParser(usage) + parser.add_option('-s', type="int", dest='sector_size', default=4096, + help='Sector size in bytes [Default:4096(4KB)]', + metavar='SIZE') + + (options, args) = parser.parse_args() + if len(args) != 2: + print("Invalid arguments! Exiting...\n") + parser.print_help() + sys.exit(1) + + if options.sector_size != 4096 and options.sector_size != 512: + print("Invalid Sector Size") + sys.exit(1) + + options.inputfile = args[0] + options.outputfile = args[1] + + with open(options.inputfile, 'r+') as fin: + bb_buffer = fin.read() + + # Round up to next sector if bootblock size not evenly divisible + options.end_lba = ((len(bb_buffer) + options.sector_size - 1) / + options.sector_size) + # Add 3 sectors for MBR, GPT header and GPT partition entry + options.end_lba += 3 + # Subtract one because this is last usable LBA, not amount of LBAs + options.end_lba -= 1 + + GPTBlobBuffer = [0] * (options.sector_size*3) #Size of MBR+GPT+PART_ENTRY + + UpdateMBR(options, GPTBlobBuffer) + + UpdatePartitionEntry(options, GPTBlobBuffer) + + UpdateGPTHeader(options, GPTBlobBuffer) + + with open(options.outputfile, 'wb') as fout: + for b in GPTBlobBuffer: + fout.write(struct.pack("B", b)) + fout.write(bb_buffer) |