123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- /*
- * Support for decoding of KVM_* ioctl commands.
- *
- * Copyright (c) 2017 Masatake YAMATO <yamato@redhat.com>
- * Copyright (c) 2017 Red Hat, Inc.
- * Copyright (c) 2017-2019 The strace developers.
- * All rights reserved.
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
- #include "defs.h"
-
- #ifdef HAVE_LINUX_KVM_H
- # include <linux/kvm.h>
- # include "print_fields.h"
- # include "arch_kvm.c"
- # include "xmalloc.h"
- # include "mmap_cache.h"
-
- struct vcpu_info {
- struct vcpu_info *next;
- int fd;
- int cpuid;
- long mmap_addr;
- unsigned long mmap_len;
- bool resolved;
- };
-
- static bool dump_kvm_run_structure;
-
- static struct vcpu_info *
- vcpu_find(struct tcb *const tcp, int fd)
- {
- for (struct vcpu_info *vcpu_info = tcp->vcpu_info_list;
- vcpu_info;
- vcpu_info = vcpu_info->next)
- if (vcpu_info->fd == fd)
- return vcpu_info;
-
- return NULL;
- }
-
- static struct vcpu_info *
- vcpu_alloc(struct tcb *const tcp, int fd, int cpuid)
- {
- struct vcpu_info *vcpu_info = xzalloc(sizeof(*vcpu_info));
-
- vcpu_info->fd = fd;
- vcpu_info->cpuid = cpuid;
-
- vcpu_info->next = tcp->vcpu_info_list;
- tcp->vcpu_info_list = vcpu_info;
-
- return vcpu_info;
- }
-
- void
- kvm_vcpu_info_free(struct tcb *tcp)
- {
- struct vcpu_info *head, *next;
-
- for (head = tcp->vcpu_info_list; head; head = next) {
- next = head->next;
- free(head);
- }
-
- tcp->vcpu_info_list = NULL;
- }
-
- static void
- vcpu_register(struct tcb *const tcp, int fd, int cpuid)
- {
- if (fd < 0)
- return;
-
- struct vcpu_info *vcpu_info = vcpu_find(tcp, fd);
-
- if (!vcpu_info) {
- vcpu_alloc(tcp, fd, cpuid);
- } else if (vcpu_info->cpuid != cpuid) {
- vcpu_info->cpuid = cpuid;
- vcpu_info->resolved = false;
- }
- }
-
- static bool
- is_map_for_file(struct mmap_cache_entry_t *map_info, void *data)
- {
- /* major version for anon inode may be given in get_anon_bdev()
- * in linux kernel.
- *
- * *p = MKDEV(0, dev & MINORMASK);
- *-----------------^
- */
- return map_info->binary_filename &&
- map_info->major == 0 &&
- strcmp(map_info->binary_filename, data) == 0;
- }
-
- static unsigned long
- map_len(struct mmap_cache_entry_t *map_info)
- {
- return map_info->start_addr < map_info->end_addr
- ? map_info->end_addr - map_info->start_addr
- : 0;
- }
-
- # define VCPU_DENTRY_PREFIX "anon_inode:kvm-vcpu:"
-
- static struct vcpu_info*
- vcpu_get_info(struct tcb *const tcp, int fd)
- {
- struct vcpu_info *vcpu_info = vcpu_find(tcp, fd);
- struct mmap_cache_entry_t *map_info;
- const char *cpuid_str;
-
- enum mmap_cache_rebuild_result mc_stat =
- mmap_cache_rebuild_if_invalid(tcp, __func__);
- if (mc_stat == MMAP_CACHE_REBUILD_NOCACHE)
- return NULL;
-
- if (vcpu_info && vcpu_info->resolved) {
- if (mc_stat == MMAP_CACHE_REBUILD_READY)
- return vcpu_info;
- else {
- map_info = mmap_cache_search(tcp, vcpu_info->mmap_addr);
- if (map_info) {
- cpuid_str =
- STR_STRIP_PREFIX(map_info->binary_filename,
- VCPU_DENTRY_PREFIX);
- if (cpuid_str != map_info->binary_filename) {
- int cpuid = string_to_uint(cpuid_str);
- if (cpuid < 0)
- return NULL;
- if (vcpu_info->cpuid == cpuid)
- return vcpu_info;
- }
- }
-
- /* The vcpu vma may be mremap'ed. */
- vcpu_info->resolved = false;
- }
- }
-
- /* Slow path: !vcpu_info || !vcpu_info->resolved */
- char path[PATH_MAX + 1];
- cpuid_str = path;
- if (getfdpath(tcp, fd, path, sizeof(path)) >= 0)
- cpuid_str = STR_STRIP_PREFIX(path, VCPU_DENTRY_PREFIX);
- if (cpuid_str == path)
- map_info = NULL;
- else
- map_info = mmap_cache_search_custom(tcp, is_map_for_file, path);
-
- if (map_info) {
- int cpuid = string_to_uint(cpuid_str);
- if (cpuid < 0)
- return NULL;
- if (!vcpu_info)
- vcpu_info = vcpu_alloc(tcp, fd, cpuid);
- else if (vcpu_info->cpuid != cpuid)
- vcpu_info->cpuid = cpuid;
- vcpu_info->mmap_addr = map_info->start_addr;
- vcpu_info->mmap_len = map_len(map_info);
- vcpu_info->resolved = true;
- return vcpu_info;
- }
-
- return NULL;
- }
-
- static int
- kvm_ioctl_create_vcpu(struct tcb *const tcp, const kernel_ulong_t arg)
- {
- uint32_t cpuid = arg;
-
- if (entering(tcp)) {
- tprintf(", %u", cpuid);
- if (dump_kvm_run_structure)
- return 0;
- } else if (!syserror(tcp)) {
- vcpu_register(tcp, tcp->u_rval, cpuid);
- }
-
- return RVAL_IOCTL_DECODED | RVAL_FD;
- }
-
- # ifdef HAVE_STRUCT_KVM_USERSPACE_MEMORY_REGION
- # include "xlat/kvm_mem_flags.h"
- static int
- kvm_ioctl_set_user_memory_region(struct tcb *const tcp, const kernel_ulong_t arg)
- {
- struct kvm_userspace_memory_region u_memory_region;
-
- tprints(", ");
- if (umove_or_printaddr(tcp, arg, &u_memory_region))
- return RVAL_IOCTL_DECODED;
-
- PRINT_FIELD_U("{", u_memory_region, slot);
- PRINT_FIELD_FLAGS(", ", u_memory_region, flags, kvm_mem_flags,
- "KVM_MEM_???");
- PRINT_FIELD_X(", ", u_memory_region, guest_phys_addr);
- PRINT_FIELD_U(", ", u_memory_region, memory_size);
- PRINT_FIELD_X(", ", u_memory_region, userspace_addr);
- tprints("}");
-
- return RVAL_IOCTL_DECODED;
- }
- # endif /* HAVE_STRUCT_KVM_USERSPACE_MEMORY_REGION */
-
- # ifdef HAVE_STRUCT_KVM_REGS
- static int
- kvm_ioctl_decode_regs(struct tcb *const tcp, const unsigned int code,
- const kernel_ulong_t arg)
- {
- struct kvm_regs regs;
-
- if (code == KVM_GET_REGS && entering(tcp))
- return 0;
-
- tprints(", ");
- if (!umove_or_printaddr(tcp, arg, ®s))
- arch_print_kvm_regs(tcp, arg, ®s);
-
- return RVAL_IOCTL_DECODED;
- }
- # endif /* HAVE_STRUCT_KVM_REGS */
-
- # ifdef HAVE_STRUCT_KVM_CPUID2
- # include "xlat/kvm_cpuid_flags.h"
- static bool
- print_kvm_cpuid_entry(struct tcb *const tcp,
- void* elem_buf, size_t elem_size, void* data)
- {
- const struct kvm_cpuid_entry2 *entry = elem_buf;
- PRINT_FIELD_X("{", *entry, function);
- PRINT_FIELD_X(", ", *entry, index);
- PRINT_FIELD_FLAGS(", ", *entry, flags, kvm_cpuid_flags,
- "KVM_CPUID_FLAG_???");
- PRINT_FIELD_X(", ", *entry, eax);
- PRINT_FIELD_X(", ", *entry, ebx);
- PRINT_FIELD_X(", ", *entry, ecx);
- PRINT_FIELD_X(", ", *entry, edx);
- tprints("}");
-
- return true;
- }
-
- static int
- kvm_ioctl_decode_cpuid2(struct tcb *const tcp, const unsigned int code,
- const kernel_ulong_t arg)
- {
- struct kvm_cpuid2 cpuid;
-
- if (entering(tcp) && (code == KVM_GET_SUPPORTED_CPUID
- # ifdef KVM_GET_EMULATED_CPUID
- || code == KVM_GET_EMULATED_CPUID
- # endif
- ))
- return 0;
-
- tprints(", ");
- if (!umove_or_printaddr(tcp, arg, &cpuid)) {
- PRINT_FIELD_U("{", cpuid, nent);
-
- tprints(", entries=");
- if (abbrev(tcp)) {
- tprints("[");
- if (cpuid.nent)
- tprints("...");
- tprints("]");
-
- } else {
- struct kvm_cpuid_entry2 entry;
- print_array(tcp, arg + sizeof(cpuid), cpuid.nent,
- &entry, sizeof(entry), tfetch_mem,
- print_kvm_cpuid_entry, NULL);
- }
- tprints("}");
- }
-
- return RVAL_IOCTL_DECODED;
- }
- # endif /* HAVE_STRUCT_KVM_CPUID2 */
-
- # ifdef HAVE_STRUCT_KVM_SREGS
- static int
- kvm_ioctl_decode_sregs(struct tcb *const tcp, const unsigned int code,
- const kernel_ulong_t arg)
- {
- struct kvm_sregs sregs;
-
- if (code == KVM_GET_SREGS && entering(tcp))
- return 0;
-
- tprints(", ");
- if (!umove_or_printaddr(tcp, arg, &sregs))
- arch_print_kvm_sregs(tcp, arg, &sregs);
-
- return RVAL_IOCTL_DECODED;
- }
- # endif /* HAVE_STRUCT_KVM_SREGS */
-
- # include "xlat/kvm_cap.h"
- static int
- kvm_ioctl_decode_check_extension(struct tcb *const tcp, const unsigned int code,
- const kernel_ulong_t arg)
- {
- tprints(", ");
- printxval64(kvm_cap, arg, "KVM_CAP_???");
- return RVAL_IOCTL_DECODED;
- }
-
- # include "xlat/kvm_exit_reason.h"
- static void
- kvm_ioctl_run_attach_auxstr(struct tcb *const tcp,
- struct vcpu_info *info)
-
- {
- static struct kvm_run vcpu_run_struct;
-
- if (info->mmap_len < sizeof(vcpu_run_struct))
- return;
-
- if (umove(tcp, info->mmap_addr, &vcpu_run_struct) < 0)
- return;
-
- tcp->auxstr = xlookup(kvm_exit_reason, vcpu_run_struct.exit_reason);
- if (!tcp->auxstr)
- tcp->auxstr = "KVM_EXIT_???";
- }
-
- static int
- kvm_ioctl_decode_run(struct tcb *const tcp)
- {
-
- if (entering(tcp))
- return 0;
-
- int r = RVAL_DECODED;
-
- if (syserror(tcp))
- return r;
-
- if (dump_kvm_run_structure) {
- tcp->auxstr = NULL;
- int fd = tcp->u_arg[0];
- struct vcpu_info *info = vcpu_get_info(tcp, fd);
-
- if (info) {
- kvm_ioctl_run_attach_auxstr(tcp, info);
- if (tcp->auxstr)
- r |= RVAL_STR;
- }
- }
-
- return r;
- }
-
- int
- kvm_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ulong_t arg)
- {
- switch (code) {
- case KVM_CREATE_VCPU:
- return kvm_ioctl_create_vcpu(tcp, arg);
-
- # ifdef HAVE_STRUCT_KVM_USERSPACE_MEMORY_REGION
- case KVM_SET_USER_MEMORY_REGION:
- return kvm_ioctl_set_user_memory_region(tcp, arg);
- # endif
-
- # ifdef HAVE_STRUCT_KVM_REGS
- case KVM_SET_REGS:
- case KVM_GET_REGS:
- return kvm_ioctl_decode_regs(tcp, code, arg);
- # endif
-
- # ifdef HAVE_STRUCT_KVM_SREGS
- case KVM_SET_SREGS:
- case KVM_GET_SREGS:
- return kvm_ioctl_decode_sregs(tcp, code, arg);
- # endif
-
- # ifdef HAVE_STRUCT_KVM_CPUID2
- case KVM_SET_CPUID2:
- case KVM_GET_SUPPORTED_CPUID:
- # ifdef KVM_GET_EMULATED_CPUID
- case KVM_GET_EMULATED_CPUID:
- # endif
- return kvm_ioctl_decode_cpuid2(tcp, code, arg);
- # endif
-
- case KVM_CHECK_EXTENSION:
- return kvm_ioctl_decode_check_extension(tcp, code, arg);
-
- case KVM_CREATE_VM:
- return RVAL_DECODED | RVAL_FD;
-
- case KVM_RUN:
- return kvm_ioctl_decode_run(tcp);
-
- case KVM_GET_VCPU_MMAP_SIZE:
- case KVM_GET_API_VERSION:
- default:
- return RVAL_DECODED;
- }
- }
-
- void
- kvm_run_structure_decoder_init(void)
- {
- dump_kvm_run_structure = true;
- mmap_cache_enable();
- }
-
- #endif /* HAVE_LINUX_KVM_H */
|