blob: 6615ef1e3d3d3391b916f0e8e137d710c6be79cf [file] [log] [blame]
#
# behave like gdb
#
proc p { reg { t 0 } { c 0 } } {
switch -regexp $reg {
^r$ {
set val [mysim cpu $c thread $t display gprs]
}
^r[0-9]+$ {
regexp "r(\[0-9\]*)" $reg dummy num
set val [mysim cpu $c thread $t display gpr $num]
}
^f[0-9]+$ {
regexp "f(\[0-9\]*)" $reg dummy num
set val [mysim cpu $c thread $t display fpr $num]
}
^v[0-9]+$ {
regexp "v(\[0-9\]*)" $reg dummy num
set val [mysim cpu $c thread $t display vmxr $num]
}
default {
set val [mysim cpu $c thread $t display spr $reg]
}
}
return "$val"
}
#
# behave like gdb
#
proc sr { reg val {t 0}} {
switch -regexp $reg {
^r[0-9]+$ {
regexp "r(\[0-9\]*)" $reg dummy num
mysim cpu 0:$t set gpr $num $val
}
^f[0-9]+$ {
regexp "f(\[0-9\]*)" $reg dummy num
mysim cpu 0:$t set fpr $num $val
}
^v[0-9]+$ {
regexp "v(\[0-9\]*)" $reg dummy num
mysim cpu 0:$t set vmxr $num $val
}
default {
mysim cpu 0:$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 0 } { c 0 } } {
set pc_laddr [mysim cpu $c util itranslate $pc]
set inst [mysim cpu $c memory display $pc_laddr 4]
set disasm [mysim cpu $c util ppc_disasm $inst $pc]
return "\[$c:$t\]: $pc ($pc_laddr) Enc:$inst : $disasm"
}
proc ipc { {t 0} {c 0}} {
set pc [mysim cpu $c thread $t display spr pc]
i $pc $t $c
}
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 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 } {
set pa [ mysim util dtranslate $ea ]
puts "$pa"
}
proc x { pa { size 8 } } {
set val [ mysim memory display $pa $size ]
puts "$pa : $val"
}
proc it { ea } {
mysim util itranslate $ea
}
proc dt { ea } {
mysim util dtranslate $ea
}
proc ex { ea { size 8 } } {
set pa [ mysim util dtranslate $ea ]
set val [ mysim memory display $pa $size ]
puts "$pa : $val"
}
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 {} {
puts [mysim cpu 0 display slb valid]
}
proc regs { { t 0 } { c 0 } } {
puts "GPRS:"
puts [mysim cpu $c thread $t display gprs]
}
proc tlbv { { c 0 } } {
puts "$c:TLB: ----------------------"
puts [mysim cpu $c display tlb valid]
}
proc exc { { i SystemReset } { c 0 } } {
puts "$c:EXCEPTION:$i"
puts [mysim cpu $c interrupt $i]
}
proc just_stop { args } {
simstop
ipca
}
proc st { count } {
set sp [mysim cpu 0 display gpr 1]
puts "SP: $sp"
ipc
set lr [mysim cpu 0 display spr lr]
i $lr
while { $count > 0 } {
set sp [mysim 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 { {t 0} } {
mysim set fast off
mysim debugger wait $t
}
proc egdb { {t 0} } {
set srr0 [mysim cpu 0 display spr srr0]
set srr1 [mysim cpu 0 display spr srr1]
mysim cpu 0 set spr pc $srr0
mysim cpu 0 set spr msr $srr1
gdb $t
}
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} } {
set t 0
if { $sp < 16 } {
set t $sp
set sp 0
}
set lr [mysim cpu 0:$t display spr pc]
puts "pc:\t\t\t\t$lr"
if { $sp == 0 } {
set sp [mysim cpu 0:$t display gpr 1]
}
set lr [mysim cpu 0:$t display spr lr]
puts "lr:\t\t\t\t$lr"
set msr [mysim cpu 0:$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 util dtranslate $sp ]
set bc [ mem_display_64 $pa $le ]
set lr [ mem_display_64 [ expr $pa + 16 ] $le ]
puts "stack:$pa \t$lr"
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
}
proc start_qtrace { { qtfile qtrace.qt } } {
global env
mysim mode simple
ereader expect 1
simemit set "Header_Record" 1
simemit set "Footer_Record" 1
simemit set "Instructions" 1
simemit set "Interrupt" 1
simemit set "External_Int" 1
simemit set "Config" 1
simemit set "MSR" 1
simemit set "Pid_Creatd" 1
simemit set "Pid_Killed" 1
simemit set "TLB_Inst_Miss" 1
simemit set "TLB_Data_Miss" 1
simemit set "SLB_Inst_Miss" 1
simemit set "SLB_Data_Miss" 1
simemit set "L1_ICache_Miss" 1
simemit set "L1_DCache_Miss" 1
simemit set "L2_Cache_Miss" 1
simemit set "Memory_Write" 1
simemit set "Memory_Read" 1
simemit set "Bus_Wait" 1
ereader start $env(EXEC_DIR)/emitter/qtracer [pid] -outfile $qtfile
}
proc current_insn { { t 0 } { c 0 } } {
set pc [mysim cpu $c thread $t display spr pc]
set pc_laddr [mysim cpu $c util itranslate $pc]
set inst [mysim cpu $c memory display $pc_laddr 4]
set disasm [mysim cpu $c util ppc_disasm $inst $pc]
return $disasm
}
global SRR1
global DSISR
global DAR
proc sreset_trigger { args } {
variable SRR1
mysim trigger clear pc 0x100
set s [expr [mysim cpu 0 display spr srr1] & ~0x00000000003c0002]
set SRR1 [expr $SRR1 | $s]
mysim cpu 0 set spr srr1 $SRR1
}
proc exc_sreset { } {
variable SRR1
variable DSISR
variable DAR
# 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 0 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
mysim trigger set pc 0x100 "sreset_trigger"
mysim cpu 0 interrupt MachineCheck
# XXX: only trigger if pc is 0x100
sreset_trigger
} else {
mysim trigger set pc 0x100 "sreset_trigger"
mysim cpu 0 interrupt SystemReset
}
}
proc mce_trigger { args } {
variable SRR1
variable DSISR
variable DAR
mysim trigger clear pc 0x200
set s [expr [mysim cpu 0 display spr srr1] & ~0x00000000801f0002]
set SRR1 [expr $SRR1 | $s]
mysim cpu 0 set spr srr1 $SRR1
mysim cpu 0 set spr dsisr $DSISR
mysim cpu 0 set spr dar $DAR
}
#
# 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 cause=0x80 - recoverable d-side SLB multi-hit
# 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 } } {
variable SRR1
variable DSISR
variable DAR
# 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 0 display spr msr] & 0x2]
set SRR1_powersave 0
}
if { !$recoverable } {
set msr_ri 0x0
}
# recoverable d-side SLB multihit
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
mysim trigger set pc 0x200 "mce_trigger"
mysim cpu 0 interrupt MachineCheck
# XXX: only trigger if pc is 0x200
mce_trigger
} else {
mysim trigger set pc 0x200 "mce_trigger"
mysim cpu 0 interrupt MachineCheck
}
}
global R1
# 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 } {
variable R1
set r1 [mysim cpu 0 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 { } {
variable R1
set R1 [mysim cpu 0 display gpr 1]
set pc [mysim cpu 0 display spr pc]
mysim trigger set pc $pc "stop_stack_match"
exc_mce
c
mysim trigger clear pc $pc ; list
}
# 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} } {
for { set i 0 } { $i < $nr } { incr i 1 } {
if { [expr [mysim cpu 0 display spr msr] & 0x2] } {
inject_mce
}
s
}
}