This diff has been collapsed as it changes many lines, (681 lines changed)
Show them
Hide them
|
|
@@
-0,0
+1,681
|
|
|
1
|
#! /usr/bin/tclsh
|
|
|
2
|
#
|
|
|
3
|
# TCL script to scan disassembled programs against the "LEON3FT Stale Cache
|
|
|
4
|
# Entry After Store with Data Tag Parity Error" errata, GRLIB-TN-0009.
|
|
|
5
|
#
|
|
|
6
|
# The following Cobham components are affected by GRLIB-TN-0009:
|
|
|
7
|
# - GR712RC
|
|
|
8
|
# - LEON3FT-RTAX (all versions)
|
|
|
9
|
# - UT699
|
|
|
10
|
# - UT699E
|
|
|
11
|
# - UT700
|
|
|
12
|
#
|
|
|
13
|
# For information on how custom LEON3 systems may be affected, see
|
|
|
14
|
# GRLIB-TN-0009.
|
|
|
15
|
#
|
|
|
16
|
# Copyright (C) 2016, Cobham Gaisler
|
|
|
17
|
#
|
|
|
18
|
# Usage: sparc-elf-objdump -d program | leon3ft-b2bst-scan.tcl
|
|
|
19
|
#
|
|
|
20
|
# Command line parameters:
|
|
|
21
|
# -nonote: Do not print NOTE messages.
|
|
|
22
|
# -noinfo: Do not print INFO messages.
|
|
|
23
|
# -silent: Do not print NOTE or INFO messages.
|
|
|
24
|
#
|
|
|
25
|
# Return values:
|
|
|
26
|
# 0: No potential error locations found.
|
|
|
27
|
# 1: At least one potential error location found.
|
|
|
28
|
#
|
|
|
29
|
# Rev. history:
|
|
|
30
|
# rev 1, MH, Initial revision, based on ut699scan.tcl. Add scanning for
|
|
|
31
|
# store-store errata, reorganize comments
|
|
|
32
|
# rev 2, MA, Restruction and clean-up, fixed Sequence B.
|
|
|
33
|
# rev 3, MA, Added decoding of casa instruction, exit status and verbosity
|
|
|
34
|
# parameters.
|
|
|
35
|
# rev 4, MA, Reduced false warnings due to Store into Alternate space
|
|
|
36
|
# instructions which can not trig the errata. Script renamed to
|
|
|
37
|
# leon3ft-b2bst-scan.tcl.
|
|
|
38
|
#
|
|
|
39
|
# Note 1: Two modes are available with respect to call/ret (dslot_mode):
|
|
|
40
|
# 1) ret, and call are assumed not to have stores in their delay slots.
|
|
|
41
|
# The tool will warn if any such operations are found in the delay
|
|
|
42
|
# slot of ret,call during the scan
|
|
|
43
|
# 0) ret and call may have stores in their delay slots
|
|
|
44
|
# The tool will not warn if any such operations are found in the
|
|
|
45
|
# delay slot during the scan. Instead it will assume this
|
|
|
46
|
# possibility exists when evaluating insns following a function call
|
|
|
47
|
#
|
|
|
48
|
|
|
|
49
|
### CONFIGURATION
|
|
|
50
|
set b2bst_seqa_scan 1; # enable/disable scanning for Sequence A
|
|
|
51
|
set b2bst_seqb_scan 1; # enable/disable scanning for Sequence B
|
|
|
52
|
# set dslot_mode 0; # assume store can be in delay slot of call,jmpl
|
|
|
53
|
set dslot_mode 1; # do not allow store in delay slot of call,jmpl
|
|
|
54
|
###
|
|
|
55
|
|
|
|
56
|
# Mask values for types of output messages
|
|
|
57
|
set MSG_INFO 1; # Information on things to check manually.
|
|
|
58
|
set MSG_NOTE 2; # Notices on branch following
|
|
|
59
|
set msglevel [expr {$MSG_INFO | $MSG_NOTE}]
|
|
|
60
|
|
|
|
61
|
set errata_desc "LEON3FT Stale Cache Entry After Store with Data Tag Parity Error"
|
|
|
62
|
set util_rev 4
|
|
|
63
|
set util_date "20170215"
|
|
|
64
|
|
|
|
65
|
set b2bst_seqa_desc "Sequence A"
|
|
|
66
|
set b2bst_seqb_desc "Sequence B"
|
|
|
67
|
|
|
|
68
|
set op_count 0
|
|
|
69
|
set lineskip_count 0
|
|
|
70
|
set line_count 0
|
|
|
71
|
set err_count 0
|
|
|
72
|
set fn_count 0
|
|
|
73
|
set fninst_count 0
|
|
|
74
|
|
|
|
75
|
proc puts_info {str} {
|
|
|
76
|
global msglevel MSG_INFO
|
|
|
77
|
if {$msglevel & $MSG_INFO} { puts "INFO: $str" }
|
|
|
78
|
}
|
|
|
79
|
|
|
|
80
|
proc puts_note {str} {
|
|
|
81
|
global msglevel MSG_NOTE
|
|
|
82
|
if {$msglevel & $MSG_NOTE} { puts "NOTE: $str" }
|
|
|
83
|
}
|
|
|
84
|
|
|
|
85
|
# ----------------------------------------------------------------------------
|
|
|
86
|
# Routine that decodes specified opcode and returns list of
|
|
|
87
|
# 0 :optype Class of opcode
|
|
|
88
|
# one of call,jmpl,branch,sethi,ld,st,atomic,alu,fpop,save,restore
|
|
|
89
|
# 1 :target-reg Register modified by op
|
|
|
90
|
# 2 :source-reg1 First source register (rs1)
|
|
|
91
|
# 3 :source-reg2 Second source register (rs2)
|
|
|
92
|
# 4 :store-reg Source register for store data
|
|
|
93
|
# regs are numbered 0-31 integer regs, 32-63 FP regs
|
|
|
94
|
# 5 :double-flag
|
|
|
95
|
# ORed mask: 8) target-reg is DP, 4) sreg1 is DP, 2) sreg2 is DP, 1) store-reg is DP
|
|
|
96
|
# 6 :immed Immediate operand
|
|
|
97
|
# 7 :nnpc1 Next instructions nPC if branch not taken (normal case), normally nPC+4
|
|
|
98
|
# 8 :nnpc2 Next instruction's nPC if branch is taken, if not branch then nnpc2=nnpc1
|
|
|
99
|
# 9 :annul
|
|
|
100
|
# ORed mask: next inst annulled if 2) branch not taken 1) branch taken
|
|
|
101
|
# 10:bubbles Number of extra cycles spent in decode stage (bubbles inserted)
|
|
|
102
|
proc decode_inst { opcode {pc "0x00000000"} {npc "0x00000004"}} {
|
|
|
103
|
# Some "sta" instructions do not trig the errata. The related ASI:s are
|
|
|
104
|
# defined here.
|
|
|
105
|
set safeasi [list 0x02 0x03 0x04 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x13 0x14 0x15 0x18 0x19 0x1c 0x1d 0x1e]
|
|
|
106
|
#Default values if no cond matches
|
|
|
107
|
set opname "unknown"
|
|
|
108
|
set destreg 0
|
|
|
109
|
set sreg1 0
|
|
|
110
|
set sreg2 0
|
|
|
111
|
set streg 0
|
|
|
112
|
set dblflag 0
|
|
|
113
|
set immed 0
|
|
|
114
|
|
|
|
115
|
if { [string is integer $npc] } {
|
|
|
116
|
set nnpc1 [format "0x%08x" [ expr {$npc+4} ] ]
|
|
|
117
|
} else {
|
|
|
118
|
set nnpc1 "$npc+4"
|
|
|
119
|
}
|
|
|
120
|
set nnpc2 $nnpc1
|
|
|
121
|
set annul 0
|
|
|
122
|
set bubbles 0
|
|
|
123
|
#Decode
|
|
|
124
|
set op1 [ expr {$opcode >> 30} ]
|
|
|
125
|
if { $op1 == 1 } {
|
|
|
126
|
set opname "call"
|
|
|
127
|
set disp [ expr { ($opcode & 0x3fffffff)<<2 } ]
|
|
|
128
|
set nnpc1 [ format "0x%08x" [ expr { $pc + $disp } ] ]
|
|
|
129
|
set nnpc2 $nnpc1
|
|
|
130
|
} elseif { $op1 == 0 } {
|
|
|
131
|
set op2 [ expr {($opcode >> 22) & 7} ]
|
|
|
132
|
# NOTE: 2=Bicc, 6=FBcc, 7=CBcc
|
|
|
133
|
if { $op2 == 2 || $op2 == 6 } {
|
|
|
134
|
set opname "branch"
|
|
|
135
|
set disp [expr { ($opcode & 0x3fffff) << 2 } ]
|
|
|
136
|
if { $disp > 0x800000 } { set disp [expr {$disp - 0x1000000}] }
|
|
|
137
|
# Todo check cond,annul, handle always or never
|
|
|
138
|
set nnpc2 [ format "0x%08x" [ expr { $pc + $disp } ] ]
|
|
|
139
|
set cond [ expr { ($opcode >> 25) & 15 } ]
|
|
|
140
|
set annulbit [ expr { $opcode & 0x20000000 } ]
|
|
|
141
|
if { $cond == 0 } { # Branch never
|
|
|
142
|
set nnpc2 $nnpc1
|
|
|
143
|
if { $annulbit } { set annul 3 }
|
|
|
144
|
} elseif { $cond == 8 } { # Branch always
|
|
|
145
|
set nnpc1 $nnpc2
|
|
|
146
|
if { $annulbit } { set annul 3 }
|
|
|
147
|
} else { # True cond branch
|
|
|
148
|
if { $annulbit } { set annul 2 }
|
|
|
149
|
}
|
|
|
150
|
} elseif { $op2 == 4 } {
|
|
|
151
|
set opname "sethi"
|
|
|
152
|
set destreg [ expr { ($opcode >> 25) & 31 } ]
|
|
|
153
|
set immed [ expr { ($opcode & 0x3fffff) << 10 } ]
|
|
|
154
|
}
|
|
|
155
|
} elseif { $op1 == 3 } {
|
|
|
156
|
# SPARC V8 Table F-4
|
|
|
157
|
# set opname "mem" - changed to use ld/st/atomic instead
|
|
|
158
|
set op3 [ expr { ($opcode >> 19) & 63 } ]
|
|
|
159
|
set rs1 [ expr { (($opcode >> 14) & 31) } ]
|
|
|
160
|
set rs2 [ expr { (($opcode ) & 31) } ]
|
|
|
161
|
set rd [ expr { (($opcode >> 25) & 31) } ]
|
|
|
162
|
set sreg1 $rs1
|
|
|
163
|
if { $opcode & 0x2000 } {
|
|
|
164
|
set immed [ expr { $opcode & 0x1fff } ]
|
|
|
165
|
if { $immed > 0xfff } { set immed [ expr { $immed-(0x2000) } ] }
|
|
|
166
|
} else {
|
|
|
167
|
set sreg2 $rs2
|
|
|
168
|
}
|
|
|
169
|
if { (($op3 & 13) == 13) || ($op3 == 0x3c) } {
|
|
|
170
|
# Atomic ldst/swap/casa
|
|
|
171
|
set opname "atomic"
|
|
|
172
|
set destreg $rd
|
|
|
173
|
set streg $rd
|
|
|
174
|
} else {
|
|
|
175
|
# Adjust reg no if FP ld/st (op3[5]=1)
|
|
|
176
|
if { ($op3 & 0x20) != 0 } { set rd [expr {32 + $rd} ] }
|
|
|
177
|
# Check if read or write op3[2]
|
|
|
178
|
if { ($op3 & 4) != 0 } {
|
|
|
179
|
set opname "st"
|
|
|
180
|
set streg $rd
|
|
|
181
|
set allow_safe_sta true
|
|
|
182
|
if { $allow_safe_sta && (($op3 & 0x3c) == 0x14) } {
|
|
|
183
|
# op3 is 0x14, 0x15, 0x16 or 0x17 (sta, stba, stha or stda)
|
|
|
184
|
# (if i = 0)
|
|
|
185
|
set asi [expr { (($opcode >> 5) & 0xff) }]
|
|
|
186
|
set safematch [lsearch -exact -integer $safeasi $asi]
|
|
|
187
|
if {0 <= $safematch} {
|
|
|
188
|
set opname "sta_safe"
|
|
|
189
|
}
|
|
|
190
|
}
|
|
|
191
|
} else {
|
|
|
192
|
set opname "ld"
|
|
|
193
|
set destreg $rd
|
|
|
194
|
}
|
|
|
195
|
# Check size op3[1:0]
|
|
|
196
|
if { ($op3 & 3) == 3 } {
|
|
|
197
|
if { $opname eq "st" } { set dblflag 1 } else { set dblflag 8 }
|
|
|
198
|
}
|
|
|
199
|
}
|
|
|
200
|
} else {
|
|
|
201
|
set op3 [ expr { ($opcode >> 19) & 63 } ]
|
|
|
202
|
if { $op3 == 0x34 || $op3 == 0x35 } {
|
|
|
203
|
set opname "fpop"
|
|
|
204
|
set opf [ expr { ($opcode >> 5) & 0x1ff } ]
|
|
|
205
|
set rs1 [ expr { 32 + (($opcode >> 14) & 31) } ]
|
|
|
206
|
set rs2 [ expr { 32 + (($opcode ) & 31) } ]
|
|
|
207
|
set rd [ expr { 32 + (($opcode >> 25) & 31) } ]
|
|
|
208
|
# opf[1:0] corresponds to operand size of rs2
|
|
|
209
|
# opf[7:6]=11 is conv op, opf[3:2] gives opsize of rd, rs1 unused
|
|
|
210
|
# opf[7:6]=00 is single-operand op, rd same size as rs2, rs1 unused
|
|
|
211
|
# opf[7:4]=0100 regular op, rs1,rd same size as rs2
|
|
|
212
|
# opf[7:4]=0101 comp op, rd unused, rs1 same size as rs2
|
|
|
213
|
# opf[7:4]=0110 conv+mul, opf[3:2] gives opsize of rd, rs1 same size as rs2
|
|
|
214
|
set sreg2 $rs2
|
|
|
215
|
if { ($opf & 3) > 1 } { set dblflag 2 }
|
|
|
216
|
if { ($opf & 0xc0) == 0x40 } {
|
|
|
217
|
set sreg1 $rs1
|
|
|
218
|
if { $dblflag & 2 } { set dblflag [ expr { $dblflag | 4 } ] }
|
|
|
219
|
}
|
|
|
220
|
if { ($opf & 0xc0) == 0xc0 || ($opf & 0xf0) == 0x60 } {
|
|
|
221
|
set destreg $rd
|
|
|
222
|
if { ($opf & 0xc) > 4 } { set dblflag [ expr { $dblflag | 8 } ] }
|
|
|
223
|
} elseif { ($opf & 0xf0) != 0x50 } {
|
|
|
224
|
set destreg $rd
|
|
|
225
|
if { $dblflag & 2 } { set dblflag [ expr { $dblflag | 8 } ] }
|
|
|
226
|
}
|
|
|
227
|
} else {
|
|
|
228
|
set opname "alu"
|
|
|
229
|
set destreg [ expr { (($opcode >> 25) & 31) } ]
|
|
|
230
|
set sreg1 [ expr { (($opcode >> 14) & 31) } ]
|
|
|
231
|
if { $opcode & 0x2000 } {
|
|
|
232
|
set immed [ expr { $opcode & 0x1fff } ]
|
|
|
233
|
if { $immed > 0xfff } { set immed [ expr { $immed-(0x2000) } ] }
|
|
|
234
|
} else {
|
|
|
235
|
set sreg2 [ expr { (($opcode ) & 31) } ]
|
|
|
236
|
}
|
|
|
237
|
if { $op3 == 0x38 } {
|
|
|
238
|
set opname "jmpl"
|
|
|
239
|
set nnpc1 "jmpaddr"
|
|
|
240
|
set nnpc2 "jmpaddr"
|
|
|
241
|
set bubbles 2
|
|
|
242
|
} elseif { $op3 == 0x3c } {
|
|
|
243
|
set opname "save"
|
|
|
244
|
} elseif { $op3 == 0x3d } {
|
|
|
245
|
set opname "restore"
|
|
|
246
|
}
|
|
|
247
|
}
|
|
|
248
|
}
|
|
|
249
|
#Return values
|
|
|
250
|
return [list $opname $destreg $sreg1 $sreg2 $streg $dblflag $immed $nnpc1 $nnpc2 $annul $bubbles]
|
|
|
251
|
}
|
|
|
252
|
|
|
|
253
|
proc is_store { opcode } {
|
|
|
254
|
set op1 [ expr { ($opcode >> 30) } ]
|
|
|
255
|
set op3 [ expr { ($opcode >> 19) & 63 } ]
|
|
|
256
|
return [expr {($op1 == 3) && (($op3 & 0xc) == 0x4)}]
|
|
|
257
|
}
|
|
|
258
|
|
|
|
259
|
# -------------------------------------------------------------------------------------
|
|
|
260
|
|
|
|
261
|
proc unpack { l args } {
|
|
|
262
|
for { set i 0 } { $i < [llength $args] } { incr i } {
|
|
|
263
|
uplevel 1 "set [lindex $args $i] [lindex $l $i]"
|
|
|
264
|
}
|
|
|
265
|
}
|
|
|
266
|
|
|
|
267
|
# --------- Postprocessing on whole function
|
|
|
268
|
|
|
|
269
|
# Number of instructions printed is starti + 2
|
|
|
270
|
proc print_trace { fnpc state fnarr seqstart {starti 4}} {
|
|
|
271
|
upvar $fnarr fnops
|
|
|
272
|
|
|
|
273
|
puts " # PC Opcode"
|
|
|
274
|
set cnt [expr {$seqstart-$starti}]
|
|
|
275
|
for { set x $starti } { $x > -2 } { set x [expr {$x-1}] } {
|
|
|
276
|
incr cnt
|
|
|
277
|
if { $cnt > 0 } { set cntv $cnt } else { set cntv " " }
|
|
|
278
|
if { $x >= 0 } { set pc [lindex $state $x] } else { set pc $fnpc }
|
|
|
279
|
if { $pc eq "?" || $pc eq "call" || $pc eq "ret" || $pc eq "dslot" } {
|
|
|
280
|
puts " $cntv (other fn) $pc"
|
|
|
281
|
} else {
|
|
|
282
|
if { [string index $pc 0] eq "A" } {
|
|
|
283
|
set pc [string range $pc 1 end]
|
|
|
284
|
set annul " (annulled)"
|
|
|
285
|
} else {
|
|
|
286
|
set annul ""
|
|
|
287
|
}
|
|
|
288
|
if { [info exists fnops($pc)] } {
|
|
|
289
|
set op $fnops($pc)
|
|
|
290
|
} else {
|
|
|
291
|
set op [list "0x???????? (outside func)"]
|
|
|
292
|
}
|
|
|
293
|
puts " $cntv $pc [join $op] $annul"
|
|
|
294
|
}
|
|
|
295
|
}
|
|
|
296
|
puts ""
|
|
|
297
|
}
|
|
|
298
|
|
|
|
299
|
# Routine called by scan_wholefunc_main to check
|
|
|
300
|
# instruction sequences described in GRLIB-TN-009, Issue 1.0
|
|
|
301
|
#
|
|
|
302
|
# "Sequence A"
|
|
|
303
|
# 1. store of word size or less (st / stb / sth / stf)
|
|
|
304
|
# 2. any single instruction that is not a load or store
|
|
|
305
|
# 3. any store instruction (st / stb / sth / stf / std / stdf)
|
|
|
306
|
#
|
|
|
307
|
# "Sequence B"
|
|
|
308
|
# 1. store of double word size (std / stdf)
|
|
|
309
|
# 2. any store instruction (st / stb / sth / stf / std / stdf)
|
|
|
310
|
proc scan_b2bst { fnname fnpc fnop opdec fnarr state } {
|
|
|
311
|
global b2bst_seqa_scan
|
|
|
312
|
global b2bst_seqb_scan
|
|
|
313
|
upvar $fnarr fnops
|
|
|
314
|
global dslot_mode
|
|
|
315
|
|
|
|
316
|
# Check latest 3 insn if they can be st/std/regular
|
|
|
317
|
set pca [list $fnpc [lindex $state 0] [lindex $state 1]]
|
|
|
318
|
set can_be_arr [list]
|
|
|
319
|
for { set i 0 } { $i < 3 } { incr i } {
|
|
|
320
|
set can_be [list 0 0 0] ; # st std regular
|
|
|
321
|
set pcv [lindex $pca $i]
|
|
|
322
|
if { $pcv eq "?" } {
|
|
|
323
|
set can_be [list 1 1 1]
|
|
|
324
|
} elseif { $pcv eq "dslot" } {
|
|
|
325
|
set can_be [list [expr {1-$dslot_mode}] [expr {1-$dslot_mode}] 1]
|
|
|
326
|
} elseif { [string index $pcv 0] eq "A" || $pcv eq "call" || $pcv eq "ret" } {
|
|
|
327
|
set can_be [list 0 0 1]
|
|
|
328
|
} else {
|
|
|
329
|
set op $fnops($pcv)
|
|
|
330
|
set ophex [lindex $op 0]
|
|
|
331
|
set opdec [decode_inst $ophex]
|
|
|
332
|
set optype [lindex $opdec 0]
|
|
|
333
|
set opstorereg [lindex $opdec 4]
|
|
|
334
|
set opdblflag [lindex $opdec 5]
|
|
|
335
|
if { $optype eq "st" && ($opdblflag & 1) } {
|
|
|
336
|
set can_be [list 0 1 0]
|
|
|
337
|
} elseif { $optype eq "st" } {
|
|
|
338
|
set can_be [list 1 0 0]
|
|
|
339
|
} elseif { $optype ne "ld" && $optype ne "atomic" } {
|
|
|
340
|
# Any single instruction that is not a load or store
|
|
|
341
|
set can_be [list 0 0 1]
|
|
|
342
|
}
|
|
|
343
|
}
|
|
|
344
|
lappend can_be_arr $can_be
|
|
|
345
|
}
|
|
|
346
|
# can_be_arr has at index
|
|
|
347
|
# - at index 2: previous previous instruction
|
|
|
348
|
# - at index 1: previous instruction
|
|
|
349
|
# - at index 0: current instruction
|
|
|
350
|
set pp_insn [lindex $can_be_arr 2]
|
|
|
351
|
set p_insn [lindex $can_be_arr 1]
|
|
|
352
|
set c_insn [lindex $can_be_arr 0]
|
|
|
353
|
# Sequence A
|
|
|
354
|
# 1. store of word size or less
|
|
|
355
|
# 2. any single instruction that is not a load or store
|
|
|
356
|
# 3. any store instruction
|
|
|
357
|
if {
|
|
|
358
|
(
|
|
|
359
|
$b2bst_seqa_scan &&
|
|
|
360
|
[lindex $pp_insn 0] &&
|
|
|
361
|
[lindex $p_insn 2] &&
|
|
|
362
|
([lindex $c_insn 0] || [lindex $c_insn 1])
|
|
|
363
|
) } {
|
|
|
364
|
puts "WARNING: Possible sequence matching LEON3FT b2bst errata (sequence A) in function $fnname"
|
|
|
365
|
print_trace $fnpc $state fnops 1
|
|
|
366
|
puts ""
|
|
|
367
|
return 1
|
|
|
368
|
}
|
|
|
369
|
|
|
|
370
|
# Sequence B
|
|
|
371
|
# 1. any store instruction
|
|
|
372
|
# 2. store of double word size
|
|
|
373
|
if {
|
|
|
374
|
(
|
|
|
375
|
$b2bst_seqb_scan &&
|
|
|
376
|
[lindex $p_insn 1] &&
|
|
|
377
|
([lindex $c_insn 0] || [lindex $c_insn 1])
|
|
|
378
|
) } {
|
|
|
379
|
puts "WARNING: Possible sequence matching LEON3FT b2bst errata (sequence B) in function $fnname"
|
|
|
380
|
print_trace $fnpc $state fnops 0
|
|
|
381
|
puts ""
|
|
|
382
|
return 1
|
|
|
383
|
}
|
|
|
384
|
return 0
|
|
|
385
|
}
|
|
|
386
|
|
|
|
387
|
|
|
|
388
|
# Main recursive function doing scan
|
|
|
389
|
# fnname - funcion name
|
|
|
390
|
# fnpc, fnnpc, PC/nPC of current insn (8-digit hex, 0x prefix) prefix also with A if annulled,
|
|
|
391
|
# fnarr - array/hash-table, see descr for scan_wholefunc
|
|
|
392
|
# dpdist - number of SP FPOPs done minimum since last DP FPOP
|
|
|
393
|
# state - List of 5 last instruction addresses (hex w 0x prefix), special values "?"=unknown "call"=call from calling proc
|
|
|
394
|
# smap - Hash table used to track where we have been already
|
|
|
395
|
proc scan_wholefunc_main { fnname fnpc fnnpc fnarr savelevel dpdist state smap lastoptype } {
|
|
|
396
|
global dslot_mode
|
|
|
397
|
upvar $fnarr fnops
|
|
|
398
|
upvar $smap statemap
|
|
|
399
|
|
|
|
400
|
set r 0
|
|
|
401
|
|
|
|
402
|
# puts "entering scan_wholefunc_main: PC:$fnpc nPC:$fnnpc dpdist:$dpdist savelevel:$savelevel state:$state"
|
|
|
403
|
# puts "fnops exists [info exists fnops] [array exists fnops] [array names fnops]"
|
|
|
404
|
while {1} {
|
|
|
405
|
# puts "scan_wholefunc_main: PC:$fnpc nPC:$fnnpc dpdist:$dpdist savelevel:$savelevel state:$state"
|
|
|
406
|
set statedesc [list $fnpc $fnnpc $savelevel $dpdist $state]
|
|
|
407
|
if { [info exists statemap($statedesc)] } {
|
|
|
408
|
# Already been here
|
|
|
409
|
return $r
|
|
|
410
|
}
|
|
|
411
|
set statemap($statedesc) 1
|
|
|
412
|
|
|
|
413
|
set fnpca $fnpc
|
|
|
414
|
set pcannul 0
|
|
|
415
|
if { [string index $fnpc 0] eq "A" } {
|
|
|
416
|
set pcannul 1
|
|
|
417
|
set fnpc [string range $fnpc 1 end]
|
|
|
418
|
set ophex "0x01000000"
|
|
|
419
|
} else {
|
|
|
420
|
if {![info exists fnops($fnpc)]} {
|
|
|
421
|
puts_note "$fnname: Execution leaves function without return";
|
|
|
422
|
return $r
|
|
|
423
|
}
|
|
|
424
|
|
|
|
425
|
set op $fnops($fnpc)
|
|
|
426
|
set ophex [lindex $op 0]
|
|
|
427
|
set opname [lindex $op 1]
|
|
|
428
|
}
|
|
|
429
|
|
|
|
430
|
set opdec [ decode_inst $ophex $fnpc $fnnpc ]
|
|
|
431
|
unpack $opdec optype treg sreg1 sreg2 streg dblflag immed nnpc1 nnpc2 annul bubbles
|
|
|
432
|
|
|
|
433
|
set prev_savelevel $savelevel
|
|
|
434
|
if { $optype eq "fpop" } {
|
|
|
435
|
# update dpdist
|
|
|
436
|
if { ($dblflag & 6) == 0 } {
|
|
|
437
|
if { $dpdist < 2 } { incr dpdist }
|
|
|
438
|
} else {
|
|
|
439
|
set dpdist 0
|
|
|
440
|
}
|
|
|
441
|
} elseif { $optype eq "save" } {
|
|
|
442
|
if { $savelevel > 9 } {
|
|
|
443
|
puts_note "$fnname: More than 10 deep saves (possibly regfile clear loop)"
|
|
|
444
|
} else {
|
|
|
445
|
incr savelevel
|
|
|
446
|
}
|
|
|
447
|
# puts "save at $fnpc, new savelevel: $savelevel"
|
|
|
448
|
} elseif { $optype eq "restore" } {
|
|
|
449
|
if { $savelevel < -9 } {
|
|
|
450
|
puts_note "$fnname: More than 10 deep restores (possibly regfile clear loop)"
|
|
|
451
|
} else {
|
|
|
452
|
if { $savelevel == 0 } {
|
|
|
453
|
puts_note "$fnname: More restore than save instructions"
|
|
|
454
|
}
|
|
|
455
|
set savelevel [ expr { $savelevel-1} ]
|
|
|
456
|
}
|
|
|
457
|
# puts "restore at $fnpc, new savelevel: $savelevel"
|
|
|
458
|
}
|
|
|
459
|
if { true && ($optype eq "st") } {
|
|
|
460
|
if { [scan_b2bst $fnname $fnpc $op $opdec fnops $state] } { incr r }
|
|
|
461
|
}
|
|
|
462
|
|
|
|
463
|
# Handle jmpl and call
|
|
|
464
|
# If in delay slot of jmpl:
|
|
|
465
|
# If ret or retl, stop recursing further
|
|
|
466
|
# If other jmpl, check savelevel if it will return or not
|
|
|
467
|
# If in delay slot of call, set dpdist to 0 (called proc may have done DP operation)
|
|
|
468
|
set is_call 0
|
|
|
469
|
if {
|
|
|
470
|
(
|
|
|
471
|
$dslot_mode != 0 &&
|
|
|
472
|
($lastoptype eq "branch" || $lastoptype eq "jmpl" || $lastoptype eq "call")
|
|
|
473
|
) } {
|
|
|
474
|
if { $optype eq "st" } {
|
|
|
475
|
puts "WARNING: $fnname: $opname in delay slot of branch/call/ret at $fnpc may cause errata"
|
|
|
476
|
print_trace $fnpc $state fnops -1 1
|
|
|
477
|
puts ""
|
|
|
478
|
incr r
|
|
|
479
|
}
|
|
|
480
|
}
|
|
|
481
|
if { $lastoptype eq "jmpl" } {
|
|
|
482
|
set linst [lindex $state 0]
|
|
|
483
|
set q $fnops($linst)
|
|
|
484
|
set qh [lindex $q 0]
|
|
|
485
|
set qdec [ decode_inst $qh $linst $fnpc ]
|
|
|
486
|
unpack $qdec qdtype qdtreg qdsreg1 qdsreg2 qdstreg qddblflag qdimmed qdnnpc1 qdnnpc2 qdannul
|
|
|
487
|
if { $qdtreg==15 && $prev_savelevel==1 && $savelevel==1 } {
|
|
|
488
|
# Function call via pointer
|
|
|
489
|
incr is_call
|
|
|
490
|
} elseif { $qdtreg==0 && $savelevel==0 } {
|
|
|
491
|
# Return or tail recursion
|
|
|
492
|
return $r
|
|
|
493
|
} else {
|
|
|
494
|
# Computed goto or some other non-standard construct
|
|
|
495
|
puts_info "$fnname: Unable to trace jmpl at $linst - check manually"
|
|
|
496
|
return $r
|
|
|
497
|
}
|
|
|
498
|
} elseif { $lastoptype eq "call" } {
|
|
|
499
|
if { $savelevel==0 && $prev_savelevel==1 } {
|
|
|
500
|
# call,restore tail recursion into other fn
|
|
|
501
|
return $r
|
|
|
502
|
} elseif { $savelevel==0 && $prev_savelevel==0 && $treg == 15 } {
|
|
|
503
|
puts_note "$fnname: Leaf tail recursion construct at $fnpc"
|
|
|
504
|
return $r
|
|
|
505
|
} else {
|
|
|
506
|
incr is_call
|
|
|
507
|
}
|
|
|
508
|
}
|
|
|
509
|
# Update history
|
|
|
510
|
for { set z 0 } { $z <= $bubbles } { incr z } {
|
|
|
511
|
set state [concat $fnpca [lrange $state 0 4] ]
|
|
|
512
|
}
|
|
|
513
|
if { $is_call } {
|
|
|
514
|
if { $optype eq "branch" || $optype eq "call" || $optype eq "jmpl" } {
|
|
|
515
|
puts_info "$fnname: jump in delay slot of call - check manually"
|
|
|
516
|
}
|
|
|
517
|
# Setup state after call has returned
|
|
|
518
|
set dpdist 0
|
|
|
519
|
set state [list "dslot" "ret" "ret" "ret" "?" ]
|
|
|
520
|
set optype "?"; # for lastoptype
|
|
|
521
|
set fnnpc [ format "0x%08x" [expr {$fnpc+4}]]
|
|
|
522
|
set nnpc1 [ format "0x%08x" [expr {$fnpc+8}]]
|
|
|
523
|
set nnpc2 $nnpc1
|
|
|
524
|
set annul 0
|
|
|
525
|
}
|
|
|
526
|
# Handle annullment
|
|
|
527
|
if { ($annul & 2) != 0 } { set npc1 "A$fnnpc" } else { set npc1 $fnnpc }
|
|
|
528
|
if { ($annul & 1) != 0 } { set npc2 "A$fnnpc" } else { set npc2 $fnnpc }
|
|
|
529
|
# Recurse if multiple nPC:s, tail recurse for last element
|
|
|
530
|
if { $nnpc1 != $nnpc2 || $npc1 != $npc2 } {
|
|
|
531
|
# puts "Recursion nnpc:$nnpc2!"
|
|
|
532
|
set q [scan_wholefunc_main $fnname $npc1 $nnpc2 fnops $savelevel $dpdist $state statemap $optype]
|
|
|
533
|
set r [expr {$r+$q}]
|
|
|
534
|
}
|
|
|
535
|
set fnpc $npc1
|
|
|
536
|
set fnnpc $nnpc1
|
|
|
537
|
set lastoptype $optype
|
|
|
538
|
}
|
|
|
539
|
}
|
|
|
540
|
|
|
|
541
|
# Whole function scan for "LEON3FT Stale Cache After Store with Data Tag Parity Error" errata
|
|
|
542
|
# fnname - function name, fnaddr - function addres
|
|
|
543
|
# fnarr - name of array/hash-table containing operations
|
|
|
544
|
# key to the array is the address as 8-digit hex number prefixed with 0x
|
|
|
545
|
# value is a list containing 0:opcode, 1:mnemonic, 2:args
|
|
|
546
|
proc scan_wholefunc { fnname fnaddr fnarr } {
|
|
|
547
|
global fn_count fninst_count;
|
|
|
548
|
upvar $fnarr fnops
|
|
|
549
|
|
|
|
550
|
if { $fnname eq ".text" } {
|
|
|
551
|
puts "WARNING: The disassembled binary appears to be stripped, the script can not scan stripped binaries"
|
|
|
552
|
}
|
|
|
553
|
|
|
|
554
|
# Does the fn not contain any store at all, then we can skip it
|
|
|
555
|
set sid [array startsearch fnops]
|
|
|
556
|
set found_st 0
|
|
|
557
|
while { !$found_st } {
|
|
|
558
|
set op [array nextelement fnops $sid]
|
|
|
559
|
if { $op eq "" } { array donesearch fnops $sid; break }
|
|
|
560
|
set ophex [lindex $fnops($op) 0]
|
|
|
561
|
# puts "$ophex"
|
|
|
562
|
if { [is_store $ophex] } { incr found_st }
|
|
|
563
|
}
|
|
|
564
|
if { !$found_st } {
|
|
|
565
|
# puts "No store in $fnname!"
|
|
|
566
|
return 0
|
|
|
567
|
}
|
|
|
568
|
|
|
|
569
|
# -- Function contains store, proceed with scanning
|
|
|
570
|
# puts "Checking $fnname"
|
|
|
571
|
set pc "0x$fnaddr"
|
|
|
572
|
# puts "pc: $pc"
|
|
|
573
|
set npc [format "0x%08x" [expr {$pc+4}]]
|
|
|
574
|
array unset statemap
|
|
|
575
|
array set statemap ""
|
|
|
576
|
set ec [scan_wholefunc_main $fnname $pc $npc fnops 0 0 [list "dslot" "call" "?" "?" "?" "?"] statemap "?"]
|
|
|
577
|
# Update fn_count/fninst_count
|
|
|
578
|
incr fn_count
|
|
|
579
|
array unset itagchk
|
|
|
580
|
array set itagchk ""
|
|
|
581
|
set sid [array startsearch statemap]
|
|
|
582
|
while { [array anymore statemap $sid] } {
|
|
|
583
|
set x [array nextelement statemap $sid]
|
|
|
584
|
set iaddr [lindex $x 0]
|
|
|
585
|
# puts "iaddr: $iaddr"
|
|
|
586
|
if { [info exists fnops($iaddr)] && ![info exists itagchk($iaddr)]} {
|
|
|
587
|
incr fninst_count
|
|
|
588
|
set itagchk($iaddr) 1
|
|
|
589
|
}
|
|
|
590
|
}
|
|
|
591
|
array donesearch statemap $sid
|
|
|
592
|
return $ec
|
|
|
593
|
}
|
|
|
594
|
|
|
|
595
|
|
|
|
596
|
# --------- Main routine
|
|
|
597
|
|
|
|
598
|
if { ! [info exists sourcing] } {
|
|
|
599
|
|
|
|
600
|
# Parse command line
|
|
|
601
|
for {set i 0} {$i < $argc} {incr i} {
|
|
|
602
|
set arg [lindex $argv $i]
|
|
|
603
|
puts "arg=$arg"
|
|
|
604
|
if {"-noinfo" eq $arg} {
|
|
|
605
|
set msglevel [expr {$msglevel & ~($MSG_INFO)}]
|
|
|
606
|
}
|
|
|
607
|
if {"-nonote" eq $arg} {
|
|
|
608
|
set msglevel [expr {$msglevel & ~($MSG_NOTE)}]
|
|
|
609
|
}
|
|
|
610
|
if {"-silent" eq $arg} {
|
|
|
611
|
set msglevel 0
|
|
|
612
|
}
|
|
|
613
|
}
|
|
|
614
|
|
|
|
615
|
puts "\n$errata_desc errata scanning utility, rev $util_rev ($util_date)"
|
|
|
616
|
|
|
|
617
|
puts "Searching objdump -d output on standard input for:"
|
|
|
618
|
if {$b2bst_seqa_scan} {
|
|
|
619
|
puts " - $b2bst_seqa_desc"
|
|
|
620
|
}
|
|
|
621
|
if {$b2bst_seqb_scan} {
|
|
|
622
|
puts " - $b2bst_seqb_desc"
|
|
|
623
|
}
|
|
|
624
|
puts ""
|
|
|
625
|
|
|
|
626
|
set fn_valid 0
|
|
|
627
|
while {! [eof stdin]} {
|
|
|
628
|
set l [gets stdin]
|
|
|
629
|
incr line_count
|
|
|
630
|
# puts "line $l"
|
|
|
631
|
# Remove comment after '!'
|
|
|
632
|
set x [string first "!" "$l"]
|
|
|
633
|
if { $x >= 0 } then { set l [string range "$l" 0 $x] }
|
|
|
634
|
# check if entry point / function start
|
|
|
635
|
set m [regexp {^ *([0-9A-Fa-f]+) <([^ ]+)>:} "$l" t1 addr symname]
|
|
|
636
|
if { $m > 0 } then {
|
|
|
637
|
# puts "Function name=$symname addr=$addr"
|
|
|
638
|
if { $fn_valid } {
|
|
|
639
|
# puts $curfn_ops(0x40000000)
|
|
|
640
|
set q [scan_wholefunc $curfn $curfn_addr curfn_ops]
|
|
|
641
|
# puts "q=$q err_count=$err_count"
|
|
|
642
|
set err_count [ expr { $err_count + $q } ]
|
|
|
643
|
}
|
|
|
644
|
set curfn "$symname"
|
|
|
645
|
set curfn_addr "$addr"
|
|
|
646
|
array unset curfn_ops
|
|
|
647
|
array set curfn_ops ""
|
|
|
648
|
set fn_valid 0
|
|
|
649
|
} else {
|
|
|
650
|
# Check if opcode
|
|
|
651
|
set m [regexp {^ *([0-9A-Fa-f]+): *\t(.. .. .. ..) *\t([^ ]+)(.*)$} "$l" t1 addr hexcode opname opargs]
|
|
|
652
|
if { $m > 0 } then {
|
|
|
653
|
# puts "Opcode name=$opname args=$opargs addr=$addr"
|
|
|
654
|
incr op_count
|
|
|
655
|
set addr [format "%08x" 0x$addr]
|
|
|
656
|
set hexcode [string map {" " ""} $hexcode]
|
|
|
657
|
# puts "setting curfn_ops 0x$addr"
|
|
|
658
|
set curfn_ops(0x$addr) [list "0x$hexcode" $opname $opargs "" ""]
|
|
|
659
|
incr fn_valid
|
|
|
660
|
} else {
|
|
|
661
|
incr lineskip_count
|
|
|
662
|
}
|
|
|
663
|
}
|
|
|
664
|
}
|
|
|
665
|
if { $fn_valid } {
|
|
|
666
|
set q [scan_wholefunc $curfn $curfn_addr curfn_ops]
|
|
|
667
|
set err_count [ expr { $err_count + $q } ]
|
|
|
668
|
}
|
|
|
669
|
|
|
|
670
|
puts "\nObjdump lines processed: $line_count, lines skipped: $lineskip_count"
|
|
|
671
|
puts "Functions scanned: ${fn_count}, reachable instruction count: ${fninst_count}"
|
|
|
672
|
puts "Potential error locations found: $err_count\n"
|
|
|
673
|
|
|
|
674
|
if {$err_count == 0} {
|
|
|
675
|
exit 0
|
|
|
676
|
} else {
|
|
|
677
|
exit 1
|
|
|
678
|
}
|
|
|
679
|
|
|
|
680
|
}
|
|
|
681
|
|