~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/kvm/coalesced_io_test.c

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 #include <signal.h>
  3 #include <stdio.h>
  4 #include <stdlib.h>
  5 #include <string.h>
  6 #include <sys/ioctl.h>
  7 
  8 #include <linux/sizes.h>
  9 
 10 #include <kvm_util.h>
 11 #include <processor.h>
 12 
 13 #include "ucall_common.h"
 14 
 15 struct kvm_coalesced_io {
 16         struct kvm_coalesced_mmio_ring *ring;
 17         uint32_t ring_size;
 18         uint64_t mmio_gpa;
 19         uint64_t *mmio;
 20 
 21         /*
 22          * x86-only, but define pio_port for all architectures to minimize the
 23          * amount of #ifdeffery and complexity, without having to sacrifice
 24          * verbose error messages.
 25          */
 26         uint8_t pio_port;
 27 };
 28 
 29 static struct kvm_coalesced_io kvm_builtin_io_ring;
 30 
 31 #ifdef __x86_64__
 32 static const int has_pio = 1;
 33 #else
 34 static const int has_pio = 0;
 35 #endif
 36 
 37 static void guest_code(struct kvm_coalesced_io *io)
 38 {
 39         int i, j;
 40 
 41         for (;;) {
 42                 for (j = 0; j < 1 + has_pio; j++) {
 43                         /*
 44                          * KVM always leaves one free entry, i.e. exits to
 45                          * userspace before the last entry is filled.
 46                          */
 47                         for (i = 0; i < io->ring_size - 1; i++) {
 48 #ifdef __x86_64__
 49                                 if (i & 1)
 50                                         outl(io->pio_port, io->pio_port + i);
 51                                 else
 52 #endif
 53                                         WRITE_ONCE(*io->mmio, io->mmio_gpa + i);
 54                         }
 55 #ifdef __x86_64__
 56                         if (j & 1)
 57                                 outl(io->pio_port, io->pio_port + i);
 58                         else
 59 #endif
 60                                 WRITE_ONCE(*io->mmio, io->mmio_gpa + i);
 61                 }
 62                 GUEST_SYNC(0);
 63 
 64                 WRITE_ONCE(*io->mmio, io->mmio_gpa + i);
 65 #ifdef __x86_64__
 66                 outl(io->pio_port, io->pio_port + i);
 67 #endif
 68         }
 69 }
 70 
 71 static void vcpu_run_and_verify_io_exit(struct kvm_vcpu *vcpu,
 72                                         struct kvm_coalesced_io *io,
 73                                         uint32_t ring_start,
 74                                         uint32_t expected_exit)
 75 {
 76         const bool want_pio = expected_exit == KVM_EXIT_IO;
 77         struct kvm_coalesced_mmio_ring *ring = io->ring;
 78         struct kvm_run *run = vcpu->run;
 79         uint32_t pio_value;
 80 
 81         WRITE_ONCE(ring->first, ring_start);
 82         WRITE_ONCE(ring->last, ring_start);
 83 
 84         vcpu_run(vcpu);
 85 
 86         /*
 87          * Annoyingly, reading PIO data is safe only for PIO exits, otherwise
 88          * data_offset is garbage, e.g. an MMIO gpa.
 89          */
 90         if (run->exit_reason == KVM_EXIT_IO)
 91                 pio_value = *(uint32_t *)((void *)run + run->io.data_offset);
 92         else
 93                 pio_value = 0;
 94 
 95         TEST_ASSERT((!want_pio && (run->exit_reason == KVM_EXIT_MMIO && run->mmio.is_write &&
 96                                    run->mmio.phys_addr == io->mmio_gpa && run->mmio.len == 8 &&
 97                                    *(uint64_t *)run->mmio.data == io->mmio_gpa + io->ring_size - 1)) ||
 98                     (want_pio  && (run->exit_reason == KVM_EXIT_IO && run->io.port == io->pio_port &&
 99                                    run->io.direction == KVM_EXIT_IO_OUT && run->io.count == 1 &&
100                                    pio_value == io->pio_port + io->ring_size - 1)),
101                     "For start = %u, expected exit on %u-byte %s write 0x%llx = %lx, got exit_reason = %u (%s)\n  "
102                     "(MMIO addr = 0x%llx, write = %u, len = %u, data = %lx)\n  "
103                     "(PIO port = 0x%x, write = %u, len = %u, count = %u, data = %x",
104                     ring_start, want_pio ? 4 : 8, want_pio ? "PIO" : "MMIO",
105                     want_pio ? (unsigned long long)io->pio_port : io->mmio_gpa,
106                     (want_pio ? io->pio_port : io->mmio_gpa) + io->ring_size - 1, run->exit_reason,
107                     run->exit_reason == KVM_EXIT_MMIO ? "MMIO" : run->exit_reason == KVM_EXIT_IO ? "PIO" : "other",
108                     run->mmio.phys_addr, run->mmio.is_write, run->mmio.len, *(uint64_t *)run->mmio.data,
109                     run->io.port, run->io.direction, run->io.size, run->io.count, pio_value);
110 }
111 
112 static void vcpu_run_and_verify_coalesced_io(struct kvm_vcpu *vcpu,
113                                              struct kvm_coalesced_io *io,
114                                              uint32_t ring_start,
115                                              uint32_t expected_exit)
116 {
117         struct kvm_coalesced_mmio_ring *ring = io->ring;
118         int i;
119 
120         vcpu_run_and_verify_io_exit(vcpu, io, ring_start, expected_exit);
121 
122         TEST_ASSERT((ring->last + 1) % io->ring_size == ring->first,
123                     "Expected ring to be full (minus 1), first = %u, last = %u, max = %u, start = %u",
124                     ring->first, ring->last, io->ring_size, ring_start);
125 
126         for (i = 0; i < io->ring_size - 1; i++) {
127                 uint32_t idx = (ring->first + i) % io->ring_size;
128                 struct kvm_coalesced_mmio *entry = &ring->coalesced_mmio[idx];
129 
130 #ifdef __x86_64__
131                 if (i & 1)
132                         TEST_ASSERT(entry->phys_addr == io->pio_port &&
133                                     entry->len == 4 && entry->pio &&
134                                     *(uint32_t *)entry->data == io->pio_port + i,
135                                     "Wanted 4-byte port I/O 0x%x = 0x%x in entry %u, got %u-byte %s 0x%llx = 0x%x",
136                                     io->pio_port, io->pio_port + i, i,
137                                     entry->len, entry->pio ? "PIO" : "MMIO",
138                                     entry->phys_addr, *(uint32_t *)entry->data);
139                 else
140 #endif
141                         TEST_ASSERT(entry->phys_addr == io->mmio_gpa &&
142                                     entry->len == 8 && !entry->pio,
143                                     "Wanted 8-byte MMIO to 0x%lx = %lx in entry %u, got %u-byte %s 0x%llx = 0x%lx",
144                                     io->mmio_gpa, io->mmio_gpa + i, i,
145                                     entry->len, entry->pio ? "PIO" : "MMIO",
146                                     entry->phys_addr, *(uint64_t *)entry->data);
147         }
148 }
149 
150 static void test_coalesced_io(struct kvm_vcpu *vcpu,
151                               struct kvm_coalesced_io *io, uint32_t ring_start)
152 {
153         struct kvm_coalesced_mmio_ring *ring = io->ring;
154 
155         kvm_vm_register_coalesced_io(vcpu->vm, io->mmio_gpa, 8, false /* pio */);
156 #ifdef __x86_64__
157         kvm_vm_register_coalesced_io(vcpu->vm, io->pio_port, 8, true /* pio */);
158 #endif
159 
160         vcpu_run_and_verify_coalesced_io(vcpu, io, ring_start, KVM_EXIT_MMIO);
161 #ifdef __x86_64__
162         vcpu_run_and_verify_coalesced_io(vcpu, io, ring_start, KVM_EXIT_IO);
163 #endif
164 
165         /*
166          * Verify ucall, which may use non-coalesced MMIO or PIO, generates an
167          * immediate exit.
168          */
169         WRITE_ONCE(ring->first, ring_start);
170         WRITE_ONCE(ring->last, ring_start);
171         vcpu_run(vcpu);
172         TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_SYNC);
173         TEST_ASSERT_EQ(ring->first, ring_start);
174         TEST_ASSERT_EQ(ring->last, ring_start);
175 
176         /* Verify that non-coalesced MMIO/PIO generates an exit to userspace. */
177         kvm_vm_unregister_coalesced_io(vcpu->vm, io->mmio_gpa, 8, false /* pio */);
178         vcpu_run_and_verify_io_exit(vcpu, io, ring_start, KVM_EXIT_MMIO);
179 
180 #ifdef __x86_64__
181         kvm_vm_unregister_coalesced_io(vcpu->vm, io->pio_port, 8, true /* pio */);
182         vcpu_run_and_verify_io_exit(vcpu, io, ring_start, KVM_EXIT_IO);
183 #endif
184 }
185 
186 int main(int argc, char *argv[])
187 {
188         struct kvm_vcpu *vcpu;
189         struct kvm_vm *vm;
190         int i;
191 
192         TEST_REQUIRE(kvm_has_cap(KVM_CAP_COALESCED_MMIO));
193 
194 #ifdef __x86_64__
195         TEST_REQUIRE(kvm_has_cap(KVM_CAP_COALESCED_PIO));
196 #endif
197 
198         vm = vm_create_with_one_vcpu(&vcpu, guest_code);
199 
200         kvm_builtin_io_ring = (struct kvm_coalesced_io) {
201                 /*
202                  * The I/O ring is a kernel-allocated page whose address is
203                  * relative to each vCPU's run page, with the page offset
204                  * provided by KVM in the return of KVM_CAP_COALESCED_MMIO.
205                  */
206                 .ring = (void *)vcpu->run +
207                         (kvm_check_cap(KVM_CAP_COALESCED_MMIO) * getpagesize()),
208 
209                 /*
210                  * The size of the I/O ring is fixed, but KVM defines the sized
211                  * based on the kernel's PAGE_SIZE.  Thus, userspace must query
212                  * the host's page size at runtime to compute the ring size.
213                  */
214                 .ring_size = (getpagesize() - sizeof(struct kvm_coalesced_mmio_ring)) /
215                              sizeof(struct kvm_coalesced_mmio),
216 
217                 /*
218                  * Arbitrary address+port (MMIO mustn't overlap memslots), with
219                  * the MMIO GPA identity mapped in the guest.
220                  */
221                 .mmio_gpa = 4ull * SZ_1G,
222                 .mmio = (uint64_t *)(4ull * SZ_1G),
223                 .pio_port = 0x80,
224         };
225 
226         virt_map(vm, (uint64_t)kvm_builtin_io_ring.mmio, kvm_builtin_io_ring.mmio_gpa, 1);
227 
228         sync_global_to_guest(vm, kvm_builtin_io_ring);
229         vcpu_args_set(vcpu, 1, &kvm_builtin_io_ring);
230 
231         for (i = 0; i < kvm_builtin_io_ring.ring_size; i++)
232                 test_coalesced_io(vcpu, &kvm_builtin_io_ring, i);
233 
234         kvm_vm_free(vm);
235         return 0;
236 }
237 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php