# # behave 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 } }