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

pfncache.c (7527B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Kernel-based Virtual Machine driver for Linux
      4 *
      5 * This module enables kernel and guest-mode vCPU access to guest physical
      6 * memory with suitable invalidation mechanisms.
      7 *
      8 * Copyright © 2021 Amazon.com, Inc. or its affiliates.
      9 *
     10 * Authors:
     11 *   David Woodhouse <dwmw2@infradead.org>
     12 */
     13
     14#include <linux/kvm_host.h>
     15#include <linux/kvm.h>
     16#include <linux/highmem.h>
     17#include <linux/module.h>
     18#include <linux/errno.h>
     19
     20#include "kvm_mm.h"
     21
     22/*
     23 * MMU notifier 'invalidate_range_start' hook.
     24 */
     25void gfn_to_pfn_cache_invalidate_start(struct kvm *kvm, unsigned long start,
     26				       unsigned long end, bool may_block)
     27{
     28	DECLARE_BITMAP(vcpu_bitmap, KVM_MAX_VCPUS);
     29	struct gfn_to_pfn_cache *gpc;
     30	bool evict_vcpus = false;
     31
     32	spin_lock(&kvm->gpc_lock);
     33	list_for_each_entry(gpc, &kvm->gpc_list, list) {
     34		write_lock_irq(&gpc->lock);
     35
     36		/* Only a single page so no need to care about length */
     37		if (gpc->valid && !is_error_noslot_pfn(gpc->pfn) &&
     38		    gpc->uhva >= start && gpc->uhva < end) {
     39			gpc->valid = false;
     40
     41			/*
     42			 * If a guest vCPU could be using the physical address,
     43			 * it needs to be forced out of guest mode.
     44			 */
     45			if (gpc->usage & KVM_GUEST_USES_PFN) {
     46				if (!evict_vcpus) {
     47					evict_vcpus = true;
     48					bitmap_zero(vcpu_bitmap, KVM_MAX_VCPUS);
     49				}
     50				__set_bit(gpc->vcpu->vcpu_idx, vcpu_bitmap);
     51			}
     52		}
     53		write_unlock_irq(&gpc->lock);
     54	}
     55	spin_unlock(&kvm->gpc_lock);
     56
     57	if (evict_vcpus) {
     58		/*
     59		 * KVM needs to ensure the vCPU is fully out of guest context
     60		 * before allowing the invalidation to continue.
     61		 */
     62		unsigned int req = KVM_REQ_OUTSIDE_GUEST_MODE;
     63		bool called;
     64
     65		/*
     66		 * If the OOM reaper is active, then all vCPUs should have
     67		 * been stopped already, so perform the request without
     68		 * KVM_REQUEST_WAIT and be sad if any needed to be IPI'd.
     69		 */
     70		if (!may_block)
     71			req &= ~KVM_REQUEST_WAIT;
     72
     73		called = kvm_make_vcpus_request_mask(kvm, req, vcpu_bitmap);
     74
     75		WARN_ON_ONCE(called && !may_block);
     76	}
     77}
     78
     79bool kvm_gfn_to_pfn_cache_check(struct kvm *kvm, struct gfn_to_pfn_cache *gpc,
     80				gpa_t gpa, unsigned long len)
     81{
     82	struct kvm_memslots *slots = kvm_memslots(kvm);
     83
     84	if ((gpa & ~PAGE_MASK) + len > PAGE_SIZE)
     85		return false;
     86
     87	if (gpc->gpa != gpa || gpc->generation != slots->generation ||
     88	    kvm_is_error_hva(gpc->uhva))
     89		return false;
     90
     91	if (!gpc->valid)
     92		return false;
     93
     94	return true;
     95}
     96EXPORT_SYMBOL_GPL(kvm_gfn_to_pfn_cache_check);
     97
     98static void __release_gpc(struct kvm *kvm, kvm_pfn_t pfn, void *khva, gpa_t gpa)
     99{
    100	/* Unmap the old page if it was mapped before, and release it */
    101	if (!is_error_noslot_pfn(pfn)) {
    102		if (khva) {
    103			if (pfn_valid(pfn))
    104				kunmap(pfn_to_page(pfn));
    105#ifdef CONFIG_HAS_IOMEM
    106			else
    107				memunmap(khva);
    108#endif
    109		}
    110
    111		kvm_release_pfn(pfn, false);
    112	}
    113}
    114
    115static kvm_pfn_t hva_to_pfn_retry(struct kvm *kvm, unsigned long uhva)
    116{
    117	unsigned long mmu_seq;
    118	kvm_pfn_t new_pfn;
    119	int retry;
    120
    121	do {
    122		mmu_seq = kvm->mmu_notifier_seq;
    123		smp_rmb();
    124
    125		/* We always request a writeable mapping */
    126		new_pfn = hva_to_pfn(uhva, false, NULL, true, NULL);
    127		if (is_error_noslot_pfn(new_pfn))
    128			break;
    129
    130		KVM_MMU_READ_LOCK(kvm);
    131		retry = mmu_notifier_retry_hva(kvm, mmu_seq, uhva);
    132		KVM_MMU_READ_UNLOCK(kvm);
    133		if (!retry)
    134			break;
    135
    136		cond_resched();
    137	} while (1);
    138
    139	return new_pfn;
    140}
    141
    142int kvm_gfn_to_pfn_cache_refresh(struct kvm *kvm, struct gfn_to_pfn_cache *gpc,
    143				 gpa_t gpa, unsigned long len)
    144{
    145	struct kvm_memslots *slots = kvm_memslots(kvm);
    146	unsigned long page_offset = gpa & ~PAGE_MASK;
    147	kvm_pfn_t old_pfn, new_pfn;
    148	unsigned long old_uhva;
    149	gpa_t old_gpa;
    150	void *old_khva;
    151	bool old_valid;
    152	int ret = 0;
    153
    154	/*
    155	 * If must fit within a single page. The 'len' argument is
    156	 * only to enforce that.
    157	 */
    158	if (page_offset + len > PAGE_SIZE)
    159		return -EINVAL;
    160
    161	write_lock_irq(&gpc->lock);
    162
    163	old_gpa = gpc->gpa;
    164	old_pfn = gpc->pfn;
    165	old_khva = gpc->khva - offset_in_page(gpc->khva);
    166	old_uhva = gpc->uhva;
    167	old_valid = gpc->valid;
    168
    169	/* If the userspace HVA is invalid, refresh that first */
    170	if (gpc->gpa != gpa || gpc->generation != slots->generation ||
    171	    kvm_is_error_hva(gpc->uhva)) {
    172		gfn_t gfn = gpa_to_gfn(gpa);
    173
    174		gpc->gpa = gpa;
    175		gpc->generation = slots->generation;
    176		gpc->memslot = __gfn_to_memslot(slots, gfn);
    177		gpc->uhva = gfn_to_hva_memslot(gpc->memslot, gfn);
    178
    179		if (kvm_is_error_hva(gpc->uhva)) {
    180			gpc->pfn = KVM_PFN_ERR_FAULT;
    181			ret = -EFAULT;
    182			goto out;
    183		}
    184
    185		gpc->uhva += page_offset;
    186	}
    187
    188	/*
    189	 * If the userspace HVA changed or the PFN was already invalid,
    190	 * drop the lock and do the HVA to PFN lookup again.
    191	 */
    192	if (!old_valid || old_uhva != gpc->uhva) {
    193		unsigned long uhva = gpc->uhva;
    194		void *new_khva = NULL;
    195
    196		/* Placeholders for "hva is valid but not yet mapped" */
    197		gpc->pfn = KVM_PFN_ERR_FAULT;
    198		gpc->khva = NULL;
    199		gpc->valid = true;
    200
    201		write_unlock_irq(&gpc->lock);
    202
    203		new_pfn = hva_to_pfn_retry(kvm, uhva);
    204		if (is_error_noslot_pfn(new_pfn)) {
    205			ret = -EFAULT;
    206			goto map_done;
    207		}
    208
    209		if (gpc->usage & KVM_HOST_USES_PFN) {
    210			if (new_pfn == old_pfn) {
    211				new_khva = old_khva;
    212				old_pfn = KVM_PFN_ERR_FAULT;
    213				old_khva = NULL;
    214			} else if (pfn_valid(new_pfn)) {
    215				new_khva = kmap(pfn_to_page(new_pfn));
    216#ifdef CONFIG_HAS_IOMEM
    217			} else {
    218				new_khva = memremap(pfn_to_hpa(new_pfn), PAGE_SIZE, MEMREMAP_WB);
    219#endif
    220			}
    221			if (new_khva)
    222				new_khva += page_offset;
    223			else
    224				ret = -EFAULT;
    225		}
    226
    227	map_done:
    228		write_lock_irq(&gpc->lock);
    229		if (ret) {
    230			gpc->valid = false;
    231			gpc->pfn = KVM_PFN_ERR_FAULT;
    232			gpc->khva = NULL;
    233		} else {
    234			/* At this point, gpc->valid may already have been cleared */
    235			gpc->pfn = new_pfn;
    236			gpc->khva = new_khva;
    237		}
    238	} else {
    239		/* If the HVA→PFN mapping was already valid, don't unmap it. */
    240		old_pfn = KVM_PFN_ERR_FAULT;
    241		old_khva = NULL;
    242	}
    243
    244 out:
    245	write_unlock_irq(&gpc->lock);
    246
    247	__release_gpc(kvm, old_pfn, old_khva, old_gpa);
    248
    249	return ret;
    250}
    251EXPORT_SYMBOL_GPL(kvm_gfn_to_pfn_cache_refresh);
    252
    253void kvm_gfn_to_pfn_cache_unmap(struct kvm *kvm, struct gfn_to_pfn_cache *gpc)
    254{
    255	void *old_khva;
    256	kvm_pfn_t old_pfn;
    257	gpa_t old_gpa;
    258
    259	write_lock_irq(&gpc->lock);
    260
    261	gpc->valid = false;
    262
    263	old_khva = gpc->khva - offset_in_page(gpc->khva);
    264	old_gpa = gpc->gpa;
    265	old_pfn = gpc->pfn;
    266
    267	/*
    268	 * We can leave the GPA → uHVA map cache intact but the PFN
    269	 * lookup will need to be redone even for the same page.
    270	 */
    271	gpc->khva = NULL;
    272	gpc->pfn = KVM_PFN_ERR_FAULT;
    273
    274	write_unlock_irq(&gpc->lock);
    275
    276	__release_gpc(kvm, old_pfn, old_khva, old_gpa);
    277}
    278EXPORT_SYMBOL_GPL(kvm_gfn_to_pfn_cache_unmap);
    279
    280
    281int kvm_gfn_to_pfn_cache_init(struct kvm *kvm, struct gfn_to_pfn_cache *gpc,
    282			      struct kvm_vcpu *vcpu, enum pfn_cache_usage usage,
    283			      gpa_t gpa, unsigned long len)
    284{
    285	WARN_ON_ONCE(!usage || (usage & KVM_GUEST_AND_HOST_USE_PFN) != usage);
    286
    287	if (!gpc->active) {
    288		rwlock_init(&gpc->lock);
    289
    290		gpc->khva = NULL;
    291		gpc->pfn = KVM_PFN_ERR_FAULT;
    292		gpc->uhva = KVM_HVA_ERR_BAD;
    293		gpc->vcpu = vcpu;
    294		gpc->usage = usage;
    295		gpc->valid = false;
    296		gpc->active = true;
    297
    298		spin_lock(&kvm->gpc_lock);
    299		list_add(&gpc->list, &kvm->gpc_list);
    300		spin_unlock(&kvm->gpc_lock);
    301	}
    302	return kvm_gfn_to_pfn_cache_refresh(kvm, gpc, gpa, len);
    303}
    304EXPORT_SYMBOL_GPL(kvm_gfn_to_pfn_cache_init);
    305
    306void kvm_gfn_to_pfn_cache_destroy(struct kvm *kvm, struct gfn_to_pfn_cache *gpc)
    307{
    308	if (gpc->active) {
    309		spin_lock(&kvm->gpc_lock);
    310		list_del(&gpc->list);
    311		spin_unlock(&kvm->gpc_lock);
    312
    313		kvm_gfn_to_pfn_cache_unmap(kvm, gpc);
    314		gpc->active = false;
    315	}
    316}
    317EXPORT_SYMBOL_GPL(kvm_gfn_to_pfn_cache_destroy);