int __open_path_or_exit(constchar *path, int flags, constchar *enoent_help)
{ int fd;
fd = open(path, flags); if (fd < 0) goto error;
return fd;
error: if (errno == EACCES || errno == ENOENT)
ksft_exit_skip("- Cannot open '%s': %s. %s\n",
path, strerror(errno),
errno == EACCES ? "Root required?" : enoent_help);
TEST_FAIL("Failed to open '%s'", path);
}
int open_path_or_exit(constchar *path, int flags)
{ return __open_path_or_exit(path, flags, "");
}
/* * Open KVM_DEV_PATH if available, otherwise exit the entire program. * * Input Args: * flags - The flags to pass when opening KVM_DEV_PATH. * * Return: * The opened file descriptor of /dev/kvm.
*/ staticint _open_kvm_dev_path_or_exit(int flags)
{ return __open_path_or_exit(KVM_DEV_PATH, flags, "Is KVM loaded and enabled?");
}
int open_kvm_dev_path_or_exit(void)
{ return _open_kvm_dev_path_or_exit(O_RDONLY);
}
r = close(fd);
TEST_ASSERT(!r, "close(%s) failed", path); return bytes_read;
}
staticint get_module_param_integer(constchar *module_name, constchar *param)
{ /* * 16 bytes to hold a 64-bit value (1 byte per char), 1 byte for the * NUL char, and 1 byte because the kernel sucks and inserts a newline * at the end.
*/ char value[16 + 1 + 1];
ssize_t r;
int get_kvm_param_integer(constchar *param)
{ return get_module_param_integer("kvm", param);
}
int get_kvm_intel_param_integer(constchar *param)
{ return get_module_param_integer("kvm_intel", param);
}
int get_kvm_amd_param_integer(constchar *param)
{ return get_module_param_integer("kvm_amd", param);
}
/* * Capability * * Input Args: * cap - Capability * * Output Args: None * * Return: * On success, the Value corresponding to the capability (KVM_CAP_*) * specified by the value of cap. On failure a TEST_ASSERT failure * is produced. * * Looks up and returns the value corresponding to the capability * (KVM_CAP_*) given by cap.
*/ unsignedint kvm_check_cap(long cap)
{ int ret; int kvm_fd;
/* * Initializes vm->vpages_valid to match the canonical VA space of the * architecture. * * The default implementation is valid for architectures which split the * range addressed by a single page table into a low and high region * based on the MSB of the VA. On architectures with this behavior * the VA region spans [0, 2^(va_bits - 1)), [-(2^(va_bits - 1), -1].
*/
__weak void vm_vaddr_populate_bitmap(struct kvm_vm *vm)
{
sparsebit_set_num(vm->vpages_valid,
0, (1ULL << (vm->va_bits - 1)) >> vm->page_shift);
sparsebit_set_num(vm->vpages_valid,
(~((1ULL << (vm->va_bits - 1)) - 1)) >> vm->page_shift,
(1ULL << (vm->va_bits - 1)) >> vm->page_shift);
}
TEST_ASSERT(nr_runnable_vcpus, "Use vm_create_barebones() for VMs that _never_ have vCPUs");
TEST_ASSERT(nr_runnable_vcpus <= kvm_check_cap(KVM_CAP_MAX_VCPUS), "nr_vcpus = %d too large for host, max-vcpus = %d",
nr_runnable_vcpus, kvm_check_cap(KVM_CAP_MAX_VCPUS));
/* * Arbitrarily allocate 512 pages (2mb when page size is 4kb) for the * test code and other per-VM assets that will be loaded into memslot0.
*/
nr_pages = 512;
/* Account for the per-vCPU stacks on behalf of the test. */
nr_pages += nr_runnable_vcpus * DEFAULT_STACK_PGS;
/* * Account for the number of pages needed for the page tables. The * maximum page table size for a memory region will be when the * smallest page size is used. Considering each page contains x page * table descriptors, the total extra size for page tables (for extra * N pages) will be: N/x+N/x^2+N/x^3+... which is definitely smaller * than N/x*2.
*/
nr_pages += (nr_pages + extra_mem_pages) / PTES_PER_MIN_PAGE * 2;
/* Account for the number of pages needed by ucall. */
nr_pages += ucall_nr_pages_required(page_size);
void kvm_set_files_rlimit(uint32_t nr_vcpus)
{ /* * Each vCPU will open two file descriptors: the vCPU itself and the * vCPU's binary stats file descriptor. Add an arbitrary amount of * buffer for all other files a test may open.
*/ int nr_fds_wanted = nr_vcpus * 2 + 100; struct rlimit rl;
/* * Check that we're allowed to open nr_fds_wanted file descriptors and * try raising the limits if needed.
*/
TEST_ASSERT(!getrlimit(RLIMIT_NOFILE, &rl), "getrlimit() failed!");
if (rl.rlim_cur < nr_fds_wanted) {
rl.rlim_cur = nr_fds_wanted; if (rl.rlim_max < nr_fds_wanted) { int old_rlim_max = rl.rlim_max;
rl.rlim_max = nr_fds_wanted;
__TEST_REQUIRE(setrlimit(RLIMIT_NOFILE, &rl) >= 0, "RLIMIT_NOFILE hard limit is too low (%d, wanted %d)",
old_rlim_max, nr_fds_wanted);
} else {
TEST_ASSERT(!setrlimit(RLIMIT_NOFILE, &rl), "setrlimit() failed!");
}
}
/* * Force GUEST_MEMFD for the primary memory region if necessary, e.g. * for CoCo VMs that require GUEST_MEMFD backed private memory.
*/
flags = 0; if (is_guest_memfd_required(shape))
flags |= KVM_MEM_GUEST_MEMFD;
vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0, nr_pages, flags); for (i = 0; i < NR_MEM_REGIONS; i++)
vm->memslots[i] = 0;
kvm_vm_elf_load(vm, program_invocation_name);
/* * TODO: Add proper defines to protect the library's memslots, and then * carve out memslot1 for the ucall MMIO address. KVM treats writes to * read-only memslots as MMIO, and creating a read-only memslot for the * MMIO region would prevent silently clobbering the MMIO region.
*/
slot0 = memslot2region(vm, 0);
ucall_init(vm, slot0->region.guest_phys_addr + slot0->region.memory_size);
/* * VM Create with customized parameters * * Input Args: * mode - VM Mode (e.g. VM_MODE_P52V48_4K) * nr_vcpus - VCPU count * extra_mem_pages - Non-slot0 physical memory total size * guest_code - Guest entry point * vcpuids - VCPU IDs * * Output Args: None * * Return: * Pointer to opaque structure that describes the created VM. * * Creates a VM with the mode specified by mode (e.g. VM_MODE_P52V48_4K). * extra_mem_pages is only used to calculate the maximum page table size, * no real memory allocation for non-slot0 memory in this function.
*/ struct kvm_vm *__vm_create_with_vcpus(struct vm_shape shape, uint32_t nr_vcpus,
uint64_t extra_mem_pages, void *guest_code, struct kvm_vcpu *vcpus[])
{ struct kvm_vm *vm; int i;
TEST_ASSERT(!nr_vcpus || vcpus, "Must provide vCPU array");
vm = __vm_create(shape, nr_vcpus, extra_mem_pages);
for (i = 0; i < nr_vcpus; ++i)
vcpus[i] = vm_vcpu_add(vm, i, guest_code);
vm = __vm_create_with_vcpus(shape, 1, extra_mem_pages, guest_code, vcpus);
*vcpu = vcpus[0]; return vm;
}
/* * VM Restart * * Input Args: * vm - VM that has been released before * * Output Args: None * * Reopens the file descriptors associated to the VM and reinstates the * global state, such as the irqchip and the memory regions that are mapped * into the guest.
*/ void kvm_vm_restart(struct kvm_vm *vmp)
{ int ctr; struct userspace_mem_region *region;
vm_open(vmp); if (vmp->has_irqchip)
vm_create_irqchip(vmp);
hash_for_each(vmp->regions.slot_hash, ctr, region, slot_node) { int ret = ioctl(vmp->fd, KVM_SET_USER_MEMORY_REGION2, ®ion->region);
printf(" -c: Pin tasks to physical CPUs. Takes a list of comma separated\n" " values (target pCPU), one for each vCPU, plus an optional\n" " entry for the main application task (specified via entry\n" " ). If used, entries must be provided for all\n" " vCPUs, i.e. pinning vCPUs is all or nothing.\n\n" " E.g. to create 3 vCPUs, pin vCPU0=>pCPU22, vCPU1=>pCPU23,\n" " vCPU2=>pCPU24, and pin the application task to pCPU50:\n\n" " %s -v 3 -c 22,23,24,50\n\n" " To leave the application task unpinned, drop the final entry:\n\n" " %s -v 3 -c 22,23,24\n\n" " (default: no pinning)\n", name, name);
}
void kvm_parse_vcpu_pinning(constchar *pcpus_string, uint32_t vcpu_to_pcpu[], int nr_vcpus)
{
cpu_set_t allowed_mask; char *cpu, *cpu_list; char delim[2] = ","; int i, r;
r = sched_getaffinity(0, sizeof(allowed_mask), &allowed_mask);
TEST_ASSERT(!r, "sched_getaffinity() failed");
cpu = strtok(cpu_list, delim);
/* 1. Get all pcpus for vcpus. */ for (i = 0; i < nr_vcpus; i++) {
TEST_ASSERT(cpu, "pCPU not provided for vCPU '%d'", i);
vcpu_to_pcpu[i] = parse_pcpu(cpu, &allowed_mask);
cpu = strtok(NULL, delim);
}
/* 2. Check if the main worker needs to be pinned. */ if (cpu) {
pin_self_to_cpu(parse_pcpu(cpu, &allowed_mask));
cpu = strtok(NULL, delim);
}
/* * Userspace Memory Region Find * * Input Args: * vm - Virtual Machine * start - Starting VM physical address * end - Ending VM physical address, inclusive. * * Output Args: None * * Return: * Pointer to overlapping region, NULL if no such region. * * Searches for a region with any physical memory that overlaps with * any portion of the guest physical addresses from start to end * inclusive. If multiple overlapping regions exist, a pointer to any * of the regions is returned. Null is returned only when no overlapping * region exists.
*/ staticstruct userspace_mem_region *
userspace_mem_region_find(struct kvm_vm *vm, uint64_t start, uint64_t end)
{ struct rb_node *node;
/* * VM VCPU Remove * * Input Args: * vcpu - VCPU to remove * * Output Args: None * * Return: None, TEST_ASSERT failures for all error conditions * * Removes a vCPU from a VM and frees its resources.
*/ staticvoid vm_vcpu_rm(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
{ int ret;
if (vcpu->dirty_gfns) {
ret = munmap(vcpu->dirty_gfns, vm->dirty_ring_size);
TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("munmap()", ret));
vcpu->dirty_gfns = NULL;
}
ret = munmap(vcpu->run, vcpu_mmap_sz());
TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("munmap()", ret));
ret = close(vcpu->fd);
TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("close()", ret));
sparsebit_free(®ion->unused_phy_pages);
sparsebit_free(®ion->protected_phy_pages);
ret = munmap(region->mmap_start, region->mmap_size);
TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("munmap()", ret)); if (region->fd >= 0) { /* There's an extra map when using shared memory. */
ret = munmap(region->mmap_alias, region->mmap_size);
TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("munmap()", ret));
close(region->fd);
} if (region->region.guest_memfd >= 0)
close(region->region.guest_memfd);
free(region);
}
/* * Destroys and frees the VM pointed to by vmp.
*/ void kvm_vm_free(struct kvm_vm *vmp)
{ int ctr; struct hlist_node *node; struct userspace_mem_region *region;
cregion = container_of(*cur, typeof(*cregion), hva_node);
parent = *cur; if (region->host_mem < cregion->host_mem)
cur = &(*cur)->rb_left; else {
TEST_ASSERT(region->host_mem !=
cregion->host_mem, "Duplicate HVA in region tree");
/* FIXME: This thing needs to be ripped apart and rewritten. */ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
uint64_t guest_paddr, uint32_t slot, uint64_t npages,
uint32_t flags, int guest_memfd, uint64_t guest_memfd_offset)
{ int ret; struct userspace_mem_region *region;
size_t backing_src_pagesz = get_backing_src_pagesz(src_type);
size_t mem_size = npages * vm->page_size;
size_t alignment;
TEST_REQUIRE_SET_USER_MEMORY_REGION2();
TEST_ASSERT(vm_adjust_num_guest_pages(vm->mode, npages) == npages, "Number of guest pages is not compatible with the host. " "Try npages=%d", vm_adjust_num_guest_pages(vm->mode, npages));
/* * Confirm a mem region with an overlapping address doesn't * already exist.
*/
region = (struct userspace_mem_region *) userspace_mem_region_find(
vm, guest_paddr, (guest_paddr + npages * vm->page_size) - 1); if (region != NULL)
TEST_FAIL("overlapping userspace_mem_region already " "exists\n" " requested guest_paddr: 0x%lx npages: 0x%lx " "page_size: 0x%x\n" " existing guest_paddr: 0x%lx size: 0x%lx",
guest_paddr, npages, vm->page_size,
(uint64_t) region->region.guest_phys_addr,
(uint64_t) region->region.memory_size);
/* Confirm no region with the requested slot already exists. */
hash_for_each_possible(vm->regions.slot_hash, region, slot_node,
slot) { if (region->region.slot != slot) continue;
TEST_FAIL("A mem region with the requested slot " "already exists.\n" " requested slot: %u paddr: 0x%lx npages: 0x%lx\n" " existing slot: %u paddr: 0x%lx size: 0x%lx",
slot, guest_paddr, npages,
region->region.slot,
(uint64_t) region->region.guest_phys_addr,
(uint64_t) region->region.memory_size);
}
/* Allocate and initialize new mem region structure. */
region = calloc(1, sizeof(*region));
TEST_ASSERT(region != NULL, "Insufficient Memory");
region->mmap_size = mem_size;
#ifdef __s390x__ /* On s390x, the host address must be aligned to 1M (due to PGSTEs) */
alignment = 0x100000; #else
alignment = 1; #endif
/* * When using THP mmap is not guaranteed to returned a hugepage aligned * address so we have to pad the mmap. Padding is not needed for HugeTLB * because mmap will always return an address aligned to the HugeTLB * page size.
*/ if (src_type == VM_MEM_SRC_ANONYMOUS_THP)
alignment = max(backing_src_pagesz, alignment);
if (flags & KVM_MEM_GUEST_MEMFD) { if (guest_memfd < 0) {
uint32_t guest_memfd_flags = 0;
TEST_ASSERT(!guest_memfd_offset, "Offset must be zero when creating new guest_memfd");
guest_memfd = vm_create_guest_memfd(vm, mem_size, guest_memfd_flags);
} else { /* * Install a unique fd for each memslot so that the fd * can be closed when the region is deleted without * needing to track if the fd is owned by the framework * or by the caller.
*/
guest_memfd = dup(guest_memfd);
TEST_ASSERT(guest_memfd >= 0, __KVM_SYSCALL_ERROR("dup()", guest_memfd));
}
/* * Memslot to region * * Input Args: * vm - Virtual Machine * memslot - KVM memory slot ID * * Output Args: None * * Return: * Pointer to memory region structure that describe memory region * using kvm memory slot ID given by memslot. TEST_ASSERT failure * on error (e.g. currently no memory region using memslot as a KVM * memory slot ID).
*/ struct userspace_mem_region *
memslot2region(struct kvm_vm *vm, uint32_t memslot)
{ struct userspace_mem_region *region;
hash_for_each_possible(vm->regions.slot_hash, region, slot_node,
memslot) if (region->region.slot == memslot) return region;
fprintf(stderr, "No mem region with the requested slot found,\n" " requested slot: %u\n", memslot);
fputs("---- vm dump ----\n", stderr);
vm_dump(stderr, vm, 2);
TEST_FAIL("Mem region not found"); return NULL;
}
/* * VM Memory Region Flags Set * * Input Args: * vm - Virtual Machine * flags - Starting guest physical address * * Output Args: None * * Return: None * * Sets the flags of the memory region specified by the value of slot, * to the values given by flags.
*/ void vm_mem_region_set_flags(struct kvm_vm *vm, uint32_t slot, uint32_t flags)
{ int ret; struct userspace_mem_region *region;
region = memslot2region(vm, slot);
region->region.flags = flags;
ret = __vm_ioctl(vm, KVM_SET_USER_MEMORY_REGION2, ®ion->region);
region = userspace_mem_region_find(vm, gpa, gpa);
TEST_ASSERT(region && region->region.flags & KVM_MEM_GUEST_MEMFD, "Private memory region not found for GPA 0x%lx", gpa);
offset = gpa - region->region.guest_phys_addr;
fd_offset = region->region.guest_memfd_offset + offset;
len = min_t(uint64_t, end - gpa, region->region.memory_size - offset);
list_for_each_entry(vcpu, &vm->vcpus, list) { if (vcpu->id == vcpu_id) returntrue;
}
returnfalse;
}
/* * Adds a virtual CPU to the VM specified by vm with the ID given by vcpu_id. * No additional vCPU setup is done. Returns the vCPU.
*/ struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id)
{ struct kvm_vcpu *vcpu;
/* Confirm a vcpu with the specified id doesn't already exist. */
TEST_ASSERT(!vcpu_exists(vm, vcpu_id), "vCPU%d already exists", vcpu_id);
/* Allocate and initialize new vcpu structure. */
vcpu = calloc(1, sizeof(*vcpu));
TEST_ASSERT(vcpu != NULL, "Insufficient Memory");
if (kvm_has_cap(KVM_CAP_BINARY_STATS_FD))
vcpu->stats.fd = vcpu_get_stats_fd(vcpu); else
vcpu->stats.fd = -1;
/* Add to linked-list of VCPUs. */
list_add(&vcpu->list, &vm->vcpus);
return vcpu;
}
/* * VM Virtual Address Unused Gap * * Input Args: * vm - Virtual Machine * sz - Size (bytes) * vaddr_min - Minimum Virtual Address * * Output Args: None * * Return: * Lowest virtual address at or below vaddr_min, with at least * sz unused bytes. TEST_ASSERT failure if no area of at least * size sz is available. * * Within the VM specified by vm, locates the lowest starting virtual * address >= vaddr_min, that has at least sz unallocated bytes. A * TEST_ASSERT failure occurs for invalid input or no area of at least * sz unallocated bytes >= vaddr_min is available.
*/
vm_vaddr_t vm_vaddr_unused_gap(struct kvm_vm *vm, size_t sz,
vm_vaddr_t vaddr_min)
{
uint64_t pages = (sz + vm->page_size - 1) >> vm->page_shift;
/* Loop over section with enough valid virtual page indexes. */ if (!sparsebit_is_set_num(vm->vpages_valid,
pgidx_start, pages))
pgidx_start = sparsebit_next_set_num(vm->vpages_valid,
pgidx_start, pages); do { /* * Are there enough unused virtual pages available at * the currently proposed starting virtual page index. * If not, adjust proposed starting index to next * possible.
*/ if (sparsebit_is_clear_num(vm->vpages_mapped,
pgidx_start, pages)) goto va_found;
pgidx_start = sparsebit_next_clear_num(vm->vpages_mapped,
pgidx_start, pages); if (pgidx_start == 0) goto no_va_found;
/* * If needed, adjust proposed starting virtual address, * to next range of valid virtual addresses.
*/ if (!sparsebit_is_set_num(vm->vpages_valid,
pgidx_start, pages)) {
pgidx_start = sparsebit_next_set_num(
vm->vpages_valid, pgidx_start, pages); if (pgidx_start == 0) goto no_va_found;
}
} while (pgidx_start != 0);
no_va_found:
TEST_FAIL("No vaddr of specified pages available, pages: 0x%lx", pages);
/* * VM Virtual Address Allocate * * Input Args: * vm - Virtual Machine * sz - Size in bytes * vaddr_min - Minimum starting virtual address * * Output Args: None * * Return: * Starting guest virtual address * * Allocates at least sz bytes within the virtual address space of the vm * given by vm. The allocated bytes are mapped to a virtual address >= * the address given by vaddr_min. Note that each allocation uses a * a unique set of pages, with the minimum real allocation being at least * a page. The allocated physical space comes from the TEST_DATA memory region.
*/
vm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min)
{ return __vm_vaddr_alloc(vm, sz, vaddr_min, MEM_REGION_TEST_DATA);
}
/* * VM Virtual Address Allocate Pages * * Input Args: * vm - Virtual Machine * * Output Args: None * * Return: * Starting guest virtual address * * Allocates at least N system pages worth of bytes within the virtual address * space of the vm.
*/
vm_vaddr_t vm_vaddr_alloc_pages(struct kvm_vm *vm, int nr_pages)
{ return vm_vaddr_alloc(vm, nr_pages * getpagesize(), KVM_UTIL_MIN_VADDR);
}
/* * VM Virtual Address Allocate Page * * Input Args: * vm - Virtual Machine * * Output Args: None * * Return: * Starting guest virtual address * * Allocates at least one system page worth of bytes within the virtual address * space of the vm.
*/
vm_vaddr_t vm_vaddr_alloc_page(struct kvm_vm *vm)
{ return vm_vaddr_alloc_pages(vm, 1);
}
/* * Map a range of VM virtual address to the VM's physical address * * Input Args: * vm - Virtual Machine * vaddr - Virtuall address to map * paddr - VM Physical Address * npages - The number of pages to map * * Output Args: None * * Return: None * * Within the VM given by @vm, creates a virtual translation for * @npages starting at @vaddr to the page range starting at @paddr.
*/ void virt_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, unsignedint npages)
{
size_t page_size = vm->page_size;
size_t size = npages * page_size;
while (npages--) {
virt_pg_map(vm, vaddr, paddr);
sparsebit_set(vm->vpages_mapped, vaddr >> vm->page_shift);
vaddr += page_size;
paddr += page_size;
}
}
/* * Address VM Physical to Host Virtual * * Input Args: * vm - Virtual Machine * gpa - VM physical address * * Output Args: None * * Return: * Equivalent host virtual address * * Locates the memory region containing the VM physical address given * by gpa, within the VM given by vm. When found, the host virtual * address providing the memory to the vm physical address is returned. * A TEST_ASSERT failure occurs if no region containing gpa exists.
*/ void *addr_gpa2hva(struct kvm_vm *vm, vm_paddr_t gpa)
{ struct userspace_mem_region *region;
gpa = vm_untag_gpa(vm, gpa);
region = userspace_mem_region_find(vm, gpa, gpa); if (!region) {
TEST_FAIL("No vm physical memory at 0x%lx", gpa); return NULL;
}
/* * Address Host Virtual to VM Physical * * Input Args: * vm - Virtual Machine * hva - Host virtual address * * Output Args: None * * Return: * Equivalent VM physical address * * Locates the memory region containing the host virtual address given * by hva, within the VM given by vm. When found, the equivalent * VM physical address is returned. A TEST_ASSERT failure occurs if no * region containing hva exists.
*/
vm_paddr_t addr_hva2gpa(struct kvm_vm *vm, void *hva)
{ struct rb_node *node;
TEST_FAIL("No mapping to a guest physical address, hva: %p", hva); return -1;
}
/* * Address VM physical to Host Virtual *alias*. * * Input Args: * vm - Virtual Machine * gpa - VM physical address * * Output Args: None * * Return: * Equivalent address within the host virtual *alias* area, or NULL * (without failing the test) if the guest memory is not shared (so * no alias exists). * * Create a writable, shared virtual=>physical alias for the specific GPA. * The primary use case is to allow the host selftest to manipulate guest * memory without mapping said memory in the guest's address space. And, for * userfaultfd-based demand paging, to do so without triggering userfaults.
*/ void *addr_gpa2alias(struct kvm_vm *vm, vm_paddr_t gpa)
{ struct userspace_mem_region *region;
uintptr_t offset;
region = userspace_mem_region_find(vm, gpa, gpa); if (!region) return NULL;
/* Create an interrupt controller chip for the specified VM. */ void vm_create_irqchip(struct kvm_vm *vm)
{ int r;
/* * Allocate a fully in-kernel IRQ chip by default, but fall back to a * split model (x86 only) if that fails (KVM x86 allows compiling out * support for KVM_CREATE_IRQCHIP).
*/
r = __vm_ioctl(vm, KVM_CREATE_IRQCHIP, NULL); if (r && errno == ENOTTY && kvm_has_cap(KVM_CAP_SPLIT_IRQCHIP))
vm_enable_cap(vm, KVM_CAP_SPLIT_IRQCHIP, 24); else
TEST_ASSERT_VM_VCPU_IOCTL(!r, KVM_CREATE_IRQCHIP, r, vm);
vm->has_irqchip = true;
}
int _vcpu_run(struct kvm_vcpu *vcpu)
{ int rc;
do {
rc = __vcpu_run(vcpu);
} while (rc == -1 && errno == EINTR);
if (!rc)
assert_on_unhandled_exception(vcpu);
return rc;
}
/* * Invoke KVM_RUN on a vCPU until KVM returns something other than -EINTR. * Assert if the KVM returns an error (other than -EINTR).
*/ void vcpu_run(struct kvm_vcpu *vcpu)
{ int ret = _vcpu_run(vcpu);
/* * Get the list of guest registers which are supported for * KVM_GET_ONE_REG/KVM_SET_ONE_REG ioctls. Returns a kvm_reg_list pointer, * it is the caller's responsibility to free the list.
*/ struct kvm_reg_list *vcpu_get_reg_list(struct kvm_vcpu *vcpu)
{ struct kvm_reg_list reg_list_n = { .n = 0 }, *reg_list; int ret;
size = sizeof(struct kvm_irq_routing); /* Allocate space for the max number of entries: this wastes 196 KBs. */
size += KVM_MAX_IRQ_ROUTES * sizeof(struct kvm_irq_routing_entry);
routing = calloc(1, size);
assert(routing);
int _kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing)
{ int ret;
assert(routing);
ret = __vm_ioctl(vm, KVM_SET_GSI_ROUTING, routing);
free(routing);
return ret;
}
void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing)
{ int ret;
ret = _kvm_gsi_routing_write(vm, routing);
TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_SET_GSI_ROUTING, ret));
}
/* * VM Dump * * Input Args: * vm - Virtual Machine * indent - Left margin indent amount * * Output Args: * stream - Output FILE stream * * Return: None * * Dumps the current state of the VM given by vm, to the FILE stream * given by stream.
*/ void vm_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent)
{ int ctr; struct userspace_mem_region *region; struct kvm_vcpu *vcpu;
/* * Exit Reason String * * Input Args: * exit_reason - Exit reason * * Output Args: None * * Return: * Constant string pointer describing the exit reason. * * Locates and returns a constant string that describes the KVM exit * reason given by exit_reason. If no such string is found, a constant * string of "Unknown" is returned.
*/ constchar *exit_reason_str(unsignedint exit_reason)
{ unsignedint n1;
for (n1 = 0; n1 < ARRAY_SIZE(exit_reasons_known); n1++) { if (exit_reason == exit_reasons_known[n1].reason) return exit_reasons_known[n1].name;
}
return"Unknown";
}
/* * Physical Contiguous Page Allocator * * Input Args: * vm - Virtual Machine * num - number of pages * paddr_min - Physical address minimum * memslot - Memory region to allocate page from * protected - True if the pages will be used as protected/private memory * * Output Args: None * * Return: * Starting physical address * * Within the VM specified by vm, locates a range of available physical * pages at or above paddr_min. If found, the pages are marked as in use * and their base address is returned. A TEST_ASSERT failure occurs if * not enough pages are available at or above paddr_min.
*/
vm_paddr_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
vm_paddr_t paddr_min, uint32_t memslot, boolprotected)
{ struct userspace_mem_region *region;
sparsebit_idx_t pg, base;
TEST_ASSERT(num > 0, "Must allocate at least one page");
region = memslot2region(vm, memslot);
TEST_ASSERT(!protected || region->protected_phy_pages, "Region doesn't support protected memory");
base = pg = paddr_min >> vm->page_shift; do { for (; pg < base + num; ++pg) { if (!sparsebit_is_set(region->unused_phy_pages, pg)) {
base = pg = sparsebit_next_set(region->unused_phy_pages, pg); break;
}
}
} while (pg && pg != base + num);
/* * Read binary stats descriptors * * Input Args: * stats_fd - the file descriptor for the binary stats file from which to read * header - the binary stats metadata header corresponding to the given FD * * Output Args: None * * Return: * A pointer to a newly allocated series of stat descriptors. * Caller is responsible for freeing the returned kvm_stats_desc. * * Read the stats descriptors from the binary stats interface.
*/ struct kvm_stats_desc *read_stats_descriptors(int stats_fd, struct kvm_stats_header *header)
{ struct kvm_stats_desc *stats_desc;
ssize_t desc_size, total_size, ret;
/* * Read stat data for a particular stat * * Input Args: * stats_fd - the file descriptor for the binary stats file from which to read * header - the binary stats metadata header corresponding to the given FD * desc - the binary stat metadata for the particular stat to be read * max_elements - the maximum number of 8-byte values to read into data * * Output Args: * data - the buffer into which stat data should be read * * Read the data values of a specified stat from the binary stats interface.
*/ void read_stat_data(int stats_fd, struct kvm_stats_header *header, struct kvm_stats_desc *desc, uint64_t *data,
size_t max_elements)
{
size_t nr_elements = min_t(ssize_t, desc->size, max_elements);
size_t size = nr_elements * sizeof(*data);
ssize_t ret;
TEST_ASSERT(desc->size, "No elements in stat '%s'", desc->name);
TEST_ASSERT(max_elements, "Zero elements requested for stat '%s'", desc->name);
ret = pread(stats_fd, data, size,
header->data_offset + desc->offset);
TEST_ASSERT(ret >= 0, "pread() failed on stat '%s', errno: %i (%s)",
desc->name, errno, strerror(errno));
TEST_ASSERT(ret == size, "pread() on stat '%s' read %ld bytes, wanted %lu bytes",
desc->name, size, ret);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.