Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v6.19-rc3 163 lines 3.4 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2#include "linux/types.h" 3#include "linux/bitmap.h" 4#include "linux/atomic.h" 5 6#include "kvm_util.h" 7#include "ucall_common.h" 8 9 10#define GUEST_UCALL_FAILED -1 11 12struct ucall_header { 13 DECLARE_BITMAP(in_use, KVM_MAX_VCPUS); 14 struct ucall ucalls[KVM_MAX_VCPUS]; 15}; 16 17int ucall_nr_pages_required(uint64_t page_size) 18{ 19 return align_up(sizeof(struct ucall_header), page_size) / page_size; 20} 21 22/* 23 * ucall_pool holds per-VM values (global data is duplicated by each VM), it 24 * must not be accessed from host code. 25 */ 26static struct ucall_header *ucall_pool; 27 28void ucall_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) 29{ 30 struct ucall_header *hdr; 31 struct ucall *uc; 32 vm_vaddr_t vaddr; 33 int i; 34 35 vaddr = vm_vaddr_alloc_shared(vm, sizeof(*hdr), KVM_UTIL_MIN_VADDR, 36 MEM_REGION_DATA); 37 hdr = (struct ucall_header *)addr_gva2hva(vm, vaddr); 38 memset(hdr, 0, sizeof(*hdr)); 39 40 for (i = 0; i < KVM_MAX_VCPUS; ++i) { 41 uc = &hdr->ucalls[i]; 42 uc->hva = uc; 43 } 44 45 write_guest_global(vm, ucall_pool, (struct ucall_header *)vaddr); 46 47 ucall_arch_init(vm, mmio_gpa); 48} 49 50static struct ucall *ucall_alloc(void) 51{ 52 struct ucall *uc; 53 int i; 54 55 if (!ucall_pool) 56 goto ucall_failed; 57 58 for (i = 0; i < KVM_MAX_VCPUS; ++i) { 59 if (!test_and_set_bit(i, ucall_pool->in_use)) { 60 uc = &ucall_pool->ucalls[i]; 61 memset(uc->args, 0, sizeof(uc->args)); 62 return uc; 63 } 64 } 65 66ucall_failed: 67 /* 68 * If the vCPU cannot grab a ucall structure, make a bare ucall with a 69 * magic value to signal to get_ucall() that things went sideways. 70 * GUEST_ASSERT() depends on ucall_alloc() and so cannot be used here. 71 */ 72 ucall_arch_do_ucall(GUEST_UCALL_FAILED); 73 return NULL; 74} 75 76static void ucall_free(struct ucall *uc) 77{ 78 /* Beware, here be pointer arithmetic. */ 79 clear_bit(uc - ucall_pool->ucalls, ucall_pool->in_use); 80} 81 82void ucall_assert(uint64_t cmd, const char *exp, const char *file, 83 unsigned int line, const char *fmt, ...) 84{ 85 struct ucall *uc; 86 va_list va; 87 88 uc = ucall_alloc(); 89 uc->cmd = cmd; 90 91 WRITE_ONCE(uc->args[GUEST_ERROR_STRING], (uint64_t)(exp)); 92 WRITE_ONCE(uc->args[GUEST_FILE], (uint64_t)(file)); 93 WRITE_ONCE(uc->args[GUEST_LINE], line); 94 95 va_start(va, fmt); 96 guest_vsnprintf(uc->buffer, UCALL_BUFFER_LEN, fmt, va); 97 va_end(va); 98 99 ucall_arch_do_ucall((vm_vaddr_t)uc->hva); 100 101 ucall_free(uc); 102} 103 104void ucall_fmt(uint64_t cmd, const char *fmt, ...) 105{ 106 struct ucall *uc; 107 va_list va; 108 109 uc = ucall_alloc(); 110 uc->cmd = cmd; 111 112 va_start(va, fmt); 113 guest_vsnprintf(uc->buffer, UCALL_BUFFER_LEN, fmt, va); 114 va_end(va); 115 116 ucall_arch_do_ucall((vm_vaddr_t)uc->hva); 117 118 ucall_free(uc); 119} 120 121void ucall(uint64_t cmd, int nargs, ...) 122{ 123 struct ucall *uc; 124 va_list va; 125 int i; 126 127 uc = ucall_alloc(); 128 129 WRITE_ONCE(uc->cmd, cmd); 130 131 nargs = min(nargs, UCALL_MAX_ARGS); 132 133 va_start(va, nargs); 134 for (i = 0; i < nargs; ++i) 135 WRITE_ONCE(uc->args[i], va_arg(va, uint64_t)); 136 va_end(va); 137 138 ucall_arch_do_ucall((vm_vaddr_t)uc->hva); 139 140 ucall_free(uc); 141} 142 143uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc) 144{ 145 struct ucall ucall; 146 void *addr; 147 148 if (!uc) 149 uc = &ucall; 150 151 addr = ucall_arch_get_ucall(vcpu); 152 if (addr) { 153 TEST_ASSERT(addr != (void *)GUEST_UCALL_FAILED, 154 "Guest failed to allocate ucall struct"); 155 156 memcpy(uc, addr, sizeof(*uc)); 157 vcpu_run_complete_io(vcpu); 158 } else { 159 memset(uc, 0, sizeof(*uc)); 160 } 161 162 return uc->cmd; 163}