clinuxkvm

KVM Userspace Port I/O


I'm currently experimenting with KVM, and trying to get US (userspace) I/O working. Currently, output (i.e. out dx, eax) works, and the US code can see the written value, but input (in eax, dx) does not seem to work - the VM doesn't receive the value written by the US code.

if (run->io.port == 0xface && run->io.direction == KVM_EXIT_IO_IN)
{
    printf("Port 0xface read\n");
    *(volatile uint32_t *)((uintptr_t)run + run->io.data_offset) = 0xdeadbeefu;
    continue;
}

run is a pointer to a struct kvm_run that was mmaped earlier and has enough space (i.e. run->io.data_offset is a valid offset from the pointer). The continue statement eventually causes the VM to restart, and the code continues normally. However, when I try to get the VM's rax register (which should be 0xdeadbeef), I get zero. From what I read in the docs (kvm/Documentation/api.txt), this is how I should be doing it. Am I missing something?

On a semi-related note, if I precede the continue statement with run->io.count = run->io.count;, the I/O is triggered again (even though count isn't changed). Is this expected behavior? Or am I triggering undefined behavior?


Solution

  • The issue is with the actual mmap call:

    run = mmap(NULL, mapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, vcpuFD, 0);
    

    The flags parameter should be MAP_SHARED instead of MAP_PRIVATE:

    run = mmap(NULL, mapSize, PROT_READ | PROT_WRITE, MAP_SHARED, vcpuFD, 0);
                                                      ^^^^^^^^^^
    

    The virtual machine will see the updated values when KVM_RUN is issued to restart it.