1
+/*
2
+ * SPDX-License-Identifier: GPL-2.0-or-later
3
+ */
4
+
5
+#include "qemu/osdep.h"
6
+#include "libqtest.h"
7
+#include "hw/i386/amd_iommu.h"
8
+
9
+#define CMDBUF_ADDR 0x200000
10
+#define CMDBUF_LEN_FIELD 8
11
+#define CMDBUF_ENTRIES (1U << CMDBUF_LEN_FIELD)
12
+
13
+static inline uint64_t amdvi_reg_readq(QTestState *s, uint64_t offset)
14
+{
15
+ return qtest_readq(s, AMDVI_BASE_ADDR + offset);
16
+}
17
+
18
+static inline void amdvi_reg_writeq(QTestState *s, uint64_t offset,
19
+ uint64_t val)
20
+{
21
+ qtest_writeq(s, AMDVI_BASE_ADDR + offset, val);
22
+}
23
+
24
+static void test_cmdbuf_head_wrap(void)
25
+{
26
+ QTestState *s;
27
+ uint64_t head;
28
+ int i;
29
+ /* 16 bytes per command */
30
+ struct {
31
+ uint64_t qw0;
32
+ uint64_t qw1;
33
+ } cmdbuf[CMDBUF_ENTRIES];
34
+
35
+ if (!qtest_has_machine("q35")) {
36
+ g_test_skip("q35 machine not available");
37
+ return;
38
+ }
39
+
40
+ s = qtest_init("-M q35 -device amd-iommu");
41
+
42
+ /* fill the command buffer with COMPLETION_WAIT (no-op) commands */
43
+ for (i = 0; i < CMDBUF_ENTRIES; i++) {
44
+ cmdbuf[i].qw0 = (uint64_t)AMDVI_CMD_COMPLETION_WAIT << 60;
45
+ cmdbuf[i].qw1 = 0;
46
+ }
47
+ qtest_memwrite(s, CMDBUF_ADDR, cmdbuf, sizeof(cmdbuf));
48
+
49
+ /* point the IOMMU at the command buffer and set its length */
50
+ amdvi_reg_writeq(s, AMDVI_MMIO_COMMAND_BASE,
51
+ CMDBUF_ADDR | ((uint64_t)CMDBUF_LEN_FIELD << 56));
52
+
53
+ /* enable the IOMMU and its command buffer processor */
54
+ amdvi_reg_writeq(s, AMDVI_MMIO_CONTROL,
55
+ AMDVI_MMIO_CONTROL_AMDVIEN | AMDVI_MMIO_CONTROL_CMDBUFLEN);
56
+
57
+ /* advance tail to the last entry, consuming all but the final entry */
58
+ amdvi_reg_writeq(s, AMDVI_MMIO_COMMAND_TAIL,
59
+ (CMDBUF_ENTRIES - 1) * AMDVI_COMMAND_SIZE);
60
+
61
+ /* wrap tail to 0, consuming the final entry and completing the buffer */
62
+ amdvi_reg_writeq(s, AMDVI_MMIO_COMMAND_TAIL, 0);
63
+
64
+ /* after consuming all entries the IOMMU must wrap CmdHeadPtr to 0 */
65
+ head = amdvi_reg_readq(s, AMDVI_MMIO_COMMAND_HEAD);
66
+ g_assert((head & AMDVI_MMIO_CMDBUF_HEAD_MASK) == 0);
67
+
68
+ qtest_quit(s);
69
+}
70
+
71
+int main(int argc, char **argv)
72
+{
73
+ g_test_init(&argc, &argv, NULL);
74
+ qtest_add_func("/q35/amd-iommu/cmdbuf-head-wrap", test_cmdbuf_head_wrap);
75
+ return g_test_run();
76
+}