Loading...
1// SPDX-License-Identifier: GPL-2.0-only
2#include "kvm_util.h"
3#include "linux/types.h"
4#include "linux/bitmap.h"
5#include "linux/atomic.h"
6
7#define GUEST_UCALL_FAILED -1
8
9struct ucall_header {
10 DECLARE_BITMAP(in_use, KVM_MAX_VCPUS);
11 struct ucall ucalls[KVM_MAX_VCPUS];
12};
13
14/*
15 * ucall_pool holds per-VM values (global data is duplicated by each VM), it
16 * must not be accessed from host code.
17 */
18static struct ucall_header *ucall_pool;
19
20void ucall_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa)
21{
22 struct ucall_header *hdr;
23 struct ucall *uc;
24 vm_vaddr_t vaddr;
25 int i;
26
27 vaddr = __vm_vaddr_alloc(vm, sizeof(*hdr), KVM_UTIL_MIN_VADDR, MEM_REGION_DATA);
28 hdr = (struct ucall_header *)addr_gva2hva(vm, vaddr);
29 memset(hdr, 0, sizeof(*hdr));
30
31 for (i = 0; i < KVM_MAX_VCPUS; ++i) {
32 uc = &hdr->ucalls[i];
33 uc->hva = uc;
34 }
35
36 write_guest_global(vm, ucall_pool, (struct ucall_header *)vaddr);
37
38 ucall_arch_init(vm, mmio_gpa);
39}
40
41static struct ucall *ucall_alloc(void)
42{
43 struct ucall *uc;
44 int i;
45
46 if (!ucall_pool)
47 goto ucall_failed;
48
49 for (i = 0; i < KVM_MAX_VCPUS; ++i) {
50 if (!test_and_set_bit(i, ucall_pool->in_use)) {
51 uc = &ucall_pool->ucalls[i];
52 memset(uc->args, 0, sizeof(uc->args));
53 return uc;
54 }
55 }
56
57ucall_failed:
58 /*
59 * If the vCPU cannot grab a ucall structure, make a bare ucall with a
60 * magic value to signal to get_ucall() that things went sideways.
61 * GUEST_ASSERT() depends on ucall_alloc() and so cannot be used here.
62 */
63 ucall_arch_do_ucall(GUEST_UCALL_FAILED);
64 return NULL;
65}
66
67static void ucall_free(struct ucall *uc)
68{
69 /* Beware, here be pointer arithmetic. */
70 clear_bit(uc - ucall_pool->ucalls, ucall_pool->in_use);
71}
72
73void ucall(uint64_t cmd, int nargs, ...)
74{
75 struct ucall *uc;
76 va_list va;
77 int i;
78
79 uc = ucall_alloc();
80
81 WRITE_ONCE(uc->cmd, cmd);
82
83 nargs = min(nargs, UCALL_MAX_ARGS);
84
85 va_start(va, nargs);
86 for (i = 0; i < nargs; ++i)
87 WRITE_ONCE(uc->args[i], va_arg(va, uint64_t));
88 va_end(va);
89
90 ucall_arch_do_ucall((vm_vaddr_t)uc->hva);
91
92 ucall_free(uc);
93}
94
95uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
96{
97 struct ucall ucall;
98 void *addr;
99
100 if (!uc)
101 uc = &ucall;
102
103 addr = ucall_arch_get_ucall(vcpu);
104 if (addr) {
105 TEST_ASSERT(addr != (void *)GUEST_UCALL_FAILED,
106 "Guest failed to allocate ucall struct");
107
108 memcpy(uc, addr, sizeof(*uc));
109 vcpu_run_complete_io(vcpu);
110 } else {
111 memset(uc, 0, sizeof(*uc));
112 }
113
114 return uc->cmd;
115}
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
12struct ucall_header {
13 DECLARE_BITMAP(in_use, KVM_MAX_VCPUS);
14 struct ucall ucalls[KVM_MAX_VCPUS];
15};
16
17int 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 */
26static struct ucall_header *ucall_pool;
27
28void 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
50static 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
66ucall_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
76static 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
82void 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
104void 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
121void 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
143uint64_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}