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