Fault Injection in QEMU

QEMU provides a Python framework that allows users to read and write guest memory and set GPIO lines and QEMU Object Model (QOM) properties while the guest is executing.
This framework can be used on any platform in QEMU.

For this page, we will use examples directly in a Python interpreter, however they could just as easily be used in a Python program file.

Full documentation for the framework can be found here, however some information from the link will be repeated here for convenience.



Getting Started

QEMU arguments

In order to connect the fault injector to QEMU, QEMU must be launched with following additional command-line arguments:

ArgumentDescriptionExample
-SPause QEMU guest execution at the first instruction until resumed by the user.-S
-qmp <proto>:<path>,serverCreate a QEMU Machine Protocol (QMP) socket using protocol proto in path path.

-qmp tcp:localhost:7777,server

-qmp unix:/path/to/qmp-sock,server

Or for a full list of example boot parameters, in this case for a single-arch Versal Adaptive SoC A72 QEMU machine:

qemu-system-aarch64 \
-M arm-generic-fdt \
-serial null -serial null -serial mon:stdio \
-device loader,file=<baremetal_for_versal_a72.elf>,cpu-num=0 \
-device loader,addr=0xFD1A0300,data=0x8000000e,data-len=4 \
-hw-dtb <device tree binary for Versal> \
-m <DDR memory size> \
-display none \
-qmp tcp:localhost:7777,server \
-S

For a full list of example boot parameters for other machines, see the Boot Examples section for multi-arch environments, and the Running Bare Metal Applications section for single-arch environments.

Connecting with Python

Once QEMU is launched, the QMP socket can be connected to.

In the qemu/script/qmp directory, launch your Python interpreter and import the fault injection framework and connect to QEMU:

$ python
>>> import fault_injection
>>> inj=fault_injection.FaultInjectionFramework("localhost:7777", 0)
Connected to QEMU 5.0.50
>>>

Once this is done, you are able to inject faults to the guest through the Python interpreter.

Commands

The framework provides the commands shown below.

The examples assume you have done the steps shown above.

If a command needs a QOM path and property and you're not sure how to find them, the QOM tree can be printed by doing info qtree in the QEMU monitor.  The QOM path is the ID that corresponds to the device you want to access.

To find the property name, do qom-list <QOM path> in the QEMU monitor.

CommandDescriptionExample
cont()
Resume execution when the guest is paused.

inj.cont()

Resume guest execution.

FaultInjectionFramework(sock, debug)

Constructor for the framework.  Connects to socket sock and prints debug information depending on what debug is set to.

Debug ValueVerbosity
0Prints command responses (if any)
1Prints debug 0 information and timed traces, such as a read or write.
2Prints debug 1 and 0 information, and QMP commands as well.

Returns the fault injection framework object.

inj=fault_injection.FaultInjectionFramework("localhost:7777", 0)

Creates a fault injection framework object connected to localhost:7777 with no verbose debug printing.

get_qom_property(path, property)

Get a value from a QEMU Object Model (QOM) Property.

Returns the value of property property from QOM path.

inj.get_qom_property("wdt@0xFD4D0000", "pclk")

Returns the value of pclk from wdt@0xFD4D0000.

help()Prints a list of commands and their descriptions.

inj.help()

Prints a list of commands and their descriptions.

notify(time_ns, cb)Notify the callback cb in guest time time_ns.

inj.framework.notify(1000000000, write_mem_callback)

Calls the function write_mem_callback after 1000000000ns of guest execution time.

read(address, size, cpu)

Reads size bytes at address address accessed by CPU cpu.

cpu can be either a QOM path or the CPU ID.

Returns the value read from the address.

inj.read(0xFF000000, 4, 0)

Reads 4 bytes from 0xFF000000 (UART0:UARTDR on Zynq UltraScale+ MPSoC) accessed by CPU 0 (APU 0).

run()Starts the guest.

inj.run()

Starts guest execution.

set_gpio(path, gpio, num, value)

Sets the GPIO gpio number num in QOM path path to the value value, where value is a boolean.

GPIOs will be labeled as <irq> if using qom-list on a QOM path.

inj.set_gpio("wdt@0xFD4D0000", "pwr_cntrl", 0, 1)

Sets the pwr_cntrl GPIO in wdt@0xFD4D0000 to 1.

set_qom_property(path, property, value)Sets the property property in path path to value value.

inj.set_qom_property("wdt@0xFD4D0000", "pclk", 1000)

Sets pclk in wdt@0xFD4D0000 to 1000.

write(address, value, size, cpu)

Writes value value of size bytes from CPU cpu to address address.

cpu can be either a QOM path or the CPU ID.

inj.write(0xFF000000, 0xFF, 1, 0)

Writes 0xFF to 0xFF000000 (UART0:UARTDR on Zynq UltraScale+ MPSoC) accessed by CPU 0 (APU 0).

© Copyright 2019 - 2022 Xilinx Inc. Privacy Policy