Skip to content

Commit 5ddfffb

Browse files
committed
[ot] scripts/opentitan: otptool.py: add a partition filter option
- add a new option to select which partition(s) and fields(s) to show when decoding an OTP image file - add a new option to hide the OTP image file version Signed-off-by: Emmanuel Blot <[email protected]>
1 parent aaecaa3 commit 5ddfffb

File tree

4 files changed

+92
-24
lines changed

4 files changed

+92
-24
lines changed

docs/opentitan/otptool.md

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ controller virtual device.
66
## Usage
77

88
````text
9-
usage: otptool.py [-h] [-j HJSON] [-m VMEM] [-l SV] [-o C] [-r RAW]
9+
usage: otptool.py [-h] [-j HJSON] [-m VMEM] [-l SV] [-o FILE] [-r RAW]
1010
[-k {auto,otp,fuz}] [-e BITS] [-C CONFIG] [-c INT] [-i INT]
11-
[-w] [-n] [-s] [-E] [-D] [-U] [--empty PARTITION]
12-
[--erase PART:FIELD] [--clear-bit CLEAR_BIT]
13-
[--set-bit SET_BIT] [--toggle-bit TOGGLE_BIT] [--fix-ecc]
11+
[-w] [-n] [-f PART:FIELD] [--no-version] [-s] [-E] [-D] [-U]
12+
[--empty PARTITION] [--erase PART:FIELD]
13+
[--clear-bit CLEAR_BIT] [--set-bit SET_BIT]
14+
[--toggle-bit TOGGLE_BIT] [--fix-ecc]
1415
[-G {LCVAL,LCTPL,PARTS,REGS}] [-v] [-d]
1516
1617
QEMU OT tool to manage OTP files.
@@ -22,7 +23,7 @@ Files:
2223
-j, --otp-map HJSON input OTP controller memory map file
2324
-m, --vmem VMEM input VMEM file
2425
-l, --lifecycle SV input lifecycle system verilog file
25-
-o, --output C output filename for C file generation
26+
-o, --output FILE output filename (default to stdout)
2627
-r, --raw RAW QEMU OTP raw image file
2728
2829
Parameters:
@@ -34,6 +35,9 @@ Parameters:
3435
-i, --iv INT initialization vector for Present scrambler
3536
-w, --wide use wide output, non-abbreviated content
3637
-n, --no-decode do not attempt to decode OTP fields
38+
-f, --filter PART:FIELD
39+
filter which OTP fields are shown
40+
--no-version do not report the OTP image version
3741
3842
Commands:
3943
-s, --show show the OTP content
@@ -107,6 +111,9 @@ Fuse RAW images only use the v1 type.
107111
* `-e` specify how many bits are used in the VMEM file to store ECC information. Note that ECC
108112
information is not stored in the QEMU RAW file for now.
109113

114+
* `-f` select which partition(s) and partition field(s) should be shown when option `-s` is used.
115+
When not specified, all partitions and fields are reported.
116+
110117
* `-i` specify the initialization vector for the Present scrambler used for partition digests.
111118
This value is "usually" found within the `hw/ip/otp_ctrl/rtl/otp_ctrl_part_pkg.sv` OT file,
112119
from the last entry of `RndCnstDigestIV` array, _i.e._ item 0. It is used along with option
@@ -153,15 +160,20 @@ Fuse RAW images only use the v1 type.
153160
contain long sequence of bytes. If repeated, the empty long fields are also printed in full, as
154161
a sequence of empty bytes.
155162

163+
* `--clear-bit` clears the specified bit in the OTP data. This flag may be repeated. This option is
164+
only intended to corrupt the OTP content so that HW & SW behavior may be exercised should such
165+
a condition exists. See [Bit position syntax](#bit-syntax) for how to specify a bit.
166+
156167
* `--empty` reset a whole parition, including its digest if any and ECC bits. This option is only
157168
intended for test purposes. This flag may be repeated. Partition(s) can be specified either by
158169
their index or their name.
159170

160171
* `--erase` reset a specific field within a partition. The flag may be repeated.
161172

162-
* `--clear-bit` clears the specified bit in the OTP data. This flag may be repeated. This option is
163-
only intended to corrupt the OTP content so that HW & SW behavior may be exercised should such
164-
a condition exists. See [Bit position syntax](#bit-syntax) for how to specify a bit.
173+
* `--no-version` disable OTP image version reporting when `-s` is used.
174+
175+
* `--fix-ecc` may be used to rebuild the ECC values for all slots that have been modified using the
176+
ECC modification operations, and any detected error.
165177

166178
* `--set-bit` sets the specified bit in the OTP data. This flag may be repeated. This option is
167179
only intended to corrupt the OTP content so that HW & SW behavior may be exercised should such
@@ -171,9 +183,6 @@ Fuse RAW images only use the v1 type.
171183
is only intended to corrupt the OTP content so that HW & SW behavior may be exercised should such
172184
a condition exists. See [Bit position syntax](#bit-syntax) for how to specify a bit.
173185

174-
* `--fix-ecc` may be used to rebuild the ECC values for all slots that have been modified using the
175-
ECC modification operations, and any detected error.
176-
177186
All modification features can only be performed on RAW image, VMEM images are never modified. To
178187
modify RAW file content, either a VMEM file is required in addition to the RAW file as the data
179188
source, or the `-U` is required to tell that the RAW file should be read, modified and written back.

python/qemu/ot/otp/image.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -311,17 +311,47 @@ def verify(self, show: bool = False) -> bool:
311311
return not any(r is False for r in results.values())
312312

313313
def decode(self, decode: bool = True, wide: int = 0,
314-
ofp: Optional[TextIO] = None) -> None:
315-
"""Decode the content of the image, one partition at a time."""
314+
ofp: Optional[TextIO] = None, decode_version: bool = True,
315+
field_filters: Optional[list[str]] = None) -> None:
316+
"""Decode the content of the image, one partition at a time.
317+
318+
:param decode: whether to attempt to decode value whose encoding/
319+
meaning is known
320+
:param wide: whether to use compact, truncated long value or emit
321+
the whole value, possibly generating very long lines
322+
:param decode_version: whether to decode and report the version
323+
:param field_filters: optional filter to select which partitions/
324+
fields to report as a list of <part>:<field> strings, joker
325+
char '*' is supported.
326+
"""
316327
version = self.version
317-
if version:
328+
if version and decode_version:
318329
print(f'OTP image v{version}')
319330
if version > 1:
320331
print(f' * present iv {self._digest_iv:016x}')
321332
print(f' * present constant {self._digest_constant:032x}')
333+
part_filters: dict[str, str] = {}
334+
partnames = {p.name for p in self._partitions}
335+
for filt in field_filters or []:
336+
parts = filt.split(':')
337+
if len(parts) > 2:
338+
raise ValueError(f"Invalid filter '{filt}'")
339+
part_re = f'^{parts[0].replace("*", ".*")}$'
340+
field = parts[1] if len(parts) > 1 else '*'
341+
for partname in partnames:
342+
if not re.match(part_re, partname, re.IGNORECASE):
343+
continue
344+
if not partname in part_filters:
345+
part_filters[partname] = set()
346+
if field == '*':
347+
# any field would match, discard any existing one
348+
part_filters[partname].clear()
349+
part_filters[partname].add(field)
322350
for part in self._partitions:
351+
if part_filters and part.name not in part_filters:
352+
continue
323353
base = self._get_partition_bounds(part)[0]
324-
part.decode(base, decode, wide, ofp)
354+
part.decode(base, decode, wide, ofp, part_filters.get(part.name))
325355

326356
def clear_bits(self, bitdefs: Sequence[tuple[int, int]]) -> None:
327357
"""Clear one or more bits.

python/qemu/ot/otp/partition.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
from binascii import hexlify, unhexlify, Error as hexerror
1010
from io import BytesIO
1111
from logging import getLogger
12-
from typing import BinaryIO, Optional, TextIO
12+
from re import IGNORECASE, match
13+
from typing import BinaryIO, Optional, Sequence, TextIO
1314

1415
from .lifecycle import OtpLifecycle
1516

@@ -145,7 +146,8 @@ def set_decoder(self, decoder: OtpPartitionDecoder) -> None:
145146
self._decoder = decoder
146147

147148
def decode(self, base: Optional[int], decode: bool = True, wide: int = 0,
148-
ofp: Optional[TextIO] = None) -> None:
149+
ofp: Optional[TextIO] = None,
150+
filters = Optional[Sequence[str]]) -> None:
149151
"""Decode the content of the partition."""
150152
buf = BytesIO(self._data)
151153
if ofp:
@@ -155,6 +157,12 @@ def emit(fmt, *args):
155157
emit = self._log.info
156158
pname = self.name
157159
offset = 0
160+
soff = 0
161+
if filters:
162+
fre = '|'.join(f.replace('*', '.*') for f in filters)
163+
filter_re = f'^({fre})$'
164+
else:
165+
filter_re = r'.*'
158166
for itname, itdef in self.items.items():
159167
itsize = itdef['size']
160168
itvalue = buf.read(itsize)
@@ -164,6 +172,8 @@ def emit(fmt, *args):
164172
name = f'{pname}:{itname[len(pname)+1:]}'
165173
else:
166174
name = f'{pname}:{itname}'
175+
if not match(filter_re, itname, IGNORECASE):
176+
continue
167177
if itsize > 8:
168178
rvalue = bytes(reversed(itvalue))
169179
sval = hexlify(rvalue).decode()
@@ -172,12 +182,16 @@ def emit(fmt, *args):
172182
if dval is not None:
173183
emit('%-48s %s (decoded) %s', name, soff, dval)
174184
continue
185+
ssize = f'{{{itsize}}}'
175186
if not sum(itvalue) and wide < 2:
176-
emit('%-48s %s {%d} 0...', name, soff, itsize)
187+
if decode:
188+
emit('%-48s %s %5s (empty)', name, soff, ssize)
189+
else:
190+
emit('%-48s %s %5s 0...', name, soff, ssize)
177191
else:
178192
if not wide and itsize > self.MAX_DATA_WIDTH:
179193
sval = f'{sval[:self.MAX_DATA_WIDTH*2]}...'
180-
emit('%-48s %s {%d} %s', name, soff, itsize, sval)
194+
emit('%-48s %s %5s %s', name, soff, ssize, sval)
181195
else:
182196
ival = int.from_bytes(itvalue, 'little')
183197
if decode:
@@ -192,8 +206,13 @@ def emit(fmt, *args):
192206
continue
193207
emit('%-48s %s %x', name, soff, ival)
194208
if self._digest_bytes is not None:
195-
emit('%-48s %s %s', f'{pname}:DIGEST', soff,
196-
hexlify(self._digest_bytes).decode())
209+
if match(filter_re, 'DIGEST', IGNORECASE):
210+
if not sum(self._digest_bytes) and decode:
211+
val = '(empty)'
212+
else:
213+
val = hexlify(self._digest_bytes).decode()
214+
ssize = f'{{{len(self._digest_bytes)}}}'
215+
emit('%-48s %s %5s %s', f'{pname}:DIGEST', soff, ssize, val)
197216

198217
def empty(self) -> None:
199218
"""Empty the partition, including its digest if any."""

scripts/opentitan/otptool.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ def main():
4040
files.add_argument('-l', '--lifecycle', type=FileType('rt'),
4141
metavar='SV',
4242
help='input lifecycle system verilog file')
43-
files.add_argument('-o', '--output', metavar='C', type=FileType('wt'),
44-
help='output filename for C file generation')
43+
files.add_argument('-o', '--output', metavar='FILE',
44+
type=FileType('wt'),
45+
help='output filename (default to stdout)')
4546
files.add_argument('-r', '--raw',
4647
help='QEMU OTP raw image file')
4748
params = argparser.add_argument_group(title='Parameters')
@@ -65,6 +66,11 @@ def main():
6566
params.add_argument('-n', '--no-decode', action='store_true',
6667
default=False,
6768
help='do not attempt to decode OTP fields')
69+
params.add_argument('-f', '--filter', action='append',
70+
metavar='PART:FIELD',
71+
help='filter which OTP fields are shown')
72+
params.add_argument('--no-version', action='store_true',
73+
help='do not report the OTP image version')
6874
commands = argparser.add_argument_group(title='Commands')
6975
commands.add_argument('-s', '--show', action='store_true',
7076
help='show the OTP content')
@@ -121,6 +127,9 @@ def main():
121127
if args.vmem:
122128
argparser.error('RAW update mutually exclusive with VMEM')
123129

130+
if args.filter and not args.show:
131+
argparser.error('Filter only apply to the show command')
132+
124133
bit_actions = ('clear', 'set', 'toggle')
125134
alter_bits: list[list[tuple[int, int]]] = []
126135
for slot, bitact in enumerate(bit_actions):
@@ -225,7 +234,8 @@ def main():
225234
if lcext:
226235
otp.load_lifecycle(lcext)
227236
if args.show:
228-
otp.decode(not args.no_decode, args.wide, sys.stdout)
237+
otp.decode(not args.no_decode, args.wide, output,
238+
not args.no_version, args.filter)
229239
if args.digest:
230240
if not otp.has_present_constants:
231241
if args.raw and otp.version == 1:

0 commit comments

Comments
 (0)