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
ucall_nr_pages_required(uint64_t page_size)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
ucall_init(struct kvm_vm * vm,vm_paddr_t mmio_gpa)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
ucall_alloc(void)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
ucall_free(struct ucall * uc)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
ucall_assert(uint64_t cmd,const char * exp,const char * file,unsigned int line,const char * fmt,...)78*289c2b4dSSean Christopherson void ucall_assert(uint64_t cmd, const char *exp, const char *file,
79*289c2b4dSSean Christopherson unsigned int line, const char *fmt, ...)
80*289c2b4dSSean Christopherson {
81*289c2b4dSSean Christopherson struct ucall *uc;
82*289c2b4dSSean Christopherson va_list va;
83*289c2b4dSSean Christopherson
84*289c2b4dSSean Christopherson uc = ucall_alloc();
85*289c2b4dSSean Christopherson uc->cmd = cmd;
86*289c2b4dSSean Christopherson
87*289c2b4dSSean Christopherson WRITE_ONCE(uc->args[GUEST_ERROR_STRING], (uint64_t)(exp));
88*289c2b4dSSean Christopherson WRITE_ONCE(uc->args[GUEST_FILE], (uint64_t)(file));
89*289c2b4dSSean Christopherson WRITE_ONCE(uc->args[GUEST_LINE], line);
90*289c2b4dSSean Christopherson
91*289c2b4dSSean Christopherson va_start(va, fmt);
92*289c2b4dSSean Christopherson guest_vsnprintf(uc->buffer, UCALL_BUFFER_LEN, fmt, va);
93*289c2b4dSSean Christopherson va_end(va);
94*289c2b4dSSean Christopherson
95*289c2b4dSSean Christopherson ucall_arch_do_ucall((vm_vaddr_t)uc->hva);
96*289c2b4dSSean Christopherson
97*289c2b4dSSean Christopherson ucall_free(uc);
98*289c2b4dSSean Christopherson }
99*289c2b4dSSean Christopherson
ucall_fmt(uint64_t cmd,const char * fmt,...)10057e5c1feSAaron Lewis void ucall_fmt(uint64_t cmd, const char *fmt, ...)
10157e5c1feSAaron Lewis {
10257e5c1feSAaron Lewis struct ucall *uc;
10357e5c1feSAaron Lewis va_list va;
10457e5c1feSAaron Lewis
10557e5c1feSAaron Lewis uc = ucall_alloc();
10657e5c1feSAaron Lewis uc->cmd = cmd;
10757e5c1feSAaron Lewis
10857e5c1feSAaron Lewis va_start(va, fmt);
10957e5c1feSAaron Lewis guest_vsnprintf(uc->buffer, UCALL_BUFFER_LEN, fmt, va);
11057e5c1feSAaron Lewis va_end(va);
11157e5c1feSAaron Lewis
11257e5c1feSAaron Lewis ucall_arch_do_ucall((vm_vaddr_t)uc->hva);
11357e5c1feSAaron Lewis
11457e5c1feSAaron Lewis ucall_free(uc);
11557e5c1feSAaron Lewis }
11657e5c1feSAaron Lewis
ucall(uint64_t cmd,int nargs,...)11770466381SSean Christopherson void ucall(uint64_t cmd, int nargs, ...)
11870466381SSean Christopherson {
119426729b2SPeter Gonda struct ucall *uc;
12070466381SSean Christopherson va_list va;
12170466381SSean Christopherson int i;
12270466381SSean Christopherson
123426729b2SPeter Gonda uc = ucall_alloc();
124426729b2SPeter Gonda
125426729b2SPeter Gonda WRITE_ONCE(uc->cmd, cmd);
12670466381SSean Christopherson
12770466381SSean Christopherson nargs = min(nargs, UCALL_MAX_ARGS);
12870466381SSean Christopherson
12970466381SSean Christopherson va_start(va, nargs);
13070466381SSean Christopherson for (i = 0; i < nargs; ++i)
131426729b2SPeter Gonda WRITE_ONCE(uc->args[i], va_arg(va, uint64_t));
13270466381SSean Christopherson va_end(va);
13370466381SSean Christopherson
134426729b2SPeter Gonda ucall_arch_do_ucall((vm_vaddr_t)uc->hva);
135426729b2SPeter Gonda
136426729b2SPeter Gonda ucall_free(uc);
13770466381SSean Christopherson }
138ef38871eSSean Christopherson
get_ucall(struct kvm_vcpu * vcpu,struct ucall * uc)139ef38871eSSean Christopherson uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
140ef38871eSSean Christopherson {
141ef38871eSSean Christopherson struct ucall ucall;
142ef38871eSSean Christopherson void *addr;
143ef38871eSSean Christopherson
144ef38871eSSean Christopherson if (!uc)
145ef38871eSSean Christopherson uc = &ucall;
146ef38871eSSean Christopherson
147ef38871eSSean Christopherson addr = ucall_arch_get_ucall(vcpu);
148ef38871eSSean Christopherson if (addr) {
1492f5213b8SSean Christopherson TEST_ASSERT(addr != (void *)GUEST_UCALL_FAILED,
1502f5213b8SSean Christopherson "Guest failed to allocate ucall struct");
1512f5213b8SSean Christopherson
152ef38871eSSean Christopherson memcpy(uc, addr, sizeof(*uc));
153ef38871eSSean Christopherson vcpu_run_complete_io(vcpu);
154ef38871eSSean Christopherson } else {
155ef38871eSSean Christopherson memset(uc, 0, sizeof(*uc));
156ef38871eSSean Christopherson }
157ef38871eSSean Christopherson
158ef38871eSSean Christopherson return uc->cmd;
159ef38871eSSean Christopherson }
160