2 from pygdbmi
.gdbcontroller
import GdbController
6 launch_args_be
= ['qemu-system-ppc64',
7 '-machine', 'powernv9',
10 '-s', '-S', '-m', 'size=1024']
12 launch_args_le
= ['qemu-system-ppc64le',
13 '-machine', 'powernv9',
16 '-s', '-S', '-m', 'size=1024']
19 def swap_order(x
, nbytes
):
20 x
= x
.to_bytes(nbytes
, byteorder
='little')
21 x
= int.from_bytes(x
, byteorder
='big', signed
=False)
25 def find_uint128(val
):
27 assert val
[1:].startswith('uint128 =')
28 val
= val
.split("=")[1]
29 val
= val
.split(',')[0].strip()
31 val
= swap_order(val
, 16)
32 val
= swap_order(val
, 8)
37 def __init__(self
, kernel
, bigendian
):
39 if not isinstance(kernel
, str): # assume a file
40 self
.binfile
= tempfile
.NamedTemporaryFile(suffix
=".bin")
41 self
.binfile
.write(kernel
.read())
42 kernel
= self
.binfile
.name
45 args
= launch_args_be
+ ['-kernel', kernel
]
47 args
= launch_args_le
+ ['-kernel', kernel
]
48 self
.qemu_popen
= subprocess
.Popen(args
,
49 stdout
=subprocess
.PIPE
,
50 stdin
=subprocess
.PIPE
)
52 self
.gdb
= GdbController(gdb_path
='powerpc64-linux-gnu-gdb')
53 except ValueError as e
:
54 if len(e
.args
) == 1 and isinstance(e
.args
[0], str) and \
55 e
.args
[0].startswith("gdb executable could not be resolved"):
56 raise unittest
.SkipTest(
57 "powerpc64-linux-gnu-gdb not found") from e
59 self
.bigendian
= bigendian
62 def _rcache_trash(self
, key
=None):
63 """cache of register values, trash it on call to step or continue
68 self
._reg
_cache
.pop(key
, None)
73 def __exit__(self
, type, value
, traceback
):
77 return self
.gdb
.write('-target-select remote localhost:1234')
79 def set_endian(self
, bigendian
):
81 cmd
= '-gdb-set endian big'
83 cmd
= '-gdb-set endian little'
84 return self
.gdb
.write(cmd
)
86 def break_address(self
, addr
):
87 cmd
= '-break-insert *0x{:x}'.format(addr
)
88 return self
.gdb
.write(cmd
)
90 def delete_breakpoint(self
, breakpoint
=None):
93 breakstring
= f
' {breakpoint}'
94 return self
.gdb
.write('-break-delete' + breakstring
)
96 def set_bytes(self
, addr
, v
, wid
):
97 print("qemu set bytes", hex(addr
), hex(v
))
98 v
= swap_order(v
, wid
)
99 faddr
= '&{int}0x%x' % addr
100 fmt
= '"%%0%dx"' % (wid
* 2)
101 cmd
= '-data-write-memory-bytes %s ' + fmt
102 res
= self
.gdb
.write(cmd
% (faddr
, v
))
103 #print("confirm (byterev'd)", hex(self.get_mem(addr, 8)[0]))
105 def set_byte(self
, addr
, v
):
106 print("qemu set byte", hex(addr
), hex(v
))
107 faddr
= '&{int}0x%x' % addr
108 res
= self
.gdb
.write('-data-write-memory-bytes %s "%02x"' % (faddr
, v
))
109 print("confirm", hex(self
.get_mem(addr
, 1)[0]))
111 def get_mem(self
, addr
, nbytes
):
112 res
= self
.gdb
.write("-data-read-memory %d u 1 1 %d" %
114 print ("get_mem", res
)
116 if(x
["type"] == "result"):
117 l
= list(map(int, x
['payload']['memory'][0]['data']))
119 for j
in range(0, len(l
), 8):
121 for i
, v
in enumerate(l
[j
:j
+8]):
127 def _get_registers(self
):
128 res
= self
.gdb
.write('-data-list-register-values x')
131 if(x
["type"] == "result"):
132 assert 'register-values' in x
['payload']
133 rlist
= x
['payload']['register-values']
135 regnum
= int(rdict
['number'])
136 regval
= rdict
['value']
137 #print ("reg get", regnum, rdict)
138 if regval
.startswith("{"): # TODO, VSX
139 regval
= find_uint128(regval
)
141 regval
= int(regval
, 0)
142 self
._reg
_cache
["x %d" % regnum
] = regval
143 return self
._reg
_cache
145 def _get_register(self
, fmt
):
146 if fmt
not in self
._reg
_cache
:
147 self
._get
_registers
()
148 return self
._reg
_cache
[fmt
] # return cached reg value
150 def _get_single_register(self
, fmt
):
151 if fmt
in self
._reg
_cache
:
152 return self
._reg
_cache
[fmt
] # return cached reg value
153 res
= self
.gdb
.write('-data-list-register-values '+fmt
,
154 timeout_sec
=1.0) # increase this timeout if needed
156 if(x
["type"] == "result"):
157 assert 'register-values' in x
['payload']
158 res
= int(x
['payload']['register-values'][0]['value'], 0)
159 self
._reg
_cache
[fmt
] = res
# cache reg value
161 # return swap_order(res, 8)
164 # TODO: use -data-list-register-names instead of hardcoding the values
165 def get_pc(self
): return self
._get
_register
('x 64')
166 def get_msr(self
): return self
._get
_register
('x 65')
167 def get_cr(self
): return self
._get
_register
('x 66')
168 def get_lr(self
): return self
._get
_register
('x 67')
169 def get_ctr(self
): return self
._get
_register
('x 68') # probably
170 def get_xer(self
): return self
._get
_register
('x 69')
171 def get_fpscr(self
): return self
._get
_register
('x 70')
172 def get_mq(self
): return self
._get
_register
('x 71')
174 def get_register(self
, num
):
175 return self
._get
_register
('x {}'.format(num
))
177 def get_gpr(self
, num
):
178 return self
.get_register(num
)
180 def get_fpr(self
, num
):
181 return self
.get_register(num
+471)
183 def set_gpr(self
, reg
, val
):
184 self
._rcache
_trash
('x %d' % reg
)
185 self
.gdb_eval('$r%d=%d' % (reg
, val
))
187 def set_fpr(self
, reg
, val
):
188 self
._rcache
_trash
('x %d' % (reg
+32))
189 self
._rcache
_trash
('x %d' % (reg
+471))
190 # grr, fp set cannot enter raw data
191 #val = swap_order(val, 8)
193 valhi
= (val
>> 32) & 0xffffffff
194 vallo
= val
& 0xffffffff
195 res
= self
.gdb_eval('$vs%d.v4_int32={0,0,0x%x,0x%x}' % \
197 #res = self.gdb_eval('$fp%d=1.0')
198 #res = self.gdb_eval('$vs%d.uint128=0x%x' % \
200 print ("set fpr", reg
, hex(val
), res
)
201 print ("get fpr", hex(self
.get_fpr(reg
)))
203 def set_pc(self
, pc
):
204 self
._rcache
_trash
('x 64')
205 self
.gdb_eval('$pc=%d' % pc
)
207 def set_msr(self
, msr
):
208 self
._rcache
_trash
('x 65')
209 self
.gdb_eval('$msr=%d' % msr
)
211 def set_cr(self
, cr
):
212 self
._rcache
_trash
('x 66')
213 self
.gdb_eval('$cr=%d' % cr
)
215 def set_lr(self
, lr
):
216 self
._rcache
_trash
('x 67')
217 self
.gdb_eval('$lr=%d' % lr
)
221 return self
.gdb
.write('-exec-step-instruction')
223 def gdb_continue(self
):
225 return self
.gdb
.write('-exec-continue')
227 def gdb_eval(self
, expr
):
228 return self
.gdb
.write(f
'-data-evaluate-expression {expr}')
232 self
.qemu_popen
.kill()
233 outs
, errs
= self
.qemu_popen
.communicate()
234 self
.qemu_popen
.stdout
.close()
235 self
.qemu_popen
.stdin
.close()
236 if self
.binfile
is not None:
239 def disasm(self
, start
, end
):
240 res
= self
.gdb
.write('-data-disassemble -s "%d" -e "%d" -- 0' % \
242 return res
[0]['payload']['asm_insns']
244 def upload_mem(self
, initial_mem
, skip_zeros
=False):
245 if isinstance(initial_mem
, tuple):
246 addr
, mem
= initial_mem
# assume 8-byte width
247 for j
, v
in enumerate(mem
):
249 # sigh byte-level loads, veery slow
250 self
.set_byte(addr
+i
+j
*8, (v
>> i
*8) & 0xff)
252 if isinstance(initial_mem
, dict):
253 for addr
, v
in initial_mem
.items(): # assume 8-byte width
254 if skip_zeros
and v
== 0:
256 # sigh byte-level loads, veery slow
257 self
.set_bytes(addr
, v
, 8)
259 for addr
, (v
, wid
) in initial_mem
.items():
261 # sigh byte-level loads, veery slow
262 self
.set_byte(addr
+i
, (v
>> i
*8) & 0xff)
265 def run_program(program
, initial_mem
=None, extra_break_addr
=None,
266 bigendian
=False, start_addr
=0x20000000, init_endian
=True,
267 continuous_run
=True, initial_sprs
=None,
268 initial_regs
=None, initial_fprs
=None):
269 q
= QemuController(program
.binfile
, bigendian
)
271 q
.set_endian(init_endian
) # easier to set variables this way
273 # Run to the start of the program
276 print("pc", bigendian
, hex(pc
))
277 q
.break_address(start_addr
) # set breakpoint at start
280 # set the MSR bit 63, to set bigendian/littleendian mode
282 print("msr", bigendian
, hex(msr
))
284 # XXX this is probably wrong
286 msr
= msr
& ((1 << 64)-1)
290 print("msr set to", hex(msr
))
292 # set the CR to 0, matching the simulator
294 # delete the previous breakpoint so loops don't screw things up
295 q
.delete_breakpoint()
298 q
.break_address(start_addr
+ program
.size())
299 # or to trap (not ideal)
300 q
.break_address(0x700)
301 # or to alternative (absolute) address)
302 if extra_break_addr
is not None:
303 q
.break_address(extra_break_addr
)
304 # set endian before SPR set
305 q
.set_endian(bigendian
)
309 q
.upload_mem(initial_mem
, skip_zeros
=True)
311 # dump msr after endian set
313 print("msr", bigendian
, hex(msr
), bin(msr
))
314 # set the MSR bit 13, to set FPU
316 # XXX this is probably wrong
317 msr
= msr
& ((1 << 53)-1)
320 #msr = 0x4000000000009
323 print("msr set to", hex(msr
), bin(msr
))
327 for i
, reg
in enumerate(initial_regs
):
330 if isinstance(initial_fprs
, dict):
331 for i
, reg
in initial_fprs
.items():
334 for i
, reg
in enumerate(initial_fprs
):
338 # can't do many of these - lr, ctr, etc. etc. later, just LR for now
340 lr
= initial_sprs
.get('lr', None)
342 lr
= initial_sprs
.get('LR', None)
346 # disassemble and dump
347 d
= q
.disasm(start_addr
, start_addr
+ program
.size())
349 print ("qemu disasm", line
)
358 if __name__
== '__main__':
359 q
= QemuController("simulator/qemu_test/kernel.bin", bigendian
=True)
361 q
.break_address(0x20000000)