/* * QEMU "pci-testdev" PCI test device * * Copyright (C) 2016, Red Hat Inc, Alexander Gordeev * * This work is licensed under the terms of the GNU LGPL, version 2. */ #include "pci.h" #include "asm/io.h" struct pci_testdev_ops { u8 (*io_readb)(const volatile void *addr); u16 (*io_readw)(const volatile void *addr); u32 (*io_readl)(const volatile void *addr); void (*io_writeb)(u8 value, volatile void *addr); void (*io_writew)(u16 value, volatile void *addr); void (*io_writel)(u32 value, volatile void *addr); }; static u8 pio_readb(const volatile void *addr) { return inb((unsigned long)addr); } static u16 pio_readw(const volatile void *addr) { return inw((unsigned long)addr); } static u32 pio_readl(const volatile void *addr) { return inl((unsigned long)addr); } static void pio_writeb(u8 value, volatile void *addr) { outb(value, (unsigned long)addr); } static void pio_writew(u16 value, volatile void *addr) { outw(value, (unsigned long)addr); } static void pio_writel(u32 value, volatile void *addr) { outl(value, (unsigned long)addr); } static struct pci_testdev_ops pci_testdev_io_ops = { .io_readb = pio_readb, .io_readw = pio_readw, .io_readl = pio_readl, .io_writeb = pio_writeb, .io_writew = pio_writew, .io_writel = pio_writel }; static u8 mmio_readb(const volatile void *addr) { return *(const volatile u8 __force *)addr; } static u16 mmio_readw(const volatile void *addr) { return *(const volatile u16 __force *)addr; } static u32 mmio_readl(const volatile void *addr) { return *(const volatile u32 __force *)addr; } static void mmio_writeb(u8 value, volatile void *addr) { *(volatile u8 __force *)addr = value; } static void mmio_writew(u16 value, volatile void *addr) { *(volatile u16 __force *)addr = value; } static void mmio_writel(u32 value, volatile void *addr) { *(volatile u32 __force *)addr = value; } static struct pci_testdev_ops pci_testdev_mem_ops = { .io_readb = mmio_readb, .io_readw = mmio_readw, .io_readl = mmio_readl, .io_writeb = mmio_writeb, .io_writew = mmio_writew, .io_writel = mmio_writel }; static bool pci_testdev_one(struct pci_test_dev_hdr *test, int test_nr, struct pci_testdev_ops *ops) { u8 width; u32 count, sig, off; const int nr_writes = 16; int i; ops->io_writeb(test_nr, &test->test); count = ops->io_readl(&test->count); if (count != 0) return false; width = ops->io_readb(&test->width); if (width != 1 && width != 2 && width != 4) return false; sig = ops->io_readl(&test->data); off = ops->io_readl(&test->offset); for (i = 0; i < nr_writes; i++) { switch (width) { case 1: ops->io_writeb(sig, (void *)test + off); break; case 2: ops->io_writew(sig, (void *)test + off); break; case 4: ops->io_writel(sig, (void *)test + off); break; } } count = ops->io_readl(&test->count); if (!count) return true; return (int)count == nr_writes; } static void pci_testdev_print(struct pci_test_dev_hdr *test, struct pci_testdev_ops *ops) { bool io = (ops == &pci_testdev_io_ops); int i; printf("pci-testdev %3s: ", io ? "io" : "mem"); for (i = 0;; ++i) { char c = ops->io_readb(&test->name[i]); if (!c) break; printf("%c", c); } printf("\n"); } static int pci_testdev_all(struct pci_test_dev_hdr *test, struct pci_testdev_ops *ops) { int i; for (i = 0;; i++) { if (!pci_testdev_one(test, i, ops)) break; pci_testdev_print(test, ops); } return i; } int pci_testdev(void) { struct pci_dev pci_dev; pcidevaddr_t dev; phys_addr_t addr; void __iomem *mem, *io; int nr_tests = 0; bool ret; dev = pci_find_dev(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_TEST); if (dev == PCIDEVADDR_INVALID) { printf("'pci-testdev' device is not found, " "check QEMU '-device pci-testdev' parameter\n"); return -1; } pci_dev_init(&pci_dev, dev); ret = pci_bar_is_valid(&pci_dev, 0) && pci_bar_is_valid(&pci_dev, 1); assert(ret); addr = pci_bar_get_addr(&pci_dev, 0); mem = ioremap(addr, PAGE_SIZE); addr = pci_bar_get_addr(&pci_dev, 1); io = (void *)(unsigned long)addr; nr_tests += pci_testdev_all(mem, &pci_testdev_mem_ops); nr_tests += pci_testdev_all(io, &pci_testdev_io_ops); return nr_tests; }