170466381SSean Christopherson // SPDX-License-Identifier: GPL-2.0-only 270466381SSean Christopherson #include "kvm_util.h" 3426729b2SPeter Gonda #include "linux/types.h" 4426729b2SPeter Gonda #include "linux/bitmap.h" 5426729b2SPeter Gonda #include "linux/atomic.h" 6426729b2SPeter Gonda 72f5213b8SSean Christopherson #define GUEST_UCALL_FAILED -1 82f5213b8SSean Christopherson 9426729b2SPeter Gonda struct ucall_header { 10426729b2SPeter Gonda DECLARE_BITMAP(in_use, KVM_MAX_VCPUS); 11426729b2SPeter Gonda struct ucall ucalls[KVM_MAX_VCPUS]; 12426729b2SPeter Gonda }; 13426729b2SPeter Gonda 14215a6817SAaron Lewis int ucall_nr_pages_required(uint64_t page_size) 15215a6817SAaron Lewis { 16215a6817SAaron Lewis return align_up(sizeof(struct ucall_header), page_size) / page_size; 17215a6817SAaron Lewis } 18215a6817SAaron Lewis 19426729b2SPeter Gonda /* 20426729b2SPeter Gonda * ucall_pool holds per-VM values (global data is duplicated by each VM), it 21426729b2SPeter Gonda * must not be accessed from host code. 22426729b2SPeter Gonda */ 23426729b2SPeter Gonda static struct ucall_header *ucall_pool; 24426729b2SPeter Gonda 25426729b2SPeter Gonda void ucall_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) 26426729b2SPeter Gonda { 27426729b2SPeter Gonda struct ucall_header *hdr; 28426729b2SPeter Gonda struct ucall *uc; 29426729b2SPeter Gonda vm_vaddr_t vaddr; 30426729b2SPeter Gonda int i; 31426729b2SPeter Gonda 322afc1fbbSOliver Upton vaddr = __vm_vaddr_alloc(vm, sizeof(*hdr), KVM_UTIL_MIN_VADDR, MEM_REGION_DATA); 33426729b2SPeter Gonda hdr = (struct ucall_header *)addr_gva2hva(vm, vaddr); 34426729b2SPeter Gonda memset(hdr, 0, sizeof(*hdr)); 35426729b2SPeter Gonda 36426729b2SPeter Gonda for (i = 0; i < KVM_MAX_VCPUS; ++i) { 37426729b2SPeter Gonda uc = &hdr->ucalls[i]; 38426729b2SPeter Gonda uc->hva = uc; 39426729b2SPeter Gonda } 40426729b2SPeter Gonda 41426729b2SPeter Gonda write_guest_global(vm, ucall_pool, (struct ucall_header *)vaddr); 42426729b2SPeter Gonda 43426729b2SPeter Gonda ucall_arch_init(vm, mmio_gpa); 44426729b2SPeter Gonda } 45426729b2SPeter Gonda 46426729b2SPeter Gonda static struct ucall *ucall_alloc(void) 47426729b2SPeter Gonda { 48426729b2SPeter Gonda struct ucall *uc; 49426729b2SPeter Gonda int i; 50426729b2SPeter Gonda 512f5213b8SSean Christopherson if (!ucall_pool) 522f5213b8SSean Christopherson goto ucall_failed; 53426729b2SPeter Gonda 54426729b2SPeter Gonda for (i = 0; i < KVM_MAX_VCPUS; ++i) { 5536293352SSean Christopherson if (!test_and_set_bit(i, ucall_pool->in_use)) { 56426729b2SPeter Gonda uc = &ucall_pool->ucalls[i]; 57426729b2SPeter Gonda memset(uc->args, 0, sizeof(uc->args)); 58426729b2SPeter Gonda return uc; 59426729b2SPeter Gonda } 60426729b2SPeter Gonda } 61426729b2SPeter Gonda 622f5213b8SSean Christopherson ucall_failed: 632f5213b8SSean Christopherson /* 642f5213b8SSean Christopherson * If the vCPU cannot grab a ucall structure, make a bare ucall with a 652f5213b8SSean Christopherson * magic value to signal to get_ucall() that things went sideways. 662f5213b8SSean Christopherson * GUEST_ASSERT() depends on ucall_alloc() and so cannot be used here. 672f5213b8SSean Christopherson */ 682f5213b8SSean Christopherson ucall_arch_do_ucall(GUEST_UCALL_FAILED); 69426729b2SPeter Gonda return NULL; 70426729b2SPeter Gonda } 71426729b2SPeter Gonda 72426729b2SPeter Gonda static void ucall_free(struct ucall *uc) 73426729b2SPeter Gonda { 74426729b2SPeter Gonda /* Beware, here be pointer arithmetic. */ 75426729b2SPeter Gonda clear_bit(uc - ucall_pool->ucalls, ucall_pool->in_use); 76426729b2SPeter Gonda } 7770466381SSean Christopherson 78*57e5c1feSAaron Lewis void ucall_fmt(uint64_t cmd, const char *fmt, ...) 79*57e5c1feSAaron Lewis { 80*57e5c1feSAaron Lewis struct ucall *uc; 81*57e5c1feSAaron Lewis va_list va; 82*57e5c1feSAaron Lewis 83*57e5c1feSAaron Lewis uc = ucall_alloc(); 84*57e5c1feSAaron Lewis uc->cmd = cmd; 85*57e5c1feSAaron Lewis 86*57e5c1feSAaron Lewis va_start(va, fmt); 87*57e5c1feSAaron Lewis guest_vsnprintf(uc->buffer, UCALL_BUFFER_LEN, fmt, va); 88*57e5c1feSAaron Lewis va_end(va); 89*57e5c1feSAaron Lewis 90*57e5c1feSAaron Lewis ucall_arch_do_ucall((vm_vaddr_t)uc->hva); 91*57e5c1feSAaron Lewis 92*57e5c1feSAaron Lewis ucall_free(uc); 93*57e5c1feSAaron Lewis } 94*57e5c1feSAaron Lewis 9570466381SSean Christopherson void ucall(uint64_t cmd, int nargs, ...) 9670466381SSean Christopherson { 97426729b2SPeter Gonda struct ucall *uc; 9870466381SSean Christopherson va_list va; 9970466381SSean Christopherson int i; 10070466381SSean Christopherson 101426729b2SPeter Gonda uc = ucall_alloc(); 102426729b2SPeter Gonda 103426729b2SPeter Gonda WRITE_ONCE(uc->cmd, cmd); 10470466381SSean Christopherson 10570466381SSean Christopherson nargs = min(nargs, UCALL_MAX_ARGS); 10670466381SSean Christopherson 10770466381SSean Christopherson va_start(va, nargs); 10870466381SSean Christopherson for (i = 0; i < nargs; ++i) 109426729b2SPeter Gonda WRITE_ONCE(uc->args[i], va_arg(va, uint64_t)); 11070466381SSean Christopherson va_end(va); 11170466381SSean Christopherson 112426729b2SPeter Gonda ucall_arch_do_ucall((vm_vaddr_t)uc->hva); 113426729b2SPeter Gonda 114426729b2SPeter Gonda ucall_free(uc); 11570466381SSean Christopherson } 116ef38871eSSean Christopherson 117ef38871eSSean Christopherson uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc) 118ef38871eSSean Christopherson { 119ef38871eSSean Christopherson struct ucall ucall; 120ef38871eSSean Christopherson void *addr; 121ef38871eSSean Christopherson 122ef38871eSSean Christopherson if (!uc) 123ef38871eSSean Christopherson uc = &ucall; 124ef38871eSSean Christopherson 125ef38871eSSean Christopherson addr = ucall_arch_get_ucall(vcpu); 126ef38871eSSean Christopherson if (addr) { 1272f5213b8SSean Christopherson TEST_ASSERT(addr != (void *)GUEST_UCALL_FAILED, 1282f5213b8SSean Christopherson "Guest failed to allocate ucall struct"); 1292f5213b8SSean Christopherson 130ef38871eSSean Christopherson memcpy(uc, addr, sizeof(*uc)); 131ef38871eSSean Christopherson vcpu_run_complete_io(vcpu); 132ef38871eSSean Christopherson } else { 133ef38871eSSean Christopherson memset(uc, 0, sizeof(*uc)); 134ef38871eSSean Christopherson } 135ef38871eSSean Christopherson 136ef38871eSSean Christopherson return uc->cmd; 137ef38871eSSean Christopherson } 138