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