blob: 039bb44781c1b446cf572f84e671cad92464d764 [file] [log] [blame] [edit]
/*
* 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.
*/
#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;
}