xref: /kvm-unit-tests/lib/pci-testdev.c (revision 74ff0e9675ec6d9477f5e98ec7d5d50878fa7ebc)
1e4125c0cSAlexander Gordeev /*
2e4125c0cSAlexander Gordeev  * QEMU "pci-testdev" PCI test device
3e4125c0cSAlexander Gordeev  *
4e4125c0cSAlexander Gordeev  * Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <agordeev@redhat.com>
5e4125c0cSAlexander Gordeev  *
6e4125c0cSAlexander Gordeev  * This work is licensed under the terms of the GNU LGPL, version 2.
7e4125c0cSAlexander Gordeev  */
8e4125c0cSAlexander Gordeev #include "pci.h"
9e4125c0cSAlexander Gordeev #include "asm/io.h"
10e4125c0cSAlexander Gordeev 
11e4125c0cSAlexander Gordeev struct pci_testdev_ops {
12e4125c0cSAlexander Gordeev 	u8 (*io_readb)(const volatile void *addr);
13e4125c0cSAlexander Gordeev 	u16 (*io_readw)(const volatile void *addr);
14e4125c0cSAlexander Gordeev 	u32 (*io_readl)(const volatile void *addr);
15e4125c0cSAlexander Gordeev 	void (*io_writeb)(u8 value, volatile void *addr);
16e4125c0cSAlexander Gordeev 	void (*io_writew)(u16 value, volatile void *addr);
17e4125c0cSAlexander Gordeev 	void (*io_writel)(u32 value, volatile void *addr);
18e4125c0cSAlexander Gordeev };
19e4125c0cSAlexander Gordeev 
pio_readb(const volatile void * addr)20e4125c0cSAlexander Gordeev static u8 pio_readb(const volatile void *addr)
21e4125c0cSAlexander Gordeev {
22e4125c0cSAlexander Gordeev 	return inb((unsigned long)addr);
23e4125c0cSAlexander Gordeev }
24e4125c0cSAlexander Gordeev 
pio_readw(const volatile void * addr)25e4125c0cSAlexander Gordeev static u16 pio_readw(const volatile void *addr)
26e4125c0cSAlexander Gordeev {
27e4125c0cSAlexander Gordeev 	return inw((unsigned long)addr);
28e4125c0cSAlexander Gordeev }
29e4125c0cSAlexander Gordeev 
pio_readl(const volatile void * addr)30e4125c0cSAlexander Gordeev static u32 pio_readl(const volatile void *addr)
31e4125c0cSAlexander Gordeev {
32e4125c0cSAlexander Gordeev 	return inl((unsigned long)addr);
33e4125c0cSAlexander Gordeev }
34e4125c0cSAlexander Gordeev 
pio_writeb(u8 value,volatile void * addr)35e4125c0cSAlexander Gordeev static void pio_writeb(u8 value, volatile void *addr)
36e4125c0cSAlexander Gordeev {
37e4125c0cSAlexander Gordeev 	outb(value, (unsigned long)addr);
38e4125c0cSAlexander Gordeev }
39e4125c0cSAlexander Gordeev 
pio_writew(u16 value,volatile void * addr)40e4125c0cSAlexander Gordeev static void pio_writew(u16 value, volatile void *addr)
41e4125c0cSAlexander Gordeev {
42e4125c0cSAlexander Gordeev 	outw(value, (unsigned long)addr);
43e4125c0cSAlexander Gordeev }
44e4125c0cSAlexander Gordeev 
pio_writel(u32 value,volatile void * addr)45e4125c0cSAlexander Gordeev static void pio_writel(u32 value, volatile void *addr)
46e4125c0cSAlexander Gordeev {
47e4125c0cSAlexander Gordeev 	outl(value, (unsigned long)addr);
48e4125c0cSAlexander Gordeev }
49e4125c0cSAlexander Gordeev 
50e4125c0cSAlexander Gordeev static struct pci_testdev_ops pci_testdev_io_ops = {
51e4125c0cSAlexander Gordeev 	.io_readb	= pio_readb,
52e4125c0cSAlexander Gordeev 	.io_readw	= pio_readw,
53e4125c0cSAlexander Gordeev 	.io_readl	= pio_readl,
54e4125c0cSAlexander Gordeev 	.io_writeb	= pio_writeb,
55e4125c0cSAlexander Gordeev 	.io_writew	= pio_writew,
56e4125c0cSAlexander Gordeev 	.io_writel	= pio_writel
57e4125c0cSAlexander Gordeev };
58e4125c0cSAlexander Gordeev 
mmio_readb(const volatile void * addr)59e4125c0cSAlexander Gordeev static u8 mmio_readb(const volatile void *addr)
60e4125c0cSAlexander Gordeev {
61e4125c0cSAlexander Gordeev 	return *(const volatile u8 __force *)addr;
62e4125c0cSAlexander Gordeev }
63e4125c0cSAlexander Gordeev 
mmio_readw(const volatile void * addr)64e4125c0cSAlexander Gordeev static u16 mmio_readw(const volatile void *addr)
65e4125c0cSAlexander Gordeev {
66e4125c0cSAlexander Gordeev 	return *(const volatile u16 __force *)addr;
67e4125c0cSAlexander Gordeev }
68e4125c0cSAlexander Gordeev 
mmio_readl(const volatile void * addr)69e4125c0cSAlexander Gordeev static u32 mmio_readl(const volatile void *addr)
70e4125c0cSAlexander Gordeev {
71e4125c0cSAlexander Gordeev 	return *(const volatile u32 __force *)addr;
72e4125c0cSAlexander Gordeev }
73e4125c0cSAlexander Gordeev 
mmio_writeb(u8 value,volatile void * addr)74e4125c0cSAlexander Gordeev static void mmio_writeb(u8 value, volatile void *addr)
75e4125c0cSAlexander Gordeev {
76e4125c0cSAlexander Gordeev 	*(volatile u8 __force *)addr = value;
77e4125c0cSAlexander Gordeev }
78e4125c0cSAlexander Gordeev 
mmio_writew(u16 value,volatile void * addr)79e4125c0cSAlexander Gordeev static void mmio_writew(u16 value, volatile void *addr)
80e4125c0cSAlexander Gordeev {
81e4125c0cSAlexander Gordeev 	*(volatile u16 __force *)addr = value;
82e4125c0cSAlexander Gordeev }
83e4125c0cSAlexander Gordeev 
mmio_writel(u32 value,volatile void * addr)84e4125c0cSAlexander Gordeev static void mmio_writel(u32 value, volatile void *addr)
85e4125c0cSAlexander Gordeev {
86e4125c0cSAlexander Gordeev 	*(volatile u32 __force *)addr = value;
87e4125c0cSAlexander Gordeev }
88e4125c0cSAlexander Gordeev 
89e4125c0cSAlexander Gordeev static struct pci_testdev_ops pci_testdev_mem_ops = {
90e4125c0cSAlexander Gordeev 	.io_readb	= mmio_readb,
91e4125c0cSAlexander Gordeev 	.io_readw	= mmio_readw,
92e4125c0cSAlexander Gordeev 	.io_readl	= mmio_readl,
93e4125c0cSAlexander Gordeev 	.io_writeb	= mmio_writeb,
94e4125c0cSAlexander Gordeev 	.io_writew	= mmio_writew,
95e4125c0cSAlexander Gordeev 	.io_writel	= mmio_writel
96e4125c0cSAlexander Gordeev };
97e4125c0cSAlexander Gordeev 
pci_testdev_one(struct pci_test_dev_hdr * test,int test_nr,struct pci_testdev_ops * ops)98e4125c0cSAlexander Gordeev static bool pci_testdev_one(struct pci_test_dev_hdr *test,
99e4125c0cSAlexander Gordeev 			    int test_nr,
100e4125c0cSAlexander Gordeev 			    struct pci_testdev_ops *ops)
101e4125c0cSAlexander Gordeev {
102e4125c0cSAlexander Gordeev 	u8 width;
103e4125c0cSAlexander Gordeev 	u32 count, sig, off;
104e4125c0cSAlexander Gordeev 	const int nr_writes = 16;
105e4125c0cSAlexander Gordeev 	int i;
106e4125c0cSAlexander Gordeev 
107e4125c0cSAlexander Gordeev 	ops->io_writeb(test_nr, &test->test);
108e4125c0cSAlexander Gordeev 	count = ops->io_readl(&test->count);
109e4125c0cSAlexander Gordeev 	if (count != 0)
110e4125c0cSAlexander Gordeev 		return false;
111e4125c0cSAlexander Gordeev 
112e4125c0cSAlexander Gordeev 	width = ops->io_readb(&test->width);
113e4125c0cSAlexander Gordeev 	if (width != 1 && width != 2 && width != 4)
114e4125c0cSAlexander Gordeev 		return false;
115e4125c0cSAlexander Gordeev 
116e4125c0cSAlexander Gordeev 	sig = ops->io_readl(&test->data);
117e4125c0cSAlexander Gordeev 	off = ops->io_readl(&test->offset);
118e4125c0cSAlexander Gordeev 
119e4125c0cSAlexander Gordeev 	for (i = 0; i < nr_writes; i++) {
120e4125c0cSAlexander Gordeev 		switch (width) {
121e4125c0cSAlexander Gordeev 		case 1: ops->io_writeb(sig, (void *)test + off); break;
122e4125c0cSAlexander Gordeev 		case 2: ops->io_writew(sig, (void *)test + off); break;
123e4125c0cSAlexander Gordeev 		case 4: ops->io_writel(sig, (void *)test + off); break;
124e4125c0cSAlexander Gordeev 		}
125e4125c0cSAlexander Gordeev 	}
126e4125c0cSAlexander Gordeev 
127e4125c0cSAlexander Gordeev 	count = ops->io_readl(&test->count);
128e4125c0cSAlexander Gordeev 	if (!count)
129e4125c0cSAlexander Gordeev 		return true;
130e4125c0cSAlexander Gordeev 
131e4125c0cSAlexander Gordeev 	return (int)count == nr_writes;
132e4125c0cSAlexander Gordeev }
133e4125c0cSAlexander Gordeev 
pci_testdev_print(struct pci_test_dev_hdr * test,struct pci_testdev_ops * ops)13423b8916bSThomas Huth static void pci_testdev_print(struct pci_test_dev_hdr *test,
135e4125c0cSAlexander Gordeev 			      struct pci_testdev_ops *ops)
136e4125c0cSAlexander Gordeev {
137e4125c0cSAlexander Gordeev 	bool io = (ops == &pci_testdev_io_ops);
138e4125c0cSAlexander Gordeev 	int i;
139e4125c0cSAlexander Gordeev 
140e4125c0cSAlexander Gordeev 	printf("pci-testdev %3s: ", io ? "io" : "mem");
141e4125c0cSAlexander Gordeev 	for (i = 0;; ++i) {
142e4125c0cSAlexander Gordeev 		char c = ops->io_readb(&test->name[i]);
143e4125c0cSAlexander Gordeev 		if (!c)
144e4125c0cSAlexander Gordeev 			break;
145e4125c0cSAlexander Gordeev 		printf("%c", c);
146e4125c0cSAlexander Gordeev 	}
147e4125c0cSAlexander Gordeev 	printf("\n");
148e4125c0cSAlexander Gordeev }
149e4125c0cSAlexander Gordeev 
pci_testdev_all(struct pci_test_dev_hdr * test,struct pci_testdev_ops * ops)150e4125c0cSAlexander Gordeev static int pci_testdev_all(struct pci_test_dev_hdr *test,
151e4125c0cSAlexander Gordeev 			   struct pci_testdev_ops *ops)
152e4125c0cSAlexander Gordeev {
153e4125c0cSAlexander Gordeev 	int i;
154e4125c0cSAlexander Gordeev 
155e4125c0cSAlexander Gordeev 	for (i = 0;; i++) {
156e4125c0cSAlexander Gordeev 		if (!pci_testdev_one(test, i, ops))
157e4125c0cSAlexander Gordeev 			break;
158e4125c0cSAlexander Gordeev 		pci_testdev_print(test, ops);
159e4125c0cSAlexander Gordeev 	}
160e4125c0cSAlexander Gordeev 
161e4125c0cSAlexander Gordeev 	return i;
162e4125c0cSAlexander Gordeev }
163e4125c0cSAlexander Gordeev 
pci_testdev(void)164e4125c0cSAlexander Gordeev int pci_testdev(void)
165e4125c0cSAlexander Gordeev {
1664d6cefa9SPeter Xu 	struct pci_dev pci_dev;
1674d6cefa9SPeter Xu 	pcidevaddr_t dev;
168e4125c0cSAlexander Gordeev 	phys_addr_t addr;
169e4125c0cSAlexander Gordeev 	void __iomem *mem, *io;
170e4125c0cSAlexander Gordeev 	int nr_tests = 0;
171e4125c0cSAlexander Gordeev 	bool ret;
172e4125c0cSAlexander Gordeev 
173e4125c0cSAlexander Gordeev 	dev = pci_find_dev(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_TEST);
174e4125c0cSAlexander Gordeev 	if (dev == PCIDEVADDR_INVALID) {
175e4125c0cSAlexander Gordeev 		printf("'pci-testdev' device is not found, "
176e4125c0cSAlexander Gordeev 		       "check QEMU '-device pci-testdev' parameter\n");
177e4125c0cSAlexander Gordeev 		return -1;
178e4125c0cSAlexander Gordeev 	}
1794d6cefa9SPeter Xu 	pci_dev_init(&pci_dev, dev);
180e4125c0cSAlexander Gordeev 
1814d6cefa9SPeter Xu 	ret = pci_bar_is_valid(&pci_dev, 0) && pci_bar_is_valid(&pci_dev, 1);
182e4125c0cSAlexander Gordeev 	assert(ret);
183e4125c0cSAlexander Gordeev 
1844d6cefa9SPeter Xu 	addr = pci_bar_get_addr(&pci_dev, 0);
185e4125c0cSAlexander Gordeev 	mem = ioremap(addr, PAGE_SIZE);
186e4125c0cSAlexander Gordeev 
1874d6cefa9SPeter Xu 	addr = pci_bar_get_addr(&pci_dev, 1);
188*340cf7dbSAndrew Jones #if defined(__i386__) || defined(__x86_64__)
189e4125c0cSAlexander Gordeev 	io = (void *)(unsigned long)addr;
190*340cf7dbSAndrew Jones #else
191*340cf7dbSAndrew Jones 	io = ioremap(addr, PAGE_SIZE);
192*340cf7dbSAndrew Jones #endif
193e4125c0cSAlexander Gordeev 
194e4125c0cSAlexander Gordeev 	nr_tests += pci_testdev_all(mem, &pci_testdev_mem_ops);
195e4125c0cSAlexander Gordeev 	nr_tests += pci_testdev_all(io, &pci_testdev_io_ops);
196e4125c0cSAlexander Gordeev 
197e4125c0cSAlexander Gordeev 	return nr_tests;
198e4125c0cSAlexander Gordeev }
199