Unexpected Behavior of post-indexed LDR/STR to unmapped addresses

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi all,

 

Currently I am experimenting with KVM. I want to measure the cost of data accesses over the KVM_EXIT_MMIO mechanism.

I want to only map the executable code in a memory slot and let all variable access use the KVM_EXIT_MMIO mechanism.

I placed the .text section of my executable in a range of a mapped memory slot, whereas the .data* and *.bss sections where linked to an unmapped address range.

Then I observed some strange behavior:

When accessing unmapped address with str instructions they lead to an KVM_EXIT_MMIO as expected.

However, if the str instruction is post-indexed, KVM exits with KVM_EXIT_UNKNOWN.

 

Is this behavior expected?

 

Thanks in advance!

 

Best Regards

Jan

----------------------------------------------------------------

 

For convenience I built a minimal example to demonstrate my point (running on a raspi3 b+, consisting of start.s and minimal_kvm.c):

as start.s -o start.o

objcopy -S -O binary start.o start.img

gcc  minimal kvm.c -o minimal_kvm

./minimal_kvm

mmio_exit when accessing address 0x5000.

mmio_exit when accessing address 0x5008.

unknown exit.

 

start.s:

.global _start:

_start:

// move an unmapped addr to a gpr

mov x2, #0x5000

// first try direct mmio-exit

str xzr, [x2]

// then try pre-indexed str

str xzr, [x2, #8]

// then try the post-indexed str

str xzr, [x2], #8

 

 

minimal_kvm.c:
#include <err.h>
#include <fcntl.h>
#include <linux/kvm.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <asm/kvm.h>
#include <stddef.h>
#include <asm-generic/errno-base.h>

int main(void)
{
    int kvm, vmfd, vcpufd, ret;
    int fd;
    char *mem;
    size_t mmap_size;
    struct kvm_run *run;
    struct kvm_vcpu_init vcpu_init;
    struct stat sb;
    fd = open("./start.img", O_RDWR);
    fstat(fd, &sb);
    const char *memblock;

    memblock = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (memblock == MAP_FAILED)
       err(1, "mem map failed");

    /* check if /dev/kvm is there with proper permissions */
    kvm = open("/dev/kvm", O_RDWR | O_CLOEXEC);
    if (kvm == -1)
        err(1, "/dev/kvm");

    /* Make sure we have the stable version of the API */
    ret = ioctl(kvm, KVM_GET_API_VERSION, NULL);
    if (ret == -1)
        err(1, "KVM_GET_API_VERSION");
    if (ret != 12)
        errx(1, "KVM_GET_API_VERSION %d, expected 12", ret);

    /* create vm of type 0 (recommended by documentation) */
    vmfd = ioctl(kvm, KVM_CREATE_VM, (unsigned long)0);
    if (vmfd == -1)
        err(1, "KVM_CREATE_VM");

    /* Allocate one aligned page of guest memory to hold the code. */
    mem = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (mem == MAP_FAILED)
        err(1, "allocating guest memory");
    memcpy(mem, memblock, sb.st_size);

    struct kvm_userspace_memory_region region = {
        .slot = 0,
        .guest_phys_addr = 0x0000,
        .memory_size = 0x1000,
        .userspace_addr = (uint64_t)mem,
    };
    ret = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &region);
    if (ret == -1)
        err(1, "KVM_SET_USER_MEMORY_REGION");

    /* create virtual cpu with id 0 */
    vcpufd = ioctl(vmfd, KVM_CREATE_VCPU, (unsigned long)0);
    if (vcpufd == -1)
        err(1, "KVM_CREATE_VCPU");

    /* get the preferred guest type to match underlying host */
    ret = ioctl(vmfd, KVM_ARM_PREFERRED_TARGET, &vcpu_init);
    if (ret == -1){
      err(1, "KVM_ARM_PREFERRED_TARGET");
    }

    /* init vcpu with preferred guest type */
    ret = ioctl(vcpufd, KVM_ARM_VCPU_INIT, &vcpu_init);
    if (ret == -1){
      err(1, "KVM_ARM_VCPU_INIT");
    }

    /* Map the shared kvm_run structure and following data. */
    ret = ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE, NULL);
    if (ret == -1)
        err(1, "KVM_GET_VCPU_MMAP_SIZE");
    mmap_size = ret;
    if (mmap_size < sizeof(*run))
        err(1, "KVM_GET_VCPU_MMAP_SIZE unexpectedly small");
    run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, vcpufd, 0);
    if (!run)
        err(1, "mmap vcpu");

    // set pc
    struct kvm_one_reg reg;
    reg.id = KVM_REG_ARM64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.pc) | KVM_REG_SIZE_U64;
    __u64 reg_value = 0;
    reg.addr =(__u64)(&reg_value);
    ret = ioctl(vcpufd, KVM_SET_ONE_REG, &reg);
    if (ret != 0){
      err(1, "KVM_SET_ONE_REG");
    }

    do {
      ret = ioctl(vcpufd, KVM_RUN, NULL);
      if (run->exit_reason == KVM_EXIT_MMIO) {
        __u64 guest_addr = run->mmio.phys_addr;
        printf("mmio_exit when accessing address %#x\n", guest_addr);
      }
      if (run->exit_reason == KVM_EXIT_UNKNOWN) {
        __u64 pc = ioctl(vcpufd, KVM_GET_ONE_REG, &reg);
        printf("unknown exit.\n");
      }
    } while (run->exit_reason != KVM_EXIT_UNKNOWN);

    munmap((void*)memblock, sb.st_size);
    return 0;
}

 

 

 

 

_______________________________________________
kvmarm mailing list
kvmarm@xxxxxxxxxxxxxxxxxxxxx
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

[Index of Archives]     [Linux KVM]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux