1 /*
2 * Unit tests for the generic vPCI handler code.
3 *
4 * Copyright (C) 2017 Citrix Systems R&D
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms and conditions of the GNU General Public
8 * License, version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public
16 * License along with this program; If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "emul.h"
20
21 /* Single vcpu (current), and single domain with a single PCI device. */
22 static struct vpci vpci;
23
24 const static struct domain d;
25
26 const struct pci_dev test_pdev = {
27 .vpci = &vpci,
28 };
29
30 const static struct vcpu v = {
31 .domain = &d
32 };
33
34 const struct vcpu *current = &v;
35
36 /* Dummy hooks, write stores data, read fetches it. */
vpci_read8(const struct pci_dev * pdev,unsigned int reg,void * data)37 static uint32_t vpci_read8(const struct pci_dev *pdev, unsigned int reg,
38 void *data)
39 {
40 return *(uint8_t *)data;
41 }
42
vpci_write8(const struct pci_dev * pdev,unsigned int reg,uint32_t val,void * data)43 static void vpci_write8(const struct pci_dev *pdev, unsigned int reg,
44 uint32_t val, void *data)
45 {
46 *(uint8_t *)data = val;
47 }
48
vpci_read16(const struct pci_dev * pdev,unsigned int reg,void * data)49 static uint32_t vpci_read16(const struct pci_dev *pdev, unsigned int reg,
50 void *data)
51 {
52 return *(uint16_t *)data;
53 }
54
vpci_write16(const struct pci_dev * pdev,unsigned int reg,uint32_t val,void * data)55 static void vpci_write16(const struct pci_dev *pdev, unsigned int reg,
56 uint32_t val, void *data)
57 {
58 *(uint16_t *)data = val;
59 }
60
vpci_read32(const struct pci_dev * pdev,unsigned int reg,void * data)61 static uint32_t vpci_read32(const struct pci_dev *pdev, unsigned int reg,
62 void *data)
63 {
64 return *(uint32_t *)data;
65 }
66
vpci_write32(const struct pci_dev * pdev,unsigned int reg,uint32_t val,void * data)67 static void vpci_write32(const struct pci_dev *pdev, unsigned int reg,
68 uint32_t val, void *data)
69 {
70 *(uint32_t *)data = val;
71 }
72
73 #define VPCI_READ(reg, size, data) ({ \
74 data = vpci_read((pci_sbdf_t){ .sbdf = 0 }, reg, size); \
75 })
76
77 #define VPCI_READ_CHECK(reg, size, expected) ({ \
78 uint32_t rd; \
79 \
80 VPCI_READ(reg, size, rd); \
81 assert(rd == (expected)); \
82 })
83
84 #define VPCI_WRITE(reg, size, data) ({ \
85 vpci_write((pci_sbdf_t){ .sbdf = 0 }, reg, size, data); \
86 })
87
88 #define VPCI_WRITE_CHECK(reg, size, data) ({ \
89 VPCI_WRITE(reg, size, data); \
90 VPCI_READ_CHECK(reg, size, data); \
91 })
92
93 #define VPCI_ADD_REG(fread, fwrite, off, size, store) \
94 assert(!vpci_add_register(test_pdev.vpci, fread, fwrite, off, size, \
95 &store))
96
97 #define VPCI_ADD_INVALID_REG(fread, fwrite, off, size) \
98 assert(vpci_add_register(test_pdev.vpci, fread, fwrite, off, size, NULL))
99
100 #define VPCI_REMOVE_REG(off, size) \
101 assert(!vpci_remove_register(test_pdev.vpci, off, size))
102
103 #define VPCI_REMOVE_INVALID_REG(off, size) \
104 assert(vpci_remove_register(test_pdev.vpci, off, size))
105
106 /* Read a 32b register using all possible sizes. */
multiread4_check(unsigned int reg,uint32_t val)107 void multiread4_check(unsigned int reg, uint32_t val)
108 {
109 unsigned int i;
110
111 /* Read using bytes. */
112 for ( i = 0; i < 4; i++ )
113 VPCI_READ_CHECK(reg + i, 1, (val >> (i * 8)) & UINT8_MAX);
114
115 /* Read using 2bytes. */
116 for ( i = 0; i < 2; i++ )
117 VPCI_READ_CHECK(reg + i * 2, 2, (val >> (i * 2 * 8)) & UINT16_MAX);
118
119 VPCI_READ_CHECK(reg, 4, val);
120 }
121
multiwrite4_check(unsigned int reg)122 void multiwrite4_check(unsigned int reg)
123 {
124 unsigned int i;
125 uint32_t val = 0xa2f51732;
126
127 /* Write using bytes. */
128 for ( i = 0; i < 4; i++ )
129 VPCI_WRITE_CHECK(reg + i, 1, (val >> (i * 8)) & UINT8_MAX);
130 multiread4_check(reg, val);
131
132 /* Change the value each time to be sure writes work fine. */
133 val = 0x2b836fda;
134 /* Write using 2bytes. */
135 for ( i = 0; i < 2; i++ )
136 VPCI_WRITE_CHECK(reg + i * 2, 2, (val >> (i * 2 * 8)) & UINT16_MAX);
137 multiread4_check(reg, val);
138
139 val = 0xc4693beb;
140 VPCI_WRITE_CHECK(reg, 4, val);
141 multiread4_check(reg, val);
142 }
143
144 int
main(int argc,char ** argv)145 main(int argc, char **argv)
146 {
147 /* Index storage by offset. */
148 uint32_t r0 = 0xdeadbeef;
149 uint8_t r5 = 0xef;
150 uint8_t r6 = 0xbe;
151 uint8_t r7 = 0xef;
152 uint16_t r12 = 0x8696;
153 uint8_t r16[4] = { };
154 uint16_t r20[2] = { };
155 uint32_t r24 = 0;
156 uint8_t r28, r30;
157 unsigned int i;
158 int rc;
159
160 INIT_LIST_HEAD(&vpci.handlers);
161 spin_lock_init(&vpci.lock);
162
163 VPCI_ADD_REG(vpci_read32, vpci_write32, 0, 4, r0);
164 VPCI_READ_CHECK(0, 4, r0);
165 VPCI_WRITE_CHECK(0, 4, 0xbcbcbcbc);
166
167 VPCI_ADD_REG(vpci_read8, vpci_write8, 5, 1, r5);
168 VPCI_READ_CHECK(5, 1, r5);
169 VPCI_WRITE_CHECK(5, 1, 0xba);
170
171 VPCI_ADD_REG(vpci_read8, vpci_write8, 6, 1, r6);
172 VPCI_READ_CHECK(6, 1, r6);
173 VPCI_WRITE_CHECK(6, 1, 0xba);
174
175 VPCI_ADD_REG(vpci_read8, vpci_write8, 7, 1, r7);
176 VPCI_READ_CHECK(7, 1, r7);
177 VPCI_WRITE_CHECK(7, 1, 0xbd);
178
179 VPCI_ADD_REG(vpci_read16, vpci_write16, 12, 2, r12);
180 VPCI_READ_CHECK(12, 2, r12);
181 VPCI_READ_CHECK(12, 4, 0xffff8696);
182
183 /*
184 * At this point we have the following layout:
185 *
186 * Note that this refers to the position of the variables,
187 * but the value has already changed from the one given at
188 * initialization time because write tests have been performed.
189 *
190 * 32 24 16 8 0
191 * +-----+-----+-----+-----+
192 * | r0 | 0
193 * +-----+-----+-----+-----+
194 * | r7 | r6 | r5 |/////| 32
195 * +-----+-----+-----+-----|
196 * |///////////////////////| 64
197 * +-----------+-----------+
198 * |///////////| r12 | 96
199 * +-----------+-----------+
200 * ...
201 * / = unhandled.
202 */
203
204 /* Try to add an overlapping register handler. */
205 VPCI_ADD_INVALID_REG(vpci_read32, vpci_write32, 4, 4);
206
207 /* Try to add a non-aligned register. */
208 VPCI_ADD_INVALID_REG(vpci_read16, vpci_write16, 15, 2);
209
210 /* Try to add a register with wrong size. */
211 VPCI_ADD_INVALID_REG(vpci_read16, vpci_write16, 8, 3);
212
213 /* Try to add a register with missing handlers. */
214 VPCI_ADD_INVALID_REG(NULL, NULL, 8, 2);
215
216 /* Read/write of unset register. */
217 VPCI_READ_CHECK(8, 4, 0xffffffff);
218 VPCI_READ_CHECK(8, 2, 0xffff);
219 VPCI_READ_CHECK(8, 1, 0xff);
220 VPCI_WRITE(10, 2, 0xbeef);
221 VPCI_READ_CHECK(10, 2, 0xffff);
222
223 /* Read of multiple registers */
224 VPCI_WRITE_CHECK(7, 1, 0xbd);
225 VPCI_READ_CHECK(4, 4, 0xbdbabaff);
226
227 /* Partial read of a register. */
228 VPCI_WRITE_CHECK(0, 4, 0x1a1b1c1d);
229 VPCI_READ_CHECK(2, 1, 0x1b);
230 VPCI_READ_CHECK(6, 2, 0xbdba);
231
232 /* Write of multiple registers. */
233 VPCI_WRITE_CHECK(4, 4, 0xaabbccff);
234
235 /* Partial write of a register. */
236 VPCI_WRITE_CHECK(2, 1, 0xfe);
237 VPCI_WRITE_CHECK(6, 2, 0xfebc);
238
239 /*
240 * Test all possible read/write size combinations.
241 *
242 * Place 4 1B registers at 128bits (16B), 2 2B registers at 160bits
243 * (20B) and finally 1 4B register at 192bits (24B).
244 *
245 * Then perform all possible write and read sizes on each of them.
246 *
247 * ...
248 * 32 24 16 8 0
249 * +------+------+------+------+
250 * |r16[3]|r16[2]|r16[1]|r16[0]| 16
251 * +------+------+------+------+
252 * | r20[1] | r20[0] | 20
253 * +-------------+-------------|
254 * | r24 | 24
255 * +-------------+-------------+
256 *
257 */
258 VPCI_ADD_REG(vpci_read8, vpci_write8, 16, 1, r16[0]);
259 VPCI_ADD_REG(vpci_read8, vpci_write8, 17, 1, r16[1]);
260 VPCI_ADD_REG(vpci_read8, vpci_write8, 18, 1, r16[2]);
261 VPCI_ADD_REG(vpci_read8, vpci_write8, 19, 1, r16[3]);
262
263 VPCI_ADD_REG(vpci_read16, vpci_write16, 20, 2, r20[0]);
264 VPCI_ADD_REG(vpci_read16, vpci_write16, 22, 2, r20[1]);
265
266 VPCI_ADD_REG(vpci_read32, vpci_write32, 24, 4, r24);
267
268 /* Check the initial value is 0. */
269 multiread4_check(16, 0);
270 multiread4_check(20, 0);
271 multiread4_check(24, 0);
272
273 multiwrite4_check(16);
274 multiwrite4_check(20);
275 multiwrite4_check(24);
276
277 /*
278 * Check multiple non-consecutive gaps on the same read/write:
279 *
280 * 32 24 16 8 0
281 * +------+------+------+------+
282 * |//////| r30 |//////| r28 | 28
283 * +------+------+------+------+
284 *
285 */
286 VPCI_ADD_REG(vpci_read8, vpci_write8, 28, 1, r28);
287 VPCI_ADD_REG(vpci_read8, vpci_write8, 30, 1, r30);
288 VPCI_WRITE_CHECK(28, 4, 0xffacffdc);
289
290 /* Finally try to remove a couple of registers. */
291 VPCI_REMOVE_REG(28, 1);
292 VPCI_REMOVE_REG(24, 4);
293 VPCI_REMOVE_REG(12, 2);
294
295 VPCI_REMOVE_INVALID_REG(20, 1);
296 VPCI_REMOVE_INVALID_REG(16, 2);
297 VPCI_REMOVE_INVALID_REG(30, 2);
298
299 return 0;
300 }
301
302 /*
303 * Local variables:
304 * mode: C
305 * c-file-style: "BSD"
306 * c-basic-offset: 4
307 * indent-tabs-mode: nil
308 * End:
309 */
310