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