1# Fault injection helper script based on top of QMP. 2# 3# Developed by KONRAD Frederic <fred.konrad@greensocs.com> 4# 5# This work is licensed under the terms of the GNU GPL, version 2 or later. 6# See the COPYING file in the top-level directory. 7# 8 9import qmp 10import json 11import ast 12import readline 13import sys 14 15def die(cause): 16 sys.stderr.write('error: %s\n' % cause) 17 sys.exit(1) 18 19class FaultInjectionFramework(qmp.QEMUMonitorProtocol): 20 qemu_time = 0 21 verbose = 0 22 callback = {} 23 24 def print_v(self, msg, level): 25 if level <= self.verbose: 26 print msg 27 28 def print_qemu_version(self): 29 version = self._greeting['QMP']['version']['qemu'] 30 print 'Connected to QEMU %d.%d.%d\n' % (version['major'], 31 version['minor'], 32 version['micro']) 33 34 def __init__(self, address, verbose = 0): 35 self.verbose = verbose 36 qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address)) 37 38 try: 39 self._greeting = qmp.QEMUMonitorProtocol.connect(self) 40 except qmp.QMPConnectError: 41 die('Didn\'t get QMP greeting message') 42 except qmp.QMPCapabilitiesError: 43 die('Could not negotiate capabilities') 44 except self.error: 45 die('Could not connect to %s' % address) 46 47 self.print_qemu_version() 48 self._completer = None 49 self._pretty = False 50 self._transmode = False 51 self._actions = list() 52 53 def time_print(self, arg): 54 self.print_v('%sns: %s' % (self.qemu_time, arg), 1) 55 56 def send(self, qmpcmd): 57 self.print_v(qmpcmd, 2) 58 resp = self.cmd_obj(qmpcmd) 59 if resp is None: 60 die('Disconnected') 61 self.print_v(resp, 2) 62 return resp 63 64 def cont(self): 65 qmpcmd = {'execute': 'cont', 'arguments': {}} 66 self.send(qmpcmd) 67 68 def run(self): 69 # RUN the simulation. 70 self.time_print('Simulation is now running') 71 self.cont() 72 # Wait for an event to appear 73 shutdown_evt = False 74 while shutdown_evt == False: 75 for ev in self.get_events(True): 76 self.print_v(ev, 2) 77 if ev['event'] == 'FAULT_EVENT': 78 data = ev['data'] 79 self.qemu_time = data['time_ns']; 80 self.callback[data['event_id']]() 81 self.cont() 82 elif ev['event'] == 'SHUTDOWN': 83 shutdown_evt = True 84 self.clear_events() 85 self.close() 86 87 def notify(self, time_ns, cb): 88 # Notify a callback at qemu time time_ns 89 next_index = len(self.callback) 90 elt = 0 91 for elt in range(0, next_index + 1): 92 if elt == next_index: 93 break 94 if self.callback[elt] == cb: 95 break 96 97 self.callback[elt] = cb 98 self.time_print('Notify %s in %sns' % (cb, time_ns)) 99 qmpcmd = {'execute': 'trigger_event', 100 'arguments': {'event_id': elt, 101 'time_ns': time_ns}} 102 self.send(qmpcmd) 103 104 def write(self, address, value, size, cpu): 105 # write a value 106 self.time_print('write: 0x%08x @0x%08x size %s from cpu %s' \ 107 %(value, address, size, cpu)) 108 if type(cpu) is int: 109 qmpcmd = {'execute': 'write_mem', 110 'arguments': {'size': size, 111 'addr': address, 112 'val': value, 113 'cpu': cpu}} 114 else: 115 qmpcmd = {'execute': 'write_mem', 116 'arguments': {'size': size, 117 'addr': address, 118 'val': value, 119 'qom': cpu}} 120 self.send(qmpcmd) 121 122 def read(self, address, size, cpu): 123 # Read a value 124 self.time_print('read value: @0x%8.8X size %s from cpu %s' \ 125 %(address, size, cpu)) 126 if type(cpu) is int: 127 qmpcmd = {'execute': 'read_mem', 128 'arguments': {'size': size, 129 'addr': address, 130 'cpu': cpu}} 131 else: 132 qmpcmd = {'execute': 'read_mem', 133 'arguments': {'size': size, 134 'addr': address, 135 'qom': cpu}} 136 value = self.send(qmpcmd)['return'] 137 return value 138 139 def get_qom_property(self, path, property): 140 # Get a QOM property 141 qmpcmd = {'execute': 'qom-get', 142 'arguments': {'path': path, 143 'property': property}} 144 value = self.send(qmpcmd)['return'] 145 return value 146 147 def set_qom_property(self, path, property, value): 148 # Set a QOM property 149 qmpcmd = {'execute': 'qom-set', 150 'arguments': {'path': path, 151 'property': property, 152 'value': value}} 153 self.send(qmpcmd) 154 155 def set_gpio(self, device_name, gpio, num, value): 156 # Set a GPIO 157 if gpio != "": 158 qmpcmd = {'execute': 'inject_gpio', 159 'arguments': {'device_name': device_name, 160 'gpio': gpio, 161 'num': num, 162 'val': value}} 163 else: 164 qmpcmd = {'execute': 'inject_gpio', 165 'arguments': {'device_name': device_name, 166 'num': num, 167 'val': value}} 168 self.send(qmpcmd) 169 170 def help(self): 171 print "\nFault Injection Framework Commands" 172 print "==================================\n" 173 print "cont()" 174 print " * Resume the simulation when the Virtual Machine is stopped.\n" 175 print "run()" 176 print " * Start the simulation when the notify are set.\n" 177 print "notify(time_ns, cb)" 178 print " * Notify the callback cb in guest time time_ns.\n" 179 print "write(address, value, size, cpu)" 180 print " * Write @value of size @size at @address from @cpu." 181 print " * @cpu can be either a qom path or the cpu id.\n" 182 print "read(address, size, cpu)" 183 print " * Read a value of size @size at @address from @cpu." 184 print " * @cpu can be either a qom path or the cpu id." 185 print " * Returns the value.\n" 186 print "get_qom_property(path, property)" 187 print " * Get a qom property." 188 print " * Returns the qom property named @property in @path.\n" 189 print "set_qom_property(path, property, value)" 190 print " * Set the property named @property in @path with @value.\n" 191 print "set_gpio(path, gpio, num, value)" 192 print " * Set the gpio named @gpio number @num in @path with the @val." 193 print " * @val is a boolean.\n" 194 195 def __get_address(self, arg): 196 """ 197 Figure out if the argument is in the port:host form, if it's not it's 198 probably a file path. 199 """ 200 addr = arg.split(':') 201 if len(addr) == 2: 202 try: 203 port = int(addr[1]) 204 except ValueError: 205 raise QMPShellBadPort 206 return ( addr[0], port ) 207 # socket path 208 return arg 209 210