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 |
General Comments 0
You need to be logged in to leave comments.
Login now