diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/external/mambo/mambo_utils.tcl | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/external/mambo/mambo_utils.tcl')
-rw-r--r-- | roms/skiboot/external/mambo/mambo_utils.tcl | 794 |
1 files changed, 794 insertions, 0 deletions
diff --git a/roms/skiboot/external/mambo/mambo_utils.tcl b/roms/skiboot/external/mambo/mambo_utils.tcl new file mode 100644 index 000000000..96f8971ab --- /dev/null +++ b/roms/skiboot/external/mambo/mambo_utils.tcl @@ -0,0 +1,794 @@ +# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +# +# Make Mambo behave a bit more like gdb +# +set target_t 0 +set target_c 0 +set target_p 0 + +proc target { { t 0 } { c 0 } { p 0 } } { + global target_t + global target_c + global target_p + + set target_t $t + set target_c $c + set target_p $p + + return "targeting cpu $p:$c:$t" +} + +proc p { reg { t -1 } { c -1 } { p -1 } } { + global target_t + global target_c + global target_p + + if { $t == -1 } { set t $target_t } + if { $c == -1 } { set c $target_c } + if { $p == -1 } { set p $target_p } + + switch -regexp $reg { + ^r$ { + set val [mysim cpu $p:$c:$t display gprs] + } + ^r[0-9]+$ { + regexp "r(\[0-9\]*)" $reg dummy num + set val [mysim cpu $p:$c:$t display gpr $num] + } + ^f[0-9]+$ { + regexp "f(\[0-9\]*)" $reg dummy num + set val [mysim cpu $p:$c:$t display fpr $num] + } + ^v[0-9]+$ { + regexp "v(\[0-9\]*)" $reg dummy num + set val [mysim cpu $p:$c:$t display vmxr $num] + } + default { + set val [mysim cpu $p:$c:$t display spr $reg] + } + } + + return "$val" +} + +# +# behave like gdb +# +proc sr { reg val { t -1} { c -1 } { p -1 } } { + global target_t + global target_c + global target_p + + if { $t == -1 } { set t $target_t } + if { $c == -1 } { set c $target_c } + if { $p == -1 } { set p $target_p } + + switch -regexp $reg { + ^r[0-9]+$ { + regexp "r(\[0-9\]*)" $reg dummy num + mysim cpu $p:$c:$t set gpr $num $val + } + ^f[0-9]+$ { + regexp "f(\[0-9\]*)" $reg dummy num + mysim cpu $p:$c:$t set fpr $num $val + } + ^v[0-9]+$ { + regexp "v(\[0-9\]*)" $reg dummy num + mysim cpu $p:$c:$t set vmxr $num $val + } + default { + mysim cpu $p:$c:$t set spr $reg $val + } + } + p $reg $t +} + +proc b { addr } { + mysim trigger set pc $addr "just_stop" + set at [i $addr] + puts "breakpoint set at $at" +} + +# Run until $console_string appears on the Linux console +# +# eg. +# break_on_console "Freeing unused kernel memory:" +# break_on_console "buildroot login:" + +proc break_on_console { console_string } { + mysim trigger set console "$console_string" "just_stop" +} + +proc clear_console_break { console_string } { + mysim trigger clear console "$console_string" +} + +proc wr { start stop } { + mysim trigger set memory system w $start $stop 0 "just_stop" +} + +proc c { } { + mysim go +} + +proc i { pc { t -1 } { c -1 } { p -1 } } { + global target_t + global target_c + global target_p + + if { $t == -1 } { set t $target_t } + if { $c == -1 } { set c $target_c } + if { $p == -1 } { set p $target_p } + + set pc_laddr [mysim cpu $p:$c:$t util itranslate $pc] + set inst [mysim cpu $p:$c:$t memory display $pc_laddr 4] + set disasm [mysim cpu $p:$c:$t util ppc_disasm $inst $pc] + return "\[$p:$c:$t\]: $pc ($pc_laddr) Enc:$inst : $disasm" +} + +proc ipc { { t -1 } { c -1 } { p -1 } } { + global target_t + global target_c + global target_p + + if { $t == -1 } { set t $target_t } + if { $c == -1 } { set c $target_c } + if { $p == -1 } { set p $target_p } + + set pc [mysim cpu $p:$c:$t display spr pc] + i $pc $t $c $p +} + +proc ipca { } { + set cpus [myconf query cpus] + set threads [myconf query processor/number_of_threads] + + for { set i 0 } { $i < $cpus } { incr i 1 } { + for { set j 0 } { $j < $threads } { incr j 1 } { + puts [ipc $j $i] + } + } +} + +proc pa { spr } { + set cpus [myconf query cpus] + set threads [myconf query processor/number_of_threads] + + for { set i 0 } { $i < $cpus } { incr i 1 } { + for { set j 0 } { $j < $threads } { incr j 1 } { + set val [mysim cpu $i thread $j display spr $spr] + puts "CPU: $i THREAD: $j SPR $spr = $val" + } + } +} + +proc s { {nr 1} } { + for { set i 0 } { $i < $nr } { incr i 1 } { + mysim step 1 + ipca + } +} + +proc S { {nr 1} } { + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + + for { set i 0 } { $i < $nr } { incr i 1 } { + mysim cpu $p:$c:$t step 1 + puts [ipc] + } +} + +proc z { count } { + while { $count > 0 } { + s + incr count -1 + } +} + +proc sample_pc { sample count } { + while { $count > 0 } { + mysim cycle $sample + ipc + incr count -1 + } +} + +proc e2p { ea } { + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + + set pa [ mysim cpu $p:$c:$t util dtranslate $ea ] + puts "$pa" +} + +proc x { pa { size 8 } } { + set val [ mysim memory display $pa $size ] + puts "$pa : $val" +} + +proc it { ea } { + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + + mysim cpu $p:$c:$t util itranslate $ea +} +proc dt { ea } { + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + + mysim cpu $p:$c:$t util dtranslate $ea +} + +proc ex { ea { size 8 } } { + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + + set pa [ mysim cpu $p:$c:$t util dtranslate $ea ] + set val [ mysim memory display $pa $size ] + puts "$pa : $val" +} + +proc di { location { count 16 } } { + set addr [expr $location & 0xfffffffffffffff0] + disasm_mem mysim $addr $count +} + +proc hexdump { location count } { + set addr [expr $location & 0xfffffffffffffff0] + set top [expr $addr + ($count * 15)] + for { set i $addr } { $i < $top } { incr i 16 } { + set val [expr $i + (4 * 0)] + set val0 [format "%08x" [mysim memory display $val 4]] + set val [expr $i + (4 * 1)] + set val1 [format "%08x" [mysim memory display $val 4]] + set val [expr $i + (4 * 2)] + set val2 [format "%08x" [mysim memory display $val 4]] + set val [expr $i + (4 * 3)] + set val3 [format "%08x" [mysim memory display $val 4]] + + set ascii "" + for { set j 0 } { $j < 16 } { incr j } { + set byte [get_char [expr $i + $j]] + if { $byte < 0x20 || $byte >= 127} { + set c "." + } else { + set c [format %c $byte] + } + set ascii [string cat "$ascii" "$c"] + } + + set loc [format "0x%016x" $i] + puts "$loc: $val0 $val1 $val2 $val3 $ascii" + } +} + +proc get_char { addr } { + return [expr [mysim memory display "$addr" 1]] +} + +proc p_str { addr { limit 0 } } { + set addr_limit 0xfffffffffffffffff + if { $limit > 0 } { set addr_limit [expr $limit + $addr] } + set s "" + + for {} { [get_char "$addr"] != 0} { incr addr 1 } { + # memory display returns hex values with a leading 0x + set c [format %c [get_char "$addr"]] + set s [string cat "$s" "$c"] + if { $addr == $addr_limit } { break } + } + + puts "$s" +} + +proc slbv {} { + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + + puts [mysim cpu $p:$c:$t display slb valid] +} + +proc regs { { t -1 } { c -1 } { p -1 }} { + global target_t + global target_c + global target_p + + if { $t == -1 } { set t $target_t } + if { $c == -1 } { set c $target_c } + if { $p == -1 } { set p $target_p } + + puts "GPRS:" + puts [mysim cpu $p:$c:$t display gprs] +} + +proc tlbv {} { + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + + puts "$p:$c:$t:TLB: ----------------------" + puts [mysim cpu $p:$c:$t display tlb valid] +} + +proc exc { { i SystemReset } } { + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + + puts "$p:$c:$t:EXCEPTION:$i" + puts [mysim cpu $p:$c:$t interrupt $i] +} + +proc just_stop { args } { + simstop + ipca +} + +proc st { count } { + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + + set sp [mysim cpu $p:$c:$t display gpr 1] + puts "SP: $sp" + ipc + set lr [mysim cpu $p:$c:$t display spr lr] + i $lr + while { $count > 0 } { + set sp [mysim cpu $p:$c:$t util itranslate $sp] + set lr [mysim memory display [expr $sp++16] 8] + i $lr + set sp [mysim memory display $sp 8] + + incr count -1 + } +} + +proc mywatch { } { + while { [mysim memory display 0x700 8] != 0 } { + mysim cycle 1 + } + puts "condition occurred " + ipc +} + +# +# force gdb to attach +# +proc gdb { { timeout 0 } } { + mysim set fast off + mysim debugger wait $timeout +} + +proc egdb { { timeout 0 }} { + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + + set srr0 [mysim cpu $p:$c:$t display spr srr0] + set srr1 [mysim cpu $p:$c:$t display spr srr1] + mysim cpu $p:$c:$t set spr pc $srr0 + mysim cpu $p:$c:$t set spr msr $srr1 + gdb $timeout +} + +proc mem_display_64_le { addr } { + set data 0 + for {set i 0} {$i < 8} {incr i} { + set data [ expr $data << 8 ] + set l [ mysim memory display [ expr $addr+7-$i ] 1 ] + set data [ expr $data | $l ] + } + return [format 0x%X $data] +} + +proc mem_display_64 { addr le } { + if { $le } { + return [ mem_display_64_le $addr ] + } + # mysim memory display is big endian + return [ mysim memory display $addr 8 ] +} + +proc bt { {sp 0} } { + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + + set lr [mysim cpu $p:$c:$t display spr pc] + set sym [addr2func $lr] + puts "pc:\t\t\t\t$lr\t$sym" + if { $sp == 0 } { + set sp [mysim cpu $p:$c:$t display gpr 1] + } + set lr [mysim cpu $p:$c:$t display spr lr] + set sym [addr2func $lr] + puts "lr:\t\t\t\t$lr\t$sym" + + set msr [mysim cpu $p:$c:$t display spr msr] + set le [ expr $msr & 1 ] + + # Limit to 200 in case of an infinite loop + for {set i 0} {$i < 200} {incr i} { + set pa [ mysim cpu $p:$c:$t util dtranslate $sp ] + set bc [ mem_display_64 $pa $le ] + set lr [ mem_display_64 [ expr $pa + 16 ] $le ] + set sym [addr2func $lr] + puts "stack:$pa \t$lr\t$sym" + if { $bc == 0 } { break } + set sp $bc + } + puts "" +} + +proc ton { } {mysim mode turbo } +proc toff { } {mysim mode simple } + +proc don { opt } { + simdebug set $opt 1 +} + +proc doff { opt } { + simdebug set $opt 0 +} + +# skisym and linsym return the address of a symbol, looked up from +# the relevant System.map or skiboot.map file. +proc linsym { name } { + global linux_symbol_map + + # create a regexp that matches the symbol name + set base {([[:xdigit:]]*) (.)} + set exp [concat $base " $name\$"] + set ret "" + + foreach {line addr type} [regexp -line -inline $exp $linux_symbol_map] { + set ret "0x$addr" + } + + return $ret +} + +# skisym factors in skiboot's load address +proc skisym { name } { + global skiboot_symbol_map + global mconf + + set base {([[:xdigit:]]*) (.)} + set exp [concat $base " $name\$"] + set ret "" + + foreach {line addr type} [regexp -line -inline $exp $skiboot_symbol_map] { + set actual_addr [expr "0x$addr" + $mconf(boot_load)] + set ret [format "0x%.16x" $actual_addr] + } + + return $ret +} + +proc addr2func { addr } { + global skiboot_symbol_list + global linux_symbol_list + global user_symbol_list + global mconf + + set prevname "" + set preva "0" + + if { [ info exists linux_symbol_list ] && "$addr" >= 0xc000000000000000} { + foreach line $linux_symbol_list { + lassign $line a type name + if { "0x$a" > $addr } { + set o [format "0x%x" [expr $addr - "0x$preva"]] + return "$prevname+$o" + } + set prevname $name + set preva $a + } + } + # Assume skiboot is less that 4MB big + if { [ info exists skiboot_symbol_list ] && + "$addr" > $mconf(boot_load) && "$addr" < [expr $mconf(boot_load) + 4194304] } { + set mapaddr [expr $addr - $mconf(boot_load)] + + foreach line $skiboot_symbol_list { + lassign $line a type name + if { "0x$a" > $mapaddr } { + set o [format "0x%x" [expr $mapaddr - "0x$preva"]] + return "$prevname+$o" + } + set prevname $name + set preva $a + } + } + if { [ info exists user_symbol_list ] } { + foreach line $user_symbol_list { + lassign $line a type name + if { "0x$a" > $addr } { + set o [format "0x%x" [expr $addr - "0x$preva"]] + return "$prevname+$o" + } + set prevname $name + set preva $a + } + } + return "+$addr" +} + +proc current_insn { { t -1 } { c -1 } { p -1 }} { + global target_t + global target_c + global target_p + + if { $t == -1 } { set t $target_t } + if { $c == -1 } { set c $target_c } + if { $p == -1 } { set p $target_p } + + set pc [mysim cpu $p:$c:$t display spr pc] + set pc_laddr [mysim cpu $p:$c:$t util itranslate $pc] + set inst [mysim cpu $p:$c:$t memory display $pc_laddr 4] + set disasm [mysim cpu $p:$c:$t util ppc_disasm $inst $pc] + return $disasm +} + +set SRR1 0 +set DSISR 0 +set DAR 0 + +proc sreset_trigger { args } { + global SRR1 + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + + mysim trigger clear pc 0x100 + mysim trigger clear pc 0x104 + set s [expr [mysim cpu $p:$c:$t display spr srr1] & ~0x00000000003c0002] + set SRR1 [expr $SRR1 | $s] + mysim cpu $p:$c:$t set spr srr1 $SRR1 +} + +proc exc_sreset { } { + global SRR1 + global DSISR + global DAR + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + + # In case of recoverable MCE, idle wakeup always sets RI, others get + # RI from current environment. For unrecoverable, RI would always be + # clear by hardware. + if { [current_insn] in { "stop" "nap" "sleep" "winkle" } } { + set msr_ri 0x2 + set SRR1_powersave [expr (0x2 << (63-47))] + } else { + set msr_ri [expr [mysim cpu $p:$c:$t display spr msr] & 0x2] + set SRR1_powersave 0 + } + + # reason system reset + set SRR1_reason 0x4 + + set SRR1 [expr 0x0 | $msr_ri | $SRR1_powersave] + set SRR1 [expr $SRR1 | ((($SRR1_reason >> 3) & 0x1) << (63-42))] + set SRR1 [expr $SRR1 | ((($SRR1_reason >> 2) & 0x1) << (63-43))] + set SRR1 [expr $SRR1 | ((($SRR1_reason >> 1) & 0x1) << (63-44))] + set SRR1 [expr $SRR1 | ((($SRR1_reason >> 0) & 0x1) << (63-45))] + + if { [current_insn] in { "stop" "nap" "sleep" "winkle" } } { + # mambo has a quirk that interrupts from idle wake immediately + # and go over current instruction. + mysim trigger set pc 0x100 "sreset_trigger" + mysim trigger set pc 0x104 "sreset_trigger" + mysim cpu $p:$c:$t interrupt SystemReset + } else { + mysim trigger set pc 0x100 "sreset_trigger" + mysim trigger set pc 0x104 "sreset_trigger" + mysim cpu $p:$c:$t interrupt SystemReset + } + + # sleep and sometimes other types of interrupts do not trigger 0x100 + if { [expr [mysim cpu $p:$c:$t display spr pc] == 0x100 ] } { + sreset_trigger + } + if { [expr [mysim cpu $p:$c:$t display spr pc] == 0x104 ] } { + sreset_trigger + } +} + +proc mce_trigger { args } { + global SRR1 + global DSISR + global DAR + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + + mysim trigger clear pc 0x200 + mysim trigger clear pc 0x204 + + set s [expr [mysim cpu 0 display spr srr1] & ~0x00000000801f0002] + set SRR1 [expr $SRR1 | $s] + mysim cpu $p:$c:$t set spr srr1 $SRR1 + mysim cpu $p:$c:$t set spr dsisr $DSISR + mysim cpu $p:$c:$t set spr dar $DAR ; list +} + +# +# Inject a machine check. Recoverable MCE types can be forced to unrecoverable +# by clearing MSR_RI bit from SRR1 (which hardware may do). +# If d_side is 0, then cause goes into SRR1. Otherwise it gets put into DSISR. +# DAR is hardcoded to always 0xdeadbeefdeadbeef +# +# Default with no arguments is a recoverable i-side TLB multi-hit +# Other options: +# d_side=1 dsisr=0x80 - recoverable d-side SLB multi-hit +# d_side=1 dsisr=0x8000 - ue error on instruction fetch +# d_side=0 cause=0xd - unrecoverable i-side async store timeout (POWER9 only) +# d_side=0 cause=0x1 - unrecoverable i-side ifetch +# +proc exc_mce { { d_side 0 } { cause 0x5 } { recoverable 1 } } { + global SRR1 + global DSISR + global DAR + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + +# puts "INJECTING MCE" + + # In case of recoverable MCE, idle wakeup always sets RI, others get + # RI from current environment. For unrecoverable, RI would always be + # clear by hardware. + if { [current_insn] in { "stop" "nap" "sleep" "winkle" } } { + set msr_ri 0x2 + set SRR1_powersave [expr (0x2 << (63-47))] + } else { + set msr_ri [expr [mysim cpu $p:$c:$t display spr msr] & 0x2] + set SRR1_powersave 0 + } + + if { !$recoverable } { + set msr_ri 0x0 + } + + if { $d_side } { + set is_dside 1 + set SRR1_mc_cause 0x0 + set DSISR $cause + set DAR 0xdeadbeefdeadbeef + } else { + set is_dside 0 + set SRR1_mc_cause $cause + set DSISR 0x0 + set DAR 0x0 + } + + set SRR1 [expr 0x0 | $msr_ri | $SRR1_powersave] + + set SRR1 [expr $SRR1 | ($is_dside << (63-42))] + set SRR1 [expr $SRR1 | ((($SRR1_mc_cause >> 3) & 0x1) << (63-36))] + set SRR1 [expr $SRR1 | ((($SRR1_mc_cause >> 2) & 0x1) << (63-43))] + set SRR1 [expr $SRR1 | ((($SRR1_mc_cause >> 1) & 0x1) << (63-44))] + set SRR1 [expr $SRR1 | ((($SRR1_mc_cause >> 0) & 0x1) << (63-45))] + + if { [current_insn] in { "stop" "nap" "sleep" "winkle" } } { + # mambo has a quirk that interrupts from idle wake immediately + # and go over current instruction. + mysim trigger set pc 0x200 "mce_trigger" + mysim trigger set pc 0x204 "mce_trigger" + mysim cpu $p:$c:$t interrupt MachineCheck + } else { + mysim trigger set pc 0x200 "mce_trigger" + mysim trigger set pc 0x204 "mce_trigger" + mysim cpu $p:$c:$t interrupt MachineCheck + } + + # sleep and sometimes other types of interrupts do not trigger 0x200 + if { [expr [mysim cpu $p:$c:$t display spr pc] == 0x200 ] } { + mce_trigger + } + if { [expr [mysim cpu $p:$c:$t display spr pc] == 0x204 ] } { + mce_trigger + } +} + +set R1 0 + +# Avoid stopping if we re-enter the same code. Wait until r1 matches. +# This helps stepping over exceptions or function calls etc. +proc stop_stack_match { args } { + global R1 + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + + set r1 [mysim cpu $p:$c:$t display gpr 1] + if { $R1 == $r1 } { + simstop + ipca + } +} + +# inject default recoverable MCE and step over it. Useful for testing whether +# code copes with taking an interleaving MCE. +proc inject_mce { } { + global R1 + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + + set R1 [mysim cpu $p:$c:$t display gpr 1] + set pc [mysim cpu $p:$c:$t display spr pc] + mysim trigger set pc $pc "stop_stack_match" + exc_mce + c + mysim trigger clear pc $pc ; list +} + +# +# We've stopped at addr and we need to inject the mce and continue +# +proc trigger_mce_ue_addr {args} { + set addr [lindex [lindex $args 0] 1] + mysim trigger clear memory system rw $addr $addr + exc_mce 0x1 0x8000 0x1 +} + +proc inject_mce_ue_on_addr {addr} { + mysim trigger set memory system rw $addr $addr 1 "trigger_mce_ue_addr" +} + +# inject and step over one instruction, and repeat. +proc inject_mce_step { {nr 1} } { + for { set i 0 } { $i < $nr } { incr i 1 } { + inject_mce + s + } +} + +# inject if RI is set and step over one instruction, and repeat. +proc inject_mce_step_ri { {nr 1} } { + upvar #0 target_t t + upvar #0 target_c c + upvar #0 target_p p + + set reserve_inject 1 + set reserve_inject_skip 0 + set reserve_counter 0 + + for { set i 0 } { $i < $nr } { incr i 1 } { + if { [expr [mysim cpu $p:$c:$t display spr msr] & 0x2] } { + # inject_mce + if { [mysim cpu $p:$c:$t display reservation] in { "none" } } { + inject_mce + mysim cpu $p:$c:$t set reservation none + if { $reserve_inject_skip } { + set reserve_inject 1 + set reserve_inject_skip 0 + } + } else { + if { $reserve_inject } { + inject_mce + mysim cpu $p:$c:$t set reservation none + set reserve_inject 0 + } else { + set reserve_inject_skip 1 + set reserve_counter [ expr $reserve_counter + 1 ] + if { $reserve_counter > 30 } { + mysim cpu $p:$c:$t set reservation none + } + } + } + } + s + } +} |