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