Path: blob/master/tools/testing/selftests/kvm/lib/ucall_common.c
38235 views
// SPDX-License-Identifier: GPL-2.0-only1#include "linux/types.h"2#include "linux/bitmap.h"3#include "linux/atomic.h"45#include "kvm_util.h"6#include "ucall_common.h"789#define GUEST_UCALL_FAILED -11011struct ucall_header {12DECLARE_BITMAP(in_use, KVM_MAX_VCPUS);13struct ucall ucalls[KVM_MAX_VCPUS];14};1516int ucall_nr_pages_required(uint64_t page_size)17{18return align_up(sizeof(struct ucall_header), page_size) / page_size;19}2021/*22* ucall_pool holds per-VM values (global data is duplicated by each VM), it23* must not be accessed from host code.24*/25static struct ucall_header *ucall_pool;2627void ucall_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa)28{29struct ucall_header *hdr;30struct ucall *uc;31vm_vaddr_t vaddr;32int i;3334vaddr = vm_vaddr_alloc_shared(vm, sizeof(*hdr), KVM_UTIL_MIN_VADDR,35MEM_REGION_DATA);36hdr = (struct ucall_header *)addr_gva2hva(vm, vaddr);37memset(hdr, 0, sizeof(*hdr));3839for (i = 0; i < KVM_MAX_VCPUS; ++i) {40uc = &hdr->ucalls[i];41uc->hva = uc;42}4344write_guest_global(vm, ucall_pool, (struct ucall_header *)vaddr);4546ucall_arch_init(vm, mmio_gpa);47}4849static struct ucall *ucall_alloc(void)50{51struct ucall *uc;52int i;5354if (!ucall_pool)55goto ucall_failed;5657for (i = 0; i < KVM_MAX_VCPUS; ++i) {58if (!test_and_set_bit(i, ucall_pool->in_use)) {59uc = &ucall_pool->ucalls[i];60memset(uc->args, 0, sizeof(uc->args));61return uc;62}63}6465ucall_failed:66/*67* If the vCPU cannot grab a ucall structure, make a bare ucall with a68* magic value to signal to get_ucall() that things went sideways.69* GUEST_ASSERT() depends on ucall_alloc() and so cannot be used here.70*/71ucall_arch_do_ucall(GUEST_UCALL_FAILED);72return NULL;73}7475static void ucall_free(struct ucall *uc)76{77/* Beware, here be pointer arithmetic. */78clear_bit(uc - ucall_pool->ucalls, ucall_pool->in_use);79}8081void ucall_assert(uint64_t cmd, const char *exp, const char *file,82unsigned int line, const char *fmt, ...)83{84struct ucall *uc;85va_list va;8687uc = ucall_alloc();88uc->cmd = cmd;8990WRITE_ONCE(uc->args[GUEST_ERROR_STRING], (uint64_t)(exp));91WRITE_ONCE(uc->args[GUEST_FILE], (uint64_t)(file));92WRITE_ONCE(uc->args[GUEST_LINE], line);9394va_start(va, fmt);95guest_vsnprintf(uc->buffer, UCALL_BUFFER_LEN, fmt, va);96va_end(va);9798ucall_arch_do_ucall((vm_vaddr_t)uc->hva);99100ucall_free(uc);101}102103void ucall_fmt(uint64_t cmd, const char *fmt, ...)104{105struct ucall *uc;106va_list va;107108uc = ucall_alloc();109uc->cmd = cmd;110111va_start(va, fmt);112guest_vsnprintf(uc->buffer, UCALL_BUFFER_LEN, fmt, va);113va_end(va);114115ucall_arch_do_ucall((vm_vaddr_t)uc->hva);116117ucall_free(uc);118}119120void ucall(uint64_t cmd, int nargs, ...)121{122struct ucall *uc;123va_list va;124int i;125126uc = ucall_alloc();127128WRITE_ONCE(uc->cmd, cmd);129130nargs = min(nargs, UCALL_MAX_ARGS);131132va_start(va, nargs);133for (i = 0; i < nargs; ++i)134WRITE_ONCE(uc->args[i], va_arg(va, uint64_t));135va_end(va);136137ucall_arch_do_ucall((vm_vaddr_t)uc->hva);138139ucall_free(uc);140}141142uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)143{144struct ucall ucall;145void *addr;146147if (!uc)148uc = &ucall;149150addr = ucall_arch_get_ucall(vcpu);151if (addr) {152TEST_ASSERT(addr != (void *)GUEST_UCALL_FAILED,153"Guest failed to allocate ucall struct");154155memcpy(uc, addr, sizeof(*uc));156vcpu_run_complete_io(vcpu);157} else {158memset(uc, 0, sizeof(*uc));159}160161return uc->cmd;162}163164165