| /* |
| * Taken from perf which in turn take it from GIT |
| */ |
| |
| #include "kvm/util.h" |
| |
| #include <kvm/kvm.h> |
| #include <linux/magic.h> /* For HUGETLBFS_MAGIC */ |
| #include <linux/memfd.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <sys/statfs.h> |
| |
| static void report(const char *prefix, const char *err, va_list params) |
| { |
| char msg[1024]; |
| vsnprintf(msg, sizeof(msg), err, params); |
| fprintf(stderr, " %s%s\n", prefix, msg); |
| } |
| |
| static NORETURN void die_builtin(const char *err, va_list params) |
| { |
| report(" Fatal: ", err, params); |
| exit(128); |
| } |
| |
| static void error_builtin(const char *err, va_list params) |
| { |
| report(" Error: ", err, params); |
| } |
| |
| static void warn_builtin(const char *warn, va_list params) |
| { |
| report(" Warning: ", warn, params); |
| } |
| |
| static void info_builtin(const char *info, va_list params) |
| { |
| report(" Info: ", info, params); |
| } |
| |
| static void debug_builtin(const char *debug, va_list params) |
| { |
| report(" Debug: ", debug, params); |
| } |
| |
| void die(const char *err, ...) |
| { |
| va_list params; |
| |
| va_start(params, err); |
| die_builtin(err, params); |
| va_end(params); |
| } |
| |
| void pr_err(const char *err, ...) |
| { |
| va_list params; |
| |
| if (loglevel < LOGLEVEL_ERROR) |
| return; |
| |
| va_start(params, err); |
| error_builtin(err, params); |
| va_end(params); |
| } |
| |
| void pr_warning(const char *warn, ...) |
| { |
| va_list params; |
| |
| if (loglevel < LOGLEVEL_WARNING) |
| return; |
| |
| va_start(params, warn); |
| warn_builtin(warn, params); |
| va_end(params); |
| } |
| |
| void pr_info(const char *info, ...) |
| { |
| va_list params; |
| |
| if (loglevel < LOGLEVEL_INFO) |
| return; |
| |
| va_start(params, info); |
| info_builtin(info, params); |
| va_end(params); |
| } |
| |
| /* Do not call directly; call pr_debug() instead. */ |
| void __pr_debug(const char *debug, ...) |
| { |
| va_list params; |
| |
| va_start(params, debug); |
| debug_builtin(debug, params); |
| va_end(params); |
| } |
| |
| void die_perror(const char *s) |
| { |
| perror(s); |
| exit(1); |
| } |
| |
| static u64 get_hugepage_blk_size(const char *hugetlbfs_path) |
| { |
| struct statfs sfs; |
| |
| if (statfs(hugetlbfs_path, &sfs) < 0) |
| die("Can't stat %s", hugetlbfs_path); |
| |
| if ((unsigned int)sfs.f_type != HUGETLBFS_MAGIC) |
| die("%s is not hugetlbfs!", hugetlbfs_path); |
| |
| return sfs.f_bsize; |
| } |
| |
| static int guest_memfd_alloc(struct kvm *kvm, size_t size, bool hugetlb, u64 blk_size) |
| { |
| struct kvm_create_guest_memfd gmem = { |
| .size = size, |
| .flags = GUEST_MEMFD_FLAG_SUPPORT_SHARED, |
| }; |
| |
| BUG_ON(hugetlb); |
| BUG_ON(!kvm__supports_extension(kvm, KVM_CAP_GMEM_SHARED_MEM)); |
| |
| return ioctl(kvm->vm_fd, KVM_CREATE_GUEST_MEMFD, &gmem); |
| } |
| |
| int memfd_alloc(struct kvm *kvm, size_t size, bool hugetlb, u64 blk_size) |
| { |
| const char *name = "kvmtool"; |
| unsigned int flags = 0; |
| int fd; |
| |
| if (hugetlb) { |
| if (!is_power_of_two(blk_size)) |
| die("Hugepage size must be a power of 2"); |
| |
| flags |= MFD_HUGETLB; |
| flags |= blk_size << MFD_HUGE_SHIFT; |
| } |
| |
| fd = memfd_create(name, flags); |
| if (fd < 0) |
| die_perror("Can't memfd_create for memory map"); |
| |
| if (ftruncate(fd, size) < 0) |
| die("Can't ftruncate for mem mapping size %lld", |
| (unsigned long long)size); |
| |
| return fd; |
| } |
| |
| /* |
| * This function allocates memory aligned to align_sz. |
| * It also wraps the decision between hugetlbfs (if requested) or normal mmap. |
| */ |
| void *mmap_anon_or_hugetlbfs_align(struct kvm *kvm, const char *hugetlbfs_path, |
| u64 size, u64 align_sz) |
| { |
| u64 blk_size = 0; |
| u64 total_map = size + align_sz; |
| u64 start_off, end_off; |
| void *addr_map, *addr_align; |
| int fd; |
| |
| pr_debug("Trying to allocate %llu bytes, total map %llu, guest_memfd %d", |
| size, total_map, kvm->cfg.guest_memfd); |
| |
| align_sz = max(align_sz, (u64)PAGE_SIZE); |
| |
| /* |
| * We don't /need/ to map guest RAM from hugetlbfs, but we do so |
| * if the user specifies a hugetlbfs path. |
| */ |
| if (hugetlbfs_path) { |
| blk_size = get_hugepage_blk_size(hugetlbfs_path); |
| |
| if (blk_size == 0 || blk_size > size) { |
| die("Can't use hugetlbfs pagesize %lld for mem size %lld", |
| (unsigned long long)blk_size, (unsigned long long)size); |
| } |
| |
| kvm->ram_pagesize = blk_size; |
| } else { |
| kvm->ram_pagesize = getpagesize(); |
| } |
| |
| /* Create a mapping with room for alignment without allocating. */ |
| addr_map = mmap(NULL, total_map, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, |
| -1, 0); |
| if (addr_map == MAP_FAILED) { |
| pr_warning("addr_map failed"); |
| return MAP_FAILED; |
| } |
| |
| if (kvm->cfg.guest_memfd) |
| fd = guest_memfd_alloc(kvm, size, hugetlbfs_path, blk_size); |
| else |
| fd = memfd_alloc(kvm, size, hugetlbfs_path, blk_size); |
| if (fd < 0) { |
| pr_warning("alloc failed"); |
| return MAP_FAILED; |
| } |
| |
| /* Map the allocated memory in the fd to the specified alignment. */ |
| addr_align = (void *)ALIGN((u64)addr_map, align_sz); |
| if (mmap(addr_align, size, PROT_RW, MAP_SHARED | MAP_FIXED, fd, 0) == |
| MAP_FAILED) { |
| pr_warning("addr_align failed, addr_align is 0x%llx, addr_map is 0x%llx", (u64)addr_align, (u64)addr_map); |
| close(fd); |
| return MAP_FAILED; |
| } |
| |
| /* Remove the mapping for unused address ranges. */ |
| start_off = addr_align - addr_map; |
| if (start_off) |
| munmap(addr_map, start_off); |
| |
| end_off = align_sz - start_off; |
| if (end_off) |
| munmap((void *)((u64)addr_align + size), end_off); |
| |
| kvm->ram_fd = fd; |
| return addr_align; |
| } |
| |
| void *mmap_anon_or_hugetlbfs(struct kvm *kvm, const char *hugetlbfs_path, u64 size) |
| { |
| return mmap_anon_or_hugetlbfs_align(kvm, hugetlbfs_path, size, 0); |
| } |