xref: /openbmc/qemu/docs/devel/testing/qgraph.rst (revision ff41da50)
1*ff41da50SThomas Huth.. _qgraph:
2*ff41da50SThomas Huth
3*ff41da50SThomas HuthQtest Driver Framework
4*ff41da50SThomas Huth======================
5*ff41da50SThomas Huth
6*ff41da50SThomas HuthIn order to test a specific driver, plain libqos tests need to
7*ff41da50SThomas Huthtake care of booting QEMU with the right machine and devices.
8*ff41da50SThomas HuthThis makes each test "hardcoded" for a specific configuration, reducing
9*ff41da50SThomas Huththe possible coverage that it can reach.
10*ff41da50SThomas Huth
11*ff41da50SThomas HuthFor example, the sdhci device is supported on both x86_64 and ARM boards,
12*ff41da50SThomas Huththerefore a generic sdhci test should test all machines and drivers that
13*ff41da50SThomas Huthsupport that device.
14*ff41da50SThomas HuthUsing only libqos APIs, the test has to manually take care of
15*ff41da50SThomas Huthcovering all the setups, and build the correct command line.
16*ff41da50SThomas Huth
17*ff41da50SThomas HuthThis also introduces backward compatibility issues: if a device/driver command
18*ff41da50SThomas Huthline name is changed, all tests that use that will not work
19*ff41da50SThomas Huthproperly anymore and need to be adjusted.
20*ff41da50SThomas Huth
21*ff41da50SThomas HuthThe aim of qgraph is to create a graph of drivers, machines and tests such that
22*ff41da50SThomas Hutha test aimed to a certain driver does not have to care of
23*ff41da50SThomas Huthbooting the right QEMU machine, pick the right device, build the command line
24*ff41da50SThomas Huthand so on. Instead, it only defines what type of device it is testing
25*ff41da50SThomas Huth(interface in qgraph terms) and the framework takes care of
26*ff41da50SThomas Huthcovering all supported types of devices and machine architectures.
27*ff41da50SThomas Huth
28*ff41da50SThomas HuthFollowing the above example, an interface would be ``sdhci``,
29*ff41da50SThomas Huthso the sdhci-test should only care of linking its qgraph node with
30*ff41da50SThomas Huththat interface. In this way, if the command line of a sdhci driver
31*ff41da50SThomas Huthis changed, only the respective qgraph driver node has to be adjusted.
32*ff41da50SThomas Huth
33*ff41da50SThomas HuthQGraph concepts
34*ff41da50SThomas Huth---------------
35*ff41da50SThomas Huth
36*ff41da50SThomas HuthThe graph is composed by nodes that represent machines, drivers, tests
37*ff41da50SThomas Huthand edges that define the relationships between them (``CONSUMES``, ``PRODUCES``, and
38*ff41da50SThomas Huth``CONTAINS``).
39*ff41da50SThomas Huth
40*ff41da50SThomas HuthNodes
41*ff41da50SThomas Huth~~~~~
42*ff41da50SThomas Huth
43*ff41da50SThomas HuthA node can be of four types:
44*ff41da50SThomas Huth
45*ff41da50SThomas Huth- **QNODE_MACHINE**:   for example ``arm/raspi2b``
46*ff41da50SThomas Huth- **QNODE_DRIVER**:    for example ``generic-sdhci``
47*ff41da50SThomas Huth- **QNODE_INTERFACE**: for example ``sdhci`` (interface for all ``-sdhci``
48*ff41da50SThomas Huth  drivers).
49*ff41da50SThomas Huth  An interface is not explicitly created, it will be automatically
50*ff41da50SThomas Huth  instantiated when a node consumes or produces it.
51*ff41da50SThomas Huth  An interface is simply a struct that abstracts the various drivers
52*ff41da50SThomas Huth  for the same type of device, and offers an API to the nodes that
53*ff41da50SThomas Huth  use it ("consume" relation in qgraph terms) that is implemented/backed up by the drivers that implement it ("produce" relation in qgraph terms).
54*ff41da50SThomas Huth- **QNODE_TEST**:      for example ``sdhci-test``. A test consumes an interface
55*ff41da50SThomas Huth  and tests the functions provided by it.
56*ff41da50SThomas Huth
57*ff41da50SThomas HuthNotes for the nodes:
58*ff41da50SThomas Huth
59*ff41da50SThomas Huth- QNODE_MACHINE: each machine struct must have a ``QGuestAllocator`` and
60*ff41da50SThomas Huth  implement ``get_driver()`` to return the allocator mapped to the interface
61*ff41da50SThomas Huth  "memory". The function can also return ``NULL`` if the allocator
62*ff41da50SThomas Huth  is not set.
63*ff41da50SThomas Huth- QNODE_DRIVER:  driver names must be unique, and machines and nodes
64*ff41da50SThomas Huth  planned to be "consumed" by other nodes must match QEMU
65*ff41da50SThomas Huth  drivers name, otherwise they won't be discovered
66*ff41da50SThomas Huth
67*ff41da50SThomas HuthEdges
68*ff41da50SThomas Huth~~~~~
69*ff41da50SThomas Huth
70*ff41da50SThomas HuthAn edge relation between two nodes (drivers or machines) ``X`` and ``Y`` can be:
71*ff41da50SThomas Huth
72*ff41da50SThomas Huth- ``X CONSUMES Y``: ``Y`` can be plugged into ``X``
73*ff41da50SThomas Huth- ``X PRODUCES Y``: ``X`` provides the interface ``Y``
74*ff41da50SThomas Huth- ``X CONTAINS Y``: ``Y`` is part of ``X`` component
75*ff41da50SThomas Huth
76*ff41da50SThomas HuthExecution steps
77*ff41da50SThomas Huth~~~~~~~~~~~~~~~
78*ff41da50SThomas Huth
79*ff41da50SThomas HuthThe basic framework steps are the following:
80*ff41da50SThomas Huth
81*ff41da50SThomas Huth- All nodes and edges are created in their respective
82*ff41da50SThomas Huth  machine/driver/test files
83*ff41da50SThomas Huth- The framework starts QEMU and asks for a list of available devices
84*ff41da50SThomas Huth  and machines (note that only machines and "consumed" nodes are mapped
85*ff41da50SThomas Huth  1:1 with QEMU devices)
86*ff41da50SThomas Huth- The framework walks the graph starting from the available machines and
87*ff41da50SThomas Huth  performs a Depth First Search for tests
88*ff41da50SThomas Huth- Once a test is found, the path is walked again and all drivers are
89*ff41da50SThomas Huth  allocated accordingly and the final interface is passed to the test
90*ff41da50SThomas Huth- The test is executed
91*ff41da50SThomas Huth- Unused objects are cleaned and the path discovery is continued
92*ff41da50SThomas Huth
93*ff41da50SThomas HuthDepending on the QEMU binary used, only some drivers/machines will be
94*ff41da50SThomas Huthavailable and only test that are reached by them will be executed.
95*ff41da50SThomas Huth
96*ff41da50SThomas HuthCommand line
97*ff41da50SThomas Huth~~~~~~~~~~~~
98*ff41da50SThomas Huth
99*ff41da50SThomas HuthCommand line is built by using node names and optional arguments
100*ff41da50SThomas Huthpassed by the user when building the edges.
101*ff41da50SThomas Huth
102*ff41da50SThomas HuthThere are three types of command line arguments:
103*ff41da50SThomas Huth
104*ff41da50SThomas Huth- ``in node``      : created from the node name. For example, machines will
105*ff41da50SThomas Huth  have ``-M <machine>`` to its command line, while devices
106*ff41da50SThomas Huth  ``-device <device>``. It is automatically done by the framework.
107*ff41da50SThomas Huth- ``after node``   : added as additional argument to the node name.
108*ff41da50SThomas Huth  This argument is added optionally when creating edges,
109*ff41da50SThomas Huth  by setting the parameter ``after_cmd_line`` and
110*ff41da50SThomas Huth  ``extra_edge_opts`` in ``QOSGraphEdgeOptions``.
111*ff41da50SThomas Huth  The framework automatically adds
112*ff41da50SThomas Huth  a comma before ``extra_edge_opts``,
113*ff41da50SThomas Huth  because it is going to add attributes
114*ff41da50SThomas Huth  after the destination node pointed by
115*ff41da50SThomas Huth  the edge containing these options, and automatically
116*ff41da50SThomas Huth  adds a space before ``after_cmd_line``, because it
117*ff41da50SThomas Huth  adds an additional device, not an attribute.
118*ff41da50SThomas Huth- ``before node``  : added as additional argument to the node name.
119*ff41da50SThomas Huth  This argument is added optionally when creating edges,
120*ff41da50SThomas Huth  by setting the parameter ``before_cmd_line`` in
121*ff41da50SThomas Huth  ``QOSGraphEdgeOptions``. This attribute
122*ff41da50SThomas Huth  is going to add attributes before the destination node
123*ff41da50SThomas Huth  pointed by the edge containing these options. It is
124*ff41da50SThomas Huth  helpful to commands that are not node-representable,
125*ff41da50SThomas Huth  such as ``-fdsev`` or ``-netdev``.
126*ff41da50SThomas Huth
127*ff41da50SThomas HuthWhile adding command line in edges is always used, not all nodes names are
128*ff41da50SThomas Huthused in every path walk: this is because the contained or produced ones
129*ff41da50SThomas Huthare already added by QEMU, so only nodes that "consumes" will be used to
130*ff41da50SThomas Huthbuild the command line. Also, nodes that will have ``{ "abstract" : true }``
131*ff41da50SThomas Huthas QMP attribute will loose their command line, since they are not proper
132*ff41da50SThomas Huthdevices to be added in QEMU.
133*ff41da50SThomas Huth
134*ff41da50SThomas HuthExample::
135*ff41da50SThomas Huth
136*ff41da50SThomas Huth    QOSGraphEdgeOptions opts = {
137*ff41da50SThomas Huth        .before_cmd_line = "-drive id=drv0,if=none,file=null-co://,"
138*ff41da50SThomas Huth                           "file.read-zeroes=on,format=raw",
139*ff41da50SThomas Huth        .after_cmd_line = "-device scsi-hd,bus=vs0.0,drive=drv0",
140*ff41da50SThomas Huth
141*ff41da50SThomas Huth        opts.extra_device_opts = "id=vs0";
142*ff41da50SThomas Huth    };
143*ff41da50SThomas Huth
144*ff41da50SThomas Huth    qos_node_create_driver("virtio-scsi-device",
145*ff41da50SThomas Huth                            virtio_scsi_device_create);
146*ff41da50SThomas Huth    qos_node_consumes("virtio-scsi-device", "virtio-bus", &opts);
147*ff41da50SThomas Huth
148*ff41da50SThomas HuthWill produce the following command line:
149*ff41da50SThomas Huth``-drive id=drv0,if=none,file=null-co://, -device virtio-scsi-device,id=vs0 -device scsi-hd,bus=vs0.0,drive=drv0``
150*ff41da50SThomas Huth
151*ff41da50SThomas HuthTroubleshooting unavailable tests
152*ff41da50SThomas Huth~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
153*ff41da50SThomas Huth
154*ff41da50SThomas HuthIf there is no path from an available machine to a test then that test will be
155*ff41da50SThomas Huthunavailable and won't execute. This can happen if a test or driver did not set
156*ff41da50SThomas Huthup its qgraph node correctly. It can also happen if the necessary machine type
157*ff41da50SThomas Huthor device is missing from the QEMU binary because it was compiled out or
158*ff41da50SThomas Huthotherwise.
159*ff41da50SThomas Huth
160*ff41da50SThomas HuthIt is possible to troubleshoot unavailable tests by running::
161*ff41da50SThomas Huth
162*ff41da50SThomas Huth  $ QTEST_QEMU_BINARY=build/qemu-system-x86_64 build/tests/qtest/qos-test --verbose
163*ff41da50SThomas Huth  # ALL QGRAPH EDGES: {
164*ff41da50SThomas Huth  #   src='virtio-net'
165*ff41da50SThomas Huth  #      |-> dest='virtio-net-tests/vhost-user/multiqueue' type=2 (node=0x559142109e30)
166*ff41da50SThomas Huth  #      |-> dest='virtio-net-tests/vhost-user/migrate' type=2 (node=0x559142109d00)
167*ff41da50SThomas Huth  #   src='virtio-net-pci'
168*ff41da50SThomas Huth  #      |-> dest='virtio-net' type=1 (node=0x55914210d740)
169*ff41da50SThomas Huth  #   src='pci-bus'
170*ff41da50SThomas Huth  #      |-> dest='virtio-net-pci' type=2 (node=0x55914210d880)
171*ff41da50SThomas Huth  #   src='pci-bus-pc'
172*ff41da50SThomas Huth  #      |-> dest='pci-bus' type=1 (node=0x559142103f40)
173*ff41da50SThomas Huth  #   src='i440FX-pcihost'
174*ff41da50SThomas Huth  #      |-> dest='pci-bus-pc' type=0 (node=0x55914210ac70)
175*ff41da50SThomas Huth  #   src='x86_64/pc'
176*ff41da50SThomas Huth  #      |-> dest='i440FX-pcihost' type=0 (node=0x5591421117f0)
177*ff41da50SThomas Huth  #   src=''
178*ff41da50SThomas Huth  #      |-> dest='x86_64/pc' type=0 (node=0x559142111600)
179*ff41da50SThomas Huth  #      |-> dest='arm/raspi2b' type=0 (node=0x559142110740)
180*ff41da50SThomas Huth  ...
181*ff41da50SThomas Huth  # }
182*ff41da50SThomas Huth  # ALL QGRAPH NODES: {
183*ff41da50SThomas Huth  #   name='virtio-net-tests/announce-self' type=3 cmd_line='(null)' [available]
184*ff41da50SThomas Huth  #   name='arm/raspi2b' type=0 cmd_line='-M raspi2b ' [UNAVAILABLE]
185*ff41da50SThomas Huth  ...
186*ff41da50SThomas Huth  # }
187*ff41da50SThomas Huth
188*ff41da50SThomas HuthThe ``virtio-net-tests/announce-self`` test is listed as "available" in the
189*ff41da50SThomas Huth"ALL QGRAPH NODES" output. This means the test will execute. We can follow the
190*ff41da50SThomas Huthqgraph path in the "ALL QGRAPH EDGES" output as follows: '' -> 'x86_64/pc' ->
191*ff41da50SThomas Huth'i440FX-pcihost' -> 'pci-bus-pc' -> 'pci-bus' -> 'virtio-net-pci' ->
192*ff41da50SThomas Huth'virtio-net'. The root of the qgraph is '' and the depth first search begins
193*ff41da50SThomas Huththere.
194*ff41da50SThomas Huth
195*ff41da50SThomas HuthThe ``arm/raspi2b`` machine node is listed as "UNAVAILABLE". Although it is
196*ff41da50SThomas Huthreachable from the root via '' -> 'arm/raspi2b' the node is unavailable because
197*ff41da50SThomas Huththe QEMU binary did not list it when queried by the framework. This is expected
198*ff41da50SThomas Huthbecause we used the ``qemu-system-x86_64`` binary which does not support ARM
199*ff41da50SThomas Huthmachine types.
200*ff41da50SThomas Huth
201*ff41da50SThomas HuthIf a test is unexpectedly listed as "UNAVAILABLE", first check that the "ALL
202*ff41da50SThomas HuthQGRAPH EDGES" output reports edge connectivity from the root ('') to the test.
203*ff41da50SThomas HuthIf there is no connectivity then the qgraph nodes were not set up correctly and
204*ff41da50SThomas Huththe driver or test code is incorrect. If there is connectivity, check the
205*ff41da50SThomas Huthavailability of each node in the path in the "ALL QGRAPH NODES" output. The
206*ff41da50SThomas Huthfirst unavailable node in the path is the reason why the test is unavailable.
207*ff41da50SThomas HuthTypically this is because the QEMU binary lacks support for the necessary
208*ff41da50SThomas Huthmachine type or device.
209*ff41da50SThomas Huth
210*ff41da50SThomas HuthCreating a new driver and its interface
211*ff41da50SThomas Huth---------------------------------------
212*ff41da50SThomas Huth
213*ff41da50SThomas HuthHere we continue the ``sdhci`` use case, with the following scenario:
214*ff41da50SThomas Huth
215*ff41da50SThomas Huth- ``sdhci-test`` aims to test the ``read[q,w], writeq`` functions
216*ff41da50SThomas Huth  offered by the ``sdhci`` drivers.
217*ff41da50SThomas Huth- The current ``sdhci`` device is supported by both ``x86_64/pc`` and ``ARM``
218*ff41da50SThomas Huth  (in this example we focus on the ``arm-raspi2b``) machines.
219*ff41da50SThomas Huth- QEMU offers 2 types of drivers: ``QSDHCI_MemoryMapped`` for ``ARM`` and
220*ff41da50SThomas Huth  ``QSDHCI_PCI`` for ``x86_64/pc``. Both implement the
221*ff41da50SThomas Huth  ``read[q,w], writeq`` functions.
222*ff41da50SThomas Huth
223*ff41da50SThomas HuthIn order to implement such scenario in qgraph, the test developer needs to:
224*ff41da50SThomas Huth
225*ff41da50SThomas Huth- Create the ``x86_64/pc`` machine node. This machine uses the
226*ff41da50SThomas Huth  ``pci-bus`` architecture so it ``contains`` a PCI driver,
227*ff41da50SThomas Huth  ``pci-bus-pc``. The actual path is
228*ff41da50SThomas Huth
229*ff41da50SThomas Huth  ``x86_64/pc --contains--> 1440FX-pcihost --contains-->
230*ff41da50SThomas Huth  pci-bus-pc --produces--> pci-bus``.
231*ff41da50SThomas Huth
232*ff41da50SThomas Huth  For the sake of this example,
233*ff41da50SThomas Huth  we do not focus on the PCI interface implementation.
234*ff41da50SThomas Huth- Create the ``sdhci-pci`` driver node, representing ``QSDHCI_PCI``.
235*ff41da50SThomas Huth  The driver uses the PCI bus (and its API),
236*ff41da50SThomas Huth  so it must ``consume`` the ``pci-bus`` generic interface (which abstracts
237*ff41da50SThomas Huth  all the pci drivers available)
238*ff41da50SThomas Huth
239*ff41da50SThomas Huth  ``sdhci-pci --consumes--> pci-bus``
240*ff41da50SThomas Huth- Create an ``arm/raspi2b`` machine node. This machine ``contains``
241*ff41da50SThomas Huth  a ``generic-sdhci`` memory mapped ``sdhci`` driver node, representing
242*ff41da50SThomas Huth  ``QSDHCI_MemoryMapped``.
243*ff41da50SThomas Huth
244*ff41da50SThomas Huth  ``arm/raspi2b --contains--> generic-sdhci``
245*ff41da50SThomas Huth- Create the ``sdhci`` interface node. This interface offers the
246*ff41da50SThomas Huth  functions that are shared by all ``sdhci`` devices.
247*ff41da50SThomas Huth  The interface is produced by ``sdhci-pci`` and ``generic-sdhci``,
248*ff41da50SThomas Huth  the available architecture-specific drivers.
249*ff41da50SThomas Huth
250*ff41da50SThomas Huth  ``sdhci-pci --produces--> sdhci``
251*ff41da50SThomas Huth
252*ff41da50SThomas Huth  ``generic-sdhci --produces--> sdhci``
253*ff41da50SThomas Huth- Create the ``sdhci-test`` test node. The test ``consumes`` the
254*ff41da50SThomas Huth  ``sdhci`` interface, using its API. It doesn't need to look at
255*ff41da50SThomas Huth  the supported machines or drivers.
256*ff41da50SThomas Huth
257*ff41da50SThomas Huth  ``sdhci-test --consumes--> sdhci``
258*ff41da50SThomas Huth
259*ff41da50SThomas Huth``arm-raspi2b`` machine, simplified from
260*ff41da50SThomas Huth``tests/qtest/libqos/arm-raspi2-machine.c``::
261*ff41da50SThomas Huth
262*ff41da50SThomas Huth    #include "qgraph.h"
263*ff41da50SThomas Huth
264*ff41da50SThomas Huth    struct QRaspi2Machine {
265*ff41da50SThomas Huth        QOSGraphObject obj;
266*ff41da50SThomas Huth        QGuestAllocator alloc;
267*ff41da50SThomas Huth        QSDHCI_MemoryMapped sdhci;
268*ff41da50SThomas Huth    };
269*ff41da50SThomas Huth
270*ff41da50SThomas Huth    static void *raspi2_get_driver(void *object, const char *interface)
271*ff41da50SThomas Huth    {
272*ff41da50SThomas Huth        QRaspi2Machine *machine = object;
273*ff41da50SThomas Huth        if (!g_strcmp0(interface, "memory")) {
274*ff41da50SThomas Huth            return &machine->alloc;
275*ff41da50SThomas Huth        }
276*ff41da50SThomas Huth
277*ff41da50SThomas Huth        fprintf(stderr, "%s not present in arm/raspi2b\n", interface);
278*ff41da50SThomas Huth        g_assert_not_reached();
279*ff41da50SThomas Huth    }
280*ff41da50SThomas Huth
281*ff41da50SThomas Huth    static QOSGraphObject *raspi2_get_device(void *obj,
282*ff41da50SThomas Huth                                                const char *device)
283*ff41da50SThomas Huth    {
284*ff41da50SThomas Huth        QRaspi2Machine *machine = obj;
285*ff41da50SThomas Huth        if (!g_strcmp0(device, "generic-sdhci")) {
286*ff41da50SThomas Huth            return &machine->sdhci.obj;
287*ff41da50SThomas Huth        }
288*ff41da50SThomas Huth
289*ff41da50SThomas Huth        fprintf(stderr, "%s not present in arm/raspi2b\n", device);
290*ff41da50SThomas Huth        g_assert_not_reached();
291*ff41da50SThomas Huth    }
292*ff41da50SThomas Huth
293*ff41da50SThomas Huth    static void *qos_create_machine_arm_raspi2(QTestState *qts)
294*ff41da50SThomas Huth    {
295*ff41da50SThomas Huth        QRaspi2Machine *machine = g_new0(QRaspi2Machine, 1);
296*ff41da50SThomas Huth
297*ff41da50SThomas Huth        alloc_init(&machine->alloc, ...);
298*ff41da50SThomas Huth
299*ff41da50SThomas Huth        /* Get node(s) contained inside (CONTAINS) */
300*ff41da50SThomas Huth        machine->obj.get_device = raspi2_get_device;
301*ff41da50SThomas Huth
302*ff41da50SThomas Huth        /* Get node(s) produced (PRODUCES) */
303*ff41da50SThomas Huth        machine->obj.get_driver = raspi2_get_driver;
304*ff41da50SThomas Huth
305*ff41da50SThomas Huth        /* free the object */
306*ff41da50SThomas Huth        machine->obj.destructor = raspi2_destructor;
307*ff41da50SThomas Huth        qos_init_sdhci_mm(&machine->sdhci, ...);
308*ff41da50SThomas Huth        return &machine->obj;
309*ff41da50SThomas Huth    }
310*ff41da50SThomas Huth
311*ff41da50SThomas Huth    static void raspi2_register_nodes(void)
312*ff41da50SThomas Huth    {
313*ff41da50SThomas Huth        /* arm/raspi2b --contains--> generic-sdhci */
314*ff41da50SThomas Huth        qos_node_create_machine("arm/raspi2b",
315*ff41da50SThomas Huth                                 qos_create_machine_arm_raspi2);
316*ff41da50SThomas Huth        qos_node_contains("arm/raspi2b", "generic-sdhci", NULL);
317*ff41da50SThomas Huth    }
318*ff41da50SThomas Huth
319*ff41da50SThomas Huth    libqos_init(raspi2_register_nodes);
320*ff41da50SThomas Huth
321*ff41da50SThomas Huth``x86_64/pc`` machine, simplified from
322*ff41da50SThomas Huth``tests/qtest/libqos/x86_64_pc-machine.c``::
323*ff41da50SThomas Huth
324*ff41da50SThomas Huth    #include "qgraph.h"
325*ff41da50SThomas Huth
326*ff41da50SThomas Huth    struct i440FX_pcihost {
327*ff41da50SThomas Huth        QOSGraphObject obj;
328*ff41da50SThomas Huth        QPCIBusPC pci;
329*ff41da50SThomas Huth    };
330*ff41da50SThomas Huth
331*ff41da50SThomas Huth    struct QX86PCMachine {
332*ff41da50SThomas Huth        QOSGraphObject obj;
333*ff41da50SThomas Huth        QGuestAllocator alloc;
334*ff41da50SThomas Huth        i440FX_pcihost bridge;
335*ff41da50SThomas Huth    };
336*ff41da50SThomas Huth
337*ff41da50SThomas Huth    /* i440FX_pcihost */
338*ff41da50SThomas Huth
339*ff41da50SThomas Huth    static QOSGraphObject *i440FX_host_get_device(void *obj,
340*ff41da50SThomas Huth                                                const char *device)
341*ff41da50SThomas Huth    {
342*ff41da50SThomas Huth        i440FX_pcihost *host = obj;
343*ff41da50SThomas Huth        if (!g_strcmp0(device, "pci-bus-pc")) {
344*ff41da50SThomas Huth            return &host->pci.obj;
345*ff41da50SThomas Huth        }
346*ff41da50SThomas Huth        fprintf(stderr, "%s not present in i440FX-pcihost\n", device);
347*ff41da50SThomas Huth        g_assert_not_reached();
348*ff41da50SThomas Huth    }
349*ff41da50SThomas Huth
350*ff41da50SThomas Huth    /* x86_64/pc machine */
351*ff41da50SThomas Huth
352*ff41da50SThomas Huth    static void *pc_get_driver(void *object, const char *interface)
353*ff41da50SThomas Huth    {
354*ff41da50SThomas Huth        QX86PCMachine *machine = object;
355*ff41da50SThomas Huth        if (!g_strcmp0(interface, "memory")) {
356*ff41da50SThomas Huth            return &machine->alloc;
357*ff41da50SThomas Huth        }
358*ff41da50SThomas Huth
359*ff41da50SThomas Huth        fprintf(stderr, "%s not present in x86_64/pc\n", interface);
360*ff41da50SThomas Huth        g_assert_not_reached();
361*ff41da50SThomas Huth    }
362*ff41da50SThomas Huth
363*ff41da50SThomas Huth    static QOSGraphObject *pc_get_device(void *obj, const char *device)
364*ff41da50SThomas Huth    {
365*ff41da50SThomas Huth        QX86PCMachine *machine = obj;
366*ff41da50SThomas Huth        if (!g_strcmp0(device, "i440FX-pcihost")) {
367*ff41da50SThomas Huth            return &machine->bridge.obj;
368*ff41da50SThomas Huth        }
369*ff41da50SThomas Huth
370*ff41da50SThomas Huth        fprintf(stderr, "%s not present in x86_64/pc\n", device);
371*ff41da50SThomas Huth        g_assert_not_reached();
372*ff41da50SThomas Huth    }
373*ff41da50SThomas Huth
374*ff41da50SThomas Huth    static void *qos_create_machine_pc(QTestState *qts)
375*ff41da50SThomas Huth    {
376*ff41da50SThomas Huth        QX86PCMachine *machine = g_new0(QX86PCMachine, 1);
377*ff41da50SThomas Huth
378*ff41da50SThomas Huth        /* Get node(s) contained inside (CONTAINS) */
379*ff41da50SThomas Huth        machine->obj.get_device = pc_get_device;
380*ff41da50SThomas Huth
381*ff41da50SThomas Huth        /* Get node(s) produced (PRODUCES) */
382*ff41da50SThomas Huth        machine->obj.get_driver = pc_get_driver;
383*ff41da50SThomas Huth
384*ff41da50SThomas Huth        /* free the object */
385*ff41da50SThomas Huth        machine->obj.destructor = pc_destructor;
386*ff41da50SThomas Huth        pc_alloc_init(&machine->alloc, qts, ALLOC_NO_FLAGS);
387*ff41da50SThomas Huth
388*ff41da50SThomas Huth        /* Get node(s) contained inside (CONTAINS) */
389*ff41da50SThomas Huth        machine->bridge.obj.get_device = i440FX_host_get_device;
390*ff41da50SThomas Huth
391*ff41da50SThomas Huth        return &machine->obj;
392*ff41da50SThomas Huth    }
393*ff41da50SThomas Huth
394*ff41da50SThomas Huth    static void pc_machine_register_nodes(void)
395*ff41da50SThomas Huth    {
396*ff41da50SThomas Huth        /* x86_64/pc --contains--> 1440FX-pcihost --contains-->
397*ff41da50SThomas Huth         * pci-bus-pc [--produces--> pci-bus (in pci.h)] */
398*ff41da50SThomas Huth        qos_node_create_machine("x86_64/pc", qos_create_machine_pc);
399*ff41da50SThomas Huth        qos_node_contains("x86_64/pc", "i440FX-pcihost", NULL);
400*ff41da50SThomas Huth
401*ff41da50SThomas Huth        /* contained drivers don't need a constructor,
402*ff41da50SThomas Huth         * they will be init by the parent */
403*ff41da50SThomas Huth        qos_node_create_driver("i440FX-pcihost", NULL);
404*ff41da50SThomas Huth        qos_node_contains("i440FX-pcihost", "pci-bus-pc", NULL);
405*ff41da50SThomas Huth    }
406*ff41da50SThomas Huth
407*ff41da50SThomas Huth    libqos_init(pc_machine_register_nodes);
408*ff41da50SThomas Huth
409*ff41da50SThomas Huth``sdhci`` taken from ``tests/qtest/libqos/sdhci.c``::
410*ff41da50SThomas Huth
411*ff41da50SThomas Huth    /* Interface node, offers the sdhci API */
412*ff41da50SThomas Huth    struct QSDHCI {
413*ff41da50SThomas Huth        uint16_t (*readw)(QSDHCI *s, uint32_t reg);
414*ff41da50SThomas Huth        uint64_t (*readq)(QSDHCI *s, uint32_t reg);
415*ff41da50SThomas Huth        void (*writeq)(QSDHCI *s, uint32_t reg, uint64_t val);
416*ff41da50SThomas Huth        /* other fields */
417*ff41da50SThomas Huth    };
418*ff41da50SThomas Huth
419*ff41da50SThomas Huth    /* Memory Mapped implementation of QSDHCI */
420*ff41da50SThomas Huth    struct QSDHCI_MemoryMapped {
421*ff41da50SThomas Huth        QOSGraphObject obj;
422*ff41da50SThomas Huth        QSDHCI sdhci;
423*ff41da50SThomas Huth        /* other driver-specific fields */
424*ff41da50SThomas Huth    };
425*ff41da50SThomas Huth
426*ff41da50SThomas Huth    /* PCI implementation of QSDHCI */
427*ff41da50SThomas Huth    struct QSDHCI_PCI {
428*ff41da50SThomas Huth        QOSGraphObject obj;
429*ff41da50SThomas Huth        QSDHCI sdhci;
430*ff41da50SThomas Huth        /* other driver-specific fields */
431*ff41da50SThomas Huth    };
432*ff41da50SThomas Huth
433*ff41da50SThomas Huth    /* Memory mapped implementation of QSDHCI */
434*ff41da50SThomas Huth
435*ff41da50SThomas Huth    static void *sdhci_mm_get_driver(void *obj, const char *interface)
436*ff41da50SThomas Huth    {
437*ff41da50SThomas Huth        QSDHCI_MemoryMapped *smm = obj;
438*ff41da50SThomas Huth        if (!g_strcmp0(interface, "sdhci")) {
439*ff41da50SThomas Huth            return &smm->sdhci;
440*ff41da50SThomas Huth        }
441*ff41da50SThomas Huth        fprintf(stderr, "%s not present in generic-sdhci\n", interface);
442*ff41da50SThomas Huth        g_assert_not_reached();
443*ff41da50SThomas Huth    }
444*ff41da50SThomas Huth
445*ff41da50SThomas Huth    void qos_init_sdhci_mm(QSDHCI_MemoryMapped *sdhci, QTestState *qts,
446*ff41da50SThomas Huth                        uint32_t addr, QSDHCIProperties *common)
447*ff41da50SThomas Huth    {
448*ff41da50SThomas Huth        /* Get node contained inside (CONTAINS) */
449*ff41da50SThomas Huth        sdhci->obj.get_driver = sdhci_mm_get_driver;
450*ff41da50SThomas Huth
451*ff41da50SThomas Huth        /* SDHCI interface API */
452*ff41da50SThomas Huth        sdhci->sdhci.readw = sdhci_mm_readw;
453*ff41da50SThomas Huth        sdhci->sdhci.readq = sdhci_mm_readq;
454*ff41da50SThomas Huth        sdhci->sdhci.writeq = sdhci_mm_writeq;
455*ff41da50SThomas Huth        sdhci->qts = qts;
456*ff41da50SThomas Huth    }
457*ff41da50SThomas Huth
458*ff41da50SThomas Huth    /* PCI implementation of QSDHCI */
459*ff41da50SThomas Huth
460*ff41da50SThomas Huth    static void *sdhci_pci_get_driver(void *object,
461*ff41da50SThomas Huth                                      const char *interface)
462*ff41da50SThomas Huth    {
463*ff41da50SThomas Huth        QSDHCI_PCI *spci = object;
464*ff41da50SThomas Huth        if (!g_strcmp0(interface, "sdhci")) {
465*ff41da50SThomas Huth            return &spci->sdhci;
466*ff41da50SThomas Huth        }
467*ff41da50SThomas Huth
468*ff41da50SThomas Huth        fprintf(stderr, "%s not present in sdhci-pci\n", interface);
469*ff41da50SThomas Huth        g_assert_not_reached();
470*ff41da50SThomas Huth    }
471*ff41da50SThomas Huth
472*ff41da50SThomas Huth    static void *sdhci_pci_create(void *pci_bus,
473*ff41da50SThomas Huth                                  QGuestAllocator *alloc,
474*ff41da50SThomas Huth                                  void *addr)
475*ff41da50SThomas Huth    {
476*ff41da50SThomas Huth        QSDHCI_PCI *spci = g_new0(QSDHCI_PCI, 1);
477*ff41da50SThomas Huth        QPCIBus *bus = pci_bus;
478*ff41da50SThomas Huth        uint64_t barsize;
479*ff41da50SThomas Huth
480*ff41da50SThomas Huth        qpci_device_init(&spci->dev, bus, addr);
481*ff41da50SThomas Huth
482*ff41da50SThomas Huth        /* SDHCI interface API */
483*ff41da50SThomas Huth        spci->sdhci.readw = sdhci_pci_readw;
484*ff41da50SThomas Huth        spci->sdhci.readq = sdhci_pci_readq;
485*ff41da50SThomas Huth        spci->sdhci.writeq = sdhci_pci_writeq;
486*ff41da50SThomas Huth
487*ff41da50SThomas Huth        /* Get node(s) produced (PRODUCES) */
488*ff41da50SThomas Huth        spci->obj.get_driver = sdhci_pci_get_driver;
489*ff41da50SThomas Huth
490*ff41da50SThomas Huth        spci->obj.start_hw = sdhci_pci_start_hw;
491*ff41da50SThomas Huth        spci->obj.destructor = sdhci_destructor;
492*ff41da50SThomas Huth        return &spci->obj;
493*ff41da50SThomas Huth    }
494*ff41da50SThomas Huth
495*ff41da50SThomas Huth    static void qsdhci_register_nodes(void)
496*ff41da50SThomas Huth    {
497*ff41da50SThomas Huth        QOSGraphEdgeOptions opts = {
498*ff41da50SThomas Huth            .extra_device_opts = "addr=04.0",
499*ff41da50SThomas Huth        };
500*ff41da50SThomas Huth
501*ff41da50SThomas Huth        /* generic-sdhci */
502*ff41da50SThomas Huth        /* generic-sdhci --produces--> sdhci */
503*ff41da50SThomas Huth        qos_node_create_driver("generic-sdhci", NULL);
504*ff41da50SThomas Huth        qos_node_produces("generic-sdhci", "sdhci");
505*ff41da50SThomas Huth
506*ff41da50SThomas Huth        /* sdhci-pci */
507*ff41da50SThomas Huth        /* sdhci-pci --produces--> sdhci
508*ff41da50SThomas Huth         * sdhci-pci --consumes--> pci-bus */
509*ff41da50SThomas Huth        qos_node_create_driver("sdhci-pci", sdhci_pci_create);
510*ff41da50SThomas Huth        qos_node_produces("sdhci-pci", "sdhci");
511*ff41da50SThomas Huth        qos_node_consumes("sdhci-pci", "pci-bus", &opts);
512*ff41da50SThomas Huth    }
513*ff41da50SThomas Huth
514*ff41da50SThomas Huth    libqos_init(qsdhci_register_nodes);
515*ff41da50SThomas Huth
516*ff41da50SThomas HuthIn the above example, all possible types of relations are created::
517*ff41da50SThomas Huth
518*ff41da50SThomas Huth  x86_64/pc --contains--> 1440FX-pcihost --contains--> pci-bus-pc
519*ff41da50SThomas Huth                                                            |
520*ff41da50SThomas Huth               sdhci-pci --consumes--> pci-bus <--produces--+
521*ff41da50SThomas Huth                  |
522*ff41da50SThomas Huth                  +--produces--+
523*ff41da50SThomas Huth                               |
524*ff41da50SThomas Huth                               v
525*ff41da50SThomas Huth                             sdhci
526*ff41da50SThomas Huth                               ^
527*ff41da50SThomas Huth                               |
528*ff41da50SThomas Huth                               +--produces-- +
529*ff41da50SThomas Huth                                             |
530*ff41da50SThomas Huth               arm/raspi2b --contains--> generic-sdhci
531*ff41da50SThomas Huth
532*ff41da50SThomas Huthor inverting the consumes edge in consumed_by::
533*ff41da50SThomas Huth
534*ff41da50SThomas Huth  x86_64/pc --contains--> 1440FX-pcihost --contains--> pci-bus-pc
535*ff41da50SThomas Huth                                                            |
536*ff41da50SThomas Huth            sdhci-pci <--consumed by-- pci-bus <--produces--+
537*ff41da50SThomas Huth                |
538*ff41da50SThomas Huth                +--produces--+
539*ff41da50SThomas Huth                             |
540*ff41da50SThomas Huth                             v
541*ff41da50SThomas Huth                            sdhci
542*ff41da50SThomas Huth                             ^
543*ff41da50SThomas Huth                             |
544*ff41da50SThomas Huth                             +--produces-- +
545*ff41da50SThomas Huth                                           |
546*ff41da50SThomas Huth            arm/raspi2b --contains--> generic-sdhci
547*ff41da50SThomas Huth
548*ff41da50SThomas HuthAdding a new test
549*ff41da50SThomas Huth-----------------
550*ff41da50SThomas Huth
551*ff41da50SThomas HuthGiven the above setup, adding a new test is very simple.
552*ff41da50SThomas Huth``sdhci-test``, taken from ``tests/qtest/sdhci-test.c``::
553*ff41da50SThomas Huth
554*ff41da50SThomas Huth    static void check_capab_sdma(QSDHCI *s, bool supported)
555*ff41da50SThomas Huth    {
556*ff41da50SThomas Huth        uint64_t capab, capab_sdma;
557*ff41da50SThomas Huth
558*ff41da50SThomas Huth        capab = s->readq(s, SDHC_CAPAB);
559*ff41da50SThomas Huth        capab_sdma = FIELD_EX64(capab, SDHC_CAPAB, SDMA);
560*ff41da50SThomas Huth        g_assert_cmpuint(capab_sdma, ==, supported);
561*ff41da50SThomas Huth    }
562*ff41da50SThomas Huth
563*ff41da50SThomas Huth    static void test_registers(void *obj, void *data,
564*ff41da50SThomas Huth                                QGuestAllocator *alloc)
565*ff41da50SThomas Huth    {
566*ff41da50SThomas Huth        QSDHCI *s = obj;
567*ff41da50SThomas Huth
568*ff41da50SThomas Huth        /* example test */
569*ff41da50SThomas Huth        check_capab_sdma(s, s->props.capab.sdma);
570*ff41da50SThomas Huth    }
571*ff41da50SThomas Huth
572*ff41da50SThomas Huth    static void register_sdhci_test(void)
573*ff41da50SThomas Huth    {
574*ff41da50SThomas Huth        /* sdhci-test --consumes--> sdhci */
575*ff41da50SThomas Huth        qos_add_test("registers", "sdhci", test_registers, NULL);
576*ff41da50SThomas Huth    }
577*ff41da50SThomas Huth
578*ff41da50SThomas Huth    libqos_init(register_sdhci_test);
579*ff41da50SThomas Huth
580*ff41da50SThomas HuthHere a new test is created, consuming ``sdhci`` interface node
581*ff41da50SThomas Huthand creating a valid path from both machines to a test.
582*ff41da50SThomas HuthFinal graph will be like this::
583*ff41da50SThomas Huth
584*ff41da50SThomas Huth  x86_64/pc --contains--> 1440FX-pcihost --contains--> pci-bus-pc
585*ff41da50SThomas Huth                                                            |
586*ff41da50SThomas Huth               sdhci-pci --consumes--> pci-bus <--produces--+
587*ff41da50SThomas Huth                  |
588*ff41da50SThomas Huth                  +--produces--+
589*ff41da50SThomas Huth                               |
590*ff41da50SThomas Huth                               v
591*ff41da50SThomas Huth                             sdhci <--consumes-- sdhci-test
592*ff41da50SThomas Huth                               ^
593*ff41da50SThomas Huth                               |
594*ff41da50SThomas Huth                               +--produces-- +
595*ff41da50SThomas Huth                                             |
596*ff41da50SThomas Huth               arm/raspi2b --contains--> generic-sdhci
597*ff41da50SThomas Huth
598*ff41da50SThomas Huthor inverting the consumes edge in consumed_by::
599*ff41da50SThomas Huth
600*ff41da50SThomas Huth  x86_64/pc --contains--> 1440FX-pcihost --contains--> pci-bus-pc
601*ff41da50SThomas Huth                                                            |
602*ff41da50SThomas Huth            sdhci-pci <--consumed by-- pci-bus <--produces--+
603*ff41da50SThomas Huth                |
604*ff41da50SThomas Huth                +--produces--+
605*ff41da50SThomas Huth                             |
606*ff41da50SThomas Huth                             v
607*ff41da50SThomas Huth                            sdhci --consumed by--> sdhci-test
608*ff41da50SThomas Huth                             ^
609*ff41da50SThomas Huth                             |
610*ff41da50SThomas Huth                             +--produces-- +
611*ff41da50SThomas Huth                                           |
612*ff41da50SThomas Huth            arm/raspi2b --contains--> generic-sdhci
613*ff41da50SThomas Huth
614*ff41da50SThomas HuthAssuming there the binary is
615*ff41da50SThomas Huth``QTEST_QEMU_BINARY=./qemu-system-x86_64``
616*ff41da50SThomas Hutha valid test path will be:
617*ff41da50SThomas Huth``/x86_64/pc/1440FX-pcihost/pci-bus-pc/pci-bus/sdhci-pc/sdhci/sdhci-test``
618*ff41da50SThomas Huth
619*ff41da50SThomas Huthand for the binary ``QTEST_QEMU_BINARY=./qemu-system-arm``:
620*ff41da50SThomas Huth
621*ff41da50SThomas Huth``/arm/raspi2b/generic-sdhci/sdhci/sdhci-test``
622*ff41da50SThomas Huth
623*ff41da50SThomas HuthAdditional examples are also in ``test-qgraph.c``
624*ff41da50SThomas Huth
625*ff41da50SThomas HuthQgraph API reference
626*ff41da50SThomas Huth--------------------
627*ff41da50SThomas Huth
628*ff41da50SThomas Huth.. kernel-doc:: tests/qtest/libqos/qgraph.h
629