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