xref: /openbmc/qemu/tests/qtest/fuzz/i440fx_fuzz.c (revision 2df1eb27)
1 /*
2  * I440FX Fuzzing Target
3  *
4  * Copyright Red Hat Inc., 2019
5  *
6  * Authors:
7  *  Alexander Bulekov   <alxndr@bu.edu>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10  * See the COPYING file in the top-level directory.
11  */
12 
13 #include "qemu/osdep.h"
14 
15 #include "qemu/main-loop.h"
16 #include "tests/qtest/libqtest.h"
17 #include "tests/qtest/libqos/pci.h"
18 #include "tests/qtest/libqos/pci-pc.h"
19 #include "fuzz.h"
20 #include "qos_fuzz.h"
21 
22 
23 #define I440FX_PCI_HOST_BRIDGE_CFG 0xcf8
24 #define I440FX_PCI_HOST_BRIDGE_DATA 0xcfc
25 
26 /*
27  * the input to the fuzzing functions below is a buffer of random bytes. we
28  * want to convert these bytes into a sequence of qtest or qos calls. to do
29  * this we define some opcodes:
30  */
31 enum action_id {
32     WRITEB,
33     WRITEW,
34     WRITEL,
35     READB,
36     READW,
37     READL,
38     ACTION_MAX
39 };
40 
41 static void ioport_fuzz_qtest(QTestState *s,
42         const unsigned char *Data, size_t Size) {
43     /*
44      * loop over the Data, breaking it up into actions. each action has an
45      * opcode, address offset and value
46      */
47     struct {
48         uint8_t opcode;
49         uint8_t addr;
50         uint32_t value;
51     } a;
52 
53     while (Size >= sizeof(a)) {
54         /* make a copy of the action so we can normalize the values in-place */
55         memcpy(&a, Data, sizeof(a));
56         /* select between two i440fx Port IO addresses */
57         uint16_t addr = a.addr % 2 ? I440FX_PCI_HOST_BRIDGE_CFG :
58                                       I440FX_PCI_HOST_BRIDGE_DATA;
59         switch (a.opcode % ACTION_MAX) {
60         case WRITEB:
61             qtest_outb(s, addr, (uint8_t)a.value);
62             break;
63         case WRITEW:
64             qtest_outw(s, addr, (uint16_t)a.value);
65             break;
66         case WRITEL:
67             qtest_outl(s, addr, (uint32_t)a.value);
68             break;
69         case READB:
70             qtest_inb(s, addr);
71             break;
72         case READW:
73             qtest_inw(s, addr);
74             break;
75         case READL:
76             qtest_inl(s, addr);
77             break;
78         }
79         /* Move to the next operation */
80         Size -= sizeof(a);
81         Data += sizeof(a);
82     }
83     flush_events(s);
84 }
85 
86 static void i440fx_fuzz_qtest(QTestState *s,
87                               const unsigned char *Data,
88                               size_t Size)
89 {
90     ioport_fuzz_qtest(s, Data, Size);
91     fuzz_reset(s);
92 }
93 
94 static void pciconfig_fuzz_qos(QTestState *s, QPCIBus *bus,
95         const unsigned char *Data, size_t Size) {
96     /*
97      * Same as ioport_fuzz_qtest, but using QOS. devfn is incorporated into the
98      * value written over Port IO
99      */
100     struct {
101         uint8_t opcode;
102         uint8_t offset;
103         int devfn;
104         uint32_t value;
105     } a;
106 
107     while (Size >= sizeof(a)) {
108         memcpy(&a, Data, sizeof(a));
109         switch (a.opcode % ACTION_MAX) {
110         case WRITEB:
111             bus->config_writeb(bus, a.devfn, a.offset, (uint8_t)a.value);
112             break;
113         case WRITEW:
114             bus->config_writew(bus, a.devfn, a.offset, (uint16_t)a.value);
115             break;
116         case WRITEL:
117             bus->config_writel(bus, a.devfn, a.offset, (uint32_t)a.value);
118             break;
119         case READB:
120             bus->config_readb(bus, a.devfn, a.offset);
121             break;
122         case READW:
123             bus->config_readw(bus, a.devfn, a.offset);
124             break;
125         case READL:
126             bus->config_readl(bus, a.devfn, a.offset);
127             break;
128         }
129         Size -= sizeof(a);
130         Data += sizeof(a);
131     }
132     flush_events(s);
133 }
134 
135 static void i440fx_fuzz_qos(QTestState *s,
136                             const unsigned char *Data,
137                             size_t Size)
138 {
139     static QPCIBus *bus;
140 
141     if (!bus) {
142         bus = qpci_new_pc(s, fuzz_qos_alloc);
143     }
144 
145     pciconfig_fuzz_qos(s, bus, Data, Size);
146 }
147 
148 static const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest"
149                                        " -m 0 -display none";
150 static GString *i440fx_argv(FuzzTarget *t)
151 {
152     return g_string_new(i440fx_qtest_argv);
153 }
154 
155 
156 static void register_pci_fuzz_targets(void)
157 {
158     /* Uses simple qtest commands and reboots to reset state */
159     fuzz_add_target(&(FuzzTarget){
160                 .name = "i440fx-qtest-reboot-fuzz",
161                 .description = "Fuzz the i440fx using raw qtest commands and "
162                                "rebooting after each run",
163                 .get_init_cmdline = i440fx_argv,
164                 .fuzz = i440fx_fuzz_qtest});
165 
166 
167     /*
168      * Uses libqos. Doesn't do anything to reset state. Note that if we were to
169      * reboot after each run, we would also have to redo the qos-related
170      * initialization (qos_init_path)
171      */
172     fuzz_add_qos_target(&(FuzzTarget){
173                 .name = "i440fx-qos-noreset-fuzz",
174                 .description = "Fuzz the i440fx using raw qtest commands and "
175                                "rebooting after each run",
176                 .fuzz = i440fx_fuzz_qos,},
177                 "i440FX-pcihost",
178                 &(QOSGraphTestOptions){}
179                 );
180 }
181 
182 fuzz_target_init(register_pci_fuzz_targets);
183