tools/testing/selftests/kvm/coalesced_io_test.c

Source file repositories/reference/linux-study-clean/tools/testing/selftests/kvm/coalesced_io_test.c

File Facts

System
Linux kernel
Corpus path
tools/testing/selftests/kvm/coalesced_io_test.c
Extension
.c
Size
7253 bytes
Lines
237
Domain
Support Tooling And Documentation
Bucket
tools
Inferred role
Support Tooling And Documentation: implementation source
Status
source implementation candidate

Why This File Exists

Repository support layer: documentation, build tooling, samples, user-space helper tools, generated initramfs support, licenses, and validation utilities.

Dependency Surface

Detected Declarations

Annotated Snippet

struct kvm_coalesced_io {
	struct kvm_coalesced_mmio_ring *ring;
	u32 ring_size;
	u64 mmio_gpa;
	u64 *mmio;

	/*
	 * x86-only, but define pio_port for all architectures to minimize the
	 * amount of #ifdeffery and complexity, without having to sacrifice
	 * verbose error messages.
	 */
	u8 pio_port;
};

static struct kvm_coalesced_io kvm_builtin_io_ring;

#ifdef __x86_64__
static const int has_pio = 1;
#else
static const int has_pio = 0;
#endif

static void guest_code(struct kvm_coalesced_io *io)
{
	int i, j;

	for (;;) {
		for (j = 0; j < 1 + has_pio; j++) {
			/*
			 * KVM always leaves one free entry, i.e. exits to
			 * userspace before the last entry is filled.
			 */
			for (i = 0; i < io->ring_size - 1; i++) {
#ifdef __x86_64__
				if (i & 1)
					outl(io->pio_port, io->pio_port + i);
				else
#endif
					WRITE_ONCE(*io->mmio, io->mmio_gpa + i);
			}
#ifdef __x86_64__
			if (j & 1)
				outl(io->pio_port, io->pio_port + i);
			else
#endif
				WRITE_ONCE(*io->mmio, io->mmio_gpa + i);
		}
		GUEST_SYNC(0);

		WRITE_ONCE(*io->mmio, io->mmio_gpa + i);
#ifdef __x86_64__
		outl(io->pio_port, io->pio_port + i);
#endif
	}
}

static void vcpu_run_and_verify_io_exit(struct kvm_vcpu *vcpu,
					struct kvm_coalesced_io *io,
					u32 ring_start,
					u32 expected_exit)
{
	const bool want_pio = expected_exit == KVM_EXIT_IO;
	struct kvm_coalesced_mmio_ring *ring = io->ring;
	struct kvm_run *run = vcpu->run;
	u32 pio_value;

	WRITE_ONCE(ring->first, ring_start);
	WRITE_ONCE(ring->last, ring_start);

	vcpu_run(vcpu);

	/*
	 * Annoyingly, reading PIO data is safe only for PIO exits, otherwise
	 * data_offset is garbage, e.g. an MMIO gpa.
	 */
	if (run->exit_reason == KVM_EXIT_IO)
		pio_value = *(u32 *)((void *)run + run->io.data_offset);
	else
		pio_value = 0;

	TEST_ASSERT((!want_pio && (run->exit_reason == KVM_EXIT_MMIO && run->mmio.is_write &&
				   run->mmio.phys_addr == io->mmio_gpa && run->mmio.len == 8 &&
				   *(u64 *)run->mmio.data == io->mmio_gpa + io->ring_size - 1)) ||
		    (want_pio  && (run->exit_reason == KVM_EXIT_IO && run->io.port == io->pio_port &&
				   run->io.direction == KVM_EXIT_IO_OUT && run->io.count == 1 &&
				   pio_value == io->pio_port + io->ring_size - 1)),
		    "For start = %u, expected exit on %u-byte %s write 0x%llx = %lx, got exit_reason = %u (%s)\n  "
		    "(MMIO addr = 0x%llx, write = %u, len = %u, data = %lx)\n  "
		    "(PIO port = 0x%x, write = %u, len = %u, count = %u, data = %x",
		    ring_start, want_pio ? 4 : 8, want_pio ? "PIO" : "MMIO",

Annotation

Implementation Notes