]> sigrok.org Git - libsigrokdecode.git/blame - decoders/arm_etmv3/pd.py
Add decoder for Microchip ATSHA204A crypto module
[libsigrokdecode.git] / decoders / arm_etmv3 / pd.py
CommitLineData
686f0c36
PA
1##
2## This file is part of the libsigrokdecode project.
3##
4## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi>
5##
6## This program is free software; you can redistribute it and/or modify
7## it under the terms of the GNU General Public License as published by
8## the Free Software Foundation; either version 2 of the License, or
9## (at your option) any later version.
10##
11## This program is distributed in the hope that it will be useful,
12## but WITHOUT ANY WARRANTY; without even the implied warranty of
13## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14## GNU General Public License for more details.
15##
16## You should have received a copy of the GNU General Public License
4539e9ca 17## along with this program; if not, see <http://www.gnu.org/licenses/>.
686f0c36
PA
18##
19
20import sigrokdecode as srd
21import subprocess
22import re
23
1c023fab
PA
24# See ETMv3 Signal Protocol table 7-11: 'Encoding of Exception[8:0]'.
25exc_names = [
26 'No exception', 'IRQ1', 'IRQ2', 'IRQ3', 'IRQ4', 'IRQ5', 'IRQ6', 'IRQ7',
27 'IRQ0', 'UsageFault', 'NMI', 'SVC', 'DebugMon', 'MemManage', 'PendSV',
28 'SysTick', 'Reserved', 'Reset', 'BusFault', 'Reserved', 'Reserved'
29]
30
31for i in range(8, 496):
32 exc_names.append('IRQ%d' % i)
33
686f0c36
PA
34def parse_varint(bytes):
35 '''Parse an integer where the top bit is the continuation bit.
36 Returns value and number of parsed bytes.'''
37 v = 0
38 for i, b in enumerate(bytes):
39 v |= (b & 0x7F) << (i * 7)
40 if b & 0x80 == 0:
41 return v, i+1
42 return v, len(bytes)
43
44def parse_uint(bytes):
45 '''Parse little-endian integer.'''
46 v = 0
47 for i, b in enumerate(bytes):
48 v |= b << (i * 8)
49 return v
50
51def parse_exc_info(bytes):
52 '''Parse exception information bytes from a branch packet.'''
53 if len(bytes) < 1:
54 return None
55
56 excv, exclen = parse_varint(bytes)
57 if bytes[exclen - 1] & 0x80 != 0x00:
58 return None # Exception info not complete.
59
60 if exclen == 2 and excv & (1 << 13):
61 # Exception byte 1 was skipped, fix up the decoding.
62 excv = (excv & 0x7F) | ((excv & 0x3F80) << 7)
63
64 ns = excv & 1
65 exc = ((excv >> 1) & 0x0F) | ((excv >> 7) & 0x1F0)
66 cancel = (excv >> 5) & 1
67 altisa = (excv >> 6) & 1
68 hyp = (excv >> 12) & 1
69 resume = (excv >> 14) & 0x0F
70 return (ns, exc, cancel, altisa, hyp, resume)
71
72def parse_branch_addr(bytes, ref_addr, cpu_state, branch_enc):
73 '''Parse encoded branch address.
74 Returns addr, addrlen, cpu_state, exc_info.
75 Returns None if packet is not yet complete'''
76
77 addr, addrlen = parse_varint(bytes)
78
79 if bytes[addrlen-1] & 0x80 != 0x00:
80 return None # Branch address not complete.
81
1c023fab
PA
82 addr_bits = 7 * addrlen
83
686f0c36
PA
84 have_exc_info = False
85 if branch_enc == 'original':
86 if addrlen == 5 and bytes[4] & 0x40:
87 have_exc_info = True
88 elif branch_enc == 'alternative':
1c023fab
PA
89 addr_bits -= 1 # Top bit of address indicates exc_info.
90 if addrlen >= 2 and addr & (1 << addr_bits):
686f0c36 91 have_exc_info = True
1c023fab 92 addr &= ~(1 << addr_bits)
686f0c36
PA
93
94 exc_info = None
95 if have_exc_info:
96 exc_info = parse_exc_info(bytes[addrlen:])
97 if exc_info is None:
98 return None # Exception info not complete.
99
100 if addrlen == 5:
101 # Possible change in CPU state.
102 if bytes[4] & 0xB8 == 0x08:
103 cpu_state = 'arm'
104 elif bytes[4] & 0xB0 == 0x10:
105 cpu_state = 'thumb'
106 elif bytes[4] & 0xA0 == 0x20:
107 cpu_state = 'jazelle'
108 else:
109 raise NotImplementedError('Unhandled branch byte 4: 0x%02x' % bytes[4])
110
111 # Shift the address according to current CPU state.
112 if cpu_state == 'arm':
113 addr = (addr & 0xFFFFFFFE) << 1
1c023fab 114 addr_bits += 1
686f0c36
PA
115 elif cpu_state == 'thumb':
116 addr = addr & 0xFFFFFFFE
117 elif cpu_state == 'jazelle':
118 addr = (addr & 0xFFFFFFFFE) >> 1
1c023fab 119 addr_bits -= 1
686f0c36
PA
120 else:
121 raise NotImplementedError('Unhandled state: ' + cpu_state)
122
123 # If the address wasn't full, fill in with the previous address.
124 if addrlen < 5:
1c023fab 125 addr |= ref_addr & (0xFFFFFFFF << addr_bits)
686f0c36
PA
126
127 return addr, addrlen, cpu_state, exc_info
128
129class Decoder(srd.Decoder):
b197383c 130 api_version = 3
686f0c36
PA
131 id = 'arm_etmv3'
132 name = 'ARM ETMv3'
133 longname = 'ARM Embedded Trace Macroblock'
134 desc = 'Decode ETM instruction trace packets.'
135 license = 'gplv2+'
136 inputs = ['uart']
137 outputs = ['arm_etmv3']
138 annotations = (
139 ('trace', 'Trace info'),
140 ('branch', 'Branches'),
141 ('exception', 'Exceptions'),
142 ('execution', 'Instruction execution'),
143 ('data', 'Data access'),
144 ('pc', 'Program counter'),
145 ('instr_e', 'Executed instructions'),
146 ('instr_n', 'Not executed instructions'),
147 ('source', 'Source code'),
148 ('location', 'Current location'),
149 ('function', 'Current function'),
150 )
151 annotation_rows = (
152 ('trace', 'Trace info', (0,)),
153 ('flow', 'Code flow', (1, 2, 3,)),
154 ('data', 'Data access', (4,)),
155 ('pc', 'Program counter', (5,)),
156 ('instruction', 'Instructions', (6, 7,)),
157 ('source', 'Source code', (8,)),
158 ('location', 'Current location', (9,)),
159 ('function', 'Current function', (10,)),
160 )
161 options = (
162 {'id': 'objdump', 'desc': 'objdump path',
163 'default': 'arm-none-eabi-objdump'},
164 {'id': 'objdump_opts', 'desc': 'objdump options',
1c023fab 165 'default': '-lSC'},
686f0c36
PA
166 {'id': 'elffile', 'desc': '.elf path',
167 'default': ''},
168 {'id': 'branch_enc', 'desc': 'Branch encoding',
169 'default': 'alternative', 'values': ('alternative', 'original')},
170 )
171
92b7b49f 172 def __init__(self):
10aeb8ea
GS
173 self.reset()
174
175 def reset(self):
686f0c36
PA
176 self.buf = []
177 self.syncbuf = []
178 self.prevsample = 0
179 self.last_branch = 0
180 self.cpu_state = 'arm'
181 self.current_pc = 0
182 self.current_loc = None
183 self.current_func = None
184 self.next_instr_lookup = {}
185 self.file_lookup = {}
186 self.func_lookup = {}
187 self.disasm_lookup = {}
188 self.source_lookup = {}
189
190 def start(self):
191 self.out_ann = self.register(srd.OUTPUT_ANN)
192 self.load_objdump()
193
194 def load_objdump(self):
195 '''Parse disassembly obtained from objdump into two tables:
196 next_instr_lookup: Find the next PC addr from current PC.
197 disasm_lookup: Find the instruction text from current PC.
198 source_lookup: Find the source code line from current PC.
199 '''
200 if not (self.options['objdump'] and self.options['elffile']):
201 return
202
203 opts = [self.options['objdump']]
204 opts += self.options['objdump_opts'].split()
205 opts += [self.options['elffile']]
206
207 try:
208 disasm = subprocess.check_output(opts)
209 except subprocess.CalledProcessError:
210 return
211
212 disasm = disasm.decode('utf-8', 'replace')
213
214 instpat = re.compile('\s*([0-9a-fA-F]+):\t+([0-9a-fA-F ]+)\t+([a-zA-Z][^;]+)\s*;?.*')
215 branchpat = re.compile('(b|bl|b..|bl..|cbnz|cbz)(?:\.[wn])?\s+(?:r[0-9]+,\s*)?([0-9a-fA-F]+)')
216 filepat = re.compile('[^\s]+[/\\\\]([a-zA-Z0-9._-]+:[0-9]+)(?:\s.*)?')
1c023fab 217 funcpat = re.compile('[0-9a-fA-F]+\s*<([^>]+)>:.*')
686f0c36
PA
218
219 prev_src = ''
220 prev_file = ''
221 prev_func = ''
222
223 for line in disasm.split('\n'):
224 m = instpat.match(line)
225 if m:
226 addr = int(m.group(1), 16)
227 raw = m.group(2)
228 disas = m.group(3).strip().replace('\t', ' ')
229 self.disasm_lookup[addr] = disas
230 self.source_lookup[addr] = prev_src
231 self.file_lookup[addr] = prev_file
232 self.func_lookup[addr] = prev_func
233
234 # Next address in direct sequence.
235 ilen = len(raw.replace(' ', '')) // 2
236 next_n = addr + ilen
237
238 # Next address if branch is taken.
239 bm = branchpat.match(disas)
240 if bm:
241 next_e = int(bm.group(2), 16)
242 else:
243 next_e = next_n
244
245 self.next_instr_lookup[addr] = (next_n, next_e)
246 else:
247 m = funcpat.match(line)
248 if m:
249 prev_func = m.group(1)
1c023fab 250 prev_src = None
686f0c36
PA
251 else:
252 m = filepat.match(line)
253 if m:
254 prev_file = m.group(1)
1c023fab 255 prev_src = None
686f0c36
PA
256 else:
257 prev_src = line.strip()
258
259 def flush_current_loc(self):
260 if self.current_loc is not None:
261 ss, es, loc, src = self.current_loc
262 if loc:
263 self.put(ss, es, self.out_ann, [9, [loc]])
264 if src:
265 self.put(ss, es, self.out_ann, [8, [src]])
266 self.current_loc = None
267
268 def flush_current_func(self):
269 if self.current_func is not None:
270 ss, es, func = self.current_func
271 if func:
272 self.put(ss, es, self.out_ann, [10, [func]])
273 self.current_func = None
274
275 def instructions_executed(self, exec_status):
276 '''Advance program counter based on executed instructions.
277 Argument is a list of False for not executed and True for executed
278 instructions.
279 '''
280
1c023fab
PA
281 if len(exec_status) == 0:
282 return
283
686f0c36
PA
284 tdelta = max(1, (self.prevsample - self.startsample) / len(exec_status))
285
286 for i, exec_status in enumerate(exec_status):
287 pc = self.current_pc
288 default_next = pc + 2 if self.cpu_state == 'thumb' else pc + 4
289 target_n, target_e = self.next_instr_lookup.get(pc, (default_next, default_next))
290 ss = self.startsample + round(tdelta * i)
291 es = self.startsample + round(tdelta * (i+1))
292
293 self.put(ss, es, self.out_ann,
294 [5, ['PC 0x%08x' % pc, '0x%08x' % pc, '%08x' % pc]])
295
296 new_loc = self.file_lookup.get(pc)
297 new_src = self.source_lookup.get(pc)
298 new_dis = self.disasm_lookup.get(pc)
299 new_func = self.func_lookup.get(pc)
300
301 # Report source line only when it changes.
302 if self.current_loc is not None:
303 if new_loc != self.current_loc[2] or new_src != self.current_loc[3]:
304 self.flush_current_loc()
305
306 if self.current_loc is None:
307 self.current_loc = [ss, es, new_loc, new_src]
308 else:
309 self.current_loc[1] = es
310
311 # Report function name only when it changes.
312 if self.current_func is not None:
313 if new_func != self.current_func[2]:
314 self.flush_current_func()
315
316 if self.current_func is None:
317 self.current_func = [ss, es, new_func]
318 else:
319 self.current_func[1] = es
320
321 # Report instruction every time.
322 if new_dis:
323 if exec_status:
324 a = [6, ['Executed: ' + new_dis, new_dis, new_dis.split()[0]]]
325 else:
326 a = [7, ['Not executed: ' + new_dis, new_dis, new_dis.split()[0]]]
327 self.put(ss, es, self.out_ann, a)
328
329 if exec_status:
330 self.current_pc = target_e
331 else:
332 self.current_pc = target_n
333
334 def get_packet_type(self, byte):
335 '''Identify packet type based on its first byte.
336 See ARM IHI0014Q section "ETMv3 Signal Protocol" "Packet Types"
337 '''
338 if byte & 0x01 == 0x01:
339 return 'branch'
340 elif byte == 0x00:
341 return 'a_sync'
342 elif byte == 0x04:
343 return 'cyclecount'
344 elif byte == 0x08:
345 return 'i_sync'
346 elif byte == 0x0C:
347 return 'trigger'
348 elif byte & 0xF3 in (0x20, 0x40, 0x60):
349 return 'ooo_data'
350 elif byte == 0x50:
351 return 'store_failed'
352 elif byte == 0x70:
353 return 'i_sync'
354 elif byte & 0xDF in (0x54, 0x58, 0x5C):
355 return 'ooo_place'
356 elif byte == 0x3C:
357 return 'vmid'
358 elif byte & 0xD3 == 0x02:
359 return 'data'
360 elif byte & 0xFB == 0x42:
361 return 'timestamp'
362 elif byte == 0x62:
363 return 'data_suppressed'
364 elif byte == 0x66:
365 return 'ignore'
366 elif byte & 0xEF == 0x6A:
367 return 'value_not_traced'
368 elif byte == 0x6E:
369 return 'context_id'
370 elif byte == 0x76:
371 return 'exception_exit'
372 elif byte == 0x7E:
373 return 'exception_entry'
374 elif byte & 0x81 == 0x80:
375 return 'p_header'
376 else:
377 return 'unknown'
378
379 def fallback(self, buf):
380 ptype = self.get_packet_type(buf[0])
381 return [0, ['Unhandled ' + ptype + ': ' + ' '.join(['%02x' % b for b in buf])]]
382
383 def handle_a_sync(self, buf):
384 if buf[-1] == 0x80:
385 return [0, ['Synchronization']]
386
387 def handle_exception_exit(self, buf):
1c023fab 388 return [2, ['Exception exit']]
686f0c36
PA
389
390 def handle_exception_entry(self, buf):
1c023fab 391 return [2, ['Exception entry']]
686f0c36
PA
392
393 def handle_i_sync(self, buf):
394 contextid_bytes = 0 # This is the default ETM config.
395
396 if len(buf) < 6:
397 return None # Packet definitely not full yet.
398
399 if buf[0] == 0x08: # No cycle count.
400 cyclecount = None
401 idx = 1 + contextid_bytes # Index to info byte.
402 elif buf[0] == 0x70: # With cycle count.
403 cyclecount, cyclen = parse_varint(buf[1:6])
404 idx = 1 + cyclen + contextid_bytes
405
406 if len(buf) <= idx + 4:
407 return None
408 infobyte = buf[idx]
409 addr = parse_uint(buf[idx+1:idx+5])
410
411 reasoncode = (infobyte >> 5) & 3
412 reason = ('Periodic', 'Tracing enabled', 'After overflow', 'Exit from debug')[reasoncode]
413 jazelle = (infobyte >> 4) & 1
414 nonsec = (infobyte >> 3) & 1
415 altisa = (infobyte >> 2) & 1
416 hypervisor = (infobyte >> 1) & 1
417 thumb = addr & 1
418 addr &= 0xFFFFFFFE
419
420 if reasoncode == 0 and self.current_pc != addr:
421 self.put(self.startsample, self.prevsample, self.out_ann,
422 [0, ['WARN: Unexpected PC change 0x%08x -> 0x%08x' % \
423 (self.current_pc, addr)]])
424 elif reasoncode != 0:
425 # Reset location when the trace has been interrupted.
426 self.flush_current_loc()
427 self.flush_current_func()
428
429 self.last_branch = addr
430 self.current_pc = addr
431
432 if jazelle:
433 self.cpu_state = 'jazelle'
434 elif thumb:
435 self.cpu_state = 'thumb'
436 else:
437 self.cpu_state = 'arm'
438
439 cycstr = ''
440 if cyclecount is not None:
441 cycstr = ', cyclecount %d' % cyclecount
442
443 if infobyte & 0x80: # LSIP packet
444 self.put(self.startsample, self.prevsample, self.out_ann,
445 [0, ['WARN: LSIP I-Sync packet not implemented']])
446
447 return [0, ['I-Sync: %s, PC 0x%08x, %s state%s' % \
448 (reason, addr, self.cpu_state, cycstr), \
449 'I-Sync: %s 0x%08x' % (reason, addr)]]
450
451 def handle_trigger(self, buf):
452 return [0, ['Trigger event', 'Trigger']]
453
454 def handle_p_header(self, buf):
455 # Only non cycle-accurate mode supported.
456 if buf[0] & 0x83 == 0x80:
457 n = (buf[0] >> 6) & 1
458 e = (buf[0] >> 2) & 15
459
460 self.instructions_executed([1] * e + [0] * n)
461
462 if n:
463 return [3, ['%d instructions executed, %d skipped due to ' \
464 'condition codes' % (e, n),
465 '%d ins exec, %d skipped' % (e, n),
466 '%dE,%dN' % (e, n)]]
467 else:
468 return [3, ['%d instructions executed' % e,
469 '%d ins exec' % e, '%dE' % e]]
470 elif buf[0] & 0xF3 == 0x82:
471 i1 = (buf[0] >> 3) & 1
472 i2 = (buf[0] >> 2) & 1
473 self.instructions_executed([not i1, not i2])
474 txt1 = ('executed', 'skipped')
475 txt2 = ('E', 'S')
476 return [3, ['Instruction 1 %s, instruction 2 %s' % (txt1[i1], txt1[i2]),
477 'I1 %s, I2 %s' % (txt2[i1], txt2[i2]),
478 '%s,%s' % (txt2[i1], txt2[i2])]]
479 else:
480 return self.fallback(buf)
481
482 def handle_branch(self, buf):
483 if buf[-1] & 0x80 != 0x00:
484 return None # Not complete yet.
485
486 brinfo = parse_branch_addr(buf, self.last_branch, self.cpu_state,
487 self.options['branch_enc'])
488
489 if brinfo is None:
490 return None # Not complete yet.
491
492 addr, addrlen, cpu_state, exc_info = brinfo
493 self.last_branch = addr
494 self.current_pc = addr
495
496 txt = ''
497
498 if cpu_state != self.cpu_state:
499 txt += ', to %s state' % cpu_state
500 self.cpu_state = cpu_state
501
1c023fab
PA
502 annidx = 1
503
686f0c36 504 if exc_info:
1c023fab 505 annidx = 2
686f0c36
PA
506 ns, exc, cancel, altisa, hyp, resume = exc_info
507 if ns:
508 txt += ', to non-secure state'
509 if exc:
1c023fab
PA
510 if exc < len(exc_names):
511 txt += ', exception %s' % exc_names[exc]
512 else:
513 txt += ', exception 0x%02x' % exc
686f0c36
PA
514 if cancel:
515 txt += ', instr cancelled'
516 if altisa:
517 txt += ', to AltISA'
518 if hyp:
519 txt += ', to hypervisor'
520 if resume:
521 txt += ', instr resume 0x%02x' % resume
522
1c023fab
PA
523 return [annidx, ['Branch to 0x%08x%s' % (addr, txt),
524 'B 0x%08x%s' % (addr, txt)]]
686f0c36
PA
525
526 def decode(self, ss, es, data):
527 ptype, rxtx, pdata = data
528
529 if ptype != 'DATA':
530 return
531
532 # Reset packet if there is a long pause between bytes.
533 # This helps getting the initial synchronization.
534 self.byte_len = es - ss
535 if ss - self.prevsample > 16 * self.byte_len:
536 self.flush_current_loc()
537 self.flush_current_func()
538 self.buf = []
539 self.prevsample = es
540
541 self.buf.append(pdata[0])
542
543 # Store the start time of the packet.
544 if len(self.buf) == 1:
545 self.startsample = ss
546
547 # Keep separate buffer for detection of sync packets.
548 # Sync packets override everything else, so that we can regain sync
549 # even if some packets are corrupted.
550 self.syncbuf = self.syncbuf[-4:] + [pdata[0]]
551 if self.syncbuf == [0x00, 0x00, 0x00, 0x00, 0x80]:
552 self.buf = self.syncbuf
553 self.syncbuf = []
554
555 # See if it is ready to be decoded.
556 ptype = self.get_packet_type(self.buf[0])
557 if hasattr(self, 'handle_' + ptype):
558 func = getattr(self, 'handle_' + ptype)
559 data = func(self.buf)
560 else:
561 data = self.fallback(self.buf)
562
563 if data is not None:
564 if data:
565 self.put(self.startsample, es, self.out_ann, data)
566 self.buf = []