reasonably certain that the careful and slow use of little-endian data read/write
[soc.git] / src / soc / simulator / qemu.py
1 from pygdbmi.gdbcontroller import GdbController
2 import subprocess
3
4 launch_args = ['qemu-system-ppc64',
5 '-machine', 'powernv9',
6 '-nographic',
7 '-s', '-S']
8
9
10 class QemuController:
11 def __init__(self, kernel):
12 args = launch_args + ['-kernel', kernel]
13 self.qemu_popen = subprocess.Popen(args,
14 stdout=subprocess.PIPE,
15 stdin=subprocess.PIPE)
16 self.gdb = GdbController(gdb_path='powerpc64-linux-gnu-gdb')
17
18 def __enter__(self):
19 return self
20
21 def __exit__(self, type, value, traceback):
22 self.exit()
23
24 def connect(self):
25 return self.gdb.write('-target-select remote localhost:1234')
26
27 def break_address(self, addr):
28 cmd = '-break-insert *0x{:x}'.format(addr)
29 return self.gdb.write(cmd)
30
31 def delete_breakpoint(self, breakpoint=None):
32 breakstring = ''
33 if breakpoint:
34 breakstring = f' {breakpoint}'
35 return self.gdb.write('-break-delete' + breakstring)
36
37 def set_byte(self, addr, v):
38 print ("qemu set byte", hex(addr), hex(v))
39 faddr = '&{int}0x%x' % addr
40 res = self.gdb.write('-data-write-memory-bytes %s "%02x"' % (faddr, v))
41 print ("confirm", self.get_mem(addr, 1))
42
43 def get_mem(self, addr, nbytes):
44 res = self.gdb.write("-data-read-memory %d u 1 1 %d" % (addr, 8*nbytes))
45 #print ("get_mem", res)
46 for x in res:
47 if(x["type"]=="result"):
48 l = list(map(int, x['payload']['memory'][0]['data']))
49 res = []
50 for j in range(0, len(l), 8):
51 b = 0
52 for i, v in enumerate(l[j:j+8]):
53 b += v << (i*8)
54 res.append(b)
55 return res
56 return None
57
58 def get_registers(self):
59 return self.gdb.write('-data-list-register-values x')
60
61 def _get_register(self, fmt):
62 res = self.gdb.write('-data-list-register-values '+fmt,
63 timeout_sec=1.0) # increase this timeout if needed
64 for x in res:
65 if(x["type"]=="result"):
66 assert 'register-values' in x['payload']
67 return int(x['payload']['register-values'][0]['value'], 0)
68 return None
69
70 # TODO: use -data-list-register-names instead of hardcoding the values
71 def get_pc(self): return self._get_register('x 64')
72 def get_msr(self): return self._get_register('x 65')
73 def get_cr(self): return self._get_register('x 66')
74 def get_lr(self): return self._get_register('x 67')
75 def get_ctr(self): return self._get_register('x 68') # probably
76 def get_xer(self): return self._get_register('x 69')
77 def get_fpscr(self): return self._get_register('x 70')
78 def get_mq(self): return self._get_register('x 71')
79 def get_register(self, num):
80 return self._get_register('x {}'.format(num))
81
82 def step(self):
83 return self.gdb.write('-exec-next-instruction')
84
85 def gdb_continue(self):
86 return self.gdb.write('-exec-continue')
87
88 def gdb_eval(self, expr):
89 return self.gdb.write(f'-data-evaluate-expression {expr}')
90
91 def exit(self):
92 self.gdb.exit()
93 self.qemu_popen.kill()
94 outs, errs = self.qemu_popen.communicate()
95 self.qemu_popen.stdout.close()
96 self.qemu_popen.stdin.close()
97
98
99 def run_program(program, initial_mem=None):
100 q = QemuController(program.binfile.name)
101 q.connect()
102 # Run to the start of the program
103 q.break_address(0x20000000)
104 if initial_mem:
105 for addr, (v, wid) in initial_mem.items():
106 for i in range(wid):
107 q.set_byte(addr+i, (v>>i*8) & 0xff)
108 q.gdb_continue()
109 # set the CR to 0, matching the simulator
110 q.gdb_eval('$cr=0')
111 # delete the previous breakpoint so loops don't screw things up
112 q.delete_breakpoint()
113 # run to completion
114 q.break_address(0x20000000 + program.size())
115 q.gdb_continue()
116 return q
117
118
119 if __name__ == '__main__':
120 q = QemuController("qemu_test/kernel.bin")
121 q.connect()
122 q.break_address(0x20000000)
123 q.gdb_continue()
124 print(q.get_register(1))
125 print(q.step())
126 print(q.get_register(1))
127 q.exit()