Linux Audio

Check our new training course

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