cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ucall.c (3079B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * ucall support. A ucall is a "hypercall to userspace".
      4 *
      5 * Copyright (C) 2018, Red Hat, Inc.
      6 */
      7#include "kvm_util.h"
      8#include "../kvm_util_internal.h"
      9
     10static vm_vaddr_t *ucall_exit_mmio_addr;
     11
     12static bool ucall_mmio_init(struct kvm_vm *vm, vm_paddr_t gpa)
     13{
     14	if (kvm_userspace_memory_region_find(vm, gpa, gpa + 1))
     15		return false;
     16
     17	virt_pg_map(vm, gpa, gpa);
     18
     19	ucall_exit_mmio_addr = (vm_vaddr_t *)gpa;
     20	sync_global_to_guest(vm, ucall_exit_mmio_addr);
     21
     22	return true;
     23}
     24
     25void ucall_init(struct kvm_vm *vm, void *arg)
     26{
     27	vm_paddr_t gpa, start, end, step, offset;
     28	unsigned int bits;
     29	bool ret;
     30
     31	if (arg) {
     32		gpa = (vm_paddr_t)arg;
     33		ret = ucall_mmio_init(vm, gpa);
     34		TEST_ASSERT(ret, "Can't set ucall mmio address to %lx", gpa);
     35		return;
     36	}
     37
     38	/*
     39	 * Find an address within the allowed physical and virtual address
     40	 * spaces, that does _not_ have a KVM memory region associated with
     41	 * it. Identity mapping an address like this allows the guest to
     42	 * access it, but as KVM doesn't know what to do with it, it
     43	 * will assume it's something userspace handles and exit with
     44	 * KVM_EXIT_MMIO. Well, at least that's how it works for AArch64.
     45	 * Here we start with a guess that the addresses around 5/8th
     46	 * of the allowed space are unmapped and then work both down and
     47	 * up from there in 1/16th allowed space sized steps.
     48	 *
     49	 * Note, we need to use VA-bits - 1 when calculating the allowed
     50	 * virtual address space for an identity mapping because the upper
     51	 * half of the virtual address space is the two's complement of the
     52	 * lower and won't match physical addresses.
     53	 */
     54	bits = vm->va_bits - 1;
     55	bits = vm->pa_bits < bits ? vm->pa_bits : bits;
     56	end = 1ul << bits;
     57	start = end * 5 / 8;
     58	step = end / 16;
     59	for (offset = 0; offset < end - start; offset += step) {
     60		if (ucall_mmio_init(vm, start - offset))
     61			return;
     62		if (ucall_mmio_init(vm, start + offset))
     63			return;
     64	}
     65	TEST_FAIL("Can't find a ucall mmio address");
     66}
     67
     68void ucall_uninit(struct kvm_vm *vm)
     69{
     70	ucall_exit_mmio_addr = 0;
     71	sync_global_to_guest(vm, ucall_exit_mmio_addr);
     72}
     73
     74void ucall(uint64_t cmd, int nargs, ...)
     75{
     76	struct ucall uc = {};
     77	va_list va;
     78	int i;
     79
     80	WRITE_ONCE(uc.cmd, cmd);
     81	nargs = nargs <= UCALL_MAX_ARGS ? nargs : UCALL_MAX_ARGS;
     82
     83	va_start(va, nargs);
     84	for (i = 0; i < nargs; ++i)
     85		WRITE_ONCE(uc.args[i], va_arg(va, uint64_t));
     86	va_end(va);
     87
     88	WRITE_ONCE(*ucall_exit_mmio_addr, (vm_vaddr_t)&uc);
     89}
     90
     91uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc)
     92{
     93	struct kvm_run *run = vcpu_state(vm, vcpu_id);
     94	struct ucall ucall = {};
     95
     96	if (uc)
     97		memset(uc, 0, sizeof(*uc));
     98
     99	if (run->exit_reason == KVM_EXIT_MMIO &&
    100	    run->mmio.phys_addr == (uint64_t)ucall_exit_mmio_addr) {
    101		vm_vaddr_t gva;
    102
    103		TEST_ASSERT(run->mmio.is_write && run->mmio.len == 8,
    104			    "Unexpected ucall exit mmio address access");
    105		memcpy(&gva, run->mmio.data, sizeof(gva));
    106		memcpy(&ucall, addr_gva2hva(vm, gva), sizeof(ucall));
    107
    108		vcpu_run_complete_io(vm, vcpu_id);
    109		if (uc)
    110			memcpy(uc, &ucall, sizeof(ucall));
    111	}
    112
    113	return ucall.cmd;
    114}