From: Damien Hedde <damien.hedde@greensocs.com>
To: qemu-devel@nongnu.org
Cc: Damien Hedde <damien.hedde@greensocs.com>,
ehabkost@redhat.com, mark.burton@greensocs.com,
armbru@redhat.com, sakisp@xilinx.com, edgari@xilinx.com,
crosa@redhat.com, pbonzini@redhat.com, luc.michel@greensocs.com,
rth@twiddle.net
Subject: [Qemu-devel] [RFC PATCH 4/5] fault_injection: introduce Python scripting framework
Date: Fri, 28 Jun 2019 14:45:33 +0200 [thread overview]
Message-ID: <20190628124534.10679-5-damien.hedde@greensocs.com> (raw)
In-Reply-To: <20190628124534.10679-1-damien.hedde@greensocs.com>
This is the actual Python framework.
It provides some wrappers:
* which allow to notify a callback in a given qemu time.
* read or write some memory location
* read/write qom properties.
* set a GPIO.
This is based on the work of Frederic Konrad
Signed-off-by: Damien Hedde <damien.hedde@greensocs.com>
---
scripts/qmp/fault_injection.py | 278 +++++++++++++++++++++++++++++++++
1 file changed, 278 insertions(+)
create mode 100644 scripts/qmp/fault_injection.py
diff --git a/scripts/qmp/fault_injection.py b/scripts/qmp/fault_injection.py
new file mode 100644
index 0000000000..2d23e69d47
--- /dev/null
+++ b/scripts/qmp/fault_injection.py
@@ -0,0 +1,278 @@
+# Fault injection helper script based on top of QMP.
+#
+# Copyright (C) 2016,2019 GreenSocs SAS
+#
+# Authors:
+# Frederic Konrad <fred.konrad@greensocs.com>
+# Damien Hedde <damien.hedde@greensocs.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+#
+
+import json
+import ast
+import readline
+import sys
+import struct
+import os
+sys.path.append(os.path.join(os.path.dirname(__file__),
+ '..', '..', 'python', 'qemu'))
+import qmp
+
+def die(cause):
+ print(sys.stderr.write('error: %s\n' % cause))
+ sys.exit(1)
+
+class FaultInjectionQMPError(Exception):
+ def __init__(self, rsp):
+ self.args = (rsp,)
+
+class FaultInjectionFramework(qmp.QEMUMonitorProtocol):
+ qemu_time = 0
+ verbose = 0
+ callback = {}
+ endianness = None
+
+ def print_v(self, msg, level):
+ if level <= self.verbose:
+ print(msg)
+
+ def print_qemu_version(self):
+ version = self._greeting['QMP']['version']['qemu']
+ print('Connected to QEMU %d.%d.%d' % (version['major'],
+ version['minor'],
+ version['micro']))
+
+ def __init__(self, address, verbose = 0):
+ self.verbose = verbose
+ qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address))
+
+ try:
+ self._greeting = qmp.QEMUMonitorProtocol.connect(self)
+ except qmp.QMPConnectError:
+ die('Didn\'t get QMP greeting message')
+ except qmp.QMPCapabilitiesError:
+ die('Could not negotiate capabilities')
+ except self.error:
+ die('Could not connect to %s' % address)
+
+ self.print_qemu_version()
+ self._completer = None
+ self._pretty = False
+ self._transmode = False
+ self._actions = list()
+
+ def set_endian_big(self):
+ self.endianness = 'big'
+
+ def set_endian_little(self):
+ self.endianness = 'little'
+
+ def time_print(self, arg):
+ self.print_v('%sns: %s' % (self.qemu_time, arg), 1)
+
+ def send(self, qmpcmd):
+ self.print_v(qmpcmd, 2)
+ resp = self.cmd_obj(qmpcmd)
+ if resp is None:
+ die('Disconnected')
+ self.print_v(resp, 2)
+ if 'error' in resp:
+ raise FaultInjectionQMPError(resp)
+ return resp
+
+ def cont(self):
+ qmpcmd = {'execute': 'cont', 'arguments': {}}
+ self.send(qmpcmd)
+
+ def run_once(self):
+ # RUN the simulation until one event is received
+ self.cont()
+ # Wait for an event to appear
+ done = False
+ while done == False:
+ for ev in self.get_events(True):
+ self.print_v(ev, 2)
+ if ev['event'] == 'TIME_NOTIFICATION':
+ data = ev['data']
+ self.qemu_time = data['time_ns'];
+ self.callback[data['event_id']]()
+ self.cont()
+ done = True
+ elif ev['event'] == 'SHUTDOWN':
+ self.clear_events()
+ return True
+ self.clear_events()
+ return False
+
+ def run(self):
+ # RUN the simulation.
+ self.time_print('Simulation is now running')
+ # Wait for an event to appear
+ shutdown_evt = False
+ while shutdown_evt == False:
+ shutdown_evt = self.run_once()
+ self.close()
+
+ def notify(self, time_ns, cb, relative = False):
+ # Notify a callback at qemu time time_ns
+ next_index = len(self.callback)
+ elt = 0
+ for elt in range(0, next_index + 1):
+ if elt == next_index:
+ break
+ if self.callback[elt] == cb:
+ break
+
+ self.callback[elt] = cb
+ if relative:
+ self.time_print('Notify %s in %sns' % (cb, time_ns))
+ else:
+ self.time_print('Notify %s at %sns' % (cb, time_ns))
+ qmpcmd = {'execute': 'time-notify',
+ 'arguments': {'event_id': elt,
+ 'time_ns': time_ns,
+ 'pause' : True}}
+ if relative:
+ qmpcmd['arguments']['relative'] = True
+ self.send(qmpcmd)
+
+ def _pvmemwrite(self, virtual, address, value, cpu = None):
+ # write a value to a virtual or physical address
+ if type(value) is not list:
+ value = list(value)
+
+ self.time_print('write: @%s0x%08x size %d values ['
+ % ('V' if virtual else 'P', address, len(value))
+ + ','.join('%d' % i for i in value)
+ + '] from cpu %s' % (cpu))
+ qmpcmd = {'execute': 'memwrite' if virtual else 'pmemwrite',
+ 'arguments': {'addr': address, 'bytes': value}}
+ if cpu is not None:
+ qmpcmd['arguments']['cpu'] = cpu;
+ rsp = self.send(qmpcmd)
+
+ def _pvmemread(self, virtual, address, size, cpu = None):
+ # read a value to a virtual or physical address
+
+ self.time_print('read: @%s0x%08x size %d values from cpu %s'
+ % ('V' if virtual else 'P', address, size, cpu))
+ qmpcmd = {'execute': 'memread' if virtual else 'pmemread',
+ 'arguments': {'addr': address, 'size': size}}
+ if cpu is not None:
+ qmpcmd['arguments']['cpu'] = cpu;
+ rsp = self.send(qmpcmd)
+ value = bytearray(rsp['return']['bytes'])
+ return value
+
+ def _memstructformat(self, size):
+ if self.endianness == 'little':
+ s = '<'
+ elif self.endianness == 'big':
+ s = '>'
+ else:
+ #defaults to native
+ s = '='
+ s += {1:'B', 2:'H', 4:'I', 8:'Q'}[size]
+ return s
+
+ def read_mem(self, address, size, cpu = None):
+ fmt = self._memstructformat(size)
+ value = self._pvmemread(True, address, size, cpu)
+ return struct.unpack(fmt, value)[0]
+
+ def write_mem(self, address, size, value, cpu = None):
+ fmt = self._memstructformat(size)
+ value = bytearray(struct.pack(fmt, value))
+ self._pvmemwrite(True, address, value, cpu)
+
+ def read_pmem(self, address, size):
+ fmt = self._memstructformat(size)
+ value = self._pvmemread(False, address, size)
+ return struct.unpack(fmt, value)[0]
+
+ def write_pmem(self, address, size, value):
+ fmt = self._memstructformat(size)
+ value = bytearray(struct.pack(fmt, value))
+ self._pvmemwrite(False, address, value)
+
+ def get_qom_property(self, path, property):
+ # Get a QOM property
+ qmpcmd = {'execute': 'qom-get',
+ 'arguments': {'path': path,
+ 'property': property}}
+ value = self.send(qmpcmd)['return']
+ return value
+
+ def set_qom_property(self, path, property, value):
+ # Set a QOM property
+ qmpcmd = {'execute': 'qom-set',
+ 'arguments': {'path': path,
+ 'property': property,
+ 'value': value}}
+ self.send(qmpcmd)
+
+ def set_gpio(self, path, gpio, num, value):
+ # Set a GPIO
+ qmpcmd = {'execute': 'gpio-set',
+ 'arguments': {'path': path, 'value': value}}
+ if gpio is not None:
+ qmpcmd['arguments']['gpio'] = gpio
+ if num is not None:
+ qmpcmd['arguments']['number'] = num
+ self.send(qmpcmd)
+
+ def help(self):
+ msg = [
+ "Fault Injection Framework Commands",
+ "==================================\n",
+ "cont()",
+ " * Resume the simulation when the Virtual Machine is stopped.\n",
+ "run()",
+ " * Start the simulation when the notify are set.\n",
+ "notify(delta_ns, cb)",
+ " * Notify the callback cb in guest time delta_ns.\n",
+ "write_mem(address, size, value, cpu)",
+ " * write @value of size @size at virtual @address from @cpu.",
+ " * @size is in bytes, supported values are 1, 2, 4 and 8.",
+ " * @cpu is the cpu id.\n",
+ "read_mem(address, size, cpu)",
+ " * read a value of size @size at virtual @address from @cpu.",
+ " * @size is in bytes, supported values are 1, 2, 4 and 8.",
+ " * @cpu is the cpu id.",
+ " * returns the value.\n",
+ "write_pmem(address, size, value)",
+ " * write @value of size @size at physical @address.",
+ " * @size is in bytes, supported values are 1, 2, 4 and 8.\n",
+ "read_pmem(address, size)",
+ " * read a value of size @size at physical @address.",
+ " * @size is in bytes, supported values are 1, 2, 4 and 8.",
+ " * returns the value.\n",
+ "get_qom_property(path, property)",
+ " * Get a qom property.",
+ " * Returns the qom property named @property in @path.\n",
+ "set_qom_property(path, property, value)",
+ " * Set the property named @property in @path with @value.\n",
+ "set_gpio(path, gpio, num, value)",
+ " * Set the gpio named @gpio number @num in @path with the @val.",
+ " * @val is a boolean.\n"
+ ]
+ for m in msg:
+ print(m)
+
+ def __get_address(self, arg):
+ """
+ Figure out if the argument is in the port:host form, if it's not it's
+ probably a file path.
+ """
+ addr = arg.split(':')
+ if len(addr) == 2:
+ try:
+ port = int(addr[1])
+ except ValueError:
+ raise QMPShellBadPort
+ return ( addr[0], port )
+ # socket path
+ return arg
+
--
2.22.0
next prev parent reply other threads:[~2019-06-28 13:11 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-06-28 12:45 [Qemu-devel] [RFC PATCH 0/5] FAULT INJECTION FRAMEWORK Damien Hedde
2019-06-28 12:45 ` [Qemu-devel] [RFC PATCH 1/5] introduce [p]mem(read|write) qmp commands Damien Hedde
2019-06-28 12:45 ` [Qemu-devel] [RFC PATCH 2/5] introduce a qmp command to set gpios Damien Hedde
2019-06-28 12:45 ` [Qemu-devel] [RFC PATCH 3/5] add qmp time-notify event triggering system Damien Hedde
2019-06-28 12:45 ` Damien Hedde [this message]
2019-06-28 12:45 ` [Qemu-devel] [RFC PATCH 5/5] docs: add fault injection framework documentation Damien Hedde
2019-07-01 8:37 ` [Qemu-devel] [RFC PATCH 0/5] FAULT INJECTION FRAMEWORK Stefan Hajnoczi
2019-07-01 10:16 ` Philippe Mathieu-Daudé
2019-07-03 9:29 ` Stefan Hajnoczi
2019-07-03 15:47 ` Damien Hedde
2019-07-09 13:42 ` Stefan Hajnoczi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190628124534.10679-5-damien.hedde@greensocs.com \
--to=damien.hedde@greensocs.com \
--cc=armbru@redhat.com \
--cc=crosa@redhat.com \
--cc=edgari@xilinx.com \
--cc=ehabkost@redhat.com \
--cc=luc.michel@greensocs.com \
--cc=mark.burton@greensocs.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=rth@twiddle.net \
--cc=sakisp@xilinx.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).