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

vgic.c (5163B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * ARM Generic Interrupt Controller (GIC) v3 host support
      4 */
      5
      6#include <linux/kvm.h>
      7#include <linux/sizes.h>
      8#include <asm/kvm_para.h>
      9#include <asm/kvm.h>
     10
     11#include "kvm_util.h"
     12#include "../kvm_util_internal.h"
     13#include "vgic.h"
     14#include "gic.h"
     15#include "gic_v3.h"
     16
     17/*
     18 * vGIC-v3 default host setup
     19 *
     20 * Input args:
     21 *	vm - KVM VM
     22 *	nr_vcpus - Number of vCPUs supported by this VM
     23 *	gicd_base_gpa - Guest Physical Address of the Distributor region
     24 *	gicr_base_gpa - Guest Physical Address of the Redistributor region
     25 *
     26 * Output args: None
     27 *
     28 * Return: GIC file-descriptor or negative error code upon failure
     29 *
     30 * The function creates a vGIC-v3 device and maps the distributor and
     31 * redistributor regions of the guest. Since it depends on the number of
     32 * vCPUs for the VM, it must be called after all the vCPUs have been created.
     33 */
     34int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs,
     35		uint64_t gicd_base_gpa, uint64_t gicr_base_gpa)
     36{
     37	int gic_fd;
     38	uint64_t redist_attr;
     39	struct list_head *iter;
     40	unsigned int nr_gic_pages, nr_vcpus_created = 0;
     41
     42	TEST_ASSERT(nr_vcpus, "Number of vCPUs cannot be empty\n");
     43
     44	/*
     45	 * Make sure that the caller is infact calling this
     46	 * function after all the vCPUs are added.
     47	 */
     48	list_for_each(iter, &vm->vcpus)
     49		nr_vcpus_created++;
     50	TEST_ASSERT(nr_vcpus == nr_vcpus_created,
     51			"Number of vCPUs requested (%u) doesn't match with the ones created for the VM (%u)\n",
     52			nr_vcpus, nr_vcpus_created);
     53
     54	/* Distributor setup */
     55	if (_kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3,
     56			       false, &gic_fd) != 0)
     57		return -1;
     58
     59	kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
     60			0, &nr_irqs, true);
     61
     62	kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
     63			KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
     64
     65	kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
     66			KVM_VGIC_V3_ADDR_TYPE_DIST, &gicd_base_gpa, true);
     67	nr_gic_pages = vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_DIST_SIZE);
     68	virt_map(vm, gicd_base_gpa, gicd_base_gpa,  nr_gic_pages);
     69
     70	/* Redistributor setup */
     71	redist_attr = REDIST_REGION_ATTR_ADDR(nr_vcpus, gicr_base_gpa, 0, 0);
     72	kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
     73			KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &redist_attr, true);
     74	nr_gic_pages = vm_calc_num_guest_pages(vm->mode,
     75						KVM_VGIC_V3_REDIST_SIZE * nr_vcpus);
     76	virt_map(vm, gicr_base_gpa, gicr_base_gpa,  nr_gic_pages);
     77
     78	kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
     79				KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
     80
     81	return gic_fd;
     82}
     83
     84/* should only work for level sensitive interrupts */
     85int _kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level)
     86{
     87	uint64_t attr = 32 * (intid / 32);
     88	uint64_t index = intid % 32;
     89	uint64_t val;
     90	int ret;
     91
     92	ret = _kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO,
     93				 attr, &val, false);
     94	if (ret != 0)
     95		return ret;
     96
     97	val |= 1U << index;
     98	ret = _kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO,
     99				 attr, &val, true);
    100	return ret;
    101}
    102
    103void kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level)
    104{
    105	int ret = _kvm_irq_set_level_info(gic_fd, intid, level);
    106
    107	TEST_ASSERT(ret == 0, "KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO failed, "
    108			"rc: %i errno: %i", ret, errno);
    109}
    110
    111int _kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level)
    112{
    113	uint32_t irq = intid & KVM_ARM_IRQ_NUM_MASK;
    114
    115	TEST_ASSERT(!INTID_IS_SGI(intid), "KVM_IRQ_LINE's interface itself "
    116		"doesn't allow injecting SGIs. There's no mask for it.");
    117
    118	if (INTID_IS_PPI(intid))
    119		irq |= KVM_ARM_IRQ_TYPE_PPI << KVM_ARM_IRQ_TYPE_SHIFT;
    120	else
    121		irq |= KVM_ARM_IRQ_TYPE_SPI << KVM_ARM_IRQ_TYPE_SHIFT;
    122
    123	return _kvm_irq_line(vm, irq, level);
    124}
    125
    126void kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level)
    127{
    128	int ret = _kvm_arm_irq_line(vm, intid, level);
    129
    130	TEST_ASSERT(ret == 0, "KVM_IRQ_LINE failed, rc: %i errno: %i",
    131			ret, errno);
    132}
    133
    134static void vgic_poke_irq(int gic_fd, uint32_t intid,
    135		uint32_t vcpu, uint64_t reg_off)
    136{
    137	uint64_t reg = intid / 32;
    138	uint64_t index = intid % 32;
    139	uint64_t attr = reg_off + reg * 4;
    140	uint64_t val;
    141	bool intid_is_private = INTID_IS_SGI(intid) || INTID_IS_PPI(intid);
    142
    143	uint32_t group = intid_is_private ? KVM_DEV_ARM_VGIC_GRP_REDIST_REGS
    144					  : KVM_DEV_ARM_VGIC_GRP_DIST_REGS;
    145
    146	if (intid_is_private) {
    147		/* TODO: only vcpu 0 implemented for now. */
    148		assert(vcpu == 0);
    149		attr += SZ_64K;
    150	}
    151
    152	/* Check that the addr part of the attr is within 32 bits. */
    153	assert((attr & ~KVM_DEV_ARM_VGIC_OFFSET_MASK) == 0);
    154
    155	/*
    156	 * All calls will succeed, even with invalid intid's, as long as the
    157	 * addr part of the attr is within 32 bits (checked above). An invalid
    158	 * intid will just make the read/writes point to above the intended
    159	 * register space (i.e., ICPENDR after ISPENDR).
    160	 */
    161	kvm_device_access(gic_fd, group, attr, &val, false);
    162	val |= 1ULL << index;
    163	kvm_device_access(gic_fd, group, attr, &val, true);
    164}
    165
    166void kvm_irq_write_ispendr(int gic_fd, uint32_t intid, uint32_t vcpu)
    167{
    168	vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISPENDR);
    169}
    170
    171void kvm_irq_write_isactiver(int gic_fd, uint32_t intid, uint32_t vcpu)
    172{
    173	vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISACTIVER);
    174}