Spaces:
Runtime error
Runtime error
| /* | |
| * QEMU "pci-testdev" PCI test device | |
| * | |
| * Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <agordeev@redhat.com> | |
| * | |
| * This work is licensed under the terms of the GNU LGPL, version 2. | |
| */ | |
| 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; | |
| } | |