brintos

brintos / linux-shallow public Read only

0
0
Text · 3.3 KiB · 42151e5 Raw
164 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2#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 -111 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), it24 * 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 a69	 * 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}164