blob: d359a702d19666612a03724bef6a5eff0113b984 [file] [log] [blame]
Raef Coles59cf5d82024-12-09 15:41:13 +00001#!/usr/bin/env python3
2#-------------------------------------------------------------------------------
3# SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
4#
5# SPDX-License-Identifier: BSD-3-Clause
6#
7#-------------------------------------------------------------------------------
8
9import argparse
10
11import logging
12logger = logging.getLogger("TF-M")
13
14from elftools.elf.elffile import ELFFile
15from file_loader import load_bytes_from_file
16
17from c_struct import C_enum
18
19from os.path import isfile
20
21from crypto_conversion_utils import convert_curve_define, convert_hash_define
22
23prefix_sep = ":"
24
25def incompatible_arg(args : argparse.Namespace,
26 arg1 : str,
27 arg2 : str
28 ):
29 assert not (get_arg(args, arg1) and get_arg(args, arg2)), "Incompatible arguments {} and {} cannot both be specified".format(arg1, arg2)
30
31def add_prefixed_argument(parser: argparse.ArgumentParser,
32 arg_name : str,
33 prefix : str = "",
34 **args):
35
36 if "--" + arg_name not in parser._option_string_actions.keys():
37 parser.add_argument("--" + join_prefix(arg_name, prefix), **args)
38
39def _arg_type_enum(arg : str, enum : C_enum):
40 assert arg in enum.dict.keys(), "Invalid enum value {} not in {}".format(arg, list(enum.dict.keys()))
41 return enum.dict[arg]
42
43def arg_type_enum(enum : C_enum):
44 return lambda x: _arg_type_enum(x, enum)
45
46
47def add_prefixed_enum_argument(parser : argparse.ArgumentParser,
48 enum : C_enum,
49 arg_name : str,
50 default : str = None,
51 prefix : str = "",
52 **kwargs):
53
54 if default:
55 assert default in enum.dict.keys()
56
57 return add_prefixed_argument(parser=parser,
58 arg_name = arg_name,
59 type=arg_type_enum(enum),
60 prefix=prefix,
61 default=default,
62 metavar=enum.name,
63 choices=enum.dict.values(),
64 **kwargs)
65
66
67def join_prefix(arg : str, prefix : str):
68 assert(arg)
69
70 if not prefix:
71 return arg
72
73 if prefix_sep not in arg:
74 return prefix + prefix_sep + arg
75 else:
76 #Insert the prefix at the right of current prefixes
77 curr_prefix, arg = arg.rsplit(prefix_sep, 1)
78 return curr_prefix + prefix_sep + prefix + prefix_sep + arg
79
80def is_prefixed(arg : str, prefix : str):
81 return prefix + prefix_sep in arg
82
83def unjoin_prefix(arg : str, prefix : str):
84 if not prefix:
85 return arg
86
87 assert(is_prefixed(arg, prefix))
88
89 return arg.rsplit(prefix + prefix_sep, 1)[1]
90
91def get_arg(args : argparse.Namespace,
92 arg_name : str,
93 prefix : str = "",
94 ):
95 if hasattr(args, join_prefix(arg_name, prefix)):
96 return getattr(args, join_prefix(arg_name, prefix))
97 else:
98 logging.info("Arg {} value not set".format(arg_name))
99 return None
100
101def parse_args_automatically(args : argparse.Namespace,
102 arg_names : [str],
103 prefix : str = "",
104 ) -> dict:
105 return {a: get_arg(args, a, prefix) for a in arg_names if get_arg(args, a, prefix) is not None}
106
107def arg_type_version(arg):
108 assert (len(arg.split(".")) == 3), "Invalid image version format (must be \"x.y.z(+q)?\""
109 return [int(x) for x in arg.replace("+", ".").split(".")]
110
111def _arg_type_elf_section(arg : str,
112 section_name : str,
113 ) -> bytes:
114 if type(section_name) == str:
115 section_name = [section_name]
116
117 out = []
118
119 with ELFFile(open(arg, 'rb')) as elffile:
120 for s in section_name:
121 section = elffile.get_section_by_name(s)
122 if section:
123 out.append(section.data())
124 else:
125 out.append(None)
126 logging.warning("ELF file {} does not contain section {}".format(arg, s))
127 return out
128
129def arg_type_elf_section(section_name : str):
130 return lambda x: _arg_type_elf_section(x, section_name)
131
132def arg_type_elf(arg : str) -> bytes:
133
134 return {"code": code_section.data(), "data": data_section.data() if data_section else None}
135
136def arg_type_filepath(arg : str) -> str:
137 assert isfile(arg), "File {} does not exist".format(arg)
138 return arg
139
140def arg_type_bytes_from_file(arg : str) -> (bytes, bytes):
141 return load_bytes_from_file(arg)
142
143def arg_type_hex(arg : str) -> int:
144 assert(arg)
145 return bytes.fromhex(arg)
146
147def arg_type_bytes(arg : str) -> bytes:
148 try:
149 return arg_type_bytes_from_file(arg)
150 except FileNotFoundError:
151 assert len(arg) % 2 == 0, "Hex values must be a multiple of two characters long"
152 return arg_type_hex(arg.replace("0x", ""))
153
154def arg_type_bytes_output_file(arg : str):
155 return open(arg, "wb")
156
157def arg_type_text_output_file(arg : str):
158 return open(arg, "wt")
159
160def arg_type_hash(arg : str):
161 return convert_hash_define(arg)
162
163def arg_type_curve(arg : str):
164 return convert_curve_define(arg)
165
166def arg_type_bool(arg : str):
167 larg = arg.lower()
168
169 assert larg in ["true", "false", "on", "off"], "Invalid boolean value {}".format(arg)
170
171 return larg in ["true", "on"]
172
173def pre_parse_args(parser : argparse.ArgumentParser,
174 name : str,
175 **kwargs : dict):
176 pre_arg_parser = argparse.ArgumentParser(add_help=False)
177
178 pre_arg_parser.add_argument("--" + name, required=True, **kwargs)
179
180 if "--" + name not in parser._option_string_actions.keys():
181 parser.add_argument("--" + name, required=True, **kwargs)
182
183 parsed, _ = pre_arg_parser.parse_known_args()
184 return getattr(parsed, name)