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 7426729b2SPeter Gonda struct ucall_header { 8426729b2SPeter Gonda DECLARE_BITMAP(in_use, KVM_MAX_VCPUS); 9426729b2SPeter Gonda struct ucall ucalls[KVM_MAX_VCPUS]; 10426729b2SPeter Gonda }; 11426729b2SPeter Gonda 12426729b2SPeter Gonda /* 13426729b2SPeter Gonda * ucall_pool holds per-VM values (global data is duplicated by each VM), it 14426729b2SPeter Gonda * must not be accessed from host code. 15426729b2SPeter Gonda */ 16426729b2SPeter Gonda static struct ucall_header *ucall_pool; 17426729b2SPeter Gonda 18426729b2SPeter Gonda void ucall_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) 19426729b2SPeter Gonda { 20426729b2SPeter Gonda struct ucall_header *hdr; 21426729b2SPeter Gonda struct ucall *uc; 22426729b2SPeter Gonda vm_vaddr_t vaddr; 23426729b2SPeter Gonda int i; 24426729b2SPeter Gonda 25*2afc1fbbSOliver Upton vaddr = __vm_vaddr_alloc(vm, sizeof(*hdr), KVM_UTIL_MIN_VADDR, MEM_REGION_DATA); 26426729b2SPeter Gonda hdr = (struct ucall_header *)addr_gva2hva(vm, vaddr); 27426729b2SPeter Gonda memset(hdr, 0, sizeof(*hdr)); 28426729b2SPeter Gonda 29426729b2SPeter Gonda for (i = 0; i < KVM_MAX_VCPUS; ++i) { 30426729b2SPeter Gonda uc = &hdr->ucalls[i]; 31426729b2SPeter Gonda uc->hva = uc; 32426729b2SPeter Gonda } 33426729b2SPeter Gonda 34426729b2SPeter Gonda write_guest_global(vm, ucall_pool, (struct ucall_header *)vaddr); 35426729b2SPeter Gonda 36426729b2SPeter Gonda ucall_arch_init(vm, mmio_gpa); 37426729b2SPeter Gonda } 38426729b2SPeter Gonda 39426729b2SPeter Gonda static struct ucall *ucall_alloc(void) 40426729b2SPeter Gonda { 41426729b2SPeter Gonda struct ucall *uc; 42426729b2SPeter Gonda int i; 43426729b2SPeter Gonda 44426729b2SPeter Gonda GUEST_ASSERT(ucall_pool); 45426729b2SPeter Gonda 46426729b2SPeter Gonda for (i = 0; i < KVM_MAX_VCPUS; ++i) { 47426729b2SPeter Gonda if (!atomic_test_and_set_bit(i, ucall_pool->in_use)) { 48426729b2SPeter Gonda uc = &ucall_pool->ucalls[i]; 49426729b2SPeter Gonda memset(uc->args, 0, sizeof(uc->args)); 50426729b2SPeter Gonda return uc; 51426729b2SPeter Gonda } 52426729b2SPeter Gonda } 53426729b2SPeter Gonda 54426729b2SPeter Gonda GUEST_ASSERT(0); 55426729b2SPeter Gonda return NULL; 56426729b2SPeter Gonda } 57426729b2SPeter Gonda 58426729b2SPeter Gonda static void ucall_free(struct ucall *uc) 59426729b2SPeter Gonda { 60426729b2SPeter Gonda /* Beware, here be pointer arithmetic. */ 61426729b2SPeter Gonda clear_bit(uc - ucall_pool->ucalls, ucall_pool->in_use); 62426729b2SPeter Gonda } 6370466381SSean Christopherson 6470466381SSean Christopherson void ucall(uint64_t cmd, int nargs, ...) 6570466381SSean Christopherson { 66426729b2SPeter Gonda struct ucall *uc; 6770466381SSean Christopherson va_list va; 6870466381SSean Christopherson int i; 6970466381SSean Christopherson 70426729b2SPeter Gonda uc = ucall_alloc(); 71426729b2SPeter Gonda 72426729b2SPeter Gonda WRITE_ONCE(uc->cmd, cmd); 7370466381SSean Christopherson 7470466381SSean Christopherson nargs = min(nargs, UCALL_MAX_ARGS); 7570466381SSean Christopherson 7670466381SSean Christopherson va_start(va, nargs); 7770466381SSean Christopherson for (i = 0; i < nargs; ++i) 78426729b2SPeter Gonda WRITE_ONCE(uc->args[i], va_arg(va, uint64_t)); 7970466381SSean Christopherson va_end(va); 8070466381SSean Christopherson 81426729b2SPeter Gonda ucall_arch_do_ucall((vm_vaddr_t)uc->hva); 82426729b2SPeter Gonda 83426729b2SPeter Gonda ucall_free(uc); 8470466381SSean Christopherson } 85ef38871eSSean Christopherson 86ef38871eSSean Christopherson uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc) 87ef38871eSSean Christopherson { 88ef38871eSSean Christopherson struct ucall ucall; 89ef38871eSSean Christopherson void *addr; 90ef38871eSSean Christopherson 91ef38871eSSean Christopherson if (!uc) 92ef38871eSSean Christopherson uc = &ucall; 93ef38871eSSean Christopherson 94ef38871eSSean Christopherson addr = ucall_arch_get_ucall(vcpu); 95ef38871eSSean Christopherson if (addr) { 96ef38871eSSean Christopherson memcpy(uc, addr, sizeof(*uc)); 97ef38871eSSean Christopherson vcpu_run_complete_io(vcpu); 98ef38871eSSean Christopherson } else { 99ef38871eSSean Christopherson memset(uc, 0, sizeof(*uc)); 100ef38871eSSean Christopherson } 101ef38871eSSean Christopherson 102ef38871eSSean Christopherson return uc->cmd; 103ef38871eSSean Christopherson } 104