qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [RFC PATCH 4/5] fault_injection: introduce Python scripting


From: Damien Hedde
Subject: [Qemu-devel] [RFC PATCH 4/5] fault_injection: introduce Python scripting framework
Date: Fri, 28 Jun 2019 14:45:33 +0200

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 <address@hidden>
---
 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 <address@hidden>
+#   Damien Hedde <address@hidden>
+#
+# 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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]