1 // SPDX-License-Identifier: GPL-2.0-only 2 #include "kvm_util.h" 3 #include "linux/types.h" 4 #include "linux/bitmap.h" 5 #include "linux/atomic.h" 6 7 struct ucall_header { 8 DECLARE_BITMAP(in_use, KVM_MAX_VCPUS); 9 struct ucall ucalls[KVM_MAX_VCPUS]; 10 }; 11 12 /* 13 * ucall_pool holds per-VM values (global data is duplicated by each VM), it 14 * must not be accessed from host code. 15 */ 16 static struct ucall_header *ucall_pool; 17 18 void ucall_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) 19 { 20 struct ucall_header *hdr; 21 struct ucall *uc; 22 vm_vaddr_t vaddr; 23 int i; 24 25 vaddr = __vm_vaddr_alloc(vm, sizeof(*hdr), KVM_UTIL_MIN_VADDR, MEM_REGION_DATA); 26 hdr = (struct ucall_header *)addr_gva2hva(vm, vaddr); 27 memset(hdr, 0, sizeof(*hdr)); 28 29 for (i = 0; i < KVM_MAX_VCPUS; ++i) { 30 uc = &hdr->ucalls[i]; 31 uc->hva = uc; 32 } 33 34 write_guest_global(vm, ucall_pool, (struct ucall_header *)vaddr); 35 36 ucall_arch_init(vm, mmio_gpa); 37 } 38 39 static struct ucall *ucall_alloc(void) 40 { 41 struct ucall *uc; 42 int i; 43 44 GUEST_ASSERT(ucall_pool); 45 46 for (i = 0; i < KVM_MAX_VCPUS; ++i) { 47 if (!test_and_set_bit(i, ucall_pool->in_use)) { 48 uc = &ucall_pool->ucalls[i]; 49 memset(uc->args, 0, sizeof(uc->args)); 50 return uc; 51 } 52 } 53 54 GUEST_ASSERT(0); 55 return NULL; 56 } 57 58 static void ucall_free(struct ucall *uc) 59 { 60 /* Beware, here be pointer arithmetic. */ 61 clear_bit(uc - ucall_pool->ucalls, ucall_pool->in_use); 62 } 63 64 void ucall(uint64_t cmd, int nargs, ...) 65 { 66 struct ucall *uc; 67 va_list va; 68 int i; 69 70 uc = ucall_alloc(); 71 72 WRITE_ONCE(uc->cmd, cmd); 73 74 nargs = min(nargs, UCALL_MAX_ARGS); 75 76 va_start(va, nargs); 77 for (i = 0; i < nargs; ++i) 78 WRITE_ONCE(uc->args[i], va_arg(va, uint64_t)); 79 va_end(va); 80 81 ucall_arch_do_ucall((vm_vaddr_t)uc->hva); 82 83 ucall_free(uc); 84 } 85 86 uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc) 87 { 88 struct ucall ucall; 89 void *addr; 90 91 if (!uc) 92 uc = &ucall; 93 94 addr = ucall_arch_get_ucall(vcpu); 95 if (addr) { 96 memcpy(uc, addr, sizeof(*uc)); 97 vcpu_run_complete_io(vcpu); 98 } else { 99 memset(uc, 0, sizeof(*uc)); 100 } 101 102 return uc->cmd; 103 } 104