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