blob: 5d9d1e2a66fadb89b597e2110a45978bff6455ba [file] [log] [blame]
/*
* XXX - Credit, licence, copyright
*/
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/kvm.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "helpers.h"
#define GUEST_PHYS_ADDR (1UL << 30)
#define GUEST_MMIO_ADDR (1UL << 38)
#define GUEST_MEM_SIZE (PUD_SIZE*2)
/* x0[0] = x1; */
void guest_code()
{
asm(
"1: str x1, [x0] \n"
" brk #0 \n"
::);
}
#define GUEST_NR_INST 2
static void guest_write_memory(int vcpufd, struct kvm_run *run, uint64_t addr)
{
set_one_reg(vcpufd, REG_X(0), addr);
set_one_reg(vcpufd, REG_X(1), 1);
set_one_reg(vcpufd, REG_PC, GUEST_PHYS_ADDR);
assert(vcpu_run(vcpufd, run) == KVM_EXIT_DEBUG);
}
struct kvm_userspace_memory_region guest_prepare_memory(int vmfd, int vcpufd)
{
uint8_t *mem_code;
mem_code = mmap(NULL, GUEST_MEM_SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (mem_code == MAP_FAILED)
err(-ENOMEM, "allocating guest memory");
memcpy(mem_code, guest_code, GUEST_NR_INST * ARM64_INST_SIZE);
/* Map code memory to the second page frame. */
struct kvm_userspace_memory_region region = {
.slot = 0,
.guest_phys_addr = GUEST_PHYS_ADDR,
.memory_size = GUEST_MEM_SIZE,
.userspace_addr = (uint64_t)mem_code,
};
KVM_IOCTL(vmfd, KVM_SET_USER_MEMORY_REGION, &region);
vm_add_mmio_page(vmfd, 1, GUEST_MMIO_ADDR);
set_one_reg(vcpufd, REG_X(3), GUEST_MMIO_ADDR);
return region;
}
#define RAND_PAGE() ((rand() % (GUEST_MEM_SIZE / PAGE_SIZE - 1)) + 1)
#define NR_DIRTY_PAGES 500
int main(void)
{
struct kvm_userspace_memory_region region;
uint8_t *dirty_bm, *oracle_bm;
struct kvm_run *run = NULL;
struct kvm_dirty_log dlog;
uint64_t addr, offset;
int kvm, vmfd, vcpufd;
int i;
srand(time(NULL));
/* Prepare the guest */
kvm = get_kvm();
vmfd = create_vm(kvm);
vcpufd = create_vcpu(kvm, vmfd, &run, true);
region = guest_prepare_memory(vmfd, vcpufd);
/* We need two bitmap, one for dirty logging, and one for the oracle */
dirty_bm = calloc(1, BITMAP_SIZE(GUEST_MEM_SIZE));
oracle_bm = calloc(1, BITMAP_SIZE(GUEST_MEM_SIZE));
if (!dirty_bm || !oracle_bm)
return -ENOMEM;
/* Optional - poke random pages in memory, with logging disabled */
for (i = 0; i < NR_DIRTY_PAGES ; i++) {
offset = RAND_PAGE() * PAGE_SIZE;
addr = offset + GUEST_PHYS_ADDR;
guest_write_memory(vcpufd, run, addr);
}
/* Enable dirty logging, and try again */
region.flags = KVM_MEM_LOG_DIRTY_PAGES;
KVM_IOCTL(vmfd, KVM_SET_USER_MEMORY_REGION, &region);
for (i = 0; i < NR_DIRTY_PAGES ; i++) {
offset = RAND_PAGE() * PAGE_SIZE;
addr = offset + GUEST_PHYS_ADDR;
guest_write_memory(vcpufd, run, addr);
oracle_bm[OFFSET_IDX(offset)] |= OFFSET_BIT(offset);
}
/* Dump the dirty log bitmap, and compare to the oracle */
dlog.slot = region.slot;
dlog.dirty_bitmap = dirty_bm;
KVM_IOCTL(vmfd, KVM_GET_DIRTY_LOG, &dlog);
assert(bitmap_equal(dirty_bm, oracle_bm, BITMAP_SIZE(GUEST_MEM_SIZE)));
printf("Passing.\n");
}