Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/kvm/lib/ucall_common.c
38235 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
#include "linux/types.h"
3
#include "linux/bitmap.h"
4
#include "linux/atomic.h"
5
6
#include "kvm_util.h"
7
#include "ucall_common.h"
8
9
10
#define GUEST_UCALL_FAILED -1
11
12
struct ucall_header {
13
DECLARE_BITMAP(in_use, KVM_MAX_VCPUS);
14
struct ucall ucalls[KVM_MAX_VCPUS];
15
};
16
17
int ucall_nr_pages_required(uint64_t page_size)
18
{
19
return align_up(sizeof(struct ucall_header), page_size) / page_size;
20
}
21
22
/*
23
* ucall_pool holds per-VM values (global data is duplicated by each VM), it
24
* must not be accessed from host code.
25
*/
26
static struct ucall_header *ucall_pool;
27
28
void ucall_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa)
29
{
30
struct ucall_header *hdr;
31
struct ucall *uc;
32
vm_vaddr_t vaddr;
33
int i;
34
35
vaddr = vm_vaddr_alloc_shared(vm, sizeof(*hdr), KVM_UTIL_MIN_VADDR,
36
MEM_REGION_DATA);
37
hdr = (struct ucall_header *)addr_gva2hva(vm, vaddr);
38
memset(hdr, 0, sizeof(*hdr));
39
40
for (i = 0; i < KVM_MAX_VCPUS; ++i) {
41
uc = &hdr->ucalls[i];
42
uc->hva = uc;
43
}
44
45
write_guest_global(vm, ucall_pool, (struct ucall_header *)vaddr);
46
47
ucall_arch_init(vm, mmio_gpa);
48
}
49
50
static struct ucall *ucall_alloc(void)
51
{
52
struct ucall *uc;
53
int i;
54
55
if (!ucall_pool)
56
goto ucall_failed;
57
58
for (i = 0; i < KVM_MAX_VCPUS; ++i) {
59
if (!test_and_set_bit(i, ucall_pool->in_use)) {
60
uc = &ucall_pool->ucalls[i];
61
memset(uc->args, 0, sizeof(uc->args));
62
return uc;
63
}
64
}
65
66
ucall_failed:
67
/*
68
* If the vCPU cannot grab a ucall structure, make a bare ucall with a
69
* magic value to signal to get_ucall() that things went sideways.
70
* GUEST_ASSERT() depends on ucall_alloc() and so cannot be used here.
71
*/
72
ucall_arch_do_ucall(GUEST_UCALL_FAILED);
73
return NULL;
74
}
75
76
static void ucall_free(struct ucall *uc)
77
{
78
/* Beware, here be pointer arithmetic. */
79
clear_bit(uc - ucall_pool->ucalls, ucall_pool->in_use);
80
}
81
82
void ucall_assert(uint64_t cmd, const char *exp, const char *file,
83
unsigned int line, const char *fmt, ...)
84
{
85
struct ucall *uc;
86
va_list va;
87
88
uc = ucall_alloc();
89
uc->cmd = cmd;
90
91
WRITE_ONCE(uc->args[GUEST_ERROR_STRING], (uint64_t)(exp));
92
WRITE_ONCE(uc->args[GUEST_FILE], (uint64_t)(file));
93
WRITE_ONCE(uc->args[GUEST_LINE], line);
94
95
va_start(va, fmt);
96
guest_vsnprintf(uc->buffer, UCALL_BUFFER_LEN, fmt, va);
97
va_end(va);
98
99
ucall_arch_do_ucall((vm_vaddr_t)uc->hva);
100
101
ucall_free(uc);
102
}
103
104
void ucall_fmt(uint64_t cmd, const char *fmt, ...)
105
{
106
struct ucall *uc;
107
va_list va;
108
109
uc = ucall_alloc();
110
uc->cmd = cmd;
111
112
va_start(va, fmt);
113
guest_vsnprintf(uc->buffer, UCALL_BUFFER_LEN, fmt, va);
114
va_end(va);
115
116
ucall_arch_do_ucall((vm_vaddr_t)uc->hva);
117
118
ucall_free(uc);
119
}
120
121
void ucall(uint64_t cmd, int nargs, ...)
122
{
123
struct ucall *uc;
124
va_list va;
125
int i;
126
127
uc = ucall_alloc();
128
129
WRITE_ONCE(uc->cmd, cmd);
130
131
nargs = min(nargs, UCALL_MAX_ARGS);
132
133
va_start(va, nargs);
134
for (i = 0; i < nargs; ++i)
135
WRITE_ONCE(uc->args[i], va_arg(va, uint64_t));
136
va_end(va);
137
138
ucall_arch_do_ucall((vm_vaddr_t)uc->hva);
139
140
ucall_free(uc);
141
}
142
143
uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
144
{
145
struct ucall ucall;
146
void *addr;
147
148
if (!uc)
149
uc = &ucall;
150
151
addr = ucall_arch_get_ucall(vcpu);
152
if (addr) {
153
TEST_ASSERT(addr != (void *)GUEST_UCALL_FAILED,
154
"Guest failed to allocate ucall struct");
155
156
memcpy(uc, addr, sizeof(*uc));
157
vcpu_run_complete_io(vcpu);
158
} else {
159
memset(uc, 0, sizeof(*uc));
160
}
161
162
return uc->cmd;
163
}
164
165