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