162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * tools/testing/selftests/kvm/lib/kvm_util.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2018, Google LLC. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define _GNU_SOURCE /* for program_invocation_name */ 962306a36Sopenharmony_ci#include "test_util.h" 1062306a36Sopenharmony_ci#include "kvm_util.h" 1162306a36Sopenharmony_ci#include "processor.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <assert.h> 1462306a36Sopenharmony_ci#include <sched.h> 1562306a36Sopenharmony_ci#include <sys/mman.h> 1662306a36Sopenharmony_ci#include <sys/types.h> 1762306a36Sopenharmony_ci#include <sys/stat.h> 1862306a36Sopenharmony_ci#include <unistd.h> 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define KVM_UTIL_MIN_PFN 2 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int vcpu_mmap_sz(void); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciint open_path_or_exit(const char *path, int flags) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci int fd; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci fd = open(path, flags); 3062306a36Sopenharmony_ci __TEST_REQUIRE(fd >= 0, "%s not available (errno: %d)", path, errno); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci return fd; 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * Open KVM_DEV_PATH if available, otherwise exit the entire program. 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * Input Args: 3962306a36Sopenharmony_ci * flags - The flags to pass when opening KVM_DEV_PATH. 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * Return: 4262306a36Sopenharmony_ci * The opened file descriptor of /dev/kvm. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_cistatic int _open_kvm_dev_path_or_exit(int flags) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci return open_path_or_exit(KVM_DEV_PATH, flags); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ciint open_kvm_dev_path_or_exit(void) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci return _open_kvm_dev_path_or_exit(O_RDONLY); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic bool get_module_param_bool(const char *module_name, const char *param) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci const int path_size = 128; 5762306a36Sopenharmony_ci char path[path_size]; 5862306a36Sopenharmony_ci char value; 5962306a36Sopenharmony_ci ssize_t r; 6062306a36Sopenharmony_ci int fd; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci r = snprintf(path, path_size, "/sys/module/%s/parameters/%s", 6362306a36Sopenharmony_ci module_name, param); 6462306a36Sopenharmony_ci TEST_ASSERT(r < path_size, 6562306a36Sopenharmony_ci "Failed to construct sysfs path in %d bytes.", path_size); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci fd = open_path_or_exit(path, O_RDONLY); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci r = read(fd, &value, 1); 7062306a36Sopenharmony_ci TEST_ASSERT(r == 1, "read(%s) failed", path); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci r = close(fd); 7362306a36Sopenharmony_ci TEST_ASSERT(!r, "close(%s) failed", path); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (value == 'Y') 7662306a36Sopenharmony_ci return true; 7762306a36Sopenharmony_ci else if (value == 'N') 7862306a36Sopenharmony_ci return false; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci TEST_FAIL("Unrecognized value '%c' for boolean module param", value); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cibool get_kvm_param_bool(const char *param) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci return get_module_param_bool("kvm", param); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cibool get_kvm_intel_param_bool(const char *param) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci return get_module_param_bool("kvm_intel", param); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cibool get_kvm_amd_param_bool(const char *param) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci return get_module_param_bool("kvm_amd", param); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* 9962306a36Sopenharmony_ci * Capability 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * Input Args: 10262306a36Sopenharmony_ci * cap - Capability 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * Output Args: None 10562306a36Sopenharmony_ci * 10662306a36Sopenharmony_ci * Return: 10762306a36Sopenharmony_ci * On success, the Value corresponding to the capability (KVM_CAP_*) 10862306a36Sopenharmony_ci * specified by the value of cap. On failure a TEST_ASSERT failure 10962306a36Sopenharmony_ci * is produced. 11062306a36Sopenharmony_ci * 11162306a36Sopenharmony_ci * Looks up and returns the value corresponding to the capability 11262306a36Sopenharmony_ci * (KVM_CAP_*) given by cap. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ciunsigned int kvm_check_cap(long cap) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci int ret; 11762306a36Sopenharmony_ci int kvm_fd; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci kvm_fd = open_kvm_dev_path_or_exit(); 12062306a36Sopenharmony_ci ret = __kvm_ioctl(kvm_fd, KVM_CHECK_EXTENSION, (void *)cap); 12162306a36Sopenharmony_ci TEST_ASSERT(ret >= 0, KVM_IOCTL_ERROR(KVM_CHECK_EXTENSION, ret)); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci close(kvm_fd); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return (unsigned int)ret; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_civoid vm_enable_dirty_ring(struct kvm_vm *vm, uint32_t ring_size) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci if (vm_check_cap(vm, KVM_CAP_DIRTY_LOG_RING_ACQ_REL)) 13162306a36Sopenharmony_ci vm_enable_cap(vm, KVM_CAP_DIRTY_LOG_RING_ACQ_REL, ring_size); 13262306a36Sopenharmony_ci else 13362306a36Sopenharmony_ci vm_enable_cap(vm, KVM_CAP_DIRTY_LOG_RING, ring_size); 13462306a36Sopenharmony_ci vm->dirty_ring_size = ring_size; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic void vm_open(struct kvm_vm *vm) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci vm->kvm_fd = _open_kvm_dev_path_or_exit(O_RDWR); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci TEST_REQUIRE(kvm_has_cap(KVM_CAP_IMMEDIATE_EXIT)); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci vm->fd = __kvm_ioctl(vm->kvm_fd, KVM_CREATE_VM, (void *)vm->type); 14462306a36Sopenharmony_ci TEST_ASSERT(vm->fd >= 0, KVM_IOCTL_ERROR(KVM_CREATE_VM, vm->fd)); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ciconst char *vm_guest_mode_string(uint32_t i) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci static const char * const strings[] = { 15062306a36Sopenharmony_ci [VM_MODE_P52V48_4K] = "PA-bits:52, VA-bits:48, 4K pages", 15162306a36Sopenharmony_ci [VM_MODE_P52V48_64K] = "PA-bits:52, VA-bits:48, 64K pages", 15262306a36Sopenharmony_ci [VM_MODE_P48V48_4K] = "PA-bits:48, VA-bits:48, 4K pages", 15362306a36Sopenharmony_ci [VM_MODE_P48V48_16K] = "PA-bits:48, VA-bits:48, 16K pages", 15462306a36Sopenharmony_ci [VM_MODE_P48V48_64K] = "PA-bits:48, VA-bits:48, 64K pages", 15562306a36Sopenharmony_ci [VM_MODE_P40V48_4K] = "PA-bits:40, VA-bits:48, 4K pages", 15662306a36Sopenharmony_ci [VM_MODE_P40V48_16K] = "PA-bits:40, VA-bits:48, 16K pages", 15762306a36Sopenharmony_ci [VM_MODE_P40V48_64K] = "PA-bits:40, VA-bits:48, 64K pages", 15862306a36Sopenharmony_ci [VM_MODE_PXXV48_4K] = "PA-bits:ANY, VA-bits:48, 4K pages", 15962306a36Sopenharmony_ci [VM_MODE_P47V64_4K] = "PA-bits:47, VA-bits:64, 4K pages", 16062306a36Sopenharmony_ci [VM_MODE_P44V64_4K] = "PA-bits:44, VA-bits:64, 4K pages", 16162306a36Sopenharmony_ci [VM_MODE_P36V48_4K] = "PA-bits:36, VA-bits:48, 4K pages", 16262306a36Sopenharmony_ci [VM_MODE_P36V48_16K] = "PA-bits:36, VA-bits:48, 16K pages", 16362306a36Sopenharmony_ci [VM_MODE_P36V48_64K] = "PA-bits:36, VA-bits:48, 64K pages", 16462306a36Sopenharmony_ci [VM_MODE_P36V47_16K] = "PA-bits:36, VA-bits:47, 16K pages", 16562306a36Sopenharmony_ci }; 16662306a36Sopenharmony_ci _Static_assert(sizeof(strings)/sizeof(char *) == NUM_VM_MODES, 16762306a36Sopenharmony_ci "Missing new mode strings?"); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci TEST_ASSERT(i < NUM_VM_MODES, "Guest mode ID %d too big", i); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return strings[i]; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ciconst struct vm_guest_mode_params vm_guest_mode_params[] = { 17562306a36Sopenharmony_ci [VM_MODE_P52V48_4K] = { 52, 48, 0x1000, 12 }, 17662306a36Sopenharmony_ci [VM_MODE_P52V48_64K] = { 52, 48, 0x10000, 16 }, 17762306a36Sopenharmony_ci [VM_MODE_P48V48_4K] = { 48, 48, 0x1000, 12 }, 17862306a36Sopenharmony_ci [VM_MODE_P48V48_16K] = { 48, 48, 0x4000, 14 }, 17962306a36Sopenharmony_ci [VM_MODE_P48V48_64K] = { 48, 48, 0x10000, 16 }, 18062306a36Sopenharmony_ci [VM_MODE_P40V48_4K] = { 40, 48, 0x1000, 12 }, 18162306a36Sopenharmony_ci [VM_MODE_P40V48_16K] = { 40, 48, 0x4000, 14 }, 18262306a36Sopenharmony_ci [VM_MODE_P40V48_64K] = { 40, 48, 0x10000, 16 }, 18362306a36Sopenharmony_ci [VM_MODE_PXXV48_4K] = { 0, 0, 0x1000, 12 }, 18462306a36Sopenharmony_ci [VM_MODE_P47V64_4K] = { 47, 64, 0x1000, 12 }, 18562306a36Sopenharmony_ci [VM_MODE_P44V64_4K] = { 44, 64, 0x1000, 12 }, 18662306a36Sopenharmony_ci [VM_MODE_P36V48_4K] = { 36, 48, 0x1000, 12 }, 18762306a36Sopenharmony_ci [VM_MODE_P36V48_16K] = { 36, 48, 0x4000, 14 }, 18862306a36Sopenharmony_ci [VM_MODE_P36V48_64K] = { 36, 48, 0x10000, 16 }, 18962306a36Sopenharmony_ci [VM_MODE_P36V47_16K] = { 36, 47, 0x4000, 14 }, 19062306a36Sopenharmony_ci}; 19162306a36Sopenharmony_ci_Static_assert(sizeof(vm_guest_mode_params)/sizeof(struct vm_guest_mode_params) == NUM_VM_MODES, 19262306a36Sopenharmony_ci "Missing new mode params?"); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* 19562306a36Sopenharmony_ci * Initializes vm->vpages_valid to match the canonical VA space of the 19662306a36Sopenharmony_ci * architecture. 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * The default implementation is valid for architectures which split the 19962306a36Sopenharmony_ci * range addressed by a single page table into a low and high region 20062306a36Sopenharmony_ci * based on the MSB of the VA. On architectures with this behavior 20162306a36Sopenharmony_ci * the VA region spans [0, 2^(va_bits - 1)), [-(2^(va_bits - 1), -1]. 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_ci__weak void vm_vaddr_populate_bitmap(struct kvm_vm *vm) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci sparsebit_set_num(vm->vpages_valid, 20662306a36Sopenharmony_ci 0, (1ULL << (vm->va_bits - 1)) >> vm->page_shift); 20762306a36Sopenharmony_ci sparsebit_set_num(vm->vpages_valid, 20862306a36Sopenharmony_ci (~((1ULL << (vm->va_bits - 1)) - 1)) >> vm->page_shift, 20962306a36Sopenharmony_ci (1ULL << (vm->va_bits - 1)) >> vm->page_shift); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistruct kvm_vm *____vm_create(enum vm_guest_mode mode) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct kvm_vm *vm; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci vm = calloc(1, sizeof(*vm)); 21762306a36Sopenharmony_ci TEST_ASSERT(vm != NULL, "Insufficient Memory"); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci INIT_LIST_HEAD(&vm->vcpus); 22062306a36Sopenharmony_ci vm->regions.gpa_tree = RB_ROOT; 22162306a36Sopenharmony_ci vm->regions.hva_tree = RB_ROOT; 22262306a36Sopenharmony_ci hash_init(vm->regions.slot_hash); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci vm->mode = mode; 22562306a36Sopenharmony_ci vm->type = 0; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci vm->pa_bits = vm_guest_mode_params[mode].pa_bits; 22862306a36Sopenharmony_ci vm->va_bits = vm_guest_mode_params[mode].va_bits; 22962306a36Sopenharmony_ci vm->page_size = vm_guest_mode_params[mode].page_size; 23062306a36Sopenharmony_ci vm->page_shift = vm_guest_mode_params[mode].page_shift; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* Setup mode specific traits. */ 23362306a36Sopenharmony_ci switch (vm->mode) { 23462306a36Sopenharmony_ci case VM_MODE_P52V48_4K: 23562306a36Sopenharmony_ci vm->pgtable_levels = 4; 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci case VM_MODE_P52V48_64K: 23862306a36Sopenharmony_ci vm->pgtable_levels = 3; 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci case VM_MODE_P48V48_4K: 24162306a36Sopenharmony_ci vm->pgtable_levels = 4; 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci case VM_MODE_P48V48_64K: 24462306a36Sopenharmony_ci vm->pgtable_levels = 3; 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci case VM_MODE_P40V48_4K: 24762306a36Sopenharmony_ci case VM_MODE_P36V48_4K: 24862306a36Sopenharmony_ci vm->pgtable_levels = 4; 24962306a36Sopenharmony_ci break; 25062306a36Sopenharmony_ci case VM_MODE_P40V48_64K: 25162306a36Sopenharmony_ci case VM_MODE_P36V48_64K: 25262306a36Sopenharmony_ci vm->pgtable_levels = 3; 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci case VM_MODE_P48V48_16K: 25562306a36Sopenharmony_ci case VM_MODE_P40V48_16K: 25662306a36Sopenharmony_ci case VM_MODE_P36V48_16K: 25762306a36Sopenharmony_ci vm->pgtable_levels = 4; 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci case VM_MODE_P36V47_16K: 26062306a36Sopenharmony_ci vm->pgtable_levels = 3; 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci case VM_MODE_PXXV48_4K: 26362306a36Sopenharmony_ci#ifdef __x86_64__ 26462306a36Sopenharmony_ci kvm_get_cpu_address_width(&vm->pa_bits, &vm->va_bits); 26562306a36Sopenharmony_ci /* 26662306a36Sopenharmony_ci * Ignore KVM support for 5-level paging (vm->va_bits == 57), 26762306a36Sopenharmony_ci * it doesn't take effect unless a CR4.LA57 is set, which it 26862306a36Sopenharmony_ci * isn't for this VM_MODE. 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_ci TEST_ASSERT(vm->va_bits == 48 || vm->va_bits == 57, 27162306a36Sopenharmony_ci "Linear address width (%d bits) not supported", 27262306a36Sopenharmony_ci vm->va_bits); 27362306a36Sopenharmony_ci pr_debug("Guest physical address width detected: %d\n", 27462306a36Sopenharmony_ci vm->pa_bits); 27562306a36Sopenharmony_ci vm->pgtable_levels = 4; 27662306a36Sopenharmony_ci vm->va_bits = 48; 27762306a36Sopenharmony_ci#else 27862306a36Sopenharmony_ci TEST_FAIL("VM_MODE_PXXV48_4K not supported on non-x86 platforms"); 27962306a36Sopenharmony_ci#endif 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci case VM_MODE_P47V64_4K: 28262306a36Sopenharmony_ci vm->pgtable_levels = 5; 28362306a36Sopenharmony_ci break; 28462306a36Sopenharmony_ci case VM_MODE_P44V64_4K: 28562306a36Sopenharmony_ci vm->pgtable_levels = 5; 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci default: 28862306a36Sopenharmony_ci TEST_FAIL("Unknown guest mode, mode: 0x%x", mode); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci#ifdef __aarch64__ 29262306a36Sopenharmony_ci if (vm->pa_bits != 40) 29362306a36Sopenharmony_ci vm->type = KVM_VM_TYPE_ARM_IPA_SIZE(vm->pa_bits); 29462306a36Sopenharmony_ci#endif 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci vm_open(vm); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* Limit to VA-bit canonical virtual addresses. */ 29962306a36Sopenharmony_ci vm->vpages_valid = sparsebit_alloc(); 30062306a36Sopenharmony_ci vm_vaddr_populate_bitmap(vm); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Limit physical addresses to PA-bits. */ 30362306a36Sopenharmony_ci vm->max_gfn = vm_compute_max_gfn(vm); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* Allocate and setup memory for guest. */ 30662306a36Sopenharmony_ci vm->vpages_mapped = sparsebit_alloc(); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return vm; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic uint64_t vm_nr_pages_required(enum vm_guest_mode mode, 31262306a36Sopenharmony_ci uint32_t nr_runnable_vcpus, 31362306a36Sopenharmony_ci uint64_t extra_mem_pages) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci uint64_t page_size = vm_guest_mode_params[mode].page_size; 31662306a36Sopenharmony_ci uint64_t nr_pages; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci TEST_ASSERT(nr_runnable_vcpus, 31962306a36Sopenharmony_ci "Use vm_create_barebones() for VMs that _never_ have vCPUs\n"); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci TEST_ASSERT(nr_runnable_vcpus <= kvm_check_cap(KVM_CAP_MAX_VCPUS), 32262306a36Sopenharmony_ci "nr_vcpus = %d too large for host, max-vcpus = %d", 32362306a36Sopenharmony_ci nr_runnable_vcpus, kvm_check_cap(KVM_CAP_MAX_VCPUS)); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* 32662306a36Sopenharmony_ci * Arbitrarily allocate 512 pages (2mb when page size is 4kb) for the 32762306a36Sopenharmony_ci * test code and other per-VM assets that will be loaded into memslot0. 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_ci nr_pages = 512; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* Account for the per-vCPU stacks on behalf of the test. */ 33262306a36Sopenharmony_ci nr_pages += nr_runnable_vcpus * DEFAULT_STACK_PGS; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* 33562306a36Sopenharmony_ci * Account for the number of pages needed for the page tables. The 33662306a36Sopenharmony_ci * maximum page table size for a memory region will be when the 33762306a36Sopenharmony_ci * smallest page size is used. Considering each page contains x page 33862306a36Sopenharmony_ci * table descriptors, the total extra size for page tables (for extra 33962306a36Sopenharmony_ci * N pages) will be: N/x+N/x^2+N/x^3+... which is definitely smaller 34062306a36Sopenharmony_ci * than N/x*2. 34162306a36Sopenharmony_ci */ 34262306a36Sopenharmony_ci nr_pages += (nr_pages + extra_mem_pages) / PTES_PER_MIN_PAGE * 2; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Account for the number of pages needed by ucall. */ 34562306a36Sopenharmony_ci nr_pages += ucall_nr_pages_required(page_size); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return vm_adjust_num_guest_pages(mode, nr_pages); 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistruct kvm_vm *__vm_create(enum vm_guest_mode mode, uint32_t nr_runnable_vcpus, 35162306a36Sopenharmony_ci uint64_t nr_extra_pages) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci uint64_t nr_pages = vm_nr_pages_required(mode, nr_runnable_vcpus, 35462306a36Sopenharmony_ci nr_extra_pages); 35562306a36Sopenharmony_ci struct userspace_mem_region *slot0; 35662306a36Sopenharmony_ci struct kvm_vm *vm; 35762306a36Sopenharmony_ci int i; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci pr_debug("%s: mode='%s' pages='%ld'\n", __func__, 36062306a36Sopenharmony_ci vm_guest_mode_string(mode), nr_pages); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci vm = ____vm_create(mode); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0, nr_pages, 0); 36562306a36Sopenharmony_ci for (i = 0; i < NR_MEM_REGIONS; i++) 36662306a36Sopenharmony_ci vm->memslots[i] = 0; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci kvm_vm_elf_load(vm, program_invocation_name); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* 37162306a36Sopenharmony_ci * TODO: Add proper defines to protect the library's memslots, and then 37262306a36Sopenharmony_ci * carve out memslot1 for the ucall MMIO address. KVM treats writes to 37362306a36Sopenharmony_ci * read-only memslots as MMIO, and creating a read-only memslot for the 37462306a36Sopenharmony_ci * MMIO region would prevent silently clobbering the MMIO region. 37562306a36Sopenharmony_ci */ 37662306a36Sopenharmony_ci slot0 = memslot2region(vm, 0); 37762306a36Sopenharmony_ci ucall_init(vm, slot0->region.guest_phys_addr + slot0->region.memory_size); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci kvm_arch_vm_post_create(vm); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return vm; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci/* 38562306a36Sopenharmony_ci * VM Create with customized parameters 38662306a36Sopenharmony_ci * 38762306a36Sopenharmony_ci * Input Args: 38862306a36Sopenharmony_ci * mode - VM Mode (e.g. VM_MODE_P52V48_4K) 38962306a36Sopenharmony_ci * nr_vcpus - VCPU count 39062306a36Sopenharmony_ci * extra_mem_pages - Non-slot0 physical memory total size 39162306a36Sopenharmony_ci * guest_code - Guest entry point 39262306a36Sopenharmony_ci * vcpuids - VCPU IDs 39362306a36Sopenharmony_ci * 39462306a36Sopenharmony_ci * Output Args: None 39562306a36Sopenharmony_ci * 39662306a36Sopenharmony_ci * Return: 39762306a36Sopenharmony_ci * Pointer to opaque structure that describes the created VM. 39862306a36Sopenharmony_ci * 39962306a36Sopenharmony_ci * Creates a VM with the mode specified by mode (e.g. VM_MODE_P52V48_4K). 40062306a36Sopenharmony_ci * extra_mem_pages is only used to calculate the maximum page table size, 40162306a36Sopenharmony_ci * no real memory allocation for non-slot0 memory in this function. 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_cistruct kvm_vm *__vm_create_with_vcpus(enum vm_guest_mode mode, uint32_t nr_vcpus, 40462306a36Sopenharmony_ci uint64_t extra_mem_pages, 40562306a36Sopenharmony_ci void *guest_code, struct kvm_vcpu *vcpus[]) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci struct kvm_vm *vm; 40862306a36Sopenharmony_ci int i; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci TEST_ASSERT(!nr_vcpus || vcpus, "Must provide vCPU array"); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci vm = __vm_create(mode, nr_vcpus, extra_mem_pages); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci for (i = 0; i < nr_vcpus; ++i) 41562306a36Sopenharmony_ci vcpus[i] = vm_vcpu_add(vm, i, guest_code); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return vm; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistruct kvm_vm *__vm_create_with_one_vcpu(struct kvm_vcpu **vcpu, 42162306a36Sopenharmony_ci uint64_t extra_mem_pages, 42262306a36Sopenharmony_ci void *guest_code) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct kvm_vcpu *vcpus[1]; 42562306a36Sopenharmony_ci struct kvm_vm *vm; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci vm = __vm_create_with_vcpus(VM_MODE_DEFAULT, 1, extra_mem_pages, 42862306a36Sopenharmony_ci guest_code, vcpus); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci *vcpu = vcpus[0]; 43162306a36Sopenharmony_ci return vm; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci/* 43562306a36Sopenharmony_ci * VM Restart 43662306a36Sopenharmony_ci * 43762306a36Sopenharmony_ci * Input Args: 43862306a36Sopenharmony_ci * vm - VM that has been released before 43962306a36Sopenharmony_ci * 44062306a36Sopenharmony_ci * Output Args: None 44162306a36Sopenharmony_ci * 44262306a36Sopenharmony_ci * Reopens the file descriptors associated to the VM and reinstates the 44362306a36Sopenharmony_ci * global state, such as the irqchip and the memory regions that are mapped 44462306a36Sopenharmony_ci * into the guest. 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_civoid kvm_vm_restart(struct kvm_vm *vmp) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci int ctr; 44962306a36Sopenharmony_ci struct userspace_mem_region *region; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci vm_open(vmp); 45262306a36Sopenharmony_ci if (vmp->has_irqchip) 45362306a36Sopenharmony_ci vm_create_irqchip(vmp); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci hash_for_each(vmp->regions.slot_hash, ctr, region, slot_node) { 45662306a36Sopenharmony_ci int ret = ioctl(vmp->fd, KVM_SET_USER_MEMORY_REGION, ®ion->region); 45762306a36Sopenharmony_ci TEST_ASSERT(ret == 0, "KVM_SET_USER_MEMORY_REGION IOCTL failed,\n" 45862306a36Sopenharmony_ci " rc: %i errno: %i\n" 45962306a36Sopenharmony_ci " slot: %u flags: 0x%x\n" 46062306a36Sopenharmony_ci " guest_phys_addr: 0x%llx size: 0x%llx", 46162306a36Sopenharmony_ci ret, errno, region->region.slot, 46262306a36Sopenharmony_ci region->region.flags, 46362306a36Sopenharmony_ci region->region.guest_phys_addr, 46462306a36Sopenharmony_ci region->region.memory_size); 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci__weak struct kvm_vcpu *vm_arch_vcpu_recreate(struct kvm_vm *vm, 46962306a36Sopenharmony_ci uint32_t vcpu_id) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci return __vm_vcpu_add(vm, vcpu_id); 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistruct kvm_vcpu *vm_recreate_with_one_vcpu(struct kvm_vm *vm) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci kvm_vm_restart(vm); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return vm_vcpu_recreate(vm, 0); 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_civoid kvm_pin_this_task_to_pcpu(uint32_t pcpu) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci cpu_set_t mask; 48462306a36Sopenharmony_ci int r; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci CPU_ZERO(&mask); 48762306a36Sopenharmony_ci CPU_SET(pcpu, &mask); 48862306a36Sopenharmony_ci r = sched_setaffinity(0, sizeof(mask), &mask); 48962306a36Sopenharmony_ci TEST_ASSERT(!r, "sched_setaffinity() failed for pCPU '%u'.\n", pcpu); 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic uint32_t parse_pcpu(const char *cpu_str, const cpu_set_t *allowed_mask) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci uint32_t pcpu = atoi_non_negative("CPU number", cpu_str); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci TEST_ASSERT(CPU_ISSET(pcpu, allowed_mask), 49762306a36Sopenharmony_ci "Not allowed to run on pCPU '%d', check cgroups?\n", pcpu); 49862306a36Sopenharmony_ci return pcpu; 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_civoid kvm_print_vcpu_pinning_help(void) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci const char *name = program_invocation_name; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci printf(" -c: Pin tasks to physical CPUs. Takes a list of comma separated\n" 50662306a36Sopenharmony_ci " values (target pCPU), one for each vCPU, plus an optional\n" 50762306a36Sopenharmony_ci " entry for the main application task (specified via entry\n" 50862306a36Sopenharmony_ci " <nr_vcpus + 1>). If used, entries must be provided for all\n" 50962306a36Sopenharmony_ci " vCPUs, i.e. pinning vCPUs is all or nothing.\n\n" 51062306a36Sopenharmony_ci " E.g. to create 3 vCPUs, pin vCPU0=>pCPU22, vCPU1=>pCPU23,\n" 51162306a36Sopenharmony_ci " vCPU2=>pCPU24, and pin the application task to pCPU50:\n\n" 51262306a36Sopenharmony_ci " %s -v 3 -c 22,23,24,50\n\n" 51362306a36Sopenharmony_ci " To leave the application task unpinned, drop the final entry:\n\n" 51462306a36Sopenharmony_ci " %s -v 3 -c 22,23,24\n\n" 51562306a36Sopenharmony_ci " (default: no pinning)\n", name, name); 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_civoid kvm_parse_vcpu_pinning(const char *pcpus_string, uint32_t vcpu_to_pcpu[], 51962306a36Sopenharmony_ci int nr_vcpus) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci cpu_set_t allowed_mask; 52262306a36Sopenharmony_ci char *cpu, *cpu_list; 52362306a36Sopenharmony_ci char delim[2] = ","; 52462306a36Sopenharmony_ci int i, r; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci cpu_list = strdup(pcpus_string); 52762306a36Sopenharmony_ci TEST_ASSERT(cpu_list, "strdup() allocation failed.\n"); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci r = sched_getaffinity(0, sizeof(allowed_mask), &allowed_mask); 53062306a36Sopenharmony_ci TEST_ASSERT(!r, "sched_getaffinity() failed"); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci cpu = strtok(cpu_list, delim); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* 1. Get all pcpus for vcpus. */ 53562306a36Sopenharmony_ci for (i = 0; i < nr_vcpus; i++) { 53662306a36Sopenharmony_ci TEST_ASSERT(cpu, "pCPU not provided for vCPU '%d'\n", i); 53762306a36Sopenharmony_ci vcpu_to_pcpu[i] = parse_pcpu(cpu, &allowed_mask); 53862306a36Sopenharmony_ci cpu = strtok(NULL, delim); 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* 2. Check if the main worker needs to be pinned. */ 54262306a36Sopenharmony_ci if (cpu) { 54362306a36Sopenharmony_ci kvm_pin_this_task_to_pcpu(parse_pcpu(cpu, &allowed_mask)); 54462306a36Sopenharmony_ci cpu = strtok(NULL, delim); 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci TEST_ASSERT(!cpu, "pCPU list contains trailing garbage characters '%s'", cpu); 54862306a36Sopenharmony_ci free(cpu_list); 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci/* 55262306a36Sopenharmony_ci * Userspace Memory Region Find 55362306a36Sopenharmony_ci * 55462306a36Sopenharmony_ci * Input Args: 55562306a36Sopenharmony_ci * vm - Virtual Machine 55662306a36Sopenharmony_ci * start - Starting VM physical address 55762306a36Sopenharmony_ci * end - Ending VM physical address, inclusive. 55862306a36Sopenharmony_ci * 55962306a36Sopenharmony_ci * Output Args: None 56062306a36Sopenharmony_ci * 56162306a36Sopenharmony_ci * Return: 56262306a36Sopenharmony_ci * Pointer to overlapping region, NULL if no such region. 56362306a36Sopenharmony_ci * 56462306a36Sopenharmony_ci * Searches for a region with any physical memory that overlaps with 56562306a36Sopenharmony_ci * any portion of the guest physical addresses from start to end 56662306a36Sopenharmony_ci * inclusive. If multiple overlapping regions exist, a pointer to any 56762306a36Sopenharmony_ci * of the regions is returned. Null is returned only when no overlapping 56862306a36Sopenharmony_ci * region exists. 56962306a36Sopenharmony_ci */ 57062306a36Sopenharmony_cistatic struct userspace_mem_region * 57162306a36Sopenharmony_ciuserspace_mem_region_find(struct kvm_vm *vm, uint64_t start, uint64_t end) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct rb_node *node; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci for (node = vm->regions.gpa_tree.rb_node; node; ) { 57662306a36Sopenharmony_ci struct userspace_mem_region *region = 57762306a36Sopenharmony_ci container_of(node, struct userspace_mem_region, gpa_node); 57862306a36Sopenharmony_ci uint64_t existing_start = region->region.guest_phys_addr; 57962306a36Sopenharmony_ci uint64_t existing_end = region->region.guest_phys_addr 58062306a36Sopenharmony_ci + region->region.memory_size - 1; 58162306a36Sopenharmony_ci if (start <= existing_end && end >= existing_start) 58262306a36Sopenharmony_ci return region; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (start < existing_start) 58562306a36Sopenharmony_ci node = node->rb_left; 58662306a36Sopenharmony_ci else 58762306a36Sopenharmony_ci node = node->rb_right; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci return NULL; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci/* 59462306a36Sopenharmony_ci * KVM Userspace Memory Region Find 59562306a36Sopenharmony_ci * 59662306a36Sopenharmony_ci * Input Args: 59762306a36Sopenharmony_ci * vm - Virtual Machine 59862306a36Sopenharmony_ci * start - Starting VM physical address 59962306a36Sopenharmony_ci * end - Ending VM physical address, inclusive. 60062306a36Sopenharmony_ci * 60162306a36Sopenharmony_ci * Output Args: None 60262306a36Sopenharmony_ci * 60362306a36Sopenharmony_ci * Return: 60462306a36Sopenharmony_ci * Pointer to overlapping region, NULL if no such region. 60562306a36Sopenharmony_ci * 60662306a36Sopenharmony_ci * Public interface to userspace_mem_region_find. Allows tests to look up 60762306a36Sopenharmony_ci * the memslot datastructure for a given range of guest physical memory. 60862306a36Sopenharmony_ci */ 60962306a36Sopenharmony_cistruct kvm_userspace_memory_region * 61062306a36Sopenharmony_cikvm_userspace_memory_region_find(struct kvm_vm *vm, uint64_t start, 61162306a36Sopenharmony_ci uint64_t end) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci struct userspace_mem_region *region; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci region = userspace_mem_region_find(vm, start, end); 61662306a36Sopenharmony_ci if (!region) 61762306a36Sopenharmony_ci return NULL; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci return ®ion->region; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci__weak void vcpu_arch_free(struct kvm_vcpu *vcpu) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci/* 62862306a36Sopenharmony_ci * VM VCPU Remove 62962306a36Sopenharmony_ci * 63062306a36Sopenharmony_ci * Input Args: 63162306a36Sopenharmony_ci * vcpu - VCPU to remove 63262306a36Sopenharmony_ci * 63362306a36Sopenharmony_ci * Output Args: None 63462306a36Sopenharmony_ci * 63562306a36Sopenharmony_ci * Return: None, TEST_ASSERT failures for all error conditions 63662306a36Sopenharmony_ci * 63762306a36Sopenharmony_ci * Removes a vCPU from a VM and frees its resources. 63862306a36Sopenharmony_ci */ 63962306a36Sopenharmony_cistatic void vm_vcpu_rm(struct kvm_vm *vm, struct kvm_vcpu *vcpu) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci int ret; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (vcpu->dirty_gfns) { 64462306a36Sopenharmony_ci ret = munmap(vcpu->dirty_gfns, vm->dirty_ring_size); 64562306a36Sopenharmony_ci TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("munmap()", ret)); 64662306a36Sopenharmony_ci vcpu->dirty_gfns = NULL; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci ret = munmap(vcpu->run, vcpu_mmap_sz()); 65062306a36Sopenharmony_ci TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("munmap()", ret)); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci ret = close(vcpu->fd); 65362306a36Sopenharmony_ci TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("close()", ret)); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci list_del(&vcpu->list); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci vcpu_arch_free(vcpu); 65862306a36Sopenharmony_ci free(vcpu); 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_civoid kvm_vm_release(struct kvm_vm *vmp) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci struct kvm_vcpu *vcpu, *tmp; 66462306a36Sopenharmony_ci int ret; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci list_for_each_entry_safe(vcpu, tmp, &vmp->vcpus, list) 66762306a36Sopenharmony_ci vm_vcpu_rm(vmp, vcpu); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci ret = close(vmp->fd); 67062306a36Sopenharmony_ci TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("close()", ret)); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci ret = close(vmp->kvm_fd); 67362306a36Sopenharmony_ci TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("close()", ret)); 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic void __vm_mem_region_delete(struct kvm_vm *vm, 67762306a36Sopenharmony_ci struct userspace_mem_region *region, 67862306a36Sopenharmony_ci bool unlink) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci int ret; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (unlink) { 68362306a36Sopenharmony_ci rb_erase(®ion->gpa_node, &vm->regions.gpa_tree); 68462306a36Sopenharmony_ci rb_erase(®ion->hva_node, &vm->regions.hva_tree); 68562306a36Sopenharmony_ci hash_del(®ion->slot_node); 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci region->region.memory_size = 0; 68962306a36Sopenharmony_ci vm_ioctl(vm, KVM_SET_USER_MEMORY_REGION, ®ion->region); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci sparsebit_free(®ion->unused_phy_pages); 69262306a36Sopenharmony_ci ret = munmap(region->mmap_start, region->mmap_size); 69362306a36Sopenharmony_ci TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("munmap()", ret)); 69462306a36Sopenharmony_ci if (region->fd >= 0) { 69562306a36Sopenharmony_ci /* There's an extra map when using shared memory. */ 69662306a36Sopenharmony_ci ret = munmap(region->mmap_alias, region->mmap_size); 69762306a36Sopenharmony_ci TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("munmap()", ret)); 69862306a36Sopenharmony_ci close(region->fd); 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci free(region); 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci/* 70562306a36Sopenharmony_ci * Destroys and frees the VM pointed to by vmp. 70662306a36Sopenharmony_ci */ 70762306a36Sopenharmony_civoid kvm_vm_free(struct kvm_vm *vmp) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci int ctr; 71062306a36Sopenharmony_ci struct hlist_node *node; 71162306a36Sopenharmony_ci struct userspace_mem_region *region; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci if (vmp == NULL) 71462306a36Sopenharmony_ci return; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* Free cached stats metadata and close FD */ 71762306a36Sopenharmony_ci if (vmp->stats_fd) { 71862306a36Sopenharmony_ci free(vmp->stats_desc); 71962306a36Sopenharmony_ci close(vmp->stats_fd); 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* Free userspace_mem_regions. */ 72362306a36Sopenharmony_ci hash_for_each_safe(vmp->regions.slot_hash, ctr, node, region, slot_node) 72462306a36Sopenharmony_ci __vm_mem_region_delete(vmp, region, false); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* Free sparsebit arrays. */ 72762306a36Sopenharmony_ci sparsebit_free(&vmp->vpages_valid); 72862306a36Sopenharmony_ci sparsebit_free(&vmp->vpages_mapped); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci kvm_vm_release(vmp); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* Free the structure describing the VM. */ 73362306a36Sopenharmony_ci free(vmp); 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ciint kvm_memfd_alloc(size_t size, bool hugepages) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci int memfd_flags = MFD_CLOEXEC; 73962306a36Sopenharmony_ci int fd, r; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (hugepages) 74262306a36Sopenharmony_ci memfd_flags |= MFD_HUGETLB; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci fd = memfd_create("kvm_selftest", memfd_flags); 74562306a36Sopenharmony_ci TEST_ASSERT(fd != -1, __KVM_SYSCALL_ERROR("memfd_create()", fd)); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci r = ftruncate(fd, size); 74862306a36Sopenharmony_ci TEST_ASSERT(!r, __KVM_SYSCALL_ERROR("ftruncate()", r)); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci r = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, size); 75162306a36Sopenharmony_ci TEST_ASSERT(!r, __KVM_SYSCALL_ERROR("fallocate()", r)); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci return fd; 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci/* 75762306a36Sopenharmony_ci * Memory Compare, host virtual to guest virtual 75862306a36Sopenharmony_ci * 75962306a36Sopenharmony_ci * Input Args: 76062306a36Sopenharmony_ci * hva - Starting host virtual address 76162306a36Sopenharmony_ci * vm - Virtual Machine 76262306a36Sopenharmony_ci * gva - Starting guest virtual address 76362306a36Sopenharmony_ci * len - number of bytes to compare 76462306a36Sopenharmony_ci * 76562306a36Sopenharmony_ci * Output Args: None 76662306a36Sopenharmony_ci * 76762306a36Sopenharmony_ci * Input/Output Args: None 76862306a36Sopenharmony_ci * 76962306a36Sopenharmony_ci * Return: 77062306a36Sopenharmony_ci * Returns 0 if the bytes starting at hva for a length of len 77162306a36Sopenharmony_ci * are equal the guest virtual bytes starting at gva. Returns 77262306a36Sopenharmony_ci * a value < 0, if bytes at hva are less than those at gva. 77362306a36Sopenharmony_ci * Otherwise a value > 0 is returned. 77462306a36Sopenharmony_ci * 77562306a36Sopenharmony_ci * Compares the bytes starting at the host virtual address hva, for 77662306a36Sopenharmony_ci * a length of len, to the guest bytes starting at the guest virtual 77762306a36Sopenharmony_ci * address given by gva. 77862306a36Sopenharmony_ci */ 77962306a36Sopenharmony_ciint kvm_memcmp_hva_gva(void *hva, struct kvm_vm *vm, vm_vaddr_t gva, size_t len) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci size_t amt; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* 78462306a36Sopenharmony_ci * Compare a batch of bytes until either a match is found 78562306a36Sopenharmony_ci * or all the bytes have been compared. 78662306a36Sopenharmony_ci */ 78762306a36Sopenharmony_ci for (uintptr_t offset = 0; offset < len; offset += amt) { 78862306a36Sopenharmony_ci uintptr_t ptr1 = (uintptr_t)hva + offset; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci /* 79162306a36Sopenharmony_ci * Determine host address for guest virtual address 79262306a36Sopenharmony_ci * at offset. 79362306a36Sopenharmony_ci */ 79462306a36Sopenharmony_ci uintptr_t ptr2 = (uintptr_t)addr_gva2hva(vm, gva + offset); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* 79762306a36Sopenharmony_ci * Determine amount to compare on this pass. 79862306a36Sopenharmony_ci * Don't allow the comparsion to cross a page boundary. 79962306a36Sopenharmony_ci */ 80062306a36Sopenharmony_ci amt = len - offset; 80162306a36Sopenharmony_ci if ((ptr1 >> vm->page_shift) != ((ptr1 + amt) >> vm->page_shift)) 80262306a36Sopenharmony_ci amt = vm->page_size - (ptr1 % vm->page_size); 80362306a36Sopenharmony_ci if ((ptr2 >> vm->page_shift) != ((ptr2 + amt) >> vm->page_shift)) 80462306a36Sopenharmony_ci amt = vm->page_size - (ptr2 % vm->page_size); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci assert((ptr1 >> vm->page_shift) == ((ptr1 + amt - 1) >> vm->page_shift)); 80762306a36Sopenharmony_ci assert((ptr2 >> vm->page_shift) == ((ptr2 + amt - 1) >> vm->page_shift)); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci /* 81062306a36Sopenharmony_ci * Perform the comparison. If there is a difference 81162306a36Sopenharmony_ci * return that result to the caller, otherwise need 81262306a36Sopenharmony_ci * to continue on looking for a mismatch. 81362306a36Sopenharmony_ci */ 81462306a36Sopenharmony_ci int ret = memcmp((void *)ptr1, (void *)ptr2, amt); 81562306a36Sopenharmony_ci if (ret != 0) 81662306a36Sopenharmony_ci return ret; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* 82062306a36Sopenharmony_ci * No mismatch found. Let the caller know the two memory 82162306a36Sopenharmony_ci * areas are equal. 82262306a36Sopenharmony_ci */ 82362306a36Sopenharmony_ci return 0; 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic void vm_userspace_mem_region_gpa_insert(struct rb_root *gpa_tree, 82762306a36Sopenharmony_ci struct userspace_mem_region *region) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci struct rb_node **cur, *parent; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci for (cur = &gpa_tree->rb_node, parent = NULL; *cur; ) { 83262306a36Sopenharmony_ci struct userspace_mem_region *cregion; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci cregion = container_of(*cur, typeof(*cregion), gpa_node); 83562306a36Sopenharmony_ci parent = *cur; 83662306a36Sopenharmony_ci if (region->region.guest_phys_addr < 83762306a36Sopenharmony_ci cregion->region.guest_phys_addr) 83862306a36Sopenharmony_ci cur = &(*cur)->rb_left; 83962306a36Sopenharmony_ci else { 84062306a36Sopenharmony_ci TEST_ASSERT(region->region.guest_phys_addr != 84162306a36Sopenharmony_ci cregion->region.guest_phys_addr, 84262306a36Sopenharmony_ci "Duplicate GPA in region tree"); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci cur = &(*cur)->rb_right; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci rb_link_node(®ion->gpa_node, parent, cur); 84962306a36Sopenharmony_ci rb_insert_color(®ion->gpa_node, gpa_tree); 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic void vm_userspace_mem_region_hva_insert(struct rb_root *hva_tree, 85362306a36Sopenharmony_ci struct userspace_mem_region *region) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci struct rb_node **cur, *parent; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci for (cur = &hva_tree->rb_node, parent = NULL; *cur; ) { 85862306a36Sopenharmony_ci struct userspace_mem_region *cregion; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci cregion = container_of(*cur, typeof(*cregion), hva_node); 86162306a36Sopenharmony_ci parent = *cur; 86262306a36Sopenharmony_ci if (region->host_mem < cregion->host_mem) 86362306a36Sopenharmony_ci cur = &(*cur)->rb_left; 86462306a36Sopenharmony_ci else { 86562306a36Sopenharmony_ci TEST_ASSERT(region->host_mem != 86662306a36Sopenharmony_ci cregion->host_mem, 86762306a36Sopenharmony_ci "Duplicate HVA in region tree"); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci cur = &(*cur)->rb_right; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci rb_link_node(®ion->hva_node, parent, cur); 87462306a36Sopenharmony_ci rb_insert_color(®ion->hva_node, hva_tree); 87562306a36Sopenharmony_ci} 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ciint __vm_set_user_memory_region(struct kvm_vm *vm, uint32_t slot, uint32_t flags, 87962306a36Sopenharmony_ci uint64_t gpa, uint64_t size, void *hva) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci struct kvm_userspace_memory_region region = { 88262306a36Sopenharmony_ci .slot = slot, 88362306a36Sopenharmony_ci .flags = flags, 88462306a36Sopenharmony_ci .guest_phys_addr = gpa, 88562306a36Sopenharmony_ci .memory_size = size, 88662306a36Sopenharmony_ci .userspace_addr = (uintptr_t)hva, 88762306a36Sopenharmony_ci }; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci return ioctl(vm->fd, KVM_SET_USER_MEMORY_REGION, ®ion); 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_civoid vm_set_user_memory_region(struct kvm_vm *vm, uint32_t slot, uint32_t flags, 89362306a36Sopenharmony_ci uint64_t gpa, uint64_t size, void *hva) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci int ret = __vm_set_user_memory_region(vm, slot, flags, gpa, size, hva); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci TEST_ASSERT(!ret, "KVM_SET_USER_MEMORY_REGION failed, errno = %d (%s)", 89862306a36Sopenharmony_ci errno, strerror(errno)); 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci/* 90262306a36Sopenharmony_ci * VM Userspace Memory Region Add 90362306a36Sopenharmony_ci * 90462306a36Sopenharmony_ci * Input Args: 90562306a36Sopenharmony_ci * vm - Virtual Machine 90662306a36Sopenharmony_ci * src_type - Storage source for this region. 90762306a36Sopenharmony_ci * NULL to use anonymous memory. 90862306a36Sopenharmony_ci * guest_paddr - Starting guest physical address 90962306a36Sopenharmony_ci * slot - KVM region slot 91062306a36Sopenharmony_ci * npages - Number of physical pages 91162306a36Sopenharmony_ci * flags - KVM memory region flags (e.g. KVM_MEM_LOG_DIRTY_PAGES) 91262306a36Sopenharmony_ci * 91362306a36Sopenharmony_ci * Output Args: None 91462306a36Sopenharmony_ci * 91562306a36Sopenharmony_ci * Return: None 91662306a36Sopenharmony_ci * 91762306a36Sopenharmony_ci * Allocates a memory area of the number of pages specified by npages 91862306a36Sopenharmony_ci * and maps it to the VM specified by vm, at a starting physical address 91962306a36Sopenharmony_ci * given by guest_paddr. The region is created with a KVM region slot 92062306a36Sopenharmony_ci * given by slot, which must be unique and < KVM_MEM_SLOTS_NUM. The 92162306a36Sopenharmony_ci * region is created with the flags given by flags. 92262306a36Sopenharmony_ci */ 92362306a36Sopenharmony_civoid vm_userspace_mem_region_add(struct kvm_vm *vm, 92462306a36Sopenharmony_ci enum vm_mem_backing_src_type src_type, 92562306a36Sopenharmony_ci uint64_t guest_paddr, uint32_t slot, uint64_t npages, 92662306a36Sopenharmony_ci uint32_t flags) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci int ret; 92962306a36Sopenharmony_ci struct userspace_mem_region *region; 93062306a36Sopenharmony_ci size_t backing_src_pagesz = get_backing_src_pagesz(src_type); 93162306a36Sopenharmony_ci size_t alignment; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci TEST_ASSERT(vm_adjust_num_guest_pages(vm->mode, npages) == npages, 93462306a36Sopenharmony_ci "Number of guest pages is not compatible with the host. " 93562306a36Sopenharmony_ci "Try npages=%d", vm_adjust_num_guest_pages(vm->mode, npages)); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci TEST_ASSERT((guest_paddr % vm->page_size) == 0, "Guest physical " 93862306a36Sopenharmony_ci "address not on a page boundary.\n" 93962306a36Sopenharmony_ci " guest_paddr: 0x%lx vm->page_size: 0x%x", 94062306a36Sopenharmony_ci guest_paddr, vm->page_size); 94162306a36Sopenharmony_ci TEST_ASSERT((((guest_paddr >> vm->page_shift) + npages) - 1) 94262306a36Sopenharmony_ci <= vm->max_gfn, "Physical range beyond maximum " 94362306a36Sopenharmony_ci "supported physical address,\n" 94462306a36Sopenharmony_ci " guest_paddr: 0x%lx npages: 0x%lx\n" 94562306a36Sopenharmony_ci " vm->max_gfn: 0x%lx vm->page_size: 0x%x", 94662306a36Sopenharmony_ci guest_paddr, npages, vm->max_gfn, vm->page_size); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci /* 94962306a36Sopenharmony_ci * Confirm a mem region with an overlapping address doesn't 95062306a36Sopenharmony_ci * already exist. 95162306a36Sopenharmony_ci */ 95262306a36Sopenharmony_ci region = (struct userspace_mem_region *) userspace_mem_region_find( 95362306a36Sopenharmony_ci vm, guest_paddr, (guest_paddr + npages * vm->page_size) - 1); 95462306a36Sopenharmony_ci if (region != NULL) 95562306a36Sopenharmony_ci TEST_FAIL("overlapping userspace_mem_region already " 95662306a36Sopenharmony_ci "exists\n" 95762306a36Sopenharmony_ci " requested guest_paddr: 0x%lx npages: 0x%lx " 95862306a36Sopenharmony_ci "page_size: 0x%x\n" 95962306a36Sopenharmony_ci " existing guest_paddr: 0x%lx size: 0x%lx", 96062306a36Sopenharmony_ci guest_paddr, npages, vm->page_size, 96162306a36Sopenharmony_ci (uint64_t) region->region.guest_phys_addr, 96262306a36Sopenharmony_ci (uint64_t) region->region.memory_size); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci /* Confirm no region with the requested slot already exists. */ 96562306a36Sopenharmony_ci hash_for_each_possible(vm->regions.slot_hash, region, slot_node, 96662306a36Sopenharmony_ci slot) { 96762306a36Sopenharmony_ci if (region->region.slot != slot) 96862306a36Sopenharmony_ci continue; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci TEST_FAIL("A mem region with the requested slot " 97162306a36Sopenharmony_ci "already exists.\n" 97262306a36Sopenharmony_ci " requested slot: %u paddr: 0x%lx npages: 0x%lx\n" 97362306a36Sopenharmony_ci " existing slot: %u paddr: 0x%lx size: 0x%lx", 97462306a36Sopenharmony_ci slot, guest_paddr, npages, 97562306a36Sopenharmony_ci region->region.slot, 97662306a36Sopenharmony_ci (uint64_t) region->region.guest_phys_addr, 97762306a36Sopenharmony_ci (uint64_t) region->region.memory_size); 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* Allocate and initialize new mem region structure. */ 98162306a36Sopenharmony_ci region = calloc(1, sizeof(*region)); 98262306a36Sopenharmony_ci TEST_ASSERT(region != NULL, "Insufficient Memory"); 98362306a36Sopenharmony_ci region->mmap_size = npages * vm->page_size; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci#ifdef __s390x__ 98662306a36Sopenharmony_ci /* On s390x, the host address must be aligned to 1M (due to PGSTEs) */ 98762306a36Sopenharmony_ci alignment = 0x100000; 98862306a36Sopenharmony_ci#else 98962306a36Sopenharmony_ci alignment = 1; 99062306a36Sopenharmony_ci#endif 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci /* 99362306a36Sopenharmony_ci * When using THP mmap is not guaranteed to returned a hugepage aligned 99462306a36Sopenharmony_ci * address so we have to pad the mmap. Padding is not needed for HugeTLB 99562306a36Sopenharmony_ci * because mmap will always return an address aligned to the HugeTLB 99662306a36Sopenharmony_ci * page size. 99762306a36Sopenharmony_ci */ 99862306a36Sopenharmony_ci if (src_type == VM_MEM_SRC_ANONYMOUS_THP) 99962306a36Sopenharmony_ci alignment = max(backing_src_pagesz, alignment); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci TEST_ASSERT_EQ(guest_paddr, align_up(guest_paddr, backing_src_pagesz)); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci /* Add enough memory to align up if necessary */ 100462306a36Sopenharmony_ci if (alignment > 1) 100562306a36Sopenharmony_ci region->mmap_size += alignment; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci region->fd = -1; 100862306a36Sopenharmony_ci if (backing_src_is_shared(src_type)) 100962306a36Sopenharmony_ci region->fd = kvm_memfd_alloc(region->mmap_size, 101062306a36Sopenharmony_ci src_type == VM_MEM_SRC_SHARED_HUGETLB); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci region->mmap_start = mmap(NULL, region->mmap_size, 101362306a36Sopenharmony_ci PROT_READ | PROT_WRITE, 101462306a36Sopenharmony_ci vm_mem_backing_src_alias(src_type)->flag, 101562306a36Sopenharmony_ci region->fd, 0); 101662306a36Sopenharmony_ci TEST_ASSERT(region->mmap_start != MAP_FAILED, 101762306a36Sopenharmony_ci __KVM_SYSCALL_ERROR("mmap()", (int)(unsigned long)MAP_FAILED)); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci TEST_ASSERT(!is_backing_src_hugetlb(src_type) || 102062306a36Sopenharmony_ci region->mmap_start == align_ptr_up(region->mmap_start, backing_src_pagesz), 102162306a36Sopenharmony_ci "mmap_start %p is not aligned to HugeTLB page size 0x%lx", 102262306a36Sopenharmony_ci region->mmap_start, backing_src_pagesz); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci /* Align host address */ 102562306a36Sopenharmony_ci region->host_mem = align_ptr_up(region->mmap_start, alignment); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci /* As needed perform madvise */ 102862306a36Sopenharmony_ci if ((src_type == VM_MEM_SRC_ANONYMOUS || 102962306a36Sopenharmony_ci src_type == VM_MEM_SRC_ANONYMOUS_THP) && thp_configured()) { 103062306a36Sopenharmony_ci ret = madvise(region->host_mem, npages * vm->page_size, 103162306a36Sopenharmony_ci src_type == VM_MEM_SRC_ANONYMOUS ? MADV_NOHUGEPAGE : MADV_HUGEPAGE); 103262306a36Sopenharmony_ci TEST_ASSERT(ret == 0, "madvise failed, addr: %p length: 0x%lx src_type: %s", 103362306a36Sopenharmony_ci region->host_mem, npages * vm->page_size, 103462306a36Sopenharmony_ci vm_mem_backing_src_alias(src_type)->name); 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci region->backing_src_type = src_type; 103862306a36Sopenharmony_ci region->unused_phy_pages = sparsebit_alloc(); 103962306a36Sopenharmony_ci sparsebit_set_num(region->unused_phy_pages, 104062306a36Sopenharmony_ci guest_paddr >> vm->page_shift, npages); 104162306a36Sopenharmony_ci region->region.slot = slot; 104262306a36Sopenharmony_ci region->region.flags = flags; 104362306a36Sopenharmony_ci region->region.guest_phys_addr = guest_paddr; 104462306a36Sopenharmony_ci region->region.memory_size = npages * vm->page_size; 104562306a36Sopenharmony_ci region->region.userspace_addr = (uintptr_t) region->host_mem; 104662306a36Sopenharmony_ci ret = __vm_ioctl(vm, KVM_SET_USER_MEMORY_REGION, ®ion->region); 104762306a36Sopenharmony_ci TEST_ASSERT(ret == 0, "KVM_SET_USER_MEMORY_REGION IOCTL failed,\n" 104862306a36Sopenharmony_ci " rc: %i errno: %i\n" 104962306a36Sopenharmony_ci " slot: %u flags: 0x%x\n" 105062306a36Sopenharmony_ci " guest_phys_addr: 0x%lx size: 0x%lx", 105162306a36Sopenharmony_ci ret, errno, slot, flags, 105262306a36Sopenharmony_ci guest_paddr, (uint64_t) region->region.memory_size); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci /* Add to quick lookup data structures */ 105562306a36Sopenharmony_ci vm_userspace_mem_region_gpa_insert(&vm->regions.gpa_tree, region); 105662306a36Sopenharmony_ci vm_userspace_mem_region_hva_insert(&vm->regions.hva_tree, region); 105762306a36Sopenharmony_ci hash_add(vm->regions.slot_hash, ®ion->slot_node, slot); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci /* If shared memory, create an alias. */ 106062306a36Sopenharmony_ci if (region->fd >= 0) { 106162306a36Sopenharmony_ci region->mmap_alias = mmap(NULL, region->mmap_size, 106262306a36Sopenharmony_ci PROT_READ | PROT_WRITE, 106362306a36Sopenharmony_ci vm_mem_backing_src_alias(src_type)->flag, 106462306a36Sopenharmony_ci region->fd, 0); 106562306a36Sopenharmony_ci TEST_ASSERT(region->mmap_alias != MAP_FAILED, 106662306a36Sopenharmony_ci __KVM_SYSCALL_ERROR("mmap()", (int)(unsigned long)MAP_FAILED)); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci /* Align host alias address */ 106962306a36Sopenharmony_ci region->host_alias = align_ptr_up(region->mmap_alias, alignment); 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci} 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci/* 107462306a36Sopenharmony_ci * Memslot to region 107562306a36Sopenharmony_ci * 107662306a36Sopenharmony_ci * Input Args: 107762306a36Sopenharmony_ci * vm - Virtual Machine 107862306a36Sopenharmony_ci * memslot - KVM memory slot ID 107962306a36Sopenharmony_ci * 108062306a36Sopenharmony_ci * Output Args: None 108162306a36Sopenharmony_ci * 108262306a36Sopenharmony_ci * Return: 108362306a36Sopenharmony_ci * Pointer to memory region structure that describe memory region 108462306a36Sopenharmony_ci * using kvm memory slot ID given by memslot. TEST_ASSERT failure 108562306a36Sopenharmony_ci * on error (e.g. currently no memory region using memslot as a KVM 108662306a36Sopenharmony_ci * memory slot ID). 108762306a36Sopenharmony_ci */ 108862306a36Sopenharmony_cistruct userspace_mem_region * 108962306a36Sopenharmony_cimemslot2region(struct kvm_vm *vm, uint32_t memslot) 109062306a36Sopenharmony_ci{ 109162306a36Sopenharmony_ci struct userspace_mem_region *region; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci hash_for_each_possible(vm->regions.slot_hash, region, slot_node, 109462306a36Sopenharmony_ci memslot) 109562306a36Sopenharmony_ci if (region->region.slot == memslot) 109662306a36Sopenharmony_ci return region; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci fprintf(stderr, "No mem region with the requested slot found,\n" 109962306a36Sopenharmony_ci " requested slot: %u\n", memslot); 110062306a36Sopenharmony_ci fputs("---- vm dump ----\n", stderr); 110162306a36Sopenharmony_ci vm_dump(stderr, vm, 2); 110262306a36Sopenharmony_ci TEST_FAIL("Mem region not found"); 110362306a36Sopenharmony_ci return NULL; 110462306a36Sopenharmony_ci} 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci/* 110762306a36Sopenharmony_ci * VM Memory Region Flags Set 110862306a36Sopenharmony_ci * 110962306a36Sopenharmony_ci * Input Args: 111062306a36Sopenharmony_ci * vm - Virtual Machine 111162306a36Sopenharmony_ci * flags - Starting guest physical address 111262306a36Sopenharmony_ci * 111362306a36Sopenharmony_ci * Output Args: None 111462306a36Sopenharmony_ci * 111562306a36Sopenharmony_ci * Return: None 111662306a36Sopenharmony_ci * 111762306a36Sopenharmony_ci * Sets the flags of the memory region specified by the value of slot, 111862306a36Sopenharmony_ci * to the values given by flags. 111962306a36Sopenharmony_ci */ 112062306a36Sopenharmony_civoid vm_mem_region_set_flags(struct kvm_vm *vm, uint32_t slot, uint32_t flags) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci int ret; 112362306a36Sopenharmony_ci struct userspace_mem_region *region; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci region = memslot2region(vm, slot); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci region->region.flags = flags; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci ret = __vm_ioctl(vm, KVM_SET_USER_MEMORY_REGION, ®ion->region); 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci TEST_ASSERT(ret == 0, "KVM_SET_USER_MEMORY_REGION IOCTL failed,\n" 113262306a36Sopenharmony_ci " rc: %i errno: %i slot: %u flags: 0x%x", 113362306a36Sopenharmony_ci ret, errno, slot, flags); 113462306a36Sopenharmony_ci} 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci/* 113762306a36Sopenharmony_ci * VM Memory Region Move 113862306a36Sopenharmony_ci * 113962306a36Sopenharmony_ci * Input Args: 114062306a36Sopenharmony_ci * vm - Virtual Machine 114162306a36Sopenharmony_ci * slot - Slot of the memory region to move 114262306a36Sopenharmony_ci * new_gpa - Starting guest physical address 114362306a36Sopenharmony_ci * 114462306a36Sopenharmony_ci * Output Args: None 114562306a36Sopenharmony_ci * 114662306a36Sopenharmony_ci * Return: None 114762306a36Sopenharmony_ci * 114862306a36Sopenharmony_ci * Change the gpa of a memory region. 114962306a36Sopenharmony_ci */ 115062306a36Sopenharmony_civoid vm_mem_region_move(struct kvm_vm *vm, uint32_t slot, uint64_t new_gpa) 115162306a36Sopenharmony_ci{ 115262306a36Sopenharmony_ci struct userspace_mem_region *region; 115362306a36Sopenharmony_ci int ret; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci region = memslot2region(vm, slot); 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci region->region.guest_phys_addr = new_gpa; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci ret = __vm_ioctl(vm, KVM_SET_USER_MEMORY_REGION, ®ion->region); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci TEST_ASSERT(!ret, "KVM_SET_USER_MEMORY_REGION failed\n" 116262306a36Sopenharmony_ci "ret: %i errno: %i slot: %u new_gpa: 0x%lx", 116362306a36Sopenharmony_ci ret, errno, slot, new_gpa); 116462306a36Sopenharmony_ci} 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci/* 116762306a36Sopenharmony_ci * VM Memory Region Delete 116862306a36Sopenharmony_ci * 116962306a36Sopenharmony_ci * Input Args: 117062306a36Sopenharmony_ci * vm - Virtual Machine 117162306a36Sopenharmony_ci * slot - Slot of the memory region to delete 117262306a36Sopenharmony_ci * 117362306a36Sopenharmony_ci * Output Args: None 117462306a36Sopenharmony_ci * 117562306a36Sopenharmony_ci * Return: None 117662306a36Sopenharmony_ci * 117762306a36Sopenharmony_ci * Delete a memory region. 117862306a36Sopenharmony_ci */ 117962306a36Sopenharmony_civoid vm_mem_region_delete(struct kvm_vm *vm, uint32_t slot) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci __vm_mem_region_delete(vm, memslot2region(vm, slot), true); 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci/* Returns the size of a vCPU's kvm_run structure. */ 118562306a36Sopenharmony_cistatic int vcpu_mmap_sz(void) 118662306a36Sopenharmony_ci{ 118762306a36Sopenharmony_ci int dev_fd, ret; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci dev_fd = open_kvm_dev_path_or_exit(); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci ret = ioctl(dev_fd, KVM_GET_VCPU_MMAP_SIZE, NULL); 119262306a36Sopenharmony_ci TEST_ASSERT(ret >= sizeof(struct kvm_run), 119362306a36Sopenharmony_ci KVM_IOCTL_ERROR(KVM_GET_VCPU_MMAP_SIZE, ret)); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci close(dev_fd); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci return ret; 119862306a36Sopenharmony_ci} 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_cistatic bool vcpu_exists(struct kvm_vm *vm, uint32_t vcpu_id) 120162306a36Sopenharmony_ci{ 120262306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci list_for_each_entry(vcpu, &vm->vcpus, list) { 120562306a36Sopenharmony_ci if (vcpu->id == vcpu_id) 120662306a36Sopenharmony_ci return true; 120762306a36Sopenharmony_ci } 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci return false; 121062306a36Sopenharmony_ci} 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci/* 121362306a36Sopenharmony_ci * Adds a virtual CPU to the VM specified by vm with the ID given by vcpu_id. 121462306a36Sopenharmony_ci * No additional vCPU setup is done. Returns the vCPU. 121562306a36Sopenharmony_ci */ 121662306a36Sopenharmony_cistruct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id) 121762306a36Sopenharmony_ci{ 121862306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci /* Confirm a vcpu with the specified id doesn't already exist. */ 122162306a36Sopenharmony_ci TEST_ASSERT(!vcpu_exists(vm, vcpu_id), "vCPU%d already exists\n", vcpu_id); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci /* Allocate and initialize new vcpu structure. */ 122462306a36Sopenharmony_ci vcpu = calloc(1, sizeof(*vcpu)); 122562306a36Sopenharmony_ci TEST_ASSERT(vcpu != NULL, "Insufficient Memory"); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci vcpu->vm = vm; 122862306a36Sopenharmony_ci vcpu->id = vcpu_id; 122962306a36Sopenharmony_ci vcpu->fd = __vm_ioctl(vm, KVM_CREATE_VCPU, (void *)(unsigned long)vcpu_id); 123062306a36Sopenharmony_ci TEST_ASSERT(vcpu->fd >= 0, KVM_IOCTL_ERROR(KVM_CREATE_VCPU, vcpu->fd)); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci TEST_ASSERT(vcpu_mmap_sz() >= sizeof(*vcpu->run), "vcpu mmap size " 123362306a36Sopenharmony_ci "smaller than expected, vcpu_mmap_sz: %i expected_min: %zi", 123462306a36Sopenharmony_ci vcpu_mmap_sz(), sizeof(*vcpu->run)); 123562306a36Sopenharmony_ci vcpu->run = (struct kvm_run *) mmap(NULL, vcpu_mmap_sz(), 123662306a36Sopenharmony_ci PROT_READ | PROT_WRITE, MAP_SHARED, vcpu->fd, 0); 123762306a36Sopenharmony_ci TEST_ASSERT(vcpu->run != MAP_FAILED, 123862306a36Sopenharmony_ci __KVM_SYSCALL_ERROR("mmap()", (int)(unsigned long)MAP_FAILED)); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci /* Add to linked-list of VCPUs. */ 124162306a36Sopenharmony_ci list_add(&vcpu->list, &vm->vcpus); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci return vcpu; 124462306a36Sopenharmony_ci} 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci/* 124762306a36Sopenharmony_ci * VM Virtual Address Unused Gap 124862306a36Sopenharmony_ci * 124962306a36Sopenharmony_ci * Input Args: 125062306a36Sopenharmony_ci * vm - Virtual Machine 125162306a36Sopenharmony_ci * sz - Size (bytes) 125262306a36Sopenharmony_ci * vaddr_min - Minimum Virtual Address 125362306a36Sopenharmony_ci * 125462306a36Sopenharmony_ci * Output Args: None 125562306a36Sopenharmony_ci * 125662306a36Sopenharmony_ci * Return: 125762306a36Sopenharmony_ci * Lowest virtual address at or below vaddr_min, with at least 125862306a36Sopenharmony_ci * sz unused bytes. TEST_ASSERT failure if no area of at least 125962306a36Sopenharmony_ci * size sz is available. 126062306a36Sopenharmony_ci * 126162306a36Sopenharmony_ci * Within the VM specified by vm, locates the lowest starting virtual 126262306a36Sopenharmony_ci * address >= vaddr_min, that has at least sz unallocated bytes. A 126362306a36Sopenharmony_ci * TEST_ASSERT failure occurs for invalid input or no area of at least 126462306a36Sopenharmony_ci * sz unallocated bytes >= vaddr_min is available. 126562306a36Sopenharmony_ci */ 126662306a36Sopenharmony_civm_vaddr_t vm_vaddr_unused_gap(struct kvm_vm *vm, size_t sz, 126762306a36Sopenharmony_ci vm_vaddr_t vaddr_min) 126862306a36Sopenharmony_ci{ 126962306a36Sopenharmony_ci uint64_t pages = (sz + vm->page_size - 1) >> vm->page_shift; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci /* Determine lowest permitted virtual page index. */ 127262306a36Sopenharmony_ci uint64_t pgidx_start = (vaddr_min + vm->page_size - 1) >> vm->page_shift; 127362306a36Sopenharmony_ci if ((pgidx_start * vm->page_size) < vaddr_min) 127462306a36Sopenharmony_ci goto no_va_found; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci /* Loop over section with enough valid virtual page indexes. */ 127762306a36Sopenharmony_ci if (!sparsebit_is_set_num(vm->vpages_valid, 127862306a36Sopenharmony_ci pgidx_start, pages)) 127962306a36Sopenharmony_ci pgidx_start = sparsebit_next_set_num(vm->vpages_valid, 128062306a36Sopenharmony_ci pgidx_start, pages); 128162306a36Sopenharmony_ci do { 128262306a36Sopenharmony_ci /* 128362306a36Sopenharmony_ci * Are there enough unused virtual pages available at 128462306a36Sopenharmony_ci * the currently proposed starting virtual page index. 128562306a36Sopenharmony_ci * If not, adjust proposed starting index to next 128662306a36Sopenharmony_ci * possible. 128762306a36Sopenharmony_ci */ 128862306a36Sopenharmony_ci if (sparsebit_is_clear_num(vm->vpages_mapped, 128962306a36Sopenharmony_ci pgidx_start, pages)) 129062306a36Sopenharmony_ci goto va_found; 129162306a36Sopenharmony_ci pgidx_start = sparsebit_next_clear_num(vm->vpages_mapped, 129262306a36Sopenharmony_ci pgidx_start, pages); 129362306a36Sopenharmony_ci if (pgidx_start == 0) 129462306a36Sopenharmony_ci goto no_va_found; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci /* 129762306a36Sopenharmony_ci * If needed, adjust proposed starting virtual address, 129862306a36Sopenharmony_ci * to next range of valid virtual addresses. 129962306a36Sopenharmony_ci */ 130062306a36Sopenharmony_ci if (!sparsebit_is_set_num(vm->vpages_valid, 130162306a36Sopenharmony_ci pgidx_start, pages)) { 130262306a36Sopenharmony_ci pgidx_start = sparsebit_next_set_num( 130362306a36Sopenharmony_ci vm->vpages_valid, pgidx_start, pages); 130462306a36Sopenharmony_ci if (pgidx_start == 0) 130562306a36Sopenharmony_ci goto no_va_found; 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci } while (pgidx_start != 0); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_cino_va_found: 131062306a36Sopenharmony_ci TEST_FAIL("No vaddr of specified pages available, pages: 0x%lx", pages); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci /* NOT REACHED */ 131362306a36Sopenharmony_ci return -1; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_civa_found: 131662306a36Sopenharmony_ci TEST_ASSERT(sparsebit_is_set_num(vm->vpages_valid, 131762306a36Sopenharmony_ci pgidx_start, pages), 131862306a36Sopenharmony_ci "Unexpected, invalid virtual page index range,\n" 131962306a36Sopenharmony_ci " pgidx_start: 0x%lx\n" 132062306a36Sopenharmony_ci " pages: 0x%lx", 132162306a36Sopenharmony_ci pgidx_start, pages); 132262306a36Sopenharmony_ci TEST_ASSERT(sparsebit_is_clear_num(vm->vpages_mapped, 132362306a36Sopenharmony_ci pgidx_start, pages), 132462306a36Sopenharmony_ci "Unexpected, pages already mapped,\n" 132562306a36Sopenharmony_ci " pgidx_start: 0x%lx\n" 132662306a36Sopenharmony_ci " pages: 0x%lx", 132762306a36Sopenharmony_ci pgidx_start, pages); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci return pgidx_start * vm->page_size; 133062306a36Sopenharmony_ci} 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_civm_vaddr_t __vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min, 133362306a36Sopenharmony_ci enum kvm_mem_region_type type) 133462306a36Sopenharmony_ci{ 133562306a36Sopenharmony_ci uint64_t pages = (sz >> vm->page_shift) + ((sz % vm->page_size) != 0); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci virt_pgd_alloc(vm); 133862306a36Sopenharmony_ci vm_paddr_t paddr = vm_phy_pages_alloc(vm, pages, 133962306a36Sopenharmony_ci KVM_UTIL_MIN_PFN * vm->page_size, 134062306a36Sopenharmony_ci vm->memslots[type]); 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci /* 134362306a36Sopenharmony_ci * Find an unused range of virtual page addresses of at least 134462306a36Sopenharmony_ci * pages in length. 134562306a36Sopenharmony_ci */ 134662306a36Sopenharmony_ci vm_vaddr_t vaddr_start = vm_vaddr_unused_gap(vm, sz, vaddr_min); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci /* Map the virtual pages. */ 134962306a36Sopenharmony_ci for (vm_vaddr_t vaddr = vaddr_start; pages > 0; 135062306a36Sopenharmony_ci pages--, vaddr += vm->page_size, paddr += vm->page_size) { 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci virt_pg_map(vm, vaddr, paddr); 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci sparsebit_set(vm->vpages_mapped, vaddr >> vm->page_shift); 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci return vaddr_start; 135862306a36Sopenharmony_ci} 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci/* 136162306a36Sopenharmony_ci * VM Virtual Address Allocate 136262306a36Sopenharmony_ci * 136362306a36Sopenharmony_ci * Input Args: 136462306a36Sopenharmony_ci * vm - Virtual Machine 136562306a36Sopenharmony_ci * sz - Size in bytes 136662306a36Sopenharmony_ci * vaddr_min - Minimum starting virtual address 136762306a36Sopenharmony_ci * 136862306a36Sopenharmony_ci * Output Args: None 136962306a36Sopenharmony_ci * 137062306a36Sopenharmony_ci * Return: 137162306a36Sopenharmony_ci * Starting guest virtual address 137262306a36Sopenharmony_ci * 137362306a36Sopenharmony_ci * Allocates at least sz bytes within the virtual address space of the vm 137462306a36Sopenharmony_ci * given by vm. The allocated bytes are mapped to a virtual address >= 137562306a36Sopenharmony_ci * the address given by vaddr_min. Note that each allocation uses a 137662306a36Sopenharmony_ci * a unique set of pages, with the minimum real allocation being at least 137762306a36Sopenharmony_ci * a page. The allocated physical space comes from the TEST_DATA memory region. 137862306a36Sopenharmony_ci */ 137962306a36Sopenharmony_civm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min) 138062306a36Sopenharmony_ci{ 138162306a36Sopenharmony_ci return __vm_vaddr_alloc(vm, sz, vaddr_min, MEM_REGION_TEST_DATA); 138262306a36Sopenharmony_ci} 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci/* 138562306a36Sopenharmony_ci * VM Virtual Address Allocate Pages 138662306a36Sopenharmony_ci * 138762306a36Sopenharmony_ci * Input Args: 138862306a36Sopenharmony_ci * vm - Virtual Machine 138962306a36Sopenharmony_ci * 139062306a36Sopenharmony_ci * Output Args: None 139162306a36Sopenharmony_ci * 139262306a36Sopenharmony_ci * Return: 139362306a36Sopenharmony_ci * Starting guest virtual address 139462306a36Sopenharmony_ci * 139562306a36Sopenharmony_ci * Allocates at least N system pages worth of bytes within the virtual address 139662306a36Sopenharmony_ci * space of the vm. 139762306a36Sopenharmony_ci */ 139862306a36Sopenharmony_civm_vaddr_t vm_vaddr_alloc_pages(struct kvm_vm *vm, int nr_pages) 139962306a36Sopenharmony_ci{ 140062306a36Sopenharmony_ci return vm_vaddr_alloc(vm, nr_pages * getpagesize(), KVM_UTIL_MIN_VADDR); 140162306a36Sopenharmony_ci} 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_civm_vaddr_t __vm_vaddr_alloc_page(struct kvm_vm *vm, enum kvm_mem_region_type type) 140462306a36Sopenharmony_ci{ 140562306a36Sopenharmony_ci return __vm_vaddr_alloc(vm, getpagesize(), KVM_UTIL_MIN_VADDR, type); 140662306a36Sopenharmony_ci} 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci/* 140962306a36Sopenharmony_ci * VM Virtual Address Allocate Page 141062306a36Sopenharmony_ci * 141162306a36Sopenharmony_ci * Input Args: 141262306a36Sopenharmony_ci * vm - Virtual Machine 141362306a36Sopenharmony_ci * 141462306a36Sopenharmony_ci * Output Args: None 141562306a36Sopenharmony_ci * 141662306a36Sopenharmony_ci * Return: 141762306a36Sopenharmony_ci * Starting guest virtual address 141862306a36Sopenharmony_ci * 141962306a36Sopenharmony_ci * Allocates at least one system page worth of bytes within the virtual address 142062306a36Sopenharmony_ci * space of the vm. 142162306a36Sopenharmony_ci */ 142262306a36Sopenharmony_civm_vaddr_t vm_vaddr_alloc_page(struct kvm_vm *vm) 142362306a36Sopenharmony_ci{ 142462306a36Sopenharmony_ci return vm_vaddr_alloc_pages(vm, 1); 142562306a36Sopenharmony_ci} 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci/* 142862306a36Sopenharmony_ci * Map a range of VM virtual address to the VM's physical address 142962306a36Sopenharmony_ci * 143062306a36Sopenharmony_ci * Input Args: 143162306a36Sopenharmony_ci * vm - Virtual Machine 143262306a36Sopenharmony_ci * vaddr - Virtuall address to map 143362306a36Sopenharmony_ci * paddr - VM Physical Address 143462306a36Sopenharmony_ci * npages - The number of pages to map 143562306a36Sopenharmony_ci * 143662306a36Sopenharmony_ci * Output Args: None 143762306a36Sopenharmony_ci * 143862306a36Sopenharmony_ci * Return: None 143962306a36Sopenharmony_ci * 144062306a36Sopenharmony_ci * Within the VM given by @vm, creates a virtual translation for 144162306a36Sopenharmony_ci * @npages starting at @vaddr to the page range starting at @paddr. 144262306a36Sopenharmony_ci */ 144362306a36Sopenharmony_civoid virt_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, 144462306a36Sopenharmony_ci unsigned int npages) 144562306a36Sopenharmony_ci{ 144662306a36Sopenharmony_ci size_t page_size = vm->page_size; 144762306a36Sopenharmony_ci size_t size = npages * page_size; 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci TEST_ASSERT(vaddr + size > vaddr, "Vaddr overflow"); 145062306a36Sopenharmony_ci TEST_ASSERT(paddr + size > paddr, "Paddr overflow"); 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci while (npages--) { 145362306a36Sopenharmony_ci virt_pg_map(vm, vaddr, paddr); 145462306a36Sopenharmony_ci sparsebit_set(vm->vpages_mapped, vaddr >> vm->page_shift); 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci vaddr += page_size; 145762306a36Sopenharmony_ci paddr += page_size; 145862306a36Sopenharmony_ci } 145962306a36Sopenharmony_ci} 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci/* 146262306a36Sopenharmony_ci * Address VM Physical to Host Virtual 146362306a36Sopenharmony_ci * 146462306a36Sopenharmony_ci * Input Args: 146562306a36Sopenharmony_ci * vm - Virtual Machine 146662306a36Sopenharmony_ci * gpa - VM physical address 146762306a36Sopenharmony_ci * 146862306a36Sopenharmony_ci * Output Args: None 146962306a36Sopenharmony_ci * 147062306a36Sopenharmony_ci * Return: 147162306a36Sopenharmony_ci * Equivalent host virtual address 147262306a36Sopenharmony_ci * 147362306a36Sopenharmony_ci * Locates the memory region containing the VM physical address given 147462306a36Sopenharmony_ci * by gpa, within the VM given by vm. When found, the host virtual 147562306a36Sopenharmony_ci * address providing the memory to the vm physical address is returned. 147662306a36Sopenharmony_ci * A TEST_ASSERT failure occurs if no region containing gpa exists. 147762306a36Sopenharmony_ci */ 147862306a36Sopenharmony_civoid *addr_gpa2hva(struct kvm_vm *vm, vm_paddr_t gpa) 147962306a36Sopenharmony_ci{ 148062306a36Sopenharmony_ci struct userspace_mem_region *region; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci region = userspace_mem_region_find(vm, gpa, gpa); 148362306a36Sopenharmony_ci if (!region) { 148462306a36Sopenharmony_ci TEST_FAIL("No vm physical memory at 0x%lx", gpa); 148562306a36Sopenharmony_ci return NULL; 148662306a36Sopenharmony_ci } 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci return (void *)((uintptr_t)region->host_mem 148962306a36Sopenharmony_ci + (gpa - region->region.guest_phys_addr)); 149062306a36Sopenharmony_ci} 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci/* 149362306a36Sopenharmony_ci * Address Host Virtual to VM Physical 149462306a36Sopenharmony_ci * 149562306a36Sopenharmony_ci * Input Args: 149662306a36Sopenharmony_ci * vm - Virtual Machine 149762306a36Sopenharmony_ci * hva - Host virtual address 149862306a36Sopenharmony_ci * 149962306a36Sopenharmony_ci * Output Args: None 150062306a36Sopenharmony_ci * 150162306a36Sopenharmony_ci * Return: 150262306a36Sopenharmony_ci * Equivalent VM physical address 150362306a36Sopenharmony_ci * 150462306a36Sopenharmony_ci * Locates the memory region containing the host virtual address given 150562306a36Sopenharmony_ci * by hva, within the VM given by vm. When found, the equivalent 150662306a36Sopenharmony_ci * VM physical address is returned. A TEST_ASSERT failure occurs if no 150762306a36Sopenharmony_ci * region containing hva exists. 150862306a36Sopenharmony_ci */ 150962306a36Sopenharmony_civm_paddr_t addr_hva2gpa(struct kvm_vm *vm, void *hva) 151062306a36Sopenharmony_ci{ 151162306a36Sopenharmony_ci struct rb_node *node; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci for (node = vm->regions.hva_tree.rb_node; node; ) { 151462306a36Sopenharmony_ci struct userspace_mem_region *region = 151562306a36Sopenharmony_ci container_of(node, struct userspace_mem_region, hva_node); 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci if (hva >= region->host_mem) { 151862306a36Sopenharmony_ci if (hva <= (region->host_mem 151962306a36Sopenharmony_ci + region->region.memory_size - 1)) 152062306a36Sopenharmony_ci return (vm_paddr_t)((uintptr_t) 152162306a36Sopenharmony_ci region->region.guest_phys_addr 152262306a36Sopenharmony_ci + (hva - (uintptr_t)region->host_mem)); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci node = node->rb_right; 152562306a36Sopenharmony_ci } else 152662306a36Sopenharmony_ci node = node->rb_left; 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci TEST_FAIL("No mapping to a guest physical address, hva: %p", hva); 153062306a36Sopenharmony_ci return -1; 153162306a36Sopenharmony_ci} 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci/* 153462306a36Sopenharmony_ci * Address VM physical to Host Virtual *alias*. 153562306a36Sopenharmony_ci * 153662306a36Sopenharmony_ci * Input Args: 153762306a36Sopenharmony_ci * vm - Virtual Machine 153862306a36Sopenharmony_ci * gpa - VM physical address 153962306a36Sopenharmony_ci * 154062306a36Sopenharmony_ci * Output Args: None 154162306a36Sopenharmony_ci * 154262306a36Sopenharmony_ci * Return: 154362306a36Sopenharmony_ci * Equivalent address within the host virtual *alias* area, or NULL 154462306a36Sopenharmony_ci * (without failing the test) if the guest memory is not shared (so 154562306a36Sopenharmony_ci * no alias exists). 154662306a36Sopenharmony_ci * 154762306a36Sopenharmony_ci * Create a writable, shared virtual=>physical alias for the specific GPA. 154862306a36Sopenharmony_ci * The primary use case is to allow the host selftest to manipulate guest 154962306a36Sopenharmony_ci * memory without mapping said memory in the guest's address space. And, for 155062306a36Sopenharmony_ci * userfaultfd-based demand paging, to do so without triggering userfaults. 155162306a36Sopenharmony_ci */ 155262306a36Sopenharmony_civoid *addr_gpa2alias(struct kvm_vm *vm, vm_paddr_t gpa) 155362306a36Sopenharmony_ci{ 155462306a36Sopenharmony_ci struct userspace_mem_region *region; 155562306a36Sopenharmony_ci uintptr_t offset; 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci region = userspace_mem_region_find(vm, gpa, gpa); 155862306a36Sopenharmony_ci if (!region) 155962306a36Sopenharmony_ci return NULL; 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci if (!region->host_alias) 156262306a36Sopenharmony_ci return NULL; 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci offset = gpa - region->region.guest_phys_addr; 156562306a36Sopenharmony_ci return (void *) ((uintptr_t) region->host_alias + offset); 156662306a36Sopenharmony_ci} 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci/* Create an interrupt controller chip for the specified VM. */ 156962306a36Sopenharmony_civoid vm_create_irqchip(struct kvm_vm *vm) 157062306a36Sopenharmony_ci{ 157162306a36Sopenharmony_ci vm_ioctl(vm, KVM_CREATE_IRQCHIP, NULL); 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci vm->has_irqchip = true; 157462306a36Sopenharmony_ci} 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ciint _vcpu_run(struct kvm_vcpu *vcpu) 157762306a36Sopenharmony_ci{ 157862306a36Sopenharmony_ci int rc; 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci do { 158162306a36Sopenharmony_ci rc = __vcpu_run(vcpu); 158262306a36Sopenharmony_ci } while (rc == -1 && errno == EINTR); 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci assert_on_unhandled_exception(vcpu); 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci return rc; 158762306a36Sopenharmony_ci} 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci/* 159062306a36Sopenharmony_ci * Invoke KVM_RUN on a vCPU until KVM returns something other than -EINTR. 159162306a36Sopenharmony_ci * Assert if the KVM returns an error (other than -EINTR). 159262306a36Sopenharmony_ci */ 159362306a36Sopenharmony_civoid vcpu_run(struct kvm_vcpu *vcpu) 159462306a36Sopenharmony_ci{ 159562306a36Sopenharmony_ci int ret = _vcpu_run(vcpu); 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_RUN, ret)); 159862306a36Sopenharmony_ci} 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_civoid vcpu_run_complete_io(struct kvm_vcpu *vcpu) 160162306a36Sopenharmony_ci{ 160262306a36Sopenharmony_ci int ret; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci vcpu->run->immediate_exit = 1; 160562306a36Sopenharmony_ci ret = __vcpu_run(vcpu); 160662306a36Sopenharmony_ci vcpu->run->immediate_exit = 0; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci TEST_ASSERT(ret == -1 && errno == EINTR, 160962306a36Sopenharmony_ci "KVM_RUN IOCTL didn't exit immediately, rc: %i, errno: %i", 161062306a36Sopenharmony_ci ret, errno); 161162306a36Sopenharmony_ci} 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci/* 161462306a36Sopenharmony_ci * Get the list of guest registers which are supported for 161562306a36Sopenharmony_ci * KVM_GET_ONE_REG/KVM_SET_ONE_REG ioctls. Returns a kvm_reg_list pointer, 161662306a36Sopenharmony_ci * it is the caller's responsibility to free the list. 161762306a36Sopenharmony_ci */ 161862306a36Sopenharmony_cistruct kvm_reg_list *vcpu_get_reg_list(struct kvm_vcpu *vcpu) 161962306a36Sopenharmony_ci{ 162062306a36Sopenharmony_ci struct kvm_reg_list reg_list_n = { .n = 0 }, *reg_list; 162162306a36Sopenharmony_ci int ret; 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci ret = __vcpu_ioctl(vcpu, KVM_GET_REG_LIST, ®_list_n); 162462306a36Sopenharmony_ci TEST_ASSERT(ret == -1 && errno == E2BIG, "KVM_GET_REG_LIST n=0"); 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci reg_list = calloc(1, sizeof(*reg_list) + reg_list_n.n * sizeof(__u64)); 162762306a36Sopenharmony_ci reg_list->n = reg_list_n.n; 162862306a36Sopenharmony_ci vcpu_ioctl(vcpu, KVM_GET_REG_LIST, reg_list); 162962306a36Sopenharmony_ci return reg_list; 163062306a36Sopenharmony_ci} 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_civoid *vcpu_map_dirty_ring(struct kvm_vcpu *vcpu) 163362306a36Sopenharmony_ci{ 163462306a36Sopenharmony_ci uint32_t page_size = getpagesize(); 163562306a36Sopenharmony_ci uint32_t size = vcpu->vm->dirty_ring_size; 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci TEST_ASSERT(size > 0, "Should enable dirty ring first"); 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci if (!vcpu->dirty_gfns) { 164062306a36Sopenharmony_ci void *addr; 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci addr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, vcpu->fd, 164362306a36Sopenharmony_ci page_size * KVM_DIRTY_LOG_PAGE_OFFSET); 164462306a36Sopenharmony_ci TEST_ASSERT(addr == MAP_FAILED, "Dirty ring mapped private"); 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci addr = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_PRIVATE, vcpu->fd, 164762306a36Sopenharmony_ci page_size * KVM_DIRTY_LOG_PAGE_OFFSET); 164862306a36Sopenharmony_ci TEST_ASSERT(addr == MAP_FAILED, "Dirty ring mapped exec"); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, vcpu->fd, 165162306a36Sopenharmony_ci page_size * KVM_DIRTY_LOG_PAGE_OFFSET); 165262306a36Sopenharmony_ci TEST_ASSERT(addr != MAP_FAILED, "Dirty ring map failed"); 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci vcpu->dirty_gfns = addr; 165562306a36Sopenharmony_ci vcpu->dirty_gfns_count = size / sizeof(struct kvm_dirty_gfn); 165662306a36Sopenharmony_ci } 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci return vcpu->dirty_gfns; 165962306a36Sopenharmony_ci} 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci/* 166262306a36Sopenharmony_ci * Device Ioctl 166362306a36Sopenharmony_ci */ 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ciint __kvm_has_device_attr(int dev_fd, uint32_t group, uint64_t attr) 166662306a36Sopenharmony_ci{ 166762306a36Sopenharmony_ci struct kvm_device_attr attribute = { 166862306a36Sopenharmony_ci .group = group, 166962306a36Sopenharmony_ci .attr = attr, 167062306a36Sopenharmony_ci .flags = 0, 167162306a36Sopenharmony_ci }; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci return ioctl(dev_fd, KVM_HAS_DEVICE_ATTR, &attribute); 167462306a36Sopenharmony_ci} 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ciint __kvm_test_create_device(struct kvm_vm *vm, uint64_t type) 167762306a36Sopenharmony_ci{ 167862306a36Sopenharmony_ci struct kvm_create_device create_dev = { 167962306a36Sopenharmony_ci .type = type, 168062306a36Sopenharmony_ci .flags = KVM_CREATE_DEVICE_TEST, 168162306a36Sopenharmony_ci }; 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci return __vm_ioctl(vm, KVM_CREATE_DEVICE, &create_dev); 168462306a36Sopenharmony_ci} 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ciint __kvm_create_device(struct kvm_vm *vm, uint64_t type) 168762306a36Sopenharmony_ci{ 168862306a36Sopenharmony_ci struct kvm_create_device create_dev = { 168962306a36Sopenharmony_ci .type = type, 169062306a36Sopenharmony_ci .fd = -1, 169162306a36Sopenharmony_ci .flags = 0, 169262306a36Sopenharmony_ci }; 169362306a36Sopenharmony_ci int err; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci err = __vm_ioctl(vm, KVM_CREATE_DEVICE, &create_dev); 169662306a36Sopenharmony_ci TEST_ASSERT(err <= 0, "KVM_CREATE_DEVICE shouldn't return a positive value"); 169762306a36Sopenharmony_ci return err ? : create_dev.fd; 169862306a36Sopenharmony_ci} 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ciint __kvm_device_attr_get(int dev_fd, uint32_t group, uint64_t attr, void *val) 170162306a36Sopenharmony_ci{ 170262306a36Sopenharmony_ci struct kvm_device_attr kvmattr = { 170362306a36Sopenharmony_ci .group = group, 170462306a36Sopenharmony_ci .attr = attr, 170562306a36Sopenharmony_ci .flags = 0, 170662306a36Sopenharmony_ci .addr = (uintptr_t)val, 170762306a36Sopenharmony_ci }; 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci return __kvm_ioctl(dev_fd, KVM_GET_DEVICE_ATTR, &kvmattr); 171062306a36Sopenharmony_ci} 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ciint __kvm_device_attr_set(int dev_fd, uint32_t group, uint64_t attr, void *val) 171362306a36Sopenharmony_ci{ 171462306a36Sopenharmony_ci struct kvm_device_attr kvmattr = { 171562306a36Sopenharmony_ci .group = group, 171662306a36Sopenharmony_ci .attr = attr, 171762306a36Sopenharmony_ci .flags = 0, 171862306a36Sopenharmony_ci .addr = (uintptr_t)val, 171962306a36Sopenharmony_ci }; 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci return __kvm_ioctl(dev_fd, KVM_SET_DEVICE_ATTR, &kvmattr); 172262306a36Sopenharmony_ci} 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci/* 172562306a36Sopenharmony_ci * IRQ related functions. 172662306a36Sopenharmony_ci */ 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ciint _kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level) 172962306a36Sopenharmony_ci{ 173062306a36Sopenharmony_ci struct kvm_irq_level irq_level = { 173162306a36Sopenharmony_ci .irq = irq, 173262306a36Sopenharmony_ci .level = level, 173362306a36Sopenharmony_ci }; 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci return __vm_ioctl(vm, KVM_IRQ_LINE, &irq_level); 173662306a36Sopenharmony_ci} 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_civoid kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level) 173962306a36Sopenharmony_ci{ 174062306a36Sopenharmony_ci int ret = _kvm_irq_line(vm, irq, level); 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci TEST_ASSERT(ret >= 0, KVM_IOCTL_ERROR(KVM_IRQ_LINE, ret)); 174362306a36Sopenharmony_ci} 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_cistruct kvm_irq_routing *kvm_gsi_routing_create(void) 174662306a36Sopenharmony_ci{ 174762306a36Sopenharmony_ci struct kvm_irq_routing *routing; 174862306a36Sopenharmony_ci size_t size; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci size = sizeof(struct kvm_irq_routing); 175162306a36Sopenharmony_ci /* Allocate space for the max number of entries: this wastes 196 KBs. */ 175262306a36Sopenharmony_ci size += KVM_MAX_IRQ_ROUTES * sizeof(struct kvm_irq_routing_entry); 175362306a36Sopenharmony_ci routing = calloc(1, size); 175462306a36Sopenharmony_ci assert(routing); 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci return routing; 175762306a36Sopenharmony_ci} 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_civoid kvm_gsi_routing_irqchip_add(struct kvm_irq_routing *routing, 176062306a36Sopenharmony_ci uint32_t gsi, uint32_t pin) 176162306a36Sopenharmony_ci{ 176262306a36Sopenharmony_ci int i; 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci assert(routing); 176562306a36Sopenharmony_ci assert(routing->nr < KVM_MAX_IRQ_ROUTES); 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci i = routing->nr; 176862306a36Sopenharmony_ci routing->entries[i].gsi = gsi; 176962306a36Sopenharmony_ci routing->entries[i].type = KVM_IRQ_ROUTING_IRQCHIP; 177062306a36Sopenharmony_ci routing->entries[i].flags = 0; 177162306a36Sopenharmony_ci routing->entries[i].u.irqchip.irqchip = 0; 177262306a36Sopenharmony_ci routing->entries[i].u.irqchip.pin = pin; 177362306a36Sopenharmony_ci routing->nr++; 177462306a36Sopenharmony_ci} 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ciint _kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing) 177762306a36Sopenharmony_ci{ 177862306a36Sopenharmony_ci int ret; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci assert(routing); 178162306a36Sopenharmony_ci ret = __vm_ioctl(vm, KVM_SET_GSI_ROUTING, routing); 178262306a36Sopenharmony_ci free(routing); 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci return ret; 178562306a36Sopenharmony_ci} 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_civoid kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing) 178862306a36Sopenharmony_ci{ 178962306a36Sopenharmony_ci int ret; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci ret = _kvm_gsi_routing_write(vm, routing); 179262306a36Sopenharmony_ci TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_SET_GSI_ROUTING, ret)); 179362306a36Sopenharmony_ci} 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci/* 179662306a36Sopenharmony_ci * VM Dump 179762306a36Sopenharmony_ci * 179862306a36Sopenharmony_ci * Input Args: 179962306a36Sopenharmony_ci * vm - Virtual Machine 180062306a36Sopenharmony_ci * indent - Left margin indent amount 180162306a36Sopenharmony_ci * 180262306a36Sopenharmony_ci * Output Args: 180362306a36Sopenharmony_ci * stream - Output FILE stream 180462306a36Sopenharmony_ci * 180562306a36Sopenharmony_ci * Return: None 180662306a36Sopenharmony_ci * 180762306a36Sopenharmony_ci * Dumps the current state of the VM given by vm, to the FILE stream 180862306a36Sopenharmony_ci * given by stream. 180962306a36Sopenharmony_ci */ 181062306a36Sopenharmony_civoid vm_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) 181162306a36Sopenharmony_ci{ 181262306a36Sopenharmony_ci int ctr; 181362306a36Sopenharmony_ci struct userspace_mem_region *region; 181462306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci fprintf(stream, "%*smode: 0x%x\n", indent, "", vm->mode); 181762306a36Sopenharmony_ci fprintf(stream, "%*sfd: %i\n", indent, "", vm->fd); 181862306a36Sopenharmony_ci fprintf(stream, "%*spage_size: 0x%x\n", indent, "", vm->page_size); 181962306a36Sopenharmony_ci fprintf(stream, "%*sMem Regions:\n", indent, ""); 182062306a36Sopenharmony_ci hash_for_each(vm->regions.slot_hash, ctr, region, slot_node) { 182162306a36Sopenharmony_ci fprintf(stream, "%*sguest_phys: 0x%lx size: 0x%lx " 182262306a36Sopenharmony_ci "host_virt: %p\n", indent + 2, "", 182362306a36Sopenharmony_ci (uint64_t) region->region.guest_phys_addr, 182462306a36Sopenharmony_ci (uint64_t) region->region.memory_size, 182562306a36Sopenharmony_ci region->host_mem); 182662306a36Sopenharmony_ci fprintf(stream, "%*sunused_phy_pages: ", indent + 2, ""); 182762306a36Sopenharmony_ci sparsebit_dump(stream, region->unused_phy_pages, 0); 182862306a36Sopenharmony_ci } 182962306a36Sopenharmony_ci fprintf(stream, "%*sMapped Virtual Pages:\n", indent, ""); 183062306a36Sopenharmony_ci sparsebit_dump(stream, vm->vpages_mapped, indent + 2); 183162306a36Sopenharmony_ci fprintf(stream, "%*spgd_created: %u\n", indent, "", 183262306a36Sopenharmony_ci vm->pgd_created); 183362306a36Sopenharmony_ci if (vm->pgd_created) { 183462306a36Sopenharmony_ci fprintf(stream, "%*sVirtual Translation Tables:\n", 183562306a36Sopenharmony_ci indent + 2, ""); 183662306a36Sopenharmony_ci virt_dump(stream, vm, indent + 4); 183762306a36Sopenharmony_ci } 183862306a36Sopenharmony_ci fprintf(stream, "%*sVCPUs:\n", indent, ""); 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci list_for_each_entry(vcpu, &vm->vcpus, list) 184162306a36Sopenharmony_ci vcpu_dump(stream, vcpu, indent + 2); 184262306a36Sopenharmony_ci} 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci#define KVM_EXIT_STRING(x) {KVM_EXIT_##x, #x} 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci/* Known KVM exit reasons */ 184762306a36Sopenharmony_cistatic struct exit_reason { 184862306a36Sopenharmony_ci unsigned int reason; 184962306a36Sopenharmony_ci const char *name; 185062306a36Sopenharmony_ci} exit_reasons_known[] = { 185162306a36Sopenharmony_ci KVM_EXIT_STRING(UNKNOWN), 185262306a36Sopenharmony_ci KVM_EXIT_STRING(EXCEPTION), 185362306a36Sopenharmony_ci KVM_EXIT_STRING(IO), 185462306a36Sopenharmony_ci KVM_EXIT_STRING(HYPERCALL), 185562306a36Sopenharmony_ci KVM_EXIT_STRING(DEBUG), 185662306a36Sopenharmony_ci KVM_EXIT_STRING(HLT), 185762306a36Sopenharmony_ci KVM_EXIT_STRING(MMIO), 185862306a36Sopenharmony_ci KVM_EXIT_STRING(IRQ_WINDOW_OPEN), 185962306a36Sopenharmony_ci KVM_EXIT_STRING(SHUTDOWN), 186062306a36Sopenharmony_ci KVM_EXIT_STRING(FAIL_ENTRY), 186162306a36Sopenharmony_ci KVM_EXIT_STRING(INTR), 186262306a36Sopenharmony_ci KVM_EXIT_STRING(SET_TPR), 186362306a36Sopenharmony_ci KVM_EXIT_STRING(TPR_ACCESS), 186462306a36Sopenharmony_ci KVM_EXIT_STRING(S390_SIEIC), 186562306a36Sopenharmony_ci KVM_EXIT_STRING(S390_RESET), 186662306a36Sopenharmony_ci KVM_EXIT_STRING(DCR), 186762306a36Sopenharmony_ci KVM_EXIT_STRING(NMI), 186862306a36Sopenharmony_ci KVM_EXIT_STRING(INTERNAL_ERROR), 186962306a36Sopenharmony_ci KVM_EXIT_STRING(OSI), 187062306a36Sopenharmony_ci KVM_EXIT_STRING(PAPR_HCALL), 187162306a36Sopenharmony_ci KVM_EXIT_STRING(S390_UCONTROL), 187262306a36Sopenharmony_ci KVM_EXIT_STRING(WATCHDOG), 187362306a36Sopenharmony_ci KVM_EXIT_STRING(S390_TSCH), 187462306a36Sopenharmony_ci KVM_EXIT_STRING(EPR), 187562306a36Sopenharmony_ci KVM_EXIT_STRING(SYSTEM_EVENT), 187662306a36Sopenharmony_ci KVM_EXIT_STRING(S390_STSI), 187762306a36Sopenharmony_ci KVM_EXIT_STRING(IOAPIC_EOI), 187862306a36Sopenharmony_ci KVM_EXIT_STRING(HYPERV), 187962306a36Sopenharmony_ci KVM_EXIT_STRING(ARM_NISV), 188062306a36Sopenharmony_ci KVM_EXIT_STRING(X86_RDMSR), 188162306a36Sopenharmony_ci KVM_EXIT_STRING(X86_WRMSR), 188262306a36Sopenharmony_ci KVM_EXIT_STRING(DIRTY_RING_FULL), 188362306a36Sopenharmony_ci KVM_EXIT_STRING(AP_RESET_HOLD), 188462306a36Sopenharmony_ci KVM_EXIT_STRING(X86_BUS_LOCK), 188562306a36Sopenharmony_ci KVM_EXIT_STRING(XEN), 188662306a36Sopenharmony_ci KVM_EXIT_STRING(RISCV_SBI), 188762306a36Sopenharmony_ci KVM_EXIT_STRING(RISCV_CSR), 188862306a36Sopenharmony_ci KVM_EXIT_STRING(NOTIFY), 188962306a36Sopenharmony_ci#ifdef KVM_EXIT_MEMORY_NOT_PRESENT 189062306a36Sopenharmony_ci KVM_EXIT_STRING(MEMORY_NOT_PRESENT), 189162306a36Sopenharmony_ci#endif 189262306a36Sopenharmony_ci}; 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci/* 189562306a36Sopenharmony_ci * Exit Reason String 189662306a36Sopenharmony_ci * 189762306a36Sopenharmony_ci * Input Args: 189862306a36Sopenharmony_ci * exit_reason - Exit reason 189962306a36Sopenharmony_ci * 190062306a36Sopenharmony_ci * Output Args: None 190162306a36Sopenharmony_ci * 190262306a36Sopenharmony_ci * Return: 190362306a36Sopenharmony_ci * Constant string pointer describing the exit reason. 190462306a36Sopenharmony_ci * 190562306a36Sopenharmony_ci * Locates and returns a constant string that describes the KVM exit 190662306a36Sopenharmony_ci * reason given by exit_reason. If no such string is found, a constant 190762306a36Sopenharmony_ci * string of "Unknown" is returned. 190862306a36Sopenharmony_ci */ 190962306a36Sopenharmony_ciconst char *exit_reason_str(unsigned int exit_reason) 191062306a36Sopenharmony_ci{ 191162306a36Sopenharmony_ci unsigned int n1; 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci for (n1 = 0; n1 < ARRAY_SIZE(exit_reasons_known); n1++) { 191462306a36Sopenharmony_ci if (exit_reason == exit_reasons_known[n1].reason) 191562306a36Sopenharmony_ci return exit_reasons_known[n1].name; 191662306a36Sopenharmony_ci } 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci return "Unknown"; 191962306a36Sopenharmony_ci} 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci/* 192262306a36Sopenharmony_ci * Physical Contiguous Page Allocator 192362306a36Sopenharmony_ci * 192462306a36Sopenharmony_ci * Input Args: 192562306a36Sopenharmony_ci * vm - Virtual Machine 192662306a36Sopenharmony_ci * num - number of pages 192762306a36Sopenharmony_ci * paddr_min - Physical address minimum 192862306a36Sopenharmony_ci * memslot - Memory region to allocate page from 192962306a36Sopenharmony_ci * 193062306a36Sopenharmony_ci * Output Args: None 193162306a36Sopenharmony_ci * 193262306a36Sopenharmony_ci * Return: 193362306a36Sopenharmony_ci * Starting physical address 193462306a36Sopenharmony_ci * 193562306a36Sopenharmony_ci * Within the VM specified by vm, locates a range of available physical 193662306a36Sopenharmony_ci * pages at or above paddr_min. If found, the pages are marked as in use 193762306a36Sopenharmony_ci * and their base address is returned. A TEST_ASSERT failure occurs if 193862306a36Sopenharmony_ci * not enough pages are available at or above paddr_min. 193962306a36Sopenharmony_ci */ 194062306a36Sopenharmony_civm_paddr_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, 194162306a36Sopenharmony_ci vm_paddr_t paddr_min, uint32_t memslot) 194262306a36Sopenharmony_ci{ 194362306a36Sopenharmony_ci struct userspace_mem_region *region; 194462306a36Sopenharmony_ci sparsebit_idx_t pg, base; 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci TEST_ASSERT(num > 0, "Must allocate at least one page"); 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci TEST_ASSERT((paddr_min % vm->page_size) == 0, "Min physical address " 194962306a36Sopenharmony_ci "not divisible by page size.\n" 195062306a36Sopenharmony_ci " paddr_min: 0x%lx page_size: 0x%x", 195162306a36Sopenharmony_ci paddr_min, vm->page_size); 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci region = memslot2region(vm, memslot); 195462306a36Sopenharmony_ci base = pg = paddr_min >> vm->page_shift; 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci do { 195762306a36Sopenharmony_ci for (; pg < base + num; ++pg) { 195862306a36Sopenharmony_ci if (!sparsebit_is_set(region->unused_phy_pages, pg)) { 195962306a36Sopenharmony_ci base = pg = sparsebit_next_set(region->unused_phy_pages, pg); 196062306a36Sopenharmony_ci break; 196162306a36Sopenharmony_ci } 196262306a36Sopenharmony_ci } 196362306a36Sopenharmony_ci } while (pg && pg != base + num); 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci if (pg == 0) { 196662306a36Sopenharmony_ci fprintf(stderr, "No guest physical page available, " 196762306a36Sopenharmony_ci "paddr_min: 0x%lx page_size: 0x%x memslot: %u\n", 196862306a36Sopenharmony_ci paddr_min, vm->page_size, memslot); 196962306a36Sopenharmony_ci fputs("---- vm dump ----\n", stderr); 197062306a36Sopenharmony_ci vm_dump(stderr, vm, 2); 197162306a36Sopenharmony_ci abort(); 197262306a36Sopenharmony_ci } 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci for (pg = base; pg < base + num; ++pg) 197562306a36Sopenharmony_ci sparsebit_clear(region->unused_phy_pages, pg); 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci return base * vm->page_size; 197862306a36Sopenharmony_ci} 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_civm_paddr_t vm_phy_page_alloc(struct kvm_vm *vm, vm_paddr_t paddr_min, 198162306a36Sopenharmony_ci uint32_t memslot) 198262306a36Sopenharmony_ci{ 198362306a36Sopenharmony_ci return vm_phy_pages_alloc(vm, 1, paddr_min, memslot); 198462306a36Sopenharmony_ci} 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_civm_paddr_t vm_alloc_page_table(struct kvm_vm *vm) 198762306a36Sopenharmony_ci{ 198862306a36Sopenharmony_ci return vm_phy_page_alloc(vm, KVM_GUEST_PAGE_TABLE_MIN_PADDR, 198962306a36Sopenharmony_ci vm->memslots[MEM_REGION_PT]); 199062306a36Sopenharmony_ci} 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci/* 199362306a36Sopenharmony_ci * Address Guest Virtual to Host Virtual 199462306a36Sopenharmony_ci * 199562306a36Sopenharmony_ci * Input Args: 199662306a36Sopenharmony_ci * vm - Virtual Machine 199762306a36Sopenharmony_ci * gva - VM virtual address 199862306a36Sopenharmony_ci * 199962306a36Sopenharmony_ci * Output Args: None 200062306a36Sopenharmony_ci * 200162306a36Sopenharmony_ci * Return: 200262306a36Sopenharmony_ci * Equivalent host virtual address 200362306a36Sopenharmony_ci */ 200462306a36Sopenharmony_civoid *addr_gva2hva(struct kvm_vm *vm, vm_vaddr_t gva) 200562306a36Sopenharmony_ci{ 200662306a36Sopenharmony_ci return addr_gpa2hva(vm, addr_gva2gpa(vm, gva)); 200762306a36Sopenharmony_ci} 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ciunsigned long __weak vm_compute_max_gfn(struct kvm_vm *vm) 201062306a36Sopenharmony_ci{ 201162306a36Sopenharmony_ci return ((1ULL << vm->pa_bits) >> vm->page_shift) - 1; 201262306a36Sopenharmony_ci} 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_cistatic unsigned int vm_calc_num_pages(unsigned int num_pages, 201562306a36Sopenharmony_ci unsigned int page_shift, 201662306a36Sopenharmony_ci unsigned int new_page_shift, 201762306a36Sopenharmony_ci bool ceil) 201862306a36Sopenharmony_ci{ 201962306a36Sopenharmony_ci unsigned int n = 1 << (new_page_shift - page_shift); 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci if (page_shift >= new_page_shift) 202262306a36Sopenharmony_ci return num_pages * (1 << (page_shift - new_page_shift)); 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci return num_pages / n + !!(ceil && num_pages % n); 202562306a36Sopenharmony_ci} 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_cistatic inline int getpageshift(void) 202862306a36Sopenharmony_ci{ 202962306a36Sopenharmony_ci return __builtin_ffs(getpagesize()) - 1; 203062306a36Sopenharmony_ci} 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ciunsigned int 203362306a36Sopenharmony_civm_num_host_pages(enum vm_guest_mode mode, unsigned int num_guest_pages) 203462306a36Sopenharmony_ci{ 203562306a36Sopenharmony_ci return vm_calc_num_pages(num_guest_pages, 203662306a36Sopenharmony_ci vm_guest_mode_params[mode].page_shift, 203762306a36Sopenharmony_ci getpageshift(), true); 203862306a36Sopenharmony_ci} 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ciunsigned int 204162306a36Sopenharmony_civm_num_guest_pages(enum vm_guest_mode mode, unsigned int num_host_pages) 204262306a36Sopenharmony_ci{ 204362306a36Sopenharmony_ci return vm_calc_num_pages(num_host_pages, getpageshift(), 204462306a36Sopenharmony_ci vm_guest_mode_params[mode].page_shift, false); 204562306a36Sopenharmony_ci} 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ciunsigned int vm_calc_num_guest_pages(enum vm_guest_mode mode, size_t size) 204862306a36Sopenharmony_ci{ 204962306a36Sopenharmony_ci unsigned int n; 205062306a36Sopenharmony_ci n = DIV_ROUND_UP(size, vm_guest_mode_params[mode].page_size); 205162306a36Sopenharmony_ci return vm_adjust_num_guest_pages(mode, n); 205262306a36Sopenharmony_ci} 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci/* 205562306a36Sopenharmony_ci * Read binary stats descriptors 205662306a36Sopenharmony_ci * 205762306a36Sopenharmony_ci * Input Args: 205862306a36Sopenharmony_ci * stats_fd - the file descriptor for the binary stats file from which to read 205962306a36Sopenharmony_ci * header - the binary stats metadata header corresponding to the given FD 206062306a36Sopenharmony_ci * 206162306a36Sopenharmony_ci * Output Args: None 206262306a36Sopenharmony_ci * 206362306a36Sopenharmony_ci * Return: 206462306a36Sopenharmony_ci * A pointer to a newly allocated series of stat descriptors. 206562306a36Sopenharmony_ci * Caller is responsible for freeing the returned kvm_stats_desc. 206662306a36Sopenharmony_ci * 206762306a36Sopenharmony_ci * Read the stats descriptors from the binary stats interface. 206862306a36Sopenharmony_ci */ 206962306a36Sopenharmony_cistruct kvm_stats_desc *read_stats_descriptors(int stats_fd, 207062306a36Sopenharmony_ci struct kvm_stats_header *header) 207162306a36Sopenharmony_ci{ 207262306a36Sopenharmony_ci struct kvm_stats_desc *stats_desc; 207362306a36Sopenharmony_ci ssize_t desc_size, total_size, ret; 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci desc_size = get_stats_descriptor_size(header); 207662306a36Sopenharmony_ci total_size = header->num_desc * desc_size; 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_ci stats_desc = calloc(header->num_desc, desc_size); 207962306a36Sopenharmony_ci TEST_ASSERT(stats_desc, "Allocate memory for stats descriptors"); 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci ret = pread(stats_fd, stats_desc, total_size, header->desc_offset); 208262306a36Sopenharmony_ci TEST_ASSERT(ret == total_size, "Read KVM stats descriptors"); 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci return stats_desc; 208562306a36Sopenharmony_ci} 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci/* 208862306a36Sopenharmony_ci * Read stat data for a particular stat 208962306a36Sopenharmony_ci * 209062306a36Sopenharmony_ci * Input Args: 209162306a36Sopenharmony_ci * stats_fd - the file descriptor for the binary stats file from which to read 209262306a36Sopenharmony_ci * header - the binary stats metadata header corresponding to the given FD 209362306a36Sopenharmony_ci * desc - the binary stat metadata for the particular stat to be read 209462306a36Sopenharmony_ci * max_elements - the maximum number of 8-byte values to read into data 209562306a36Sopenharmony_ci * 209662306a36Sopenharmony_ci * Output Args: 209762306a36Sopenharmony_ci * data - the buffer into which stat data should be read 209862306a36Sopenharmony_ci * 209962306a36Sopenharmony_ci * Read the data values of a specified stat from the binary stats interface. 210062306a36Sopenharmony_ci */ 210162306a36Sopenharmony_civoid read_stat_data(int stats_fd, struct kvm_stats_header *header, 210262306a36Sopenharmony_ci struct kvm_stats_desc *desc, uint64_t *data, 210362306a36Sopenharmony_ci size_t max_elements) 210462306a36Sopenharmony_ci{ 210562306a36Sopenharmony_ci size_t nr_elements = min_t(ssize_t, desc->size, max_elements); 210662306a36Sopenharmony_ci size_t size = nr_elements * sizeof(*data); 210762306a36Sopenharmony_ci ssize_t ret; 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci TEST_ASSERT(desc->size, "No elements in stat '%s'", desc->name); 211062306a36Sopenharmony_ci TEST_ASSERT(max_elements, "Zero elements requested for stat '%s'", desc->name); 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci ret = pread(stats_fd, data, size, 211362306a36Sopenharmony_ci header->data_offset + desc->offset); 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci TEST_ASSERT(ret >= 0, "pread() failed on stat '%s', errno: %i (%s)", 211662306a36Sopenharmony_ci desc->name, errno, strerror(errno)); 211762306a36Sopenharmony_ci TEST_ASSERT(ret == size, 211862306a36Sopenharmony_ci "pread() on stat '%s' read %ld bytes, wanted %lu bytes", 211962306a36Sopenharmony_ci desc->name, size, ret); 212062306a36Sopenharmony_ci} 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci/* 212362306a36Sopenharmony_ci * Read the data of the named stat 212462306a36Sopenharmony_ci * 212562306a36Sopenharmony_ci * Input Args: 212662306a36Sopenharmony_ci * vm - the VM for which the stat should be read 212762306a36Sopenharmony_ci * stat_name - the name of the stat to read 212862306a36Sopenharmony_ci * max_elements - the maximum number of 8-byte values to read into data 212962306a36Sopenharmony_ci * 213062306a36Sopenharmony_ci * Output Args: 213162306a36Sopenharmony_ci * data - the buffer into which stat data should be read 213262306a36Sopenharmony_ci * 213362306a36Sopenharmony_ci * Read the data values of a specified stat from the binary stats interface. 213462306a36Sopenharmony_ci */ 213562306a36Sopenharmony_civoid __vm_get_stat(struct kvm_vm *vm, const char *stat_name, uint64_t *data, 213662306a36Sopenharmony_ci size_t max_elements) 213762306a36Sopenharmony_ci{ 213862306a36Sopenharmony_ci struct kvm_stats_desc *desc; 213962306a36Sopenharmony_ci size_t size_desc; 214062306a36Sopenharmony_ci int i; 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci if (!vm->stats_fd) { 214362306a36Sopenharmony_ci vm->stats_fd = vm_get_stats_fd(vm); 214462306a36Sopenharmony_ci read_stats_header(vm->stats_fd, &vm->stats_header); 214562306a36Sopenharmony_ci vm->stats_desc = read_stats_descriptors(vm->stats_fd, 214662306a36Sopenharmony_ci &vm->stats_header); 214762306a36Sopenharmony_ci } 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci size_desc = get_stats_descriptor_size(&vm->stats_header); 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci for (i = 0; i < vm->stats_header.num_desc; ++i) { 215262306a36Sopenharmony_ci desc = (void *)vm->stats_desc + (i * size_desc); 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci if (strcmp(desc->name, stat_name)) 215562306a36Sopenharmony_ci continue; 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci read_stat_data(vm->stats_fd, &vm->stats_header, desc, 215862306a36Sopenharmony_ci data, max_elements); 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci break; 216162306a36Sopenharmony_ci } 216262306a36Sopenharmony_ci} 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci__weak void kvm_arch_vm_post_create(struct kvm_vm *vm) 216562306a36Sopenharmony_ci{ 216662306a36Sopenharmony_ci} 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci__weak void kvm_selftest_arch_init(void) 216962306a36Sopenharmony_ci{ 217062306a36Sopenharmony_ci} 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_civoid __attribute((constructor)) kvm_selftest_init(void) 217362306a36Sopenharmony_ci{ 217462306a36Sopenharmony_ci /* Tell stdout not to buffer its content. */ 217562306a36Sopenharmony_ci setbuf(stdout, NULL); 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci kvm_selftest_arch_init(); 217862306a36Sopenharmony_ci} 2179