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