Skip to content

Commit e46744a

Browse files
committed
[sw,otbnsim] Replace ignored subfunction by nops
This allows subfunctions to be replaced by NOPs for constant time tests. This is useful for the case where a function runs in constant time under normal conditions but is not necessarily constant time in case of FI. Signed-off-by: Hakim Filali <[email protected]>
1 parent 44a0b23 commit e46744a

File tree

6 files changed

+54
-16
lines changed

6 files changed

+54
-16
lines changed

hw/ip/otbn/util/analyze_information_flow.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def main() -> int:
5353
'Initially secret information-flow nodes. If provided, the final '
5454
'secrets will be printed.'))
5555
args = parser.parse_args()
56-
program = decode_elf(args.elf)
56+
program = decode_elf(args.elf, [])
5757

5858
# Compute control-flow graph.
5959
if args.subroutine is None:

hw/ip/otbn/util/check_call_stack.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def main() -> int:
5151
parser.add_argument('elf', help=('The .elf file to check.'))
5252
parser.add_argument('-v', '--verbose', action='store_true')
5353
args = parser.parse_args()
54-
program = decode_elf(args.elf)
54+
program = decode_elf(args.elf, [])
5555
result = check_call_stack(program)
5656
if args.verbose or result.has_errors() or result.has_warnings():
5757
print(result.report())

hw/ip/otbn/util/check_const_time.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def main() -> int:
6464
constants = parse_required_constants(args.constants)
6565

6666
# Compute control graph and get all nodes that influence control flow.
67-
program = decode_elf(args.elf)
67+
program = decode_elf(args.elf, args.ignore)
6868
if args.subroutine is None:
6969
graph = program_control_graph(program)
7070
to_analyze = 'entire program'

hw/ip/otbn/util/check_loop.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ def main() -> int:
224224
parser.add_argument('elf', help=('The .elf file to check.'))
225225
parser.add_argument('-v', '--verbose', action='store_true')
226226
args = parser.parse_args()
227-
program = decode_elf(args.elf)
227+
program = decode_elf(args.elf, [])
228228
result = check_loop(program)
229229
if args.verbose or result.has_errors() or result.has_warnings():
230230
print(result.report())

hw/ip/otbn/util/get_instruction_count_range.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def main() -> int:
2323
help=('The specific subroutine to check. If not provided, the start '
2424
'point is _imem_start (whole program).'))
2525
args = parser.parse_args()
26-
program = decode_elf(args.elf)
26+
program = decode_elf(args.elf, [])
2727

2828
# Compute instruction count range.
2929
if args.subroutine is None:

hw/ip/otbn/util/shared/decode.py

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,57 @@
2020

2121
class OTBNProgram:
2222
def __init__(self, symbols: Dict[str, int], insns: Dict[int, int],
23-
data: Dict[int, int]):
23+
data: Dict[int, int], nop_subfuncs: List[str]):
2424
self.symbols = symbols # label -> PC
25-
self.data = data # addr -> data (32b word)
25+
self.data = data # addr -> data (32b word)
26+
27+
# Get start PCs for function symbols that we want to ignore
28+
nop_starts = [symbols[name] for name in nop_subfuncs if name in symbols]
29+
nop_ranges = []
30+
31+
# Build NOP ranges by scanning until JALR
32+
for start_pc in nop_starts:
33+
pc = start_pc
34+
while pc in insns:
35+
opcode = insns[pc]
36+
mnem = INSNS_FILE.mnem_for_word(opcode)
37+
if mnem is None:
38+
raise ValueError(f"Invalid instruction at PC {pc:#x}")
39+
insn = INSNS_FILE.mnemonic_to_insn[mnem]
40+
41+
# Stop after at the JALR instruction
42+
if mnem == "jalr":
43+
nop_ranges.append((start_pc, pc))
44+
break
45+
46+
pc += 4
47+
else:
48+
# In case we never find a ret
49+
raise RuntimeError(
50+
f"Did not find 'JALR' for function starting at {start_pc:#x}"
51+
)
2652

2753
self.insns = {}
2854
for pc, opcode in insns.items():
29-
mnem = INSNS_FILE.mnem_for_word(opcode)
30-
if mnem is None:
31-
raise ValueError(
32-
'No legal decoding for mnemonic: {}'.format(mnem))
33-
insn = INSNS_FILE.mnemonic_to_insn[mnem]
34-
assert insn.encoding is not None
35-
enc_vals = insn.encoding.extract_operands(opcode)
55+
# Check if PC lies within one of the NOP ranges
56+
in_nop_region = any(
57+
start <= pc < end for start, end in nop_ranges
58+
)
59+
60+
if in_nop_region:
61+
# Replace instruction that belong to a ignored routine with a NOP
62+
insn = INSNS_FILE.mnemonic_to_insn["addi"]
63+
enc_vals = {'imm': 0, 'grs1': 0, 'grd': 0}
64+
else:
65+
# Normal decode
66+
mnem = INSNS_FILE.mnem_for_word(opcode)
67+
if mnem is None:
68+
raise ValueError(
69+
'No legal decoding for mnemonic: {}'.format(mnem))
70+
insn = INSNS_FILE.mnemonic_to_insn[mnem]
71+
assert insn.encoding is not None
72+
enc_vals = insn.encoding.extract_operands(opcode)
73+
3674
op_vals = insn.enc_vals_to_op_vals(pc, enc_vals)
3775
self.insns[pc] = (insn, op_vals)
3876

@@ -69,7 +107,7 @@ def _decode_mem(base_addr: int, data: bytes) -> Dict[int, int]:
69107
for offset, int_val in enumerate(struct.iter_unpack('<I', data))}
70108

71109

72-
def decode_elf(path: str) -> OTBNProgram:
110+
def decode_elf(path: str, nop_subfuncs: List[str]) -> OTBNProgram:
73111
'''Read ELF file at path and decode contents into an OTBNProgram instance
74112
75113
Returns the OTBNProgram instance representing the program in the ELF file.
@@ -79,4 +117,4 @@ def decode_elf(path: str) -> OTBNProgram:
79117
insns = _decode_mem(0, imem_bytes)
80118
data = _decode_mem(0, dmem_bytes)
81119

82-
return OTBNProgram(symbols, insns, data)
120+
return OTBNProgram(symbols, insns, data, nop_subfuncs)

0 commit comments

Comments
 (0)