104f71324SAlexander Bulekov /*
204f71324SAlexander Bulekov * I440FX Fuzzing Target
304f71324SAlexander Bulekov *
404f71324SAlexander Bulekov * Copyright Red Hat Inc., 2019
504f71324SAlexander Bulekov *
604f71324SAlexander Bulekov * Authors:
704f71324SAlexander Bulekov * Alexander Bulekov <alxndr@bu.edu>
804f71324SAlexander Bulekov *
904f71324SAlexander Bulekov * This work is licensed under the terms of the GNU GPL, version 2 or later.
1004f71324SAlexander Bulekov * See the COPYING file in the top-level directory.
1104f71324SAlexander Bulekov */
1204f71324SAlexander Bulekov
1304f71324SAlexander Bulekov #include "qemu/osdep.h"
1404f71324SAlexander Bulekov
1504f71324SAlexander Bulekov #include "qemu/main-loop.h"
16907b5105SMarc-André Lureau #include "tests/qtest/libqtest.h"
1704f71324SAlexander Bulekov #include "tests/qtest/libqos/pci.h"
1804f71324SAlexander Bulekov #include "tests/qtest/libqos/pci-pc.h"
1904f71324SAlexander Bulekov #include "fuzz.h"
2064ed6f92SPaolo Bonzini #include "qos_fuzz.h"
2104f71324SAlexander Bulekov
2204f71324SAlexander Bulekov
2304f71324SAlexander Bulekov #define I440FX_PCI_HOST_BRIDGE_CFG 0xcf8
2404f71324SAlexander Bulekov #define I440FX_PCI_HOST_BRIDGE_DATA 0xcfc
2504f71324SAlexander Bulekov
2604f71324SAlexander Bulekov /*
2704f71324SAlexander Bulekov * the input to the fuzzing functions below is a buffer of random bytes. we
2804f71324SAlexander Bulekov * want to convert these bytes into a sequence of qtest or qos calls. to do
2904f71324SAlexander Bulekov * this we define some opcodes:
3004f71324SAlexander Bulekov */
3104f71324SAlexander Bulekov enum action_id {
3204f71324SAlexander Bulekov WRITEB,
3304f71324SAlexander Bulekov WRITEW,
3404f71324SAlexander Bulekov WRITEL,
3504f71324SAlexander Bulekov READB,
3604f71324SAlexander Bulekov READW,
3704f71324SAlexander Bulekov READL,
3804f71324SAlexander Bulekov ACTION_MAX
3904f71324SAlexander Bulekov };
4004f71324SAlexander Bulekov
ioport_fuzz_qtest(QTestState * s,const unsigned char * Data,size_t Size)416fb5f084SPhilippe Mathieu-Daudé static void ioport_fuzz_qtest(QTestState *s,
4204f71324SAlexander Bulekov const unsigned char *Data, size_t Size) {
4304f71324SAlexander Bulekov /*
4404f71324SAlexander Bulekov * loop over the Data, breaking it up into actions. each action has an
4504f71324SAlexander Bulekov * opcode, address offset and value
4604f71324SAlexander Bulekov */
4779e18a60SPhilippe Mathieu-Daudé struct {
4804f71324SAlexander Bulekov uint8_t opcode;
4904f71324SAlexander Bulekov uint8_t addr;
5004f71324SAlexander Bulekov uint32_t value;
5179e18a60SPhilippe Mathieu-Daudé } a;
5204f71324SAlexander Bulekov
5304f71324SAlexander Bulekov while (Size >= sizeof(a)) {
5404f71324SAlexander Bulekov /* make a copy of the action so we can normalize the values in-place */
5504f71324SAlexander Bulekov memcpy(&a, Data, sizeof(a));
5604f71324SAlexander Bulekov /* select between two i440fx Port IO addresses */
5704f71324SAlexander Bulekov uint16_t addr = a.addr % 2 ? I440FX_PCI_HOST_BRIDGE_CFG :
5804f71324SAlexander Bulekov I440FX_PCI_HOST_BRIDGE_DATA;
5904f71324SAlexander Bulekov switch (a.opcode % ACTION_MAX) {
6004f71324SAlexander Bulekov case WRITEB:
6104f71324SAlexander Bulekov qtest_outb(s, addr, (uint8_t)a.value);
6204f71324SAlexander Bulekov break;
6304f71324SAlexander Bulekov case WRITEW:
6404f71324SAlexander Bulekov qtest_outw(s, addr, (uint16_t)a.value);
6504f71324SAlexander Bulekov break;
6604f71324SAlexander Bulekov case WRITEL:
6704f71324SAlexander Bulekov qtest_outl(s, addr, (uint32_t)a.value);
6804f71324SAlexander Bulekov break;
6904f71324SAlexander Bulekov case READB:
7004f71324SAlexander Bulekov qtest_inb(s, addr);
7104f71324SAlexander Bulekov break;
7204f71324SAlexander Bulekov case READW:
7304f71324SAlexander Bulekov qtest_inw(s, addr);
7404f71324SAlexander Bulekov break;
7504f71324SAlexander Bulekov case READL:
7604f71324SAlexander Bulekov qtest_inl(s, addr);
7704f71324SAlexander Bulekov break;
7804f71324SAlexander Bulekov }
7904f71324SAlexander Bulekov /* Move to the next operation */
8004f71324SAlexander Bulekov Size -= sizeof(a);
8104f71324SAlexander Bulekov Data += sizeof(a);
8204f71324SAlexander Bulekov }
8304f71324SAlexander Bulekov flush_events(s);
8404f71324SAlexander Bulekov }
8504f71324SAlexander Bulekov
i440fx_fuzz_qtest(QTestState * s,const unsigned char * Data,size_t Size)866fb5f084SPhilippe Mathieu-Daudé static void i440fx_fuzz_qtest(QTestState *s,
876fb5f084SPhilippe Mathieu-Daudé const unsigned char *Data,
886fb5f084SPhilippe Mathieu-Daudé size_t Size)
896fb5f084SPhilippe Mathieu-Daudé {
906fb5f084SPhilippe Mathieu-Daudé ioport_fuzz_qtest(s, Data, Size);
91*f031c959SAlexander Bulekov fuzz_reset(s);
926fb5f084SPhilippe Mathieu-Daudé }
936fb5f084SPhilippe Mathieu-Daudé
pciconfig_fuzz_qos(QTestState * s,QPCIBus * bus,const unsigned char * Data,size_t Size)9484cb0a6dSPhilippe Mathieu-Daudé static void pciconfig_fuzz_qos(QTestState *s, QPCIBus *bus,
9504f71324SAlexander Bulekov const unsigned char *Data, size_t Size) {
9604f71324SAlexander Bulekov /*
976fb5f084SPhilippe Mathieu-Daudé * Same as ioport_fuzz_qtest, but using QOS. devfn is incorporated into the
9804f71324SAlexander Bulekov * value written over Port IO
9904f71324SAlexander Bulekov */
10079e18a60SPhilippe Mathieu-Daudé struct {
10104f71324SAlexander Bulekov uint8_t opcode;
10204f71324SAlexander Bulekov uint8_t offset;
10304f71324SAlexander Bulekov int devfn;
10404f71324SAlexander Bulekov uint32_t value;
10579e18a60SPhilippe Mathieu-Daudé } a;
10604f71324SAlexander Bulekov
10704f71324SAlexander Bulekov while (Size >= sizeof(a)) {
10804f71324SAlexander Bulekov memcpy(&a, Data, sizeof(a));
10904f71324SAlexander Bulekov switch (a.opcode % ACTION_MAX) {
11004f71324SAlexander Bulekov case WRITEB:
11104f71324SAlexander Bulekov bus->config_writeb(bus, a.devfn, a.offset, (uint8_t)a.value);
11204f71324SAlexander Bulekov break;
11304f71324SAlexander Bulekov case WRITEW:
11404f71324SAlexander Bulekov bus->config_writew(bus, a.devfn, a.offset, (uint16_t)a.value);
11504f71324SAlexander Bulekov break;
11604f71324SAlexander Bulekov case WRITEL:
11704f71324SAlexander Bulekov bus->config_writel(bus, a.devfn, a.offset, (uint32_t)a.value);
11804f71324SAlexander Bulekov break;
11904f71324SAlexander Bulekov case READB:
12004f71324SAlexander Bulekov bus->config_readb(bus, a.devfn, a.offset);
12104f71324SAlexander Bulekov break;
12204f71324SAlexander Bulekov case READW:
12304f71324SAlexander Bulekov bus->config_readw(bus, a.devfn, a.offset);
12404f71324SAlexander Bulekov break;
12504f71324SAlexander Bulekov case READL:
12604f71324SAlexander Bulekov bus->config_readl(bus, a.devfn, a.offset);
12704f71324SAlexander Bulekov break;
12804f71324SAlexander Bulekov }
12904f71324SAlexander Bulekov Size -= sizeof(a);
13004f71324SAlexander Bulekov Data += sizeof(a);
13104f71324SAlexander Bulekov }
13204f71324SAlexander Bulekov flush_events(s);
13304f71324SAlexander Bulekov }
13404f71324SAlexander Bulekov
i440fx_fuzz_qos(QTestState * s,const unsigned char * Data,size_t Size)13584cb0a6dSPhilippe Mathieu-Daudé static void i440fx_fuzz_qos(QTestState *s,
13684cb0a6dSPhilippe Mathieu-Daudé const unsigned char *Data,
13784cb0a6dSPhilippe Mathieu-Daudé size_t Size)
13884cb0a6dSPhilippe Mathieu-Daudé {
13984cb0a6dSPhilippe Mathieu-Daudé static QPCIBus *bus;
14084cb0a6dSPhilippe Mathieu-Daudé
14184cb0a6dSPhilippe Mathieu-Daudé if (!bus) {
14284cb0a6dSPhilippe Mathieu-Daudé bus = qpci_new_pc(s, fuzz_qos_alloc);
14384cb0a6dSPhilippe Mathieu-Daudé }
14484cb0a6dSPhilippe Mathieu-Daudé
14584cb0a6dSPhilippe Mathieu-Daudé pciconfig_fuzz_qos(s, bus, Data, Size);
14684cb0a6dSPhilippe Mathieu-Daudé }
14784cb0a6dSPhilippe Mathieu-Daudé
14804f71324SAlexander Bulekov static const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest"
14904f71324SAlexander Bulekov " -m 0 -display none";
i440fx_argv(FuzzTarget * t)150f5ec79f5SAlexander Bulekov static GString *i440fx_argv(FuzzTarget *t)
15104f71324SAlexander Bulekov {
152f5ec79f5SAlexander Bulekov return g_string_new(i440fx_qtest_argv);
15304f71324SAlexander Bulekov }
15404f71324SAlexander Bulekov
15504f71324SAlexander Bulekov
register_pci_fuzz_targets(void)15604f71324SAlexander Bulekov static void register_pci_fuzz_targets(void)
15704f71324SAlexander Bulekov {
15804f71324SAlexander Bulekov /* Uses simple qtest commands and reboots to reset state */
15904f71324SAlexander Bulekov fuzz_add_target(&(FuzzTarget){
16004f71324SAlexander Bulekov .name = "i440fx-qtest-reboot-fuzz",
16104f71324SAlexander Bulekov .description = "Fuzz the i440fx using raw qtest commands and "
16204f71324SAlexander Bulekov "rebooting after each run",
16304f71324SAlexander Bulekov .get_init_cmdline = i440fx_argv,
16404f71324SAlexander Bulekov .fuzz = i440fx_fuzz_qtest});
16504f71324SAlexander Bulekov
16604f71324SAlexander Bulekov
16704f71324SAlexander Bulekov /*
16804f71324SAlexander Bulekov * Uses libqos. Doesn't do anything to reset state. Note that if we were to
16904f71324SAlexander Bulekov * reboot after each run, we would also have to redo the qos-related
17004f71324SAlexander Bulekov * initialization (qos_init_path)
17104f71324SAlexander Bulekov */
17204f71324SAlexander Bulekov fuzz_add_qos_target(&(FuzzTarget){
17304f71324SAlexander Bulekov .name = "i440fx-qos-noreset-fuzz",
17404f71324SAlexander Bulekov .description = "Fuzz the i440fx using raw qtest commands and "
17504f71324SAlexander Bulekov "rebooting after each run",
17604f71324SAlexander Bulekov .fuzz = i440fx_fuzz_qos,},
17704f71324SAlexander Bulekov "i440FX-pcihost",
17804f71324SAlexander Bulekov &(QOSGraphTestOptions){}
17904f71324SAlexander Bulekov );
18004f71324SAlexander Bulekov }
18104f71324SAlexander Bulekov
18204f71324SAlexander Bulekov fuzz_target_init(register_pci_fuzz_targets);
183