got sv.bc working for pospopcount
[openpower-isa.git] / src / openpower / simulator / qemu.py
1 import unittest
2 from pygdbmi.gdbcontroller import GdbController
3 import subprocess
4 import tempfile
5
6 launch_args_be = ['qemu-system-ppc64',
7 '-machine', 'powernv9',
8 '-cpu', 'power9',
9 '-nographic',
10 '-s', '-S', '-m', 'size=1024']
11
12 launch_args_le = ['qemu-system-ppc64le',
13 '-machine', 'powernv9',
14 '-cpu', 'power9',
15 '-nographic',
16 '-s', '-S', '-m', 'size=1024']
17
18
19 def swap_order(x, nbytes):
20 x = x.to_bytes(nbytes, byteorder='little')
21 x = int.from_bytes(x, byteorder='big', signed=False)
22 return x
23
24
25 def find_uint128(val):
26 #print (val[1:])
27 assert val[1:].startswith('uint128 =')
28 val = val.split("=")[1]
29 val = val.split(',')[0].strip()
30 val = int(val, 0)
31 val = swap_order(val, 16)
32 val = swap_order(val, 8)
33 return val
34
35
36 class QemuController:
37 def __init__(self, kernel, bigendian):
38 self.binfile = None
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
43
44 if bigendian:
45 args = launch_args_be + ['-kernel', kernel]
46 else:
47 args = launch_args_le + ['-kernel', kernel]
48 self.qemu_popen = subprocess.Popen(args,
49 stdout=subprocess.PIPE,
50 stdin=subprocess.PIPE)
51 try:
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
58 raise
59 self.bigendian = bigendian
60 self._reg_cache = {}
61
62 def _rcache_trash(self, key=None):
63 """cache of register values, trash it on call to step or continue
64 """
65 if key is None:
66 self._reg_cache = {}
67 return
68 self._reg_cache.pop(key, None)
69
70 def __enter__(self):
71 return self
72
73 def __exit__(self, type, value, traceback):
74 self.exit()
75
76 def connect(self):
77 return self.gdb.write('-target-select remote localhost:1234')
78
79 def set_endian(self, bigendian):
80 if bigendian:
81 cmd = '-gdb-set endian big'
82 else:
83 cmd = '-gdb-set endian little'
84 return self.gdb.write(cmd)
85
86 def break_address(self, addr):
87 cmd = '-break-insert *0x{:x}'.format(addr)
88 return self.gdb.write(cmd)
89
90 def delete_breakpoint(self, breakpoint=None):
91 breakstring = ''
92 if breakpoint:
93 breakstring = f' {breakpoint}'
94 return self.gdb.write('-break-delete' + breakstring)
95
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]))
104
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]))
110
111 def get_mem(self, addr, nbytes):
112 res = self.gdb.write("-data-read-memory %d u 1 1 %d" %
113 (addr, nbytes))
114 print ("get_mem", res)
115 for x in res:
116 if(x["type"] == "result"):
117 l = list(map(int, x['payload']['memory'][0]['data']))
118 res = []
119 for j in range(0, len(l), 8):
120 b = 0
121 for i, v in enumerate(l[j:j+8]):
122 b += v << (i*8)
123 res.append(b)
124 return res
125 return None
126
127 def _get_registers(self):
128 res = self.gdb.write('-data-list-register-values x')
129 self._reg_cache = {}
130 for x in res:
131 if(x["type"] == "result"):
132 assert 'register-values' in x['payload']
133 rlist = x['payload']['register-values']
134 for rdict in rlist:
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)
140 else:
141 regval = int(regval, 0)
142 self._reg_cache["x %d" % regnum] = regval
143 return self._reg_cache
144
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
149
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
155 for x in res:
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
160 return res
161 # return swap_order(res, 8)
162 return None
163
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')
173
174 def get_register(self, num):
175 return self._get_register('x {}'.format(num))
176
177 def get_gpr(self, num):
178 return self.get_register(num)
179
180 def get_fpr(self, num):
181 return self.get_register(num+471)
182
183 def set_gpr(self, reg, val):
184 self._rcache_trash('x %d' % reg)
185 self.gdb_eval('$r%d=%d' % (reg, val))
186
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)
192 #val = 1<<31
193 valhi = (val >> 32) & 0xffffffff
194 vallo = val & 0xffffffff
195 res = self.gdb_eval('$vs%d.v4_int32={0,0,0x%x,0x%x}' % \
196 (reg, vallo, valhi))
197 #res = self.gdb_eval('$fp%d=1.0')
198 #res = self.gdb_eval('$vs%d.uint128=0x%x' % \
199 # (reg, val))
200 print ("set fpr", reg, hex(val), res)
201 print ("get fpr", hex(self.get_fpr(reg)))
202
203 def set_pc(self, pc):
204 self._rcache_trash('x 64')
205 self.gdb_eval('$pc=%d' % pc)
206
207 def set_msr(self, msr):
208 self._rcache_trash('x 65')
209 self.gdb_eval('$msr=%d' % msr)
210
211 def set_cr(self, cr):
212 self._rcache_trash('x 66')
213 self.gdb_eval('$cr=%d' % cr)
214
215 def set_lr(self, lr):
216 self._rcache_trash('x 67')
217 self.gdb_eval('$lr=%d' % lr)
218
219 def step(self):
220 self._rcache_trash()
221 return self.gdb.write('-exec-step-instruction')
222
223 def gdb_continue(self):
224 self._rcache_trash()
225 return self.gdb.write('-exec-continue')
226
227 def gdb_eval(self, expr):
228 return self.gdb.write(f'-data-evaluate-expression {expr}')
229
230 def exit(self):
231 self.gdb.exit()
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:
237 self.binfile.close()
238
239 def disasm(self, start, end):
240 res = self.gdb.write('-data-disassemble -s "%d" -e "%d" -- 0' % \
241 (start, end))
242 return res[0]['payload']['asm_insns']
243
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):
248 for i in range(8):
249 # sigh byte-level loads, veery slow
250 self.set_byte(addr+i+j*8, (v >> i*8) & 0xff)
251 return
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:
255 continue
256 # sigh byte-level loads, veery slow
257 self.set_bytes(addr, v, 8)
258 return
259 for addr, (v, wid) in initial_mem.items():
260 for i in range(wid):
261 # sigh byte-level loads, veery slow
262 self.set_byte(addr+i, (v >> i*8) & 0xff)
263
264
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)
270 q.connect()
271 q.set_endian(init_endian) # easier to set variables this way
272
273 # Run to the start of the program
274 q.set_pc(start_addr)
275 pc = q.get_pc()
276 print("pc", bigendian, hex(pc))
277 q.break_address(start_addr) # set breakpoint at start
278 q.gdb_continue()
279
280 # set the MSR bit 63, to set bigendian/littleendian mode
281 msr = q.get_msr()
282 print("msr", bigendian, hex(msr))
283 if bigendian:
284 # XXX this is probably wrong
285 msr &= ~(1 << 0)
286 msr = msr & ((1 << 64)-1)
287 else:
288 msr |= (1 << 0)
289 q.set_msr(msr)
290 print("msr set to", hex(msr))
291
292 # set the CR to 0, matching the simulator
293 q.set_cr(0)
294 # delete the previous breakpoint so loops don't screw things up
295 q.delete_breakpoint()
296
297 # allow run to end
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)
306
307 # upload memory
308 if initial_mem:
309 q.upload_mem(initial_mem, skip_zeros=True)
310
311 # dump msr after endian set
312 msr = q.get_msr()
313 print("msr", bigendian, hex(msr), bin(msr))
314 # set the MSR bit 13, to set FPU
315 if bigendian:
316 # XXX this is probably wrong
317 msr = msr & ((1 << 53)-1)
318 else:
319 msr |= (1 << 13)
320 #msr = 0x4000000000009
321
322 q.set_msr(msr)
323 print("msr set to", hex(msr), bin(msr))
324
325 # upload regs
326 if initial_regs:
327 for i, reg in enumerate(initial_regs):
328 q.set_gpr(i, reg)
329 if initial_fprs:
330 if isinstance(initial_fprs, dict):
331 for i, reg in initial_fprs.items():
332 q.set_fpr(i, reg)
333 else:
334 for i, reg in enumerate(initial_fprs):
335 if reg != 0:
336 q.set_fpr(i, reg)
337
338 # can't do many of these - lr, ctr, etc. etc. later, just LR for now
339 if initial_sprs:
340 lr = initial_sprs.get('lr', None)
341 if lr is None:
342 lr = initial_sprs.get('LR', None)
343 if lr is not None:
344 q.set_lr(lr)
345
346 # disassemble and dump
347 d = q.disasm(start_addr, start_addr + program.size())
348 for line in d:
349 print ("qemu disasm", line)
350
351 # start running
352 if continuous_run:
353 q.gdb_continue()
354
355 return q
356
357
358 if __name__ == '__main__':
359 q = QemuController("simulator/qemu_test/kernel.bin", bigendian=True)
360 q.connect()
361 q.break_address(0x20000000)
362 q.gdb_continue()
363 print(q.get_gpr(1))
364 print(q.step())
365 print(q.get_gpr(1))
366 q.exit()