1#!/usr/bin/python3 2 3""" 4Copyright (c) 2020 Cypress Semiconductor Corporation 5Licensed under the Apache License, Version 2.0 (the "License"); 6you may not use this file except in compliance with the License. 7You may obtain a copy of the License at 8 http://www.apache.org/licenses/LICENSE-2.0 9Unless required by applicable law or agreed to in writing, software 10distributed under the License is distributed on an "AS IS" BASIS, 11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12See the License for the specific language governing permissions and 13limitations under the License. 14""" 15 16import os 17import platform 18import sys, argparse 19import subprocess 20import json 21import shutil 22from cysecuretools import CySecureTools 23from intelhex import IntelHex 24 25# This script signs an output hex file using cysecuretools based 26# on the input parameters 27# Example Usage: 28# psoc64_postbuild.py --core CM4 \ 29# --secure-boot-stage single \ 30# --policy policy_single_CM0_CM4 \ 31# --target cyb06xxa \ 32# --toolchain-path ModusToolbox/tools_2.0/gcc-7.2.1 \ 33# --toolchain GCC_ARM \ 34# --build-dir Hello_World/build/CY8CKIT-064B0S2-4343W/Debug \ 35# --app-name mtb-example-psoc6-hello-world \ 36# --cm0-app-path ./libs/psoc6cm0p/COMPONENT_CM0P_SECURE \ 37# --cm0-app-name psoc6_02_cm0p_secure 38 39 40def myargs(argv): 41 parser = argparse.ArgumentParser(add_help=False) 42 parser.add_argument('-h', '--help', 43 dest='show_help', 44 action='help', 45 help='Print this help message and exit') 46 47 parser.add_argument('--policy-path', 48 dest='policy_path', 49 action='store', 50 type=str, 51 help="Path to policy file", 52 required=False) 53 54 parser.add_argument('-p', '--policy', 55 dest='policy_file', 56 action='store', 57 type=str, 58 help="Device policy file", 59 required=True) 60 61 parser.add_argument('-t', '--target', 62 dest='target_name', 63 action='store', 64 type=str, 65 help="Target name according to cysecuretools", 66 required=True) 67 68 parser.add_argument('--toolchain-path', 69 dest='toolchain_path', 70 action='store', 71 type=str, 72 help="Path to the toolchain tools to use for .hex file generation", 73 required=True) 74 75 parser.add_argument('--toolchain', 76 dest='toolchain', 77 action='store', 78 type=str, 79 help="Actual toolchain that was used to build the application", 80 required=True) 81 82 parser.add_argument('-b', '--build-dir', 83 dest='build_dir', 84 action='store', 85 type=str, 86 help="Path of build directory to find application hex files", 87 required=True) 88 89 parser.add_argument('-a', '--app-name', 90 dest='app_name', 91 action='store', 92 type=str, 93 help="Name of application", 94 required=True) 95 96 parser.add_argument('-c', '--cm0-app-path', 97 dest='cm0_app_path', 98 action='store', 99 type=str, 100 help="Path where CM0 hex file is located", 101 required=False) 102 103 parser.add_argument('--cm0-app-name', 104 dest='cm0_app_name', 105 action='store', 106 type=str, 107 help="Name of CM0 hex file", 108 required=False) 109 110 parser.add_argument('--core', 111 dest='core', 112 action='store', 113 type=str, 114 help="Building for CM0/CM4", 115 required=True) 116 117 parser.add_argument('-s', '--secure-boot-stage', 118 dest='secure_boot_stage', 119 action='store', 120 type=str, 121 help="Single/Multi stage scheme", 122 required=True) 123 124 125 options = parser.parse_args(argv) 126 return options 127 128def exec_shell_command(cmd): 129 print("Executing command: {}".format(' '.join(cmd))) 130 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, 131 stderr=subprocess.STDOUT) 132 for line in iter(p.stdout.readline, b''): 133 print("{}".format(line.decode('utf-8')), end='') 134 p.stdout.close() 135 ret = p.wait() 136 print("Command completed (ret={})".format(ret)) 137 return ret 138 139def main(argv): 140 141 options = myargs(argv) 142 print("options: {}".format(options)) 143 144 if not options.policy_path: 145 options.policy_path = 'policy' 146 147 tools = CySecureTools(options.target_name, options.policy_path + "/" + options.policy_file +'.json') 148 if (options.toolchain == 'ARM'): 149 fromelf_cmd = options.toolchain_path + "/bin/fromelf" 150 app_elf_file = options.build_dir + "/" + options.app_name + ".elf" 151 fromelf_result_dir = options.build_dir + "/" + "fromelf_result" 152 # Check if gcc tools path is valid 153 if (os.path.isdir(options.toolchain_path) == False): 154 print("ERROR: 'ARM Compiler' tools folder not found in path: {}".format(options.toolchain_path)) 155 exit(-1) 156 157 # Check if elf is valid 158 if (os.path.isfile(app_elf_file) == False): 159 print("ERROR: ELF file not found in path: {}\r\n".format(app_elf_file)) 160 exit(-1) 161 162 # Split elf file into sections 163 shell_cmd = [ fromelf_cmd, '--i32', '--output=' + fromelf_result_dir, app_elf_file ] 164 ret = exec_shell_command(shell_cmd) 165 if (ret != 0): 166 exit(ret) 167 168 em_eeprom_hex = fromelf_result_dir + "/" + ".cy_em_eeprom" 169 app_hex_path = options.build_dir + '/' + options.app_name + '.hex' 170 if (os.path.isfile(em_eeprom_hex) == True): 171 sections_list = [f for f in os.listdir(fromelf_result_dir) if os.path.isfile(os.path.join(fromelf_result_dir, f))] 172 sections_list.remove('.cy_em_eeprom') 173 flash = IntelHex() 174 175 for section in sections_list: 176 sect = IntelHex(fromelf_result_dir + "/" + section) 177 flash.merge(sect, overlap='replace') 178 179 flash.write_hex_file(app_hex_path, False) 180 181 CM0_app_src_path = options.cm0_app_path + '/' + options.cm0_app_name + '.hex' 182 CM0_app_dst_path = options.build_dir + '/' + options.cm0_app_name + '.hex' 183 184 # CySecureTools Image ID for CM4 Applications is 185 # 1) 1 for single-stage, 186 # 2) 16 in case of multi-stage, 187 # Image ID for CM0 Applications is always 1 188 if (options.core == "CM4"): 189 if (options.secure_boot_stage == "single"): 190 # Sign CM4 image 191 tools.sign_image(app_hex_path, 1) 192 else: 193 # Sign CM4 image 194 tools.sign_image(app_hex_path, 16) 195 # Make a copy of CM0P app image in build folder 196 shutil.copy2(CM0_app_src_path, CM0_app_dst_path) 197 198 # Sign CM0 image 199 tools.sign_image(CM0_app_dst_path, 1) 200 201 # Merge CM0, CM4 into a single hex file 202 ihex = IntelHex() 203 ihex.padding = 0x00 204 ihex.loadfile(app_hex_path, 'hex'); \ 205 ihex.merge(IntelHex(CM0_app_dst_path), 'ignore'); \ 206 ihex.write_hex_file(app_hex_path, write_start_addr=False, byte_count=16) 207 else: 208 tools.sign_image(app_hex_path, 1) 209 210 if (os.path.isfile(em_eeprom_hex) == True): 211 # Add emulated EEPROM Section back 212 flash = IntelHex(app_hex_path) 213 eeprom = IntelHex(em_eeprom_hex) 214 flash.merge(eeprom) 215 flash.write_hex_file(app_hex_path, False) 216 else: 217 gcc_objcopy_eabi_cmd = options.toolchain_path + '/bin/arm-none-eabi-objcopy' 218 app_elf_file = options.build_dir + "/" + options.app_name +".elf" 219 220 # Check if gcc tools path is valid 221 if(os.path.isdir(options.toolchain_path)==False): 222 print("ERROR: GCC tools folder not found in path: {}".format(options.toolchain_path)) 223 exit(-1) 224 225 # Check if elf is valid 226 if(os.path.isfile(app_elf_file) == False): 227 print("ERROR: ELF file not found in path: {}\r\n".format(app_elf_file)) 228 exit(-1) 229 230 # Strip away emulated EEPROM section from hex file before signing 231 shell_cmd = [ gcc_objcopy_eabi_cmd, '-R', '.cy_em_eeprom', '-O', 'ihex', app_elf_file, options.build_dir + "/" + options.app_name + ".hex" ] 232 ret = exec_shell_command(shell_cmd) 233 if(ret != 0): 234 exit(ret) 235 236 # Store emulated eeprom section in a seperate hex file 237 shell_cmd = [ gcc_objcopy_eabi_cmd, '-j', '.cy_em_eeprom', '-O', 'ihex', options.build_dir + "/" + options.app_name + ".elf", options.build_dir + "/em_eeprom.hex" ] 238 ret = exec_shell_command(shell_cmd) 239 if(ret != 0): 240 exit(ret) 241 242 app_hex_path = options.build_dir + '/' + options.app_name + '.hex' 243 CM0_app_src_path = options.cm0_app_path + '/' + options.cm0_app_name + '.hex' 244 CM0_app_dst_path = options.build_dir + '/' + options.cm0_app_name + '.hex' 245 246 # CySecureTools Image ID for CM4 Applications is 247 # 1) 1 for single-stage, 248 # 2) 16 in case of multi-stage, 249 # Image ID for CM0 Applications is always 1 250 if(options.core == "CM4"): 251 if(options.secure_boot_stage == "single"): 252 # Sign CM4 image 253 tools.sign_image(app_hex_path,1) 254 else: 255 # Sign CM4 image 256 tools.sign_image(app_hex_path,16) 257 # Make a copy of CM0P app image in build folder 258 shutil.copy2(CM0_app_src_path, CM0_app_dst_path) 259 260 # Sign CM0 image 261 tools.sign_image(CM0_app_dst_path,1) 262 263 # Merge CM0, CM4 into a single hex file 264 ihex = IntelHex() 265 ihex.padding = 0x00 266 ihex.loadfile(app_hex_path, 'hex'); \ 267 ihex.merge(IntelHex(CM0_app_dst_path), 'ignore'); \ 268 ihex.write_hex_file(app_hex_path, write_start_addr=False, byte_count=16) 269 else: 270 tools.sign_image(app_hex_path,1) 271 272 # Add emulated EEPROM Section back 273 flash = IntelHex(app_hex_path) 274 eeprom = IntelHex(options.build_dir + "/em_eeprom.hex") 275 flash.merge(eeprom) 276 flash.write_hex_file(app_hex_path, False) 277 278 exit(0) 279 280if __name__ == "__main__": 281 main(sys.argv[1:]) 282