From: Yang Weijiang <weijiang.yang@intel.com>
To: kvm@vger.kernel.org, linux-kernel@vger.kernel.org,
pbonzini@redhat.com, sean.j.christopherson@intel.com
Cc: mst@redhat.com, rkrcmar@redhat.com, jmattson@google.com,
yu.c.zhang@intel.com, alazar@bitdefender.com,
Yang Weijiang <weijiang.yang@intel.com>
Subject: [PATCH v5 5/9] mmu: spp: Introduce SPP {init,set,get} functions
Date: Tue, 17 Sep 2019 16:53:00 +0800 [thread overview]
Message-ID: <20190917085304.16987-6-weijiang.yang@intel.com> (raw)
In-Reply-To: <20190917085304.16987-1-weijiang.yang@intel.com>
spp_init() must be called before any {get, set}_subpage
functions, it creates subpage access bitmaps for VM memory
space then sets up SPPT root pages.
kvm_spp_set_permission() is to enable SPP bit in EPT leaf page.
If the gfn range covers hugepage, then it zapps the hugepage entries
in EPT, this induces following memory access to cause EPT page fault.
The mmu_lock must be held before above operation.
kvm_spp_get_permission() is used to query access bitmap for
protected page, it's also used in EPT fault handler to check
whether the fault EPT page is SPP protected as well.
Co-developed-by: He Chen <he.chen@linux.intel.com>
Signed-off-by: He Chen <he.chen@linux.intel.com>
Co-developed-by: Zhang Yi <yi.z.zhang@linux.intel.com>
Signed-off-by: Zhang Yi <yi.z.zhang@linux.intel.com>
Signed-off-by: Yang Weijiang <weijiang.yang@intel.com>
---
arch/x86/include/asm/kvm_host.h | 2 +
arch/x86/kvm/vmx/spp.c | 242 ++++++++++++++++++++++++++++++++
arch/x86/kvm/vmx/spp.h | 5 +
include/uapi/linux/kvm.h | 9 ++
4 files changed, 258 insertions(+)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index fe6417756983..cc38670a0c45 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -940,6 +940,8 @@ struct kvm_arch {
bool exception_payload_enabled;
struct kvm_pmu_event_filter *pmu_event_filter;
+ bool spp_active;
+
};
struct kvm_vm_stat {
diff --git a/arch/x86/kvm/vmx/spp.c b/arch/x86/kvm/vmx/spp.c
index 7e66d87186a2..ffc4ebcb64a6 100644
--- a/arch/x86/kvm/vmx/spp.c
+++ b/arch/x86/kvm/vmx/spp.c
@@ -186,6 +186,24 @@ bool is_spp_spte(struct kvm_mmu_page *sp)
return sp->role.spp;
}
+/*
+ * all vcpus share the same SPPT, vcpu->arch.mmu->sppt_root points to same
+ * SPPT root page, so any vcpu will do.
+ */
+static struct kvm_vcpu *kvm_spp_get_vcpu(struct kvm *kvm)
+{
+ struct kvm_vcpu *vcpu = NULL;
+ int idx;
+
+ for (idx = 0; idx < atomic_read(&kvm->online_vcpus); idx++) {
+ vcpu = kvm_get_vcpu(kvm, idx);
+ if (vcpu)
+ break;
+ }
+
+ return vcpu;
+}
+
#define SPPT_ENTRY_PHA_MASK (0xFFFFFFFFFF << 12)
int kvm_spp_setup_structure(struct kvm_vcpu *vcpu,
@@ -236,6 +254,40 @@ int kvm_spp_setup_structure(struct kvm_vcpu *vcpu,
}
EXPORT_SYMBOL_GPL(kvm_spp_setup_structure);
+int vmx_spp_flush_sppt(struct kvm *kvm, struct kvm_subpage *spp_info)
+{
+ struct kvm_shadow_walk_iterator iter;
+ struct kvm_vcpu *vcpu;
+ gfn_t gfn = spp_info->base_gfn;
+ int npages = spp_info->npages;
+ u64 spde;
+ int i;
+
+ vcpu = kvm_spp_get_vcpu(kvm);
+ /* direct_map spp start */
+ if (!VALID_PAGE(vcpu->arch.mmu->sppt_root))
+ return -EFAULT;
+
+ for (i = 0; i< npages; ++i) {
+ for_each_shadow_spp_entry(vcpu,(u64)gfn << PAGE_SHIFT, iter) {
+ if (!is_spp_shadow_present(*iter.sptep))
+ break;
+
+ if (iter.level == PT_DIRECTORY_LEVEL) {
+ spde = *iter.sptep;
+ spde &= ~PT_PRESENT_MASK;
+ spp_spte_set(iter.sptep, spde);
+ break;
+ }
+ }
+ gfn++;
+ }
+ kvm_flush_remote_tlbs(kvm);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vmx_spp_flush_sppt);
+
static int kvm_spp_create_bitmaps(struct kvm *kvm)
{
struct kvm_memslots *slots;
@@ -276,6 +328,196 @@ static int kvm_spp_create_bitmaps(struct kvm *kvm)
return ret;
}
+int vmx_spp_init(struct kvm *kvm)
+{
+ int i, ret;
+ struct kvm_vcpu *vcpu;
+ int root_level;
+ struct kvm_mmu_page *ssp_sp;
+
+ /* SPP feature is exclusive with nested VM.*/
+ if (kvm_x86_ops->get_nested_state)
+ return -EPERM;
+
+ if (kvm->arch.spp_active)
+ return 0;
+
+ ret = kvm_spp_create_bitmaps(kvm);
+
+ if (ret)
+ return ret;
+
+ kvm_for_each_vcpu(i, vcpu, kvm) {
+ /* prepare caches for SPP setup.*/
+ mmu_topup_memory_caches(vcpu);
+ root_level = vcpu->arch.mmu->shadow_root_level;
+ ssp_sp = kvm_spp_get_page(vcpu, 0, root_level);
+ ++ssp_sp->root_count;
+ vcpu->arch.mmu->sppt_root = __pa(ssp_sp->spt);
+ kvm_make_request(KVM_REQ_LOAD_CR3, vcpu);
+ }
+
+ kvm->arch.spp_active = true;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vmx_spp_init);
+
+int kvm_spp_get_permission(struct kvm *kvm, struct kvm_subpage *spp_info)
+{
+ u32 *access = spp_info->access_map;
+ gfn_t gfn = spp_info->base_gfn;
+ int npages = spp_info->npages;
+ struct kvm_memory_slot *slot;
+ int i;
+
+ if (!kvm->arch.spp_active)
+ return -ENODEV;
+
+ for (i = 0; i < npages; i++, gfn++) {
+ slot = gfn_to_memslot(kvm, gfn);
+ if (!slot)
+ return -EFAULT;
+ access[i] = *gfn_to_subpage_wp_info(slot, gfn);
+ }
+
+ return i;
+}
+EXPORT_SYMBOL_GPL(kvm_spp_get_permission);
+
+static void kvm_spp_zap_pte(struct kvm *kvm, u64 *spte, int level)
+{
+ u64 pte;
+
+ pte = *spte;
+ if (is_shadow_present_pte(pte) && is_last_spte(pte, level)) {
+ drop_spte(kvm, spte);
+ if (is_large_pte(pte))
+ --kvm->stat.lpages;
+ }
+}
+
+int kvm_spp_zap_entry(struct kvm *kvm, gfn_t gfn_lower, gfn_t gfn_upper,
+ u64 *sptep, int level)
+{
+ int page_num = KVM_PAGES_PER_HPAGE(level);
+ gfn_t gfn_max = (gfn_lower & ~(page_num - 1)) + page_num -1;
+ int ret;
+
+ if (gfn_upper <= gfn_max)
+ ret = gfn_upper - gfn_lower + 1;
+ else
+ ret = gfn_max - gfn_lower + 1;
+
+ kvm_spp_zap_pte(kvm, sptep, level);
+ kvm_flush_remote_tlbs(kvm);
+
+ return ret;
+}
+
+int kvm_spp_set_permission(struct kvm *kvm, struct kvm_subpage *spp_info)
+{
+ u32 *access = spp_info->access_map;
+ gfn_t gfn = spp_info->base_gfn;
+ int npages = spp_info->npages;
+ struct kvm_memory_slot *slot;
+ struct kvm_subpage sbp = {0};
+ struct kvm_shadow_walk_iterator iterator;
+ struct kvm_vcpu *vcpu;
+ gfn_t max_gfn;
+ gfn_t old_gfn = gfn;
+ u32 *wp_map;
+ int i, count;
+
+ if (!kvm->arch.spp_active)
+ return -ENODEV;
+
+ if (npages > SUBPAGE_MAX_BITMAP)
+ return -EFAULT;
+
+ for (i = 0; i < npages; i++, gfn++) {
+ slot = gfn_to_memslot(kvm, gfn);
+ if (!slot)
+ return -EFAULT;
+
+ wp_map = gfn_to_subpage_wp_info(slot, gfn);
+ *wp_map = access[i];
+ }
+
+ gfn = old_gfn;
+ max_gfn = gfn + npages - 1;
+ vcpu = kvm_spp_get_vcpu(kvm);
+
+ for (i = 0; gfn <= max_gfn; i++, gfn++) {
+ for_each_shadow_entry(vcpu, (u64)gfn << PAGE_SHIFT, iterator) {
+ if (!is_shadow_present_pte(*iterator.sptep))
+ break;
+
+ if (iterator.level == PT_PAGE_TABLE_LEVEL) {
+ sbp.base_gfn = gfn;
+ sbp.access_map[0] = access[i];
+ sbp.npages = 1;
+ if (kvm_spp_mark_protection(kvm, &sbp) < 0)
+ return -EFAULT;
+ break;
+ }
+
+ if (is_large_pte(*iterator.sptep)) {
+ count = kvm_spp_zap_entry(kvm, gfn, max_gfn,
+ iterator.sptep,
+ iterator.level);
+ if (count >= npages)
+ goto out;
+ gfn += count - 1;
+ }
+ }
+ }
+out:
+ return npages;
+}
+
+int kvm_spp_mark_protection(struct kvm *kvm, struct kvm_subpage *spp_info)
+{
+ u32 *access = spp_info->access_map;
+ gfn_t gfn = spp_info->base_gfn;
+ struct kvm_memory_slot *slot;
+ struct kvm_rmap_head *rmap_head;
+ int ret;
+
+ if (!kvm->arch.spp_active)
+ return -ENODEV;
+
+ slot = gfn_to_memslot(kvm, gfn);
+ if (!slot)
+ return -EFAULT;
+
+ /*
+ * check whether the target 4KB page exists in EPT leaf
+ * entry.If it's there, just flag SPP bit of the entry,
+ * defer the setup to SPPT miss induced vm-exit handler.
+ */
+ rmap_head = __gfn_to_rmap(gfn, PT_PAGE_TABLE_LEVEL, slot);
+
+ if (rmap_head->val) {
+ /*
+ * if all subpages are not writable, open SPP bit in
+ * EPT leaf entry to enable SPP protection for
+ * corresponding page.
+ */
+ if (access[0] != FULL_SPP_ACCESS) {
+ ret = kvm_spp_open_write_protect(kvm,
+ slot, gfn);
+ if (ret)
+ return ret;
+ } else {
+ ret = kvm_spp_clear_write_protect(kvm,
+ slot, gfn);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
void kvm_spp_free_memslot(struct kvm_memory_slot *free,
struct kvm_memory_slot *dont)
diff --git a/arch/x86/kvm/vmx/spp.h b/arch/x86/kvm/vmx/spp.h
index 94f6e39b30ed..9c3a51feddda 100644
--- a/arch/x86/kvm/vmx/spp.h
+++ b/arch/x86/kvm/vmx/spp.h
@@ -3,9 +3,14 @@
#define __KVM_X86_VMX_SPP_H
#define FULL_SPP_ACCESS ((u32)((1ULL << 32) - 1))
+
bool is_spp_spte(struct kvm_mmu_page *sp);
inline u64 construct_spptp(unsigned long root_hpa);
int kvm_spp_setup_structure(struct kvm_vcpu *vcpu,
u32 access_map, gfn_t gfn);
+int vmx_spp_flush_sppt(struct kvm *kvm, struct kvm_subpage *spp_info);
+void kvm_spp_free_memslot(struct kvm_memory_slot *free,
+ struct kvm_memory_slot *dont);
+int vmx_spp_init(struct kvm *kvm);
#endif /* __KVM_X86_VMX_SPP_H */
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 5e3f12d5359e..9460830de536 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -102,6 +102,15 @@ struct kvm_userspace_memory_region {
__u64 userspace_addr; /* start of the userspace allocated memory */
};
+/* for KVM_SUBPAGES_GET_ACCESS and KVM_SUBPAGES_SET_ACCESS */
+#define SUBPAGE_MAX_BITMAP 64
+struct kvm_subpage {
+ __u64 base_gfn;
+ __u64 npages;
+ /* sub-page write-access bitmap array */
+ __u32 access_map[SUBPAGE_MAX_BITMAP];
+};
+
/*
* The bit 0 ~ bit 15 of kvm_memory_region::flags are visible for userspace,
* other bits are reserved for kvm internal use which are defined in
--
2.17.2
next prev parent reply other threads:[~2019-09-17 8:53 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-09-17 8:52 [PATCH v5 0/9] Enable Sub-page Write Protection Support Yang Weijiang
2019-09-17 8:52 ` [PATCH v5 1/9] Documentation: Introduce EPT based Subpage Protection Yang Weijiang
2019-10-11 20:31 ` Jim Mattson
2019-10-15 8:53 ` Yang Weijiang
2019-09-17 8:52 ` [PATCH v5 2/9] vmx: spp: Add control flags for Sub-Page Protection(SPP) Yang Weijiang
2019-10-04 20:48 ` Jim Mattson
2019-10-04 21:02 ` Sean Christopherson
2019-10-15 1:53 ` Yang Weijiang
2019-09-17 8:52 ` [PATCH v5 3/9] mmu: spp: Add SPP Table setup functions Yang Weijiang
2019-09-17 8:52 ` [PATCH v5 4/9] mmu: spp: Add functions to create/destroy SPP bitmap block Yang Weijiang
2019-09-17 8:53 ` Yang Weijiang [this message]
2019-09-17 8:53 ` [PATCH v5 6/9] x86: spp: Introduce user-space SPP IOCTLs Yang Weijiang
2019-09-17 8:53 ` [PATCH v5 7/9] vmx: spp: Set up SPP paging table at vm-entry/exit Yang Weijiang
2019-09-17 10:56 ` kbuild test robot
2019-09-17 8:53 ` [PATCH v5 8/9] mmu: spp: Enable Lazy mode SPP protection Yang Weijiang
2019-09-17 8:53 ` [PATCH v5 9/9] mmu: spp: Handle SPP protected pages when VM memory changes Yang Weijiang
2019-09-17 12:59 ` [PATCH v5 0/9] Enable Sub-page Write Protection Support Konrad Rzeszutek Wilk
2019-09-17 16:24 ` Adalbert Lazăr
2019-10-09 2:17 ` Yang Weijiang
2019-10-10 21:42 ` Jim Mattson
2019-10-11 7:50 ` Yang Weijiang
2019-10-11 16:11 ` Jim Mattson
2019-10-22 6:19 ` Yang Weijiang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190917085304.16987-6-weijiang.yang@intel.com \
--to=weijiang.yang@intel.com \
--cc=alazar@bitdefender.com \
--cc=jmattson@google.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mst@redhat.com \
--cc=pbonzini@redhat.com \
--cc=rkrcmar@redhat.com \
--cc=sean.j.christopherson@intel.com \
--cc=yu.c.zhang@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox