|
13 | 13 | import sys
|
14 | 14 |
|
15 | 15 | from elftools.elf.elffile import ELFFile
|
16 |
| - |
17 | 16 | from west import manifest
|
18 | 17 | from west.commands import Verbosity
|
19 | 18 | from west.util import quote_sh_list
|
|
79 | 78 | [rimage] sections in your west config file(s); this is especially useful
|
80 | 79 | when invoking west sign _indirectly_ through CMake/ninja. See how at
|
81 | 80 | https://docs.zephyrproject.org/latest/develop/west/sign.html
|
| 81 | +
|
| 82 | +silabs_rps |
| 83 | +----- |
| 84 | +
|
| 85 | +To create a signed binary with the silabs_rps tool, run this from your build directory: |
| 86 | +
|
| 87 | + west sign -t silabs_rps -- --mic YOUR_OTA_KEY --encrypt YOUR_OTA_KEY --sign YOUR_PRIVATE_KEY.pem |
| 88 | +
|
| 89 | +For this to work, either silabs_rps commander must be installed in your PATH. |
| 90 | +
|
| 91 | +The input binary (zephyr.bin.rps) is signed using the specified OTA key and private key, |
| 92 | +producing zephyr.bin.rps by default. If CONFIG_M4_OTA_KEY and CONFIG_M4_PRIVATE_KEY are set |
| 93 | +in your build configuration, they will be used unless overridden by command-line arguments. |
| 94 | +Additional arguments after '--' are passed to silabs_rps directly. |
82 | 95 | '''
|
83 | 96 |
|
84 | 97 | class ToggleAction(argparse.Action):
|
@@ -112,7 +125,7 @@ def do_add_parser(self, parser_adder):
|
112 | 125 |
|
113 | 126 | # general options
|
114 | 127 | group = parser.add_argument_group('tool control options')
|
115 |
| - group.add_argument('-t', '--tool', choices=['imgtool', 'rimage'], |
| 128 | + group.add_argument('-t', '--tool', choices=['imgtool', 'rimage', 'silabs_rps'], |
116 | 129 | help='''image signing tool name; imgtool and rimage
|
117 | 130 | are currently supported (imgtool is deprecated)''')
|
118 | 131 | group.add_argument('-p', '--tool-path', default=None,
|
@@ -195,6 +208,8 @@ def do_run(self, args, ignored):
|
195 | 208 | signer = ImgtoolSigner()
|
196 | 209 | elif args.tool == 'rimage':
|
197 | 210 | signer = RimageSigner()
|
| 211 | + elif args.tool == 'silabs_rps': |
| 212 | + signer = Silabs_rpsSigner() |
198 | 213 | # (Add support for other signers here in elif blocks)
|
199 | 214 | else:
|
200 | 215 | if args.tool is None:
|
@@ -633,3 +648,144 @@ def sign(self, command, build_dir, build_conf, formats):
|
633 | 648 |
|
634 | 649 | os.remove(out_bin)
|
635 | 650 | os.rename(out_tmp, out_bin)
|
| 651 | + |
| 652 | +class Silabs_rpsSigner(Signer): |
| 653 | + """Signer class for Silicon Labs Commander tool via west sign -t silabs_rps.""" |
| 654 | + |
| 655 | + def sign(self, command, build_dir, build_conf, formats): |
| 656 | + """Sign the Zephyr binary using Silicon Labs Commander. |
| 657 | +
|
| 658 | + :param command: The Sign instance (provides args and utility methods) |
| 659 | + :param build_dir: The build directory path |
| 660 | + :param build_conf: BuildConfiguration object for the build directory |
| 661 | + :param formats: List of formats to generate (e.g., ['bin', 'rps']) |
| 662 | + """ |
| 663 | + self.command = command |
| 664 | + args = command.args |
| 665 | + b = pathlib.Path(build_dir) # BUILD_DIR from -d option |
| 666 | + |
| 667 | + # Check if signing is needed |
| 668 | + if not formats: |
| 669 | + if not args.quiet: |
| 670 | + command.inf("No output formats specified, skipping signing.") |
| 671 | + return |
| 672 | + |
| 673 | + # Determine workspace base based on context (automation vs. manual) |
| 674 | + zephyr_workspace_root = os.environ.get('ZEPHYR_WORKSPACE_ROOT') |
| 675 | + if zephyr_workspace_root: |
| 676 | + # Automation context: Use ZEPHYR_WORKSPACE_ROOT as the workspace root |
| 677 | + workspace_base = pathlib.Path(zephyr_workspace_root) |
| 678 | + if not workspace_base.is_dir(): |
| 679 | + command.die(f"Invalid ZEPHYR_WORKSPACE_ROOT directory: {workspace_base}. Please check the environment variable.") |
| 680 | + soc_dir = 'GIVE PATH FOR KCONFIG.SOC AFTER WORKSPACE' # Relative path from workspace root |
| 681 | + else: |
| 682 | + # Manual context: Assume current directory is workspace root |
| 683 | + workspace_base = pathlib.Path(os.getcwd()) |
| 684 | + if not args.quiet: |
| 685 | + command.inf(f"ZEPHYR_WORKSPACE_ROOT not set, using current directory: {workspace_base}") |
| 686 | + soc_dir = 'GIVE PATH FOR KCONFIG.SOC AFTER WORKSPACE' # Same relative path |
| 687 | + |
| 688 | + soc_kconfig_base = workspace_base / soc_dir |
| 689 | + |
| 690 | + # Get kernel binary name (default to 'zephyr') |
| 691 | + kernel_name = build_conf.get('CONFIG_KERNEL_BIN_NAME', 'zephyr') |
| 692 | + |
| 693 | + # Input and output files (using BUILD_DIR) |
| 694 | + in_bin = b / 'zephyr' / f"{kernel_name}.bin.rps" # Raw binary input (corrected) |
| 695 | + out_rps = args.sbin or (b / 'zephyr' / f"{kernel_name}.bin.rps") # Signed output |
| 696 | + |
| 697 | + # Check if input binary exists |
| 698 | + if not in_bin.is_file(): |
| 699 | + command.die(f"No unsigned .bin found at {in_bin}") |
| 700 | + |
| 701 | + # Load configuration from Kconfig.soc |
| 702 | + soc_kconfig = soc_kconfig_base / 'Kconfig.soc' |
| 703 | + m4_ota_key = None |
| 704 | + m4_private_key = None |
| 705 | + commander_path = None |
| 706 | + |
| 707 | + if soc_kconfig.is_file(): |
| 708 | + if not args.quiet: |
| 709 | + command.inf(f"Parsing Kconfig.soc at {soc_kconfig}") |
| 710 | + with open(soc_kconfig) as f: |
| 711 | + current_config = None |
| 712 | + for line in f: |
| 713 | + line = line.strip() |
| 714 | + if line.startswith('config '): |
| 715 | + current_config = line.split('config ')[1].split()[0] # Extract config name |
| 716 | + elif line.startswith('default ') and current_config: |
| 717 | + value = line.split('default ')[1].strip().strip('"') |
| 718 | + if current_config == 'M4_OTA_KEY': |
| 719 | + m4_ota_key = value |
| 720 | + elif current_config == 'M4_PRIVATE_KEY': |
| 721 | + m4_private_key = value |
| 722 | + elif current_config == 'COMMANDER_PATH': |
| 723 | + commander_path = value |
| 724 | + else: |
| 725 | + if not args.quiet: |
| 726 | + command.inf(f"No Kconfig.soc found at {soc_kconfig}. Using default values.") |
| 727 | + |
| 728 | + # Apply default values only if not set from Kconfig.soc |
| 729 | + m4_ota_key = m4_ota_key or "OTA_KEY_IN_HEX" # Default OTA key |
| 730 | + m4_private_key = m4_private_key or "M4_PRIVATE_KEY_PATH" # Updated default private key path |
| 731 | + commander_path = commander_path or "COMMANDER_PATH_HERE" # Updated default Commander path |
| 732 | + |
| 733 | + # Override with command-line arguments if provided |
| 734 | + m4_ota_key = getattr(args, 'm4-ota-key', m4_ota_key) |
| 735 | + m4_private_key = getattr(args, 'm4-private-key', m4_private_key) |
| 736 | + commander_path = getattr(args, 'commander-path', commander_path) |
| 737 | + |
| 738 | + # Validate required settings |
| 739 | + if not m4_ota_key: |
| 740 | + command.die("M4_OTA_KEY not set in Kconfig.soc and no --m4-ota-key provided.") |
| 741 | + if not m4_private_key: |
| 742 | + command.die("M4_PRIVATE_KEY not set in Kconfig.soc and no --m4-private-key provided.") |
| 743 | + if not commander_path: |
| 744 | + command.die("COMMANDER_PATH not set in Kconfig.soc and no --commander-path provided.") |
| 745 | + |
| 746 | + # Resolve paths |
| 747 | + tool_path = pathlib.Path(commander_path) |
| 748 | + if not tool_path.is_file(): |
| 749 | + command.die(f"Commander not found at {tool_path}. Please ensure the path is correct.") |
| 750 | + |
| 751 | + private_key_path = pathlib.Path(m4_private_key) |
| 752 | + if not private_key_path.is_file(): |
| 753 | + command.die(f"Private key not found at {private_key_path}. Please ensure the path is correct.") |
| 754 | + |
| 755 | + # Build the commander signing command |
| 756 | + sign_base = [ |
| 757 | + str(tool_path), |
| 758 | + "rps", |
| 759 | + "convert", |
| 760 | + str(out_rps), |
| 761 | + "--app", str(in_bin), |
| 762 | + "--mic", m4_ota_key, |
| 763 | + "--encrypt", m4_ota_key, |
| 764 | + "--sign", str(private_key_path) |
| 765 | + ] |
| 766 | + |
| 767 | + sign_base.extend(args.tool_args) |
| 768 | + |
| 769 | + if not args.quiet: |
| 770 | + command.inf(f"Signing with tool {tool_path}") |
| 771 | + command.inf(f"Unsigned binary: {in_bin}") |
| 772 | + command.inf(f"Signed output: {out_rps}") |
| 773 | + |
| 774 | + try: |
| 775 | + subprocess.check_call(sign_base, stdout=subprocess.PIPE if args.quiet else None, stderr=subprocess.PIPE if args.quiet else None) |
| 776 | + except subprocess.CalledProcessError as e: |
| 777 | + command.die(f"Commander signing failed with exit code {e.returncode}") |
| 778 | + |
| 779 | + if not args.quiet: |
| 780 | + command.inf(f"Successfully generated signed file: {out_rps}") |
| 781 | + |
| 782 | +# Register the signer and add command-line arguments |
| 783 | +west_signers = {'silabs_rps': Silabs_rpsSigner} |
| 784 | + |
| 785 | +def add_parser(subparsers): |
| 786 | + """Add custom arguments for the silabs_rps signer.""" |
| 787 | + parser = subparsers.add_parser('silabs_rps', help='Sign with Silicon Labs Commander') |
| 788 | + parser.add_argument('--m4-ota-key', help='OTA key for signing (overrides Kconfig.soc default)') |
| 789 | + parser.add_argument('--m4-private-key', help='Absolute path to private key file (overrides Kconfig.soc default)') |
| 790 | + parser.add_argument('--commander-path', help='Absolute path to Commander executable (overrides Kconfig.soc default)') |
| 791 | + return parser |
0 commit comments