blob: fec4385d25a7fa4e56f32d52dc776caa18a093bd [file] [log] [blame]
/*
*
* This is an Arm64 port of the x86 code accompanying "Using the KVM API"
* (https://lwn.net/Articles/658511/).
*
* Original x86 code in the file kvmtest.c and https://lwn.net/Articles/658512/.
*
* Copyright (C) 2020 Google LLC
* Author: Fuad Tabba <tabba@google.com>
*/
/* Sample code for /dev/kvm API
*
* Copyright (c) 2015 Intel Corporation
* Author: Josh Triplett <josh@joshtriplett.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <err.h>
#include <fcntl.h>
#include <linux/kvm.h>
#include <inttypes.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 "helpers.h"
asm(
".global guest_code_start, guest_code_end\n"
"guest_code_start: \n"
" mov x10, x0 \n"
" ldp x0, x1, [x10] \n"
" ldp x2, x3, [x10, #16]\n"
" add x0, x1, x0 \n"
" str x0,[x2] \n"
" brk #0 \n"
"guest_code_end: \n"
);
extern char guest_code_start[], guest_code_end[];
int run_vm(bool is_protected)
{
int kvm, vmfd, vcpufd;
const uint64_t code_address = 0x1000;
const uint64_t mmio_address = 0x2000;
uint64_t *mem_code;
struct kvm_run *run;
kvm = get_kvm();
vmfd = create_vm(kvm, is_protected);
/* Allocate one aligned page of guest memory to hold the code. */
mem_code = mmap(NULL, 0x1000,
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (!mem_code)
err(1, "allocating guest memory");
memcpy(mem_code, guest_code_start, guest_code_end-guest_code_start);
mem_code[0x100+0] = 0x101;
mem_code[0x100+1] = 0x202;
mem_code[0x100+2] = mmio_address;
mem_code[0x100+3] = 0xdeadbeef;
/* Map code memory to the second page frame. */
vm_add_mem_page(vmfd, 0, code_address, mem_code);
/* Use third page frame of guest memory to simulate MMIO. */
vm_add_mmio_page(vmfd, 1, mmio_address);
/* Create one CPU to run in the VM. */
vcpufd = create_vcpu(kvm, vmfd, &run, false);
/* Protected VM with no firmware: Note that we only control X0 and PC. */
set_one_reg(vcpufd, REG_PC, code_address);
set_one_reg(vcpufd, REG_X(0), code_address + 0x800);
/* Repeatedly run code and handle VM exits. */
for (;;) {
KVM_IOCTL(vcpufd, KVM_RUN, NULL);
switch (run->exit_reason) {
case KVM_EXIT_DEBUG:
puts("KVM_EXIT_DEBUG");
return 0;
case KVM_EXIT_MMIO:
{
uint64_t payload = *(uint64_t*)(run->mmio.data); /* sorry */
printf("KVM_EXIT_MMIO: addr = 0x%llx, len = %u, is_write = %u, data = 0x%"PRIx64"\n",
run->mmio.phys_addr, run->mmio.len, run->mmio.is_write,
payload);
return 0;/* XXX */
break;
}
case KVM_EXIT_FAIL_ENTRY:
errx(1, "KVM_EXIT_FAIL_ENTRY: hardware_entry_failure_reason = 0x%llx",
(unsigned long long)run->fail_entry.hardware_entry_failure_reason);
case KVM_EXIT_INTERNAL_ERROR:
errx(1, "KVM_EXIT_INTERNAL_ERROR: suberror = 0x%x",
run->internal.suberror);
default:
errx(1, "exit_reason = 0x%x", run->exit_reason);
}
}
}
int main(int argc, char **argv)
{
return run_vm(argc > 1);
}
/*
* Local variables:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/