Skip to contentSkip to conversation

Enable Sub-Page Write Protection Support

PATCH Superseded Yang Weijiang 2 participants 36 messages Nov 19, 2019 – Nov 22, 2019

Subsystems (4)

v7 of 12 Superseded Latest is v12 →

lore
Share & tools
lore
#0 lore raw

v7 cover Enable Sub-Page Write Protection Support

EPT-Based Sub-Page write Protection(SPP) allows Virtual Machine Monitor(VMM) specify write-permission for guest physical memory at a sub-page(128 byte) granularity. When SPP works, HW enforces write-access check for sub-pages within a protected 4KB page.

The feature targets to provide fine-grained memory protection for usages such as memory guard and VM introspection etc.

SPP is active when the "sub-page write protection" (bit 23) is 1 in Secondary VM-Execution Controls. The feature is backed with a Sub-Page Permission Table(SPPT), and subpage permission vector is stored in the leaf entry of SPPT. The root page is referenced via a Sub-Page Permission Table Pointer (SPPTP) in VMCS.

To enable SPP for guest memory, the guest page should be first mapped to a 4KB EPT entry, then set SPP bit 61 of the corresponding entry. While HW walks EPT, it traverses SPPT with the gpa to look up the sub-page permission vector within SPPT leaf entry. If the corresponding bit is set, write to sub-page is permitted, otherwise, SPP induced EPT violation is generated.

This patch serial passed SPP function test and selftest on Ice-Lake platform.

Please refer to the SPP introduction document in this patch set and Intel SDM for details:

Intel SDM:
https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf

SPP selftest patch:
https://lkml.org/lkml/2019/6/18/1197

Patch 1: Documentation for SPP and related API. Patch 2: Add control flags for Sub-Page Protection(SPP). Patch 3: Add SPP Table setup functions.
Patch 4: Add functions to create/destroy SPP bitmap block. Patch 5: Introduce user-space SPP IOCTLs.
Patch 6: Set up SPP paging table at vmentry/vmexit. Patch 7: Enable Lazy mode SPP protection.
Patch 8: Handle SPP protected pages when VM memory changes. Patch 9: Add SPP protection check in emulation case.

Change logs:
V6 -> V7:
1. Configured all available protected pages once SPP induced vmexit happens since there's no PRESENT bit in SPPT leaf entry.
2. Changed SPP protection check flow in tdp_page_fault().
3. Code refactor and minior fixes.

V5 -> V6:
1. Added SPP protection patch for emulation cases per Jim's review.
2. Modified documentation and added API description per Jim's review.
3. Other minior changes suggested by Jim.

V4 -> V5:
1. Enable SPP support for Hugepage(1GB/2MB) to extend application.
2. Make SPP miss vm-exit handler as the unified place to set up SPPT.
3. If SPP protected pages are access-tracked or dirty-page-tracked, store SPP flag in reserved address bit, restore it in fast_page_fault() handler.
4. Move SPP specific functions to vmx/spp.c and vmx/spp.h
5. Rebased code to kernel v5.3
6. Other change suggested by KVM community.

V3 -> V4:
1. Modified documentation to make it consistent with patches.
2. Allocated SPPT root page in init_spp() instead of vmx_set_cr3() to avoid SPPT miss error.
3. Added back co-developers and sign-offs.

V2 -> V3:
1. Rebased patches to kernel 5.1 release
2. Deferred SPPT setup to EPT fault handler if the page is not available while set_subpage() is being called.
3. Added init IOCTL to reduce extra cost if SPP is not used.
4. Refactored patch structure, cleaned up cross referenced functions.
5. Added code to deal with memory swapping/migration/shrinker cases.

V2 -> V1:
1. Rebased to 4.20-rc1
2. Move VMCS change to a separated patch.
3. Code refine and Bug fix

Yang Weijiang (9):
Documentation: Introduce EPT based Subpage Protection and related
ioctls
vmx: spp: Add control flags for Sub-Page Protection(SPP)
mmu: spp: Add SPP Table setup functions
mmu: spp: Add functions to create/destroy SPP bitmap block
x86: spp: Introduce user-space SPP IOCTLs
vmx: spp: Set up SPP paging table at vmentry/vmexit
mmu: spp: Enable Lazy mode SPP protection
mmu: spp: Handle SPP protected pages when VM memory changes
x86: spp: Add SPP protection check in emulation.

 Documentation/virt/kvm/api.txt        |  46 ++
 Documentation/virtual/kvm/spp_kvm.txt | 180 +++++++
 arch/x86/include/asm/cpufeatures.h    |   1 +
 arch/x86/include/asm/kvm_host.h       |  10 +-
 arch/x86/include/asm/vmx.h            |  10 +
 arch/x86/include/uapi/asm/vmx.h       |   2 +
 arch/x86/kernel/cpu/intel.c           |   4 +
 arch/x86/kvm/mmu.c                    |  78 ++-
 arch/x86/kvm/mmu.h                    |   2 +
 arch/x86/kvm/vmx/capabilities.h       |   5 +
 arch/x86/kvm/vmx/spp.c                | 651 ++++++++++++++++++++++++++
 arch/x86/kvm/vmx/spp.h                |  28 ++
 arch/x86/kvm/vmx/vmx.c                | 113 +++++
 arch/x86/kvm/x86.c                    |  87 ++++
 include/uapi/linux/kvm.h              |  17 +
 15 files changed, 1232 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/virtual/kvm/spp_kvm.txt
 create mode 100644 arch/x86/kvm/vmx/spp.c
 create mode 100644 arch/x86/kvm/vmx/spp.h
Signature · 1 line
2.17.2
#1 lore raw

PATCH v7 1/9 Documentation: Introduce EPT based Subpage Protection and related ioctls

---
 Documentation/virt/kvm/api.txt        |  46 +++++++
 Documentation/virtual/kvm/spp_kvm.txt | 180 ++++++++++++++++++++++++++
 2 files changed, 226 insertions(+)
 create mode 100644 Documentation/virtual/kvm/spp_kvm.txt
Documentation/virt/kvm/api.txt +46 -0
index 2d067767b617..85a764812f3d 100644
@@ -4111,6 +4111,52 @@ Valid values for 'action':
 #define KVM_PMU_EVENT_ALLOW 0
 #define KVM_PMU_EVENT_DENY 1
 
+4.121 KVM_INIT_SPP
+
+Architectures: x86
+Type: vm ioctl
+Parameters: none
+Returns: 0 on success, < 0 on error
+
+This ioctl initializes storage for sub-page permission vectors and
+SPPT root page.
+
+4.121 KVM_SUBPAGES_GET_ACCESS
+
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_subpage_info (in/out)
+Returns: 0 on success, < 0 on error
+
+#define SUBPAGE_MAX_BITMAP   64
+struct kvm_subpage_info {
+	__u64 gfn;    /* the first page gfn of the contiguous pages */
+	__u64 npages; /* number of 4K pages */
+	__u32 access_map[SUBPAGE_MAX_BITMAP]; /* sub-page write-access bitmap array */
+};
+
+This ioctl fetches subpage permission from contiguous pages starting with
+gfn. npages is the number of contiguous pages to fetch. access_map contains permission
+vectors fetched for all the pages.
+
+4.122 KVM_SUBPAGES_SET_ACCESS
+
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_subpage_info (in/out)
+Returns: 0 on success, < 0 on error
+
+#define SUBPAGE_MAX_BITMAP   64
+struct kvm_subpage_info {
+	__u64 gfn;    /* the first page gfn of the contiguous pages */
+	__u64 npages; /* number of 4K pages */
+	__u32 access_map[SUBPAGE_MAX_BITMAP]; /* sub-page write-access bitmap array */
+};
+
+This ioctl sets subpage permission for contiguous pages starting with gfn. npages is
+the number of contiguous pages to set. access_map contains permission vectors for all the
+pages. Since during execution of the ioctl, it holds mmu_lock, so limits the MAX pages
+to 64 to reduce the impact to EPT.
 
 5. The kvm_run structure
 ------------------------
Documentation/virtual/kvm/spp_kvm.txt new file +180 -0

180 lines not on this page · raw message

Signature · 1 line
2.17.2
maintainer
#2 lore raw

Re: [PATCH v7 1/9] Documentation: Introduce EPT based Subpage Protection and related ioctls

Review

Quoted 2 lines · Yang Weijiang
(quoted diff) +2 -0
+
+#define SUBPAGE_MAX_BITMAP   64

Please rename this to KVM_SUBPAGE_MAX_PAGES

Documentation/virt/kvm/api.txt Valid values for 'action': · line 4134

Quoted 3 lines
Documentation/virt/kvm/api.txt +3 -0
+struct kvm_subpage_info {
+	__u64 gfn;    /* the first page gfn of the contiguous pages */
+	__u64 npages; /* number of 4K pages */

This can be

	u32 npages;
	u32 flags;

Check that the flags are 0, and fail the ioctl if they aren't. This will make it easy to extend the API in the future.

Review

Quoted 2 lines
(quoted diff) +2 -0
+	__u32 access_map[SUBPAGE_MAX_BITMAP]; /* sub-page write-access bitmap array */
+};

Please make this access_map[0], since the number of entries actually depends on npages.

Likewise, kvm_arch_vm_ioctl should read the header first, then allocate memory for the access_map and read into it. It's probably simpler if you make kvm_vm_ioctl_get_subpages/kvm_vm_ioctl_set_subpages take parameters like

int kvm_vm_ioctl_get_subpages(struct kvm *kvm, u64 gfn, u32 npages,

			      u32 *access_map);
int kvm_vm_ioctl_set_subpages(struct kvm *kvm, u64 gfn, u32 npages,
			      u32 *access_map);

Thanks,

Paolo

#3 lore raw

Re: [PATCH v7 1/9] Documentation: Introduce EPT based Subpage Protection and related ioctls

Quoted 6 lines · Paolo Bonzini

Review

Quoted 2 lines · Yang Weijiang
(quoted diff) +2 -0
+
+#define SUBPAGE_MAX_BITMAP   64

Please rename this to KVM_SUBPAGE_MAX_PAGES

OK.

Quoted 6 lines

Documentation/virt/kvm/api.txt Valid values for 'action': · line 4134

Quoted 3 lines
Documentation/virt/kvm/api.txt +3 -0
+struct kvm_subpage_info {
+	__u64 gfn;    /* the first page gfn of the contiguous pages */
+	__u64 npages; /* number of 4K pages */

This can be

Quoted 6 lines
	u32 npages;
	u32 flags;

Check that the flags are 0, and fail the ioctl if they aren't. This will make it easy to extend the API in the future.

Cool, thanks for the suggestion!

Quoted 11 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+	__u32 access_map[SUBPAGE_MAX_BITMAP]; /* sub-page write-access bitmap array */
+};

Please make this access_map[0], since the number of entries actually depends on npages.

Likewise, kvm_arch_vm_ioctl should read the header first, then allocate memory for the access_map and read into it. It's probably simpler if you make kvm_vm_ioctl_get_subpages/kvm_vm_ioctl_set_subpages take parameters like

Quoted 5 lines

int kvm_vm_ioctl_get_subpages(struct kvm *kvm, u64 gfn, u32 npages,

			      u32 *access_map);
int kvm_vm_ioctl_set_subpages(struct kvm *kvm, u64 gfn, u32 npages,
			      u32 *access_map);

Sure, will make the change. thank you!

Quoted 3 lines

Thanks,

Paolo

#4 lore raw

PATCH v7 2/9 vmx: spp: Add control flags for Sub-Page Protection(SPP)

In reply to Yang Weijiang

Check SPP capability in MSR_IA32_VMX_PROCBASED_CTLS2, its 23-bit indicates SPP capability. Enable SPP feature bit in CPU capabilities bitmap if it's supported.

---
 arch/x86/include/asm/cpufeatures.h | 1 +
 arch/x86/include/asm/vmx.h         | 1 +
 arch/x86/kernel/cpu/intel.c        | 4 ++++
 arch/x86/kvm/mmu.h                 | 2 ++
 arch/x86/kvm/vmx/capabilities.h    | 5 +++++
 arch/x86/kvm/vmx/vmx.c             | 9 +++++++++
 6 files changed, 22 insertions(+)
arch/x86/include/asm/cpufeatures.h +1 -0
index e880f2408e29..ee2c76fdadf6 100644
@@ -228,6 +228,7 @@
 #define X86_FEATURE_FLEXPRIORITY	( 8*32+ 2) /* Intel FlexPriority */
 #define X86_FEATURE_EPT			( 8*32+ 3) /* Intel Extended Page Table */
 #define X86_FEATURE_VPID		( 8*32+ 4) /* Intel Virtual Processor ID */
+#define X86_FEATURE_SPP			( 8*32+ 5) /* Intel EPT-based Sub-Page Write Protection */
 
 #define X86_FEATURE_VMMCALL		( 8*32+15) /* Prefer VMMCALL to VMCALL */
 #define X86_FEATURE_XENPV		( 8*32+16) /* "" Xen paravirtual guest */
arch/x86/include/asm/vmx.h +1 -0
index a39136b0d509..e1137807affc 100644
@@ -68,6 +68,7 @@
 #define SECONDARY_EXEC_XSAVES			0x00100000
 #define SECONDARY_EXEC_PT_USE_GPA		0x01000000
 #define SECONDARY_EXEC_MODE_BASED_EPT_EXEC	0x00400000
+#define SECONDARY_EXEC_ENABLE_SPP		0x00800000
 #define SECONDARY_EXEC_TSC_SCALING              0x02000000
 
 #define PIN_BASED_EXT_INTR_MASK                 0x00000001
arch/x86/kernel/cpu/intel.c +4 -0
index 8d6d92ebeb54..27617e522f01 100644
@@ -503,6 +503,7 @@ static void detect_vmx_virtcap(struct cpuinfo_x86 *c)
 #define X86_VMX_FEATURE_PROC_CTLS2_EPT		0x00000002
 #define X86_VMX_FEATURE_PROC_CTLS2_VPID		0x00000020
 #define x86_VMX_FEATURE_EPT_CAP_AD		0x00200000
+#define X86_VMX_FEATURE_PROC_CTLS2_SPP		0x00800000
 
 	u32 vmx_msr_low, vmx_msr_high, msr_ctl, msr_ctl2;
 	u32 msr_vpid_cap, msr_ept_cap;
@@ -513,6 +514,7 @@ static void detect_vmx_virtcap(struct cpuinfo_x86 *c)
 	clear_cpu_cap(c, X86_FEATURE_EPT);
 	clear_cpu_cap(c, X86_FEATURE_VPID);
 	clear_cpu_cap(c, X86_FEATURE_EPT_AD);
+	clear_cpu_cap(c, X86_FEATURE_SPP);
 
 	rdmsr(MSR_IA32_VMX_PROCBASED_CTLS, vmx_msr_low, vmx_msr_high);
 	msr_ctl = vmx_msr_high | vmx_msr_low;
@@ -536,6 +538,8 @@ static void detect_vmx_virtcap(struct cpuinfo_x86 *c)
 		}
 		if (msr_ctl2 & X86_VMX_FEATURE_PROC_CTLS2_VPID)
 			set_cpu_cap(c, X86_FEATURE_VPID);
+		if (msr_ctl2 & X86_VMX_FEATURE_PROC_CTLS2_SPP)
+			set_cpu_cap(c, X86_FEATURE_SPP);
 	}
 }
 
arch/x86/kvm/mmu.h +2 -0
index 54c2a377795b..3c1423526a98 100644
@@ -26,6 +26,8 @@
 #define PT_PAGE_SIZE_MASK (1ULL << PT_PAGE_SIZE_SHIFT)
 #define PT_PAT_MASK (1ULL << 7)
 #define PT_GLOBAL_MASK (1ULL << 8)
+#define PT_SPP_SHIFT 61
+#define PT_SPP_MASK (1ULL << PT_SPP_SHIFT)
 #define PT64_NX_SHIFT 63
 #define PT64_NX_MASK (1ULL << PT64_NX_SHIFT)
 
arch/x86/kvm/vmx/capabilities.h +5 -0
index d6664ee3d127..e3bde7a32123 100644
@@ -241,6 +241,11 @@ static inline bool cpu_has_vmx_pml(void)
 	return vmcs_config.cpu_based_2nd_exec_ctrl & SECONDARY_EXEC_ENABLE_PML;
 }
 
+static inline bool cpu_has_vmx_ept_spp(void)
+{
+	return vmcs_config.cpu_based_2nd_exec_ctrl & SECONDARY_EXEC_ENABLE_SPP;
+}
+
 static inline bool vmx_xsaves_supported(void)
 {
 	return vmcs_config.cpu_based_2nd_exec_ctrl &
arch/x86/kvm/vmx/vmx.c +9 -0
index c030c96fc81a..0fcd02b531ef 100644
@@ -60,6 +60,7 @@
 #include "vmcs12.h"
 #include "vmx.h"
 #include "x86.h"
+#include "spp.h"
 
 MODULE_AUTHOR("Qumranet");
 MODULE_LICENSE("GPL");
@@ -113,6 +114,7 @@ module_param_named(pml, enable_pml, bool, S_IRUGO);
 
 static bool __read_mostly dump_invalid_vmcs = 0;
 module_param(dump_invalid_vmcs, bool, 0644);
+static bool __read_mostly spp_supported = 0;
 
 #define MSR_BITMAP_MODE_X2APIC		1
 #define MSR_BITMAP_MODE_X2APIC_APICV	2
@@ -2279,6 +2281,7 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf,
 			SECONDARY_EXEC_RDSEED_EXITING |
 			SECONDARY_EXEC_RDRAND_EXITING |
 			SECONDARY_EXEC_ENABLE_PML |
+			SECONDARY_EXEC_ENABLE_SPP |
 			SECONDARY_EXEC_TSC_SCALING |
 			SECONDARY_EXEC_PT_USE_GPA |
 			SECONDARY_EXEC_PT_CONCEAL_VMX |
@@ -3931,6 +3934,9 @@ static void vmx_compute_secondary_exec_control(struct vcpu_vmx *vmx)
 	if (!enable_pml)
 		exec_control &= ~SECONDARY_EXEC_ENABLE_PML;
 
+	if (!spp_supported)
+		exec_control &= ~SECONDARY_EXEC_ENABLE_SPP;
+
 	if (vmx_xsaves_supported()) {
 		/* Exposing XSAVES only when XSAVE is exposed */
 		bool xsaves_enabled =
@@ -7521,6 +7527,9 @@ static __init int hardware_setup(void)
 	if (!cpu_has_vmx_flexpriority())
 		flexpriority_enabled = 0;
 
+	if (cpu_has_vmx_ept_spp() && enable_ept)
+		spp_supported = 1;
+
 	if (!cpu_has_virtual_nmis())
 		enable_vnmi = 0;
 
Signature · 1 line
2.17.2
maintainer
#5 lore raw

Re: [PATCH v7 2/9] vmx: spp: Add control flags for Sub-Page Protection(SPP)

arch/x86/include/asm/cpufeatures.h line 231

Quoted 5 lines · Yang Weijiang
arch/x86/include/asm/cpufeatures.h +1 -0
@@ -228,6 +228,7 @@
 #define X86_FEATURE_FLEXPRIORITY	( 8*32+ 2) /* Intel FlexPriority */
 #define X86_FEATURE_EPT			( 8*32+ 3) /* Intel Extended Page Table */
 #define X86_FEATURE_VPID		( 8*32+ 4) /* Intel Virtual Processor ID */
+#define X86_FEATURE_SPP			( 8*32+ 5) /* Intel EPT-based Sub-Page Write Protection */

Please do not include X86_FEATURE_SPP. In general I don't like the VMX features word, but apart from that SPP is not a feature that affects all VMs in the same way as EPT or FlexPriority.

Paolo

#6 lore raw

Re: [PATCH v7 2/9] vmx: spp: Add control flags for Sub-Page Protection(SPP)

Quoted 11 lines · Paolo Bonzini

arch/x86/include/asm/cpufeatures.h line 231

Quoted 5 lines · Yang Weijiang
arch/x86/include/asm/cpufeatures.h +1 -0
@@ -228,6 +228,7 @@
 #define X86_FEATURE_FLEXPRIORITY	( 8*32+ 2) /* Intel FlexPriority */
 #define X86_FEATURE_EPT			( 8*32+ 3) /* Intel Extended Page Table */
 #define X86_FEATURE_VPID		( 8*32+ 4) /* Intel Virtual Processor ID */
+#define X86_FEATURE_SPP			( 8*32+ 5) /* Intel EPT-based Sub-Page Write Protection */

Please do not include X86_FEATURE_SPP. In general I don't like the VMX features word, but apart from that SPP is not a feature that affects all VMs in the same way as EPT or FlexPriority.

So what's a friendly way to let a user check if SPP feature is there?

Quoted 1 line

Paolo

maintainer
#7 lore raw

Re: [PATCH v7 2/9] vmx: spp: Add control flags for Sub-Page Protection(SPP)

Quoted 12 lines · Yang Weijiang
Quoted 11 lines · Paolo Bonzini

arch/x86/include/asm/cpufeatures.h line 231

Quoted 5 lines · Yang Weijiang
arch/x86/include/asm/cpufeatures.h +1 -0
@@ -228,6 +228,7 @@
 #define X86_FEATURE_FLEXPRIORITY	( 8*32+ 2) /* Intel FlexPriority */
 #define X86_FEATURE_EPT			( 8*32+ 3) /* Intel Extended Page Table */
 #define X86_FEATURE_VPID		( 8*32+ 4) /* Intel Virtual Processor ID */
+#define X86_FEATURE_SPP			( 8*32+ 5) /* Intel EPT-based Sub-Page Write Protection */

Please do not include X86_FEATURE_SPP. In general I don't like the VMX features word, but apart from that SPP is not a feature that affects all VMs in the same way as EPT or FlexPriority.

Quoted 1 line

So what's a friendly way to let a user check if SPP feature is there?

QEMU for example ships with a program called vmxcap (though it requires root). We also could write a program to analyze the KVM capabilities and print them, and put it in tools/kvm.

Thanks,

Paolo

#8 lore raw

Re: [PATCH v7 2/9] vmx: spp: Add control flags for Sub-Page Protection(SPP)

Quoted 13 lines · Paolo Bonzini
Quoted 12 lines · Yang Weijiang
Quoted 11 lines · Paolo Bonzini

arch/x86/include/asm/cpufeatures.h line 231

Quoted 5 lines · Yang Weijiang
arch/x86/include/asm/cpufeatures.h +1 -0
@@ -228,6 +228,7 @@
 #define X86_FEATURE_FLEXPRIORITY	( 8*32+ 2) /* Intel FlexPriority */
 #define X86_FEATURE_EPT			( 8*32+ 3) /* Intel Extended Page Table */
 #define X86_FEATURE_VPID		( 8*32+ 4) /* Intel Virtual Processor ID */
+#define X86_FEATURE_SPP			( 8*32+ 5) /* Intel EPT-based Sub-Page Write Protection */

Please do not include X86_FEATURE_SPP. In general I don't like the VMX features word, but apart from that SPP is not a feature that affects all VMs in the same way as EPT or FlexPriority.

Quoted 6 lines
Quoted 1 line

So what's a friendly way to let a user check if SPP feature is there?

QEMU for example ships with a program called vmxcap (though it requires root). We also could write a program to analyze the KVM capabilities and print them, and put it in tools/kvm.

OK, will update vmxcap to add SPP feature bit, thanks!

Quoted 3 lines

Thanks,

Paolo

maintainer
#9 lore raw

Re: [PATCH v7 2/9] vmx: spp: Add control flags for Sub-Page Protection(SPP)

Quoted 5 lines · Yang Weijiang
Quoted 4 lines

QEMU for example ships with a program called vmxcap (though it requires root). We also could write a program to analyze the KVM capabilities and print them, and put it in tools/kvm.

OK, will update vmxcap to add SPP feature bit, thanks!

It's already there. :)

Paolo

#10 lore raw

Re: [PATCH v7 2/9] vmx: spp: Add control flags for Sub-Page Protection(SPP)

Quoted 9 lines · Paolo Bonzini
Quoted 5 lines · Yang Weijiang
Quoted 4 lines

QEMU for example ships with a program called vmxcap (though it requires root). We also could write a program to analyze the KVM capabilities and print them, and put it in tools/kvm.

OK, will update vmxcap to add SPP feature bit, thanks!

It's already there. :)

Yeah, I saw it, thanks a lot for the kind review! :-))

Quoted 1 line

Paolo

#11 lore raw

PATCH v7 3/9 mmu: spp: Add SPP Table setup functions

In reply to Yang Weijiang

SPPT is a 4-level paging structure similar to EPT, when SPP is armed for target physical page, bit 61 of the corresponding EPT entry is flaged, then SPPT is traversed with the gfn, the leaf entry of SPPT contains the access bitmap of subpages inside the target 4KB physical page, one bit per 128-byte subpage.

---
 arch/x86/include/asm/kvm_host.h |   4 +-
 arch/x86/kvm/vmx/spp.c          | 239 ++++++++++++++++++++++++++++++++
 arch/x86/kvm/vmx/spp.h          |  10 ++
 3 files changed, 252 insertions(+), 1 deletion(-)
 create mode 100644 arch/x86/kvm/vmx/spp.c
 create mode 100644 arch/x86/kvm/vmx/spp.h
arch/x86/include/asm/kvm_host.h +3 -1
index bdc16b0aa7c6..eb18f4dd993d 100644
@@ -270,7 +270,8 @@ union kvm_mmu_page_role {
 		unsigned smap_andnot_wp:1;
 		unsigned ad_disabled:1;
 		unsigned guest_mode:1;
-		unsigned :6;
+		unsigned spp:1;
+		unsigned reserved:5;
 
 		/*
 		 * This is left at the top of the word so that
@@ -399,6 +400,7 @@ struct kvm_mmu {
 			   u64 *spte, const void *pte);
 	hpa_t root_hpa;
 	gpa_t root_cr3;
+	hpa_t sppt_root;
 	union kvm_mmu_role mmu_role;
 	u8 root_level;
 	u8 shadow_root_level;
arch/x86/kvm/vmx/spp.c new file +239 -0

239 lines not on this page · raw message

arch/x86/kvm/vmx/spp.h new file +10 -0
new file mode 100644
index 000000000000..25a23a4277eb
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __KVM_X86_VMX_SPP_H
+#define __KVM_X86_VMX_SPP_H
+
+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);
+
+#endif /* __KVM_X86_VMX_SPP_H */
Signature · 1 line
2.17.2
maintainer
#12 lore raw

Re: [PATCH v7 3/9] mmu: spp: Add SPP Table setup functions

arch/x86/kvm/vmx/spp.c line 205

Quoted 5 lines · Yang Weijiang
arch/x86/kvm/vmx/spp.c new file +5 -0
+			if (old_spte != spp_spte) {
+				spp_spte_set(iter.sptep, spp_spte);
+				//kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);
+				kvm_flush_remote_tlbs(vcpu->kvm);
+			}

Please do not leave commented code in the middle of the series.

Paolo

#13 lore raw

Re: [PATCH v7 3/9] mmu: spp: Add SPP Table setup functions

Quoted 8 lines · Paolo Bonzini

arch/x86/kvm/vmx/spp.c line 205

Quoted 5 lines · Yang Weijiang
arch/x86/kvm/vmx/spp.c new file +5 -0
+			if (old_spte != spp_spte) {
+				spp_spte_set(iter.sptep, spp_spte);
+				//kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);
+				kvm_flush_remote_tlbs(vcpu->kvm);
+			}

Please do not leave commented code in the middle of the series.

Oops, I should have removed them, thanks for pointing it out.

Quoted 1 line

Paolo

maintainer
#14 lore raw

Re: [PATCH v7 3/9] mmu: spp: Add SPP Table setup functions

In reply to Yang Weijiang

arch/x86/kvm/vmx/spp.c line 24

Quoted 5 lines · Yang Weijiang
arch/x86/kvm/vmx/spp.c new file +5 -0
+static int is_spp_shadow_present(u64 pte)
+{
+	return pte & PT_PRESENT_MASK;
+}
+

This should not be needed, is_shadow_present_pte works well for SPP PTEs as well (and in fact you're already using it here and there, so it's confusing to have both).

Paolo

#15 lore raw

Re: [PATCH v7 3/9] mmu: spp: Add SPP Table setup functions

Quoted 11 lines · Paolo Bonzini

arch/x86/kvm/vmx/spp.c line 24

Quoted 5 lines · Yang Weijiang
arch/x86/kvm/vmx/spp.c new file +5 -0
+static int is_spp_shadow_present(u64 pte)
+{
+	return pte & PT_PRESENT_MASK;
+}
+

This should not be needed, is_shadow_present_pte works well for SPP PTEs as well (and in fact you're already using it here and there, so it's confusing to have both).

OK, will remove it.

Quoted 1 line

Paolo

#16 lore raw

PATCH v7 4/9 mmu: spp: Add functions to create/destroy SPP bitmap block

In reply to Yang Weijiang

Create access bitmap for SPP subpages, the bitmap can be accessed with a gfn. The initial access bitmap for each physical page is 0xFFFFFFFF, meaning SPP is not enabled for the subpages.

---
 arch/x86/include/asm/kvm_host.h |   3 +
 arch/x86/kvm/vmx/spp.c          | 311 +++++++++++++++++++++++++++++++-
 arch/x86/kvm/vmx/spp.h          |   7 +
 include/uapi/linux/kvm.h        |   9 +
 4 files changed, 325 insertions(+), 5 deletions(-)
arch/x86/include/asm/kvm_host.h +3 -0
index eb18f4dd993d..cc38670a0c45 100644
@@ -799,6 +799,7 @@ struct kvm_lpage_info {
 
 struct kvm_arch_memory_slot {
 	struct kvm_rmap_head *rmap[KVM_NR_PAGE_SIZES];
+	u32 *subpage_wp_info;
 	struct kvm_lpage_info *lpage_info[KVM_NR_PAGE_SIZES - 1];
 	unsigned short *gfn_track[KVM_PAGE_TRACK_MAX];
 };
@@ -939,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 {
arch/x86/kvm/vmx/spp.c +306 -5

340 lines not on this page · raw message

arch/x86/kvm/vmx/spp.h +7 -0
index 25a23a4277eb..56b287ec15fd 100644
@@ -2,9 +2,16 @@
 #ifndef __KVM_X86_VMX_SPP_H
 #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);
+u32 *gfn_to_subpage_wp_info(struct kvm_memory_slot *slot, gfn_t gfn);
 
 #endif /* __KVM_X86_VMX_SPP_H */
include/uapi/linux/kvm.h +9 -0
index 5e3f12d5359e..9460830de536 100644
@@ -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
Signature · 1 line
2.17.2
maintainer
#17 lore raw

Re: [PATCH v7 4/9] mmu: spp: Add functions to create/destroy SPP bitmap block

arch/x86/kvm/vmx/spp.c bool is_spp_spte(struct kvm_mmu_page *sp) · line 198

Quoted 9 lines · Yang Weijiang
arch/x86/kvm/vmx/spp.c +8 -0
+/*
+ * 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;

Is this true? Perhaps you need one with VALID_PAGE(vcpu->arch.mmu->sppt_root) for kvm_spp_set_permission?

Also, since vcpu->arch.mmu->sppt_root is the same for all vCPUs, perhaps it should be kvm->arch.sppt_root instead?

If you can get rid of this function, it would be much better (but if you cannot, kvm_get_vcpu(kvm, 0) should give the same result).

Review

Quoted 3 lines
(quoted diff) +2 -0
+	if (npages > SUBPAGE_MAX_BITMAP)
+		return -EFAULT;

This is not needed here, the restriction only applies to the ioctl.

Paolo

#18 lore raw

Re: [PATCH v7 4/9] mmu: spp: Add functions to create/destroy SPP bitmap block

Quoted 14 lines · Paolo Bonzini

arch/x86/kvm/vmx/spp.c bool is_spp_spte(struct kvm_mmu_page *sp) · line 198

Quoted 9 lines · Yang Weijiang
arch/x86/kvm/vmx/spp.c +8 -0
+/*
+ * 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;

Is this true? Perhaps you need one with VALID_PAGE(vcpu->arch.mmu->sppt_root) for kvm_spp_set_permission?

Yes, I'd like to keep single sppt_root, thank you!

Quoted 3 lines

Also, since vcpu->arch.mmu->sppt_root is the same for all vCPUs, perhaps it should be kvm->arch.sppt_root instead?

Sure, make sense, will change it.

Quoted 3 lines

If you can get rid of this function, it would be much better (but if you cannot, kvm_get_vcpu(kvm, 0) should give the same result).

Great, I was not sure if such usage is correct. Thanks.

Quoted 6 lines

Review

Quoted 3 lines
(quoted diff) +2 -0
+	if (npages > SUBPAGE_MAX_BITMAP)
+		return -EFAULT;

This is not needed here, the restriction only applies to the ioctl.

OK.

Quoted 1 line

Paolo

#19 lore raw

PATCH v7 5/9 x86: spp: Introduce user-space SPP IOCTLs

In reply to Yang Weijiang

User application, e.g., QEMU or VMI, must initialize SPP before gets/sets SPP subpages, the dynamic initialization is to reduce the extra storage cost if the SPP feature is not not used.

---
 arch/x86/include/asm/kvm_host.h |  3 ++
 arch/x86/kvm/vmx/spp.c          | 54 +++++++++++++++++++++++++++++++++
 arch/x86/kvm/vmx/spp.h          |  5 +++
 arch/x86/kvm/vmx/vmx.c          |  8 +++++
 arch/x86/kvm/x86.c              | 49 ++++++++++++++++++++++++++++++
 include/uapi/linux/kvm.h        |  3 ++
 6 files changed, 122 insertions(+)
arch/x86/include/asm/kvm_host.h +3 -0
index cc38670a0c45..3863eb3c0e6a 100644
@@ -1216,6 +1216,9 @@ struct kvm_x86_ops {
 	uint16_t (*nested_get_evmcs_version)(struct kvm_vcpu *vcpu);
 
 	bool (*need_emulation_on_page_fault)(struct kvm_vcpu *vcpu);
+
+	int (*init_spp)(struct kvm *kvm);
+	int (*flush_subpages)(struct kvm *kvm, struct kvm_subpage *spp_info);
 };
 
 struct kvm_arch_async_pf {
arch/x86/kvm/vmx/spp.c +54 -0

57 lines not on this page · raw message

arch/x86/kvm/vmx/spp.h +5 -0
index 56b287ec15fd..ad13a35bfca2 100644
@@ -6,6 +6,11 @@
 
 bool is_spp_spte(struct kvm_mmu_page *sp);
 inline u64 construct_spptp(unsigned long root_hpa);
+int kvm_vm_ioctl_get_subpages(struct kvm *kvm,
+			      struct kvm_subpage *spp_info);
+int kvm_vm_ioctl_set_subpages(struct kvm *kvm,
+			      struct kvm_subpage *spp_info);
+int kvm_vm_ioctl_init_spp(struct kvm *kvm);
 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);
arch/x86/kvm/vmx/vmx.c +8 -0
index 0fcd02b531ef..af296a290173 100644
@@ -7596,6 +7596,11 @@ static __init int hardware_setup(void)
 		kvm_x86_ops->enable_log_dirty_pt_masked = NULL;
 	}
 
+	if (!spp_supported) {
+		kvm_x86_ops->flush_subpages = NULL;
+		kvm_x86_ops->init_spp = NULL;
+	}
+
 	if (!cpu_has_vmx_preemption_timer())
 		enable_preemption_timer = false;
 
@@ -7808,6 +7813,9 @@ static struct kvm_x86_ops vmx_x86_ops __ro_after_init = {
 	.nested_enable_evmcs = NULL,
 	.nested_get_evmcs_version = NULL,
 	.need_emulation_on_page_fault = vmx_need_emulation_on_page_fault,
+
+	.flush_subpages = vmx_spp_flush_sppt,
+	.init_spp = vmx_spp_init,
 };
 
 static void vmx_cleanup_l1d_flush(void)
arch/x86/kvm/x86.c +49 -0

61 lines not on this page · raw message

include/uapi/linux/kvm.h +3 -0
index 9460830de536..700f0825336d 100644
@@ -1257,6 +1257,9 @@ struct kvm_vfio_spapr_tce {
 					struct kvm_userspace_memory_region)
 #define KVM_SET_TSS_ADDR          _IO(KVMIO,   0x47)
 #define KVM_SET_IDENTITY_MAP_ADDR _IOW(KVMIO,  0x48, __u64)
+#define KVM_SUBPAGES_GET_ACCESS   _IOR(KVMIO,  0x49, __u64)
+#define KVM_SUBPAGES_SET_ACCESS   _IOW(KVMIO,  0x4a, __u64)
+#define KVM_INIT_SPP              _IOW(KVMIO,  0x4b, __u64)
 
 /* enable ucontrol for s390 */
 struct kvm_s390_ucas_mapping {
Signature · 1 line
2.17.2
maintainer
#20 lore raw

Re: [PATCH v7 5/9] x86: spp: Introduce user-space SPP IOCTLs

Quoted 12 lines · Yang Weijiang
arch/x86/kvm/x86.c +4 -0
+	case KVM_INIT_SPP: {
+		r = kvm_vm_ioctl_init_spp(kvm);
+		break;
+	}
 	default:
 		r = -ENOTTY;
 	}
include/uapi/linux/kvm.h +0 -0
index 9460830de536..700f0825336d 100644

include/uapi/linux/kvm.h struct kvm_vfio_spapr_tce { · line 1262

Quoted 6 lines
include/uapi/linux/kvm.h +3 -0
@@ -1257,6 +1257,9 @@ struct kvm_vfio_spapr_tce {
 					struct kvm_userspace_memory_region)
 #define KVM_SET_TSS_ADDR          _IO(KVMIO,   0x47)
 #define KVM_SET_IDENTITY_MAP_ADDR _IOW(KVMIO,  0x48, __u64)
+#define KVM_SUBPAGES_GET_ACCESS   _IOR(KVMIO,  0x49, __u64)
+#define KVM_SUBPAGES_SET_ACCESS   _IOW(KVMIO,  0x4a, __u64)
+#define KVM_INIT_SPP              _IOW(KVMIO,  0x4b, __u64)

You also need to define a capability and return a value for it in kvm_vm_ioctl_check_extension. We could return SUBPAGE_MAX_BITMAP (now KVM_SUBPAGE_MAX_PAGES). And instead of introducing KVM_INIT_SPP, you can then use KVM_ENABLE_CAP on the new capability.

Paolo

#21 lore raw

Re: [PATCH v7 5/9] x86: spp: Introduce user-space SPP IOCTLs

Quoted 16 lines · Paolo Bonzini
Quoted 15 lines · Yang Weijiang
arch/x86/kvm/x86.c +4 -0
+	case KVM_INIT_SPP: {
+		r = kvm_vm_ioctl_init_spp(kvm);
+		break;
+	}
 	default:
 		r = -ENOTTY;
 	}
include/uapi/linux/kvm.h +0 -0
index 9460830de536..700f0825336d 100644
@@ -1257,6 +1257,9 @@ struct kvm_vfio_spapr_tce {
 					struct kvm_userspace_memory_region)
 #define KVM_SET_TSS_ADDR          _IO(KVMIO,   0x47)
 #define KVM_SET_IDENTITY_MAP_ADDR _IOW(KVMIO,  0x48, __u64)
Quoted 9 lines

include/uapi/linux/kvm.h struct kvm_vfio_spapr_tce { · line 1262

Quoted 3 lines
include/uapi/linux/kvm.h +3 -0
+#define KVM_SUBPAGES_GET_ACCESS   _IOR(KVMIO,  0x49, __u64)
+#define KVM_SUBPAGES_SET_ACCESS   _IOW(KVMIO,  0x4a, __u64)
+#define KVM_INIT_SPP              _IOW(KVMIO,  0x4b, __u64)

You also need to define a capability and return a value for it in kvm_vm_ioctl_check_extension. We could return SUBPAGE_MAX_BITMAP (now KVM_SUBPAGE_MAX_PAGES). And instead of introducing KVM_INIT_SPP, you can then use KVM_ENABLE_CAP on the new capability.

Yep, will change these stuffs, thanks.

Quoted 1 line

Paolo

#22 lore raw

PATCH v7 6/9 vmx: spp: Set up SPP paging table at vmentry/vmexit

In reply to Yang Weijiang

If write to subpage is not allowed, EPT violation generates and it's handled in fast_page_fault().

In current implementation, SPPT setup is only handled in handle_spp() vmexit handler, it's triggered when SPP bit is set in EPT leaf entry while SPPT entries are not ready.

A SPP specific bit(11) is added to exit_qualification and a new exit reason(66) is introduced for SPP.

---
 arch/x86/include/asm/vmx.h      |  9 ++++
 arch/x86/include/uapi/asm/vmx.h |  2 +
 arch/x86/kvm/mmu.c              | 42 ++++++++++++++-
 arch/x86/kvm/vmx/spp.c          | 12 +++++
 arch/x86/kvm/vmx/spp.h          |  2 +
 arch/x86/kvm/vmx/vmx.c          | 94 +++++++++++++++++++++++++++++++++
 arch/x86/kvm/x86.c              |  2 +
 include/uapi/linux/kvm.h        |  5 ++
 8 files changed, 167 insertions(+), 1 deletion(-)
arch/x86/include/asm/vmx.h +9 -0
index e1137807affc..f41989eae5e2 100644
@@ -211,6 +211,8 @@ enum vmcs_field {
 	XSS_EXIT_BITMAP_HIGH            = 0x0000202D,
 	ENCLS_EXITING_BITMAP		= 0x0000202E,
 	ENCLS_EXITING_BITMAP_HIGH	= 0x0000202F,
+	SPPT_POINTER			= 0x00002030,
+	SPPT_POINTER_HIGH		= 0x00002031,
 	TSC_MULTIPLIER                  = 0x00002032,
 	TSC_MULTIPLIER_HIGH             = 0x00002033,
 	GUEST_PHYSICAL_ADDRESS          = 0x00002400,
@@ -532,6 +534,13 @@ struct vmx_msr_entry {
 #define EPT_VIOLATION_EXECUTABLE	(1 << EPT_VIOLATION_EXECUTABLE_BIT)
 #define EPT_VIOLATION_GVA_TRANSLATED	(1 << EPT_VIOLATION_GVA_TRANSLATED_BIT)
 
+/*
+ * Exit Qualifications for SPPT-Induced vmexits
+ */
+#define SPPT_INDUCED_EXIT_TYPE_BIT     11
+#define SPPT_INDUCED_EXIT_TYPE         (1 << SPPT_INDUCED_EXIT_TYPE_BIT)
+#define SPPT_INTR_INFO_UNBLOCK_NMI     INTR_INFO_UNBLOCK_NMI
+
 /*
  * VM-instruction error numbers
  */
arch/x86/include/uapi/asm/vmx.h +2 -0
index f0b0c90dd398..ac67622bac5a 100644
@@ -85,6 +85,7 @@
 #define EXIT_REASON_PML_FULL            62
 #define EXIT_REASON_XSAVES              63
 #define EXIT_REASON_XRSTORS             64
+#define EXIT_REASON_SPP                 66
 
 #define VMX_EXIT_REASONS \
 	{ EXIT_REASON_EXCEPTION_NMI,         "EXCEPTION_NMI" }, \
@@ -141,6 +142,7 @@
 	{ EXIT_REASON_ENCLS,                 "ENCLS" }, \
 	{ EXIT_REASON_RDSEED,                "RDSEED" }, \
 	{ EXIT_REASON_PML_FULL,              "PML_FULL" }, \
+	{ EXIT_REASON_SPP,                   "SPP" }, \
 	{ EXIT_REASON_XSAVES,                "XSAVES" }, \
 	{ EXIT_REASON_XRSTORS,               "XRSTORS" }
 
arch/x86/kvm/mmu.c +41 -1

99 lines not on this page · raw message

arch/x86/kvm/vmx/spp.c +12 -0
index b5327faf512e..0ff23b97970a 100644
@@ -17,6 +17,18 @@ static void shadow_spp_walk_init(struct kvm_shadow_walk_iterator *iterator,
 	iterator->level = PT64_ROOT_4LEVEL;
 }
 
+/* Restore an spp armed PTE */
+void restore_spp_bit(u64 *spte)
+{
+	*spte &= ~PT64_SPP_SAVED_BIT;
+	*spte |= PT_SPP_MASK;
+}
+
+bool was_spp_armed(u64 spte)
+{
+	return !!(spte & PT64_SPP_SAVED_BIT);
+}
+
 static int is_spp_shadow_present(u64 pte)
 {
 	return pte & PT_PRESENT_MASK;
arch/x86/kvm/vmx/spp.h +2 -0
index ad13a35bfca2..208b557cac7d 100644
@@ -5,6 +5,8 @@
 #define FULL_SPP_ACCESS		((u32)((1ULL << 32) - 1))
 
 bool is_spp_spte(struct kvm_mmu_page *sp);
+void restore_spp_bit(u64 *spte);
+bool was_spp_armed(u64 spte);
 inline u64 construct_spptp(unsigned long root_hpa);
 int kvm_vm_ioctl_get_subpages(struct kvm *kvm,
 			      struct kvm_subpage *spp_info);
arch/x86/kvm/vmx/vmx.c +94 -0

124 lines not on this page · raw message

arch/x86/kvm/x86.c +2 -0
index 78605d75690e..fa114b5db672 100644
@@ -9523,6 +9523,8 @@ void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
 	}
 
 	kvm_page_track_free_memslot(free, dont);
+	if (kvm->arch.spp_active)
+		kvm_spp_free_memslot(free, dont);
 }
 
 int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
include/uapi/linux/kvm.h +5 -0
index 700f0825336d..3816f0ac5dad 100644
@@ -244,6 +244,7 @@ struct kvm_hyperv_exit {
 #define KVM_EXIT_S390_STSI        25
 #define KVM_EXIT_IOAPIC_EOI       26
 #define KVM_EXIT_HYPERV           27
+#define KVM_EXIT_SPP              28
 
 /* For KVM_EXIT_INTERNAL_ERROR */
 /* Emulate instruction failed. */
@@ -399,6 +400,10 @@ struct kvm_run {
 		struct {
 			__u8 vector;
 		} eoi;
+		/* KVM_EXIT_SPP */
+		struct {
+			__u64 addr;
+		} spp;
 		/* KVM_EXIT_HYPERV */
 		struct kvm_hyperv_exit hyperv;
 		/* Fix the size of the union. */
Signature · 1 line
2.17.2
maintainer
#23 lore raw

Re: [PATCH v7 6/9] vmx: spp: Set up SPP paging table at vmentry/vmexit

arch/x86/kvm/mmu.c static bool fast_page_fault(struct kvm_vcpu *vcpu, gva_t gva, int level, · line 3478

Quoted 5 lines · Yang Weijiang
arch/x86/kvm/mmu.c +5 -0
+			if (spte & PT_SPP_MASK) {
+				fault_handled = true;
+				vcpu->run->exit_reason = KVM_EXIT_SPP;
+				vcpu->run->spp.addr = gva;
+				kvm_skip_emulated_instruction(vcpu);

Do you really want to skip the current instruction? Who will do the write?

arch/x86/kvm/vmx/vmx.c static int handle_monitor_trap(struct kvm_vcpu *vcpu) · line 5376

Quoted 1 line
arch/x86/kvm/vmx/vmx.c +1 -0
+		pr_info("SPP - SPPT entry missing! gfn = 0x%llx\n", gfn);

Please replace pr_info with a tracepoint.

arch/x86/kvm/vmx/vmx.c static int handle_monitor_trap(struct kvm_vcpu *vcpu) · line 5379

Quoted 3 lines
arch/x86/kvm/vmx/vmx.c +3 -0
+		slot = gfn_to_memslot(vcpu->kvm, gfn);
+		if (!slot)
+			return -EFAULT;

You want either a goto to the misconfig case, so that there is a warn

arch/x86/kvm/vmx/vmx.c static int handle_monitor_trap(struct kvm_vcpu *vcpu) · line 5391

Quoted 6 lines
arch/x86/kvm/vmx/vmx.c +6 -0
+		spp_info.base_gfn = gfn;
+		spp_info.npages = 1;
+
+		spin_lock(&vcpu->kvm->mmu_lock);
+		ret = kvm_spp_get_permission(vcpu->kvm, &spp_info);
+		if (ret == 1) {

Can you clarify when ret will not be 1? In this case you already have a slot, so it seems to me that you do not need to go through kvm_spp_get_permission and you can just test "if (kvm->arch.spp_active)". But then, spp_active should be 1 if you get here, I think?

arch/x86/kvm/vmx/vmx.c static int handle_monitor_trap(struct kvm_vcpu *vcpu) · line 5423

Quoted 2 lines
arch/x86/kvm/vmx/vmx.c +2 -0
+	pr_alert("SPP - SPPT Misconfiguration!\n");
+	return 0;

pr_alert not needed since you've just warned.

Paolo

#24 lore raw

Re: [PATCH v7 6/9] vmx: spp: Set up SPP paging table at vmentry/vmexit

Quoted 9 lines · Paolo Bonzini

arch/x86/kvm/mmu.c static bool fast_page_fault(struct kvm_vcpu *vcpu, gva_t gva, int level, · line 3478

Quoted 5 lines · Yang Weijiang
arch/x86/kvm/mmu.c +5 -0
+			if (spte & PT_SPP_MASK) {
+				fault_handled = true;
+				vcpu->run->exit_reason = KVM_EXIT_SPP;
+				vcpu->run->spp.addr = gva;
+				kvm_skip_emulated_instruction(vcpu);

Do you really want to skip the current instruction? Who will do the write?

If the destination memory is SPP protected, the target memory is expected unchanged on a "write op" in guest, so would like to skip current instruction.

Quoted 4 lines

arch/x86/kvm/vmx/vmx.c static int handle_monitor_trap(struct kvm_vcpu *vcpu) · line 5376

Quoted 1 line
arch/x86/kvm/vmx/vmx.c +1 -0
+		pr_info("SPP - SPPT entry missing! gfn = 0x%llx\n", gfn);

Please replace pr_info with a tracepoint.

OK.

Quoted 6 lines

arch/x86/kvm/vmx/vmx.c static int handle_monitor_trap(struct kvm_vcpu *vcpu) · line 5379

Quoted 3 lines
arch/x86/kvm/vmx/vmx.c +3 -0
+		slot = gfn_to_memslot(vcpu->kvm, gfn);
+		if (!slot)
+			return -EFAULT;

You want either a goto to the misconfig case, so that there is a warn

OK.

Quoted 4 lines
Quoted 4 lines
arch/x86/kvm/vmx/vmx.c +4 -0
+		spp_info.base_gfn = gfn;
+		spp_info.npages = 1;
+
+		spin_lock(&vcpu->kvm->mmu_lock);
Quoted 9 lines

arch/x86/kvm/vmx/vmx.c static int handle_monitor_trap(struct kvm_vcpu *vcpu) · line 5391

Quoted 2 lines
arch/x86/kvm/vmx/vmx.c +2 -0
+		ret = kvm_spp_get_permission(vcpu->kvm, &spp_info);
+		if (ret == 1) {

Can you clarify when ret will not be 1? In this case you already have a slot, so it seems to me that you do not need to go through kvm_spp_get_permission and you can just test "if (kvm->arch.spp_active)". But then, spp_active should be 1 if you get here, I think?

Hmm, getting permission bits from gfn directly should work here. Thank you!

Quoted 6 lines

arch/x86/kvm/vmx/vmx.c static int handle_monitor_trap(struct kvm_vcpu *vcpu) · line 5423

Quoted 2 lines
arch/x86/kvm/vmx/vmx.c +2 -0
+	pr_alert("SPP - SPPT Misconfiguration!\n");
+	return 0;

pr_alert not needed since you've just warned.

OK, will remove it.

Quoted 1 line

Paolo

maintainer
#25 lore raw

Re: [PATCH v7 6/9] vmx: spp: Set up SPP paging table at vmentry/vmexit

Quoted 10 lines · Yang Weijiang
Quoted 9 lines · Paolo Bonzini

arch/x86/kvm/mmu.c static bool fast_page_fault(struct kvm_vcpu *vcpu, gva_t gva, int level, · line 3478

Quoted 5 lines · Yang Weijiang
arch/x86/kvm/mmu.c +5 -0
+			if (spte & PT_SPP_MASK) {
+				fault_handled = true;
+				vcpu->run->exit_reason = KVM_EXIT_SPP;
+				vcpu->run->spp.addr = gva;
+				kvm_skip_emulated_instruction(vcpu);

Do you really want to skip the current instruction? Who will do the write?

Quoted 3 lines

If the destination memory is SPP protected, the target memory is expected unchanged on a "write op" in guest, so would like to skip current instruction.

This is how you are expecting SPP to be used, but another possibility is to unprotect and reenter the guest. In this case kvm_skip_emulated_instruction would be wrong (and once this decision is made, it would be very, very hard to change it).

However, you clearly need a way to skip the instruction, and for that you could store the current instruction length in vcpu->run->spp. Then userspace can adjust RIP manually if desired.

Thanks,

Paolo

#26 lore raw

Re: [PATCH v7 6/9] vmx: spp: Set up SPP paging table at vmentry/vmexit

Quoted 15 lines · Paolo Bonzini
Quoted 10 lines · Yang Weijiang
Quoted 9 lines · Paolo Bonzini

arch/x86/kvm/mmu.c static bool fast_page_fault(struct kvm_vcpu *vcpu, gva_t gva, int level, · line 3478

Quoted 5 lines · Yang Weijiang
arch/x86/kvm/mmu.c +5 -0
+			if (spte & PT_SPP_MASK) {
+				fault_handled = true;
+				vcpu->run->exit_reason = KVM_EXIT_SPP;
+				vcpu->run->spp.addr = gva;
+				kvm_skip_emulated_instruction(vcpu);

Do you really want to skip the current instruction? Who will do the write?

Quoted 3 lines

If the destination memory is SPP protected, the target memory is expected unchanged on a "write op" in guest, so would like to skip current instruction.

Quoted 9 lines

This is how you are expecting SPP to be used, but another possibility is to unprotect and reenter the guest. In this case kvm_skip_emulated_instruction would be wrong (and once this decision is made, it would be very, very hard to change it).

However, you clearly need a way to skip the instruction, and for that you could store the current instruction length in vcpu->run->spp. Then userspace can adjust RIP manually if desired.

Looks good to me, will add the length, thanks!

Quoted 3 lines

Thanks,

Paolo

maintainer
#27 lore raw

Re: [PATCH v7 6/9] vmx: spp: Set up SPP paging table at vmentry/vmexit

In reply to Yang Weijiang

arch/x86/kvm/mmu.c int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u64 error_code, · line 5440

Quoted 8 lines · Yang Weijiang
arch/x86/kvm/mmu.c +4 -0
@@ -5400,6 +5434,10 @@ int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u64 error_code,
 		r = vcpu->arch.mmu->page_fault(vcpu, cr2,
 					       lower_32_bits(error_code),
 					       false);
+
+		if (vcpu->run->exit_reason == KVM_EXIT_SPP)
+			return 0;
+

Instead of this, please add a RET_PF_USERSPACE case to the RET_PF_* enum in mmu.c.

Paolo

#28 lore raw

Re: [PATCH v7 6/9] vmx: spp: Set up SPP paging table at vmentry/vmexit

Quoted 12 lines · Paolo Bonzini

arch/x86/kvm/mmu.c int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u64 error_code, · line 5440

Quoted 8 lines · Yang Weijiang
arch/x86/kvm/mmu.c +4 -0
@@ -5400,6 +5434,10 @@ int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u64 error_code,
 		r = vcpu->arch.mmu->page_fault(vcpu, cr2,
 					       lower_32_bits(error_code),
 					       false);
+
+		if (vcpu->run->exit_reason == KVM_EXIT_SPP)
+			return 0;
+

Instead of this, please add a RET_PF_USERSPACE case to the RET_PF_* enum in mmu.c.

OK, will add it, thank you!

Quoted 2 lines

Paolo

#29 lore raw

PATCH v7 7/9 mmu: spp: Enable Lazy mode SPP protection

In reply to Yang Weijiang

To deal with SPP protected 4KB pages within hugepage(2MB,1GB etc), the hugepage entry is first zapped when set subpage permission, then in tdp_page_fault(), it checks whether the gfn should be mapped to PT_PAGE_TABLE_LEVEL or PT_DIRECTORY_LEVEL level depending on gfn inclusion of SPP protected page range.

---
 arch/x86/kvm/mmu.c     | 14 ++++++++++++++
 arch/x86/kvm/vmx/spp.c | 39 +++++++++++++++++++++++++++++++++++++++
 arch/x86/kvm/vmx/spp.h |  4 ++++
 3 files changed, 57 insertions(+)
arch/x86/kvm/mmu.c +14 -0
index a632c6b3c326..9c5be402a0b2 100644
@@ -3240,6 +3240,17 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t gpa, int write,
 			   map_writable);
 	direct_pte_prefetch(vcpu, it.sptep);
 	++vcpu->stat.pf_fixed;
+	if (level == PT_PAGE_TABLE_LEVEL) {
+		struct kvm_subpage sbp = {0};
+		int pages;
+
+		sbp.base_gfn = gfn;
+		sbp.npages = 1;
+		pages = kvm_spp_get_permission(vcpu->kvm, &sbp);
+		if (pages == 1  && sbp.access_map[0] != FULL_SPP_ACCESS)
+			kvm_spp_mark_protection(vcpu->kvm, &sbp);
+	}
+
 	return ret;
 }
 
@@ -4183,6 +4194,9 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,
 		if (level > PT_DIRECTORY_LEVEL &&
 		    !check_hugepage_cache_consistency(vcpu, gfn, level))
 			level = PT_DIRECTORY_LEVEL;
+
+		check_spp_protection(vcpu, gfn, &force_pt_level, &level);
+
 		gfn &= ~(KVM_PAGES_PER_HPAGE(level) - 1);
 	}
 
arch/x86/kvm/vmx/spp.c +39 -0
index 0ff23b97970a..111e11bb2598 100644
@@ -550,6 +550,45 @@ inline u64 construct_spptp(unsigned long root_hpa)
 }
 EXPORT_SYMBOL_GPL(construct_spptp);
 
+bool is_spp_protected(struct kvm_memory_slot *slot, gfn_t gfn, int level)
+{
+	int page_num = KVM_PAGES_PER_HPAGE(level);
+	int i;
+
+	gfn &= ~(page_num - 1);
+	for (i = 0; i < page_num; ++i) {
+		if (*gfn_to_subpage_wp_info(slot, gfn + i) != FULL_SPP_ACCESS)
+			return true;
+	}
+	return false;
+}
+
+bool check_spp_protection(struct kvm_vcpu *vcpu, gfn_t gfn,
+			  bool *force_pt_level, int *level)
+{
+	struct kvm *kvm = vcpu->kvm;
+	struct kvm_memory_slot *slot;
+	bool protected;
+	int old_level = *level;
+
+	if (!kvm->arch.spp_active)
+		return false;
+
+	slot = gfn_to_memslot(kvm, gfn);
+
+	if (!slot)
+		return false;
+	protected = is_spp_protected(slot, gfn, PT_DIRECTORY_LEVEL);
+
+	if (protected) {
+		*level = PT_PAGE_TABLE_LEVEL;
+		*force_pt_level = true;
+	} else if (is_spp_protected(slot, gfn, PT_PDPE_LEVEL))
+		*level = PT_DIRECTORY_LEVEL;
+
+	return (old_level != *level);
+}
+
 int kvm_vm_ioctl_get_subpages(struct kvm *kvm,
 			      struct kvm_subpage *spp_info)
 {
arch/x86/kvm/vmx/spp.h +4 -0
index 208b557cac7d..1ad526866977 100644
@@ -4,9 +4,13 @@
 
 #define FULL_SPP_ACCESS		((u32)((1ULL << 32) - 1))
 
+int kvm_spp_get_permission(struct kvm *kvm, struct kvm_subpage *spp_info);
+int kvm_spp_mark_protection(struct kvm *kvm, struct kvm_subpage *spp_info);
 bool is_spp_spte(struct kvm_mmu_page *sp);
 void restore_spp_bit(u64 *spte);
 bool was_spp_armed(u64 spte);
+bool check_spp_protection(struct kvm_vcpu *vcpu, gfn_t gfn,
+			  bool *force_pt_level, int *level);
 inline u64 construct_spptp(unsigned long root_hpa);
 int kvm_vm_ioctl_get_subpages(struct kvm *kvm,
 			      struct kvm_subpage *spp_info);
Signature · 1 line
2.17.2
#30 lore raw

PATCH v7 8/9 mmu: spp: Handle SPP protected pages when VM memory changes

In reply to Yang Weijiang

Host page swapping/migration may change the translation in EPT leaf entry, if the target page is SPP protected, re-enable SPP protection in MMU notifier. If SPPT shadow page is reclaimed, the level1 pages don't have rmap to clear.

---
 arch/x86/kvm/mmu.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
arch/x86/kvm/mmu.c +22 -0
index 9c5be402a0b2..7e9959a4a12b 100644
@@ -1828,6 +1828,24 @@ static int kvm_set_pte_rmapp(struct kvm *kvm, struct kvm_rmap_head *rmap_head,
 			new_spte &= ~PT_WRITABLE_MASK;
 			new_spte &= ~SPTE_HOST_WRITEABLE;
 
+			/*
+			 * if it's EPT leaf entry and the physical page is
+			 * SPP protected, then re-enable SPP protection for
+			 * the page.
+			 */
+			if (kvm->arch.spp_active &&
+			    level == PT_PAGE_TABLE_LEVEL) {
+				struct kvm_subpage spp_info = {0};
+				int i;
+
+				spp_info.base_gfn = gfn;
+				spp_info.npages = 1;
+				i = kvm_spp_get_permission(kvm, &spp_info);
+				if (i == 1 &&
+				    spp_info.access_map[0] != FULL_SPP_ACCESS)
+					new_spte |= PT_SPP_MASK;
+			}
+
 			new_spte = mark_spte_for_access_track(new_spte);
 
 			mmu_spte_clear_track_bits(sptep);
@@ -2677,6 +2695,10 @@ static bool mmu_page_zap_pte(struct kvm *kvm, struct kvm_mmu_page *sp,
 	pte = *spte;
 	if (is_shadow_present_pte(pte)) {
 		if (is_last_spte(pte, sp->role.level)) {
+			/* SPPT leaf entries don't have rmaps*/
+			if (sp->role.level == PT_PAGE_TABLE_LEVEL &&
+			    is_spp_spte(sp))
+				return true;
 			drop_spte(kvm, spte);
 			if (is_large_pte(pte))
 				--kvm->stat.lpages;
Signature · 1 line
2.17.2
maintainer
#31 lore raw

Re: [PATCH v7 8/9] mmu: spp: Handle SPP protected pages when VM memory changes

Quoted 7 lines · Yang Weijiang
arch/x86/kvm/mmu.c +7 -0
+			/*
+			 * if it's EPT leaf entry and the physical page is
+			 * SPP protected, then re-enable SPP protection for
+			 * the page.
+			 */
+			if (kvm->arch.spp_active &&
+			    level == PT_PAGE_TABLE_LEVEL) {

arch/x86/kvm/mmu.c static int kvm_set_pte_rmapp(struct kvm *kvm, struct kvm_rmap_head *rmap_head, · line 1847

Quoted 10 lines
arch/x86/kvm/mmu.c +10 -0
+				struct kvm_subpage spp_info = {0};
+				int i;
+
+				spp_info.base_gfn = gfn;
+				spp_info.npages = 1;
+				i = kvm_spp_get_permission(kvm, &spp_info);
+				if (i == 1 &&
+				    spp_info.access_map[0] != FULL_SPP_ACCESS)
+					new_spte |= PT_SPP_MASK;
+			}

This can use gfn_to_subpage_wp_info directly (or is_spp_protected if you prefer).

Paolo

#32 lore raw

Re: [PATCH v7 8/9] mmu: spp: Handle SPP protected pages when VM memory changes

Quoted 13 lines · Paolo Bonzini
Quoted 12 lines · Yang Weijiang
arch/x86/kvm/mmu.c +12 -0
+			/*
+			 * if it's EPT leaf entry and the physical page is
+			 * SPP protected, then re-enable SPP protection for
+			 * the page.
+			 */
+			if (kvm->arch.spp_active &&
+			    level == PT_PAGE_TABLE_LEVEL) {
+				struct kvm_subpage spp_info = {0};
+				int i;
+
+				spp_info.base_gfn = gfn;
+				spp_info.npages = 1;
Quoted 9 lines

arch/x86/kvm/mmu.c static int kvm_set_pte_rmapp(struct kvm *kvm, struct kvm_rmap_head *rmap_head, · line 1847

Quoted 5 lines
arch/x86/kvm/mmu.c +5 -0
+				i = kvm_spp_get_permission(kvm, &spp_info);
+				if (i == 1 &&
+				    spp_info.access_map[0] != FULL_SPP_ACCESS)
+					new_spte |= PT_SPP_MASK;
+			}

This can use gfn_to_subpage_wp_info directly (or is_spp_protected if you prefer).

Sure, will change it, thank you!

Quoted 1 line

Paolo

#33 lore raw

PATCH v7 9/9 x86: spp: Add SPP protection check in emulation.

In reply to Yang Weijiang

In instruction/mmio emulation cases, if the target write memroy is SPP protected, exit to user-space to handle it as if it's caused by SPP induced EPT violation due to guest write.

---
 arch/x86/kvm/x86.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)
arch/x86/kvm/x86.c +36 -0
index fa114b5db672..71f5a8ae76cf 100644
@@ -5525,6 +5525,36 @@ static const struct read_write_emulator_ops write_emultor = {
 	.write = true,
 };
 
+static bool is_emulator_spp_protected(struct kvm_vcpu *vcpu,
+				      gpa_t gpa,
+				      unsigned int bytes)
+{
+	gfn_t gfn, start_gfn, end_gfn;
+	struct kvm *kvm = vcpu->kvm;
+	struct kvm_memory_slot *slot;
+	u32 access;
+
+	if (!kvm->arch.spp_active)
+		return false;
+
+	start_gfn = gpa >> PAGE_SHIFT;
+	end_gfn = (gpa + bytes) >> PAGE_SHIFT;
+	for (gfn = start_gfn; gfn <= end_gfn; gfn++) {
+		slot = gfn_to_memslot(kvm, gfn);
+		if (slot) {
+			access = *gfn_to_subpage_wp_info(slot, gfn);
+			if (access != FULL_SPP_ACCESS) {
+				vcpu->run->exit_reason = KVM_EXIT_SPP;
+				vcpu->run->spp.addr = gfn;
+				kvm_skip_emulated_instruction(vcpu);
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
 static int emulator_read_write_onepage(unsigned long addr, void *val,
 				       unsigned int bytes,
 				       struct x86_exception *exception,
@@ -5555,6 +5585,9 @@ static int emulator_read_write_onepage(unsigned long addr, void *val,
 			return X86EMUL_PROPAGATE_FAULT;
 	}
 
+	if (write && is_emulator_spp_protected(vcpu, gpa, bytes))
+		return X86EMUL_UNHANDLEABLE;
+
 	if (!ret && ops->read_write_emulate(vcpu, gpa, val, bytes))
 		return X86EMUL_CONTINUE;
 
@@ -6616,6 +6649,9 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu,
 		return EMULATE_DONE;
 
 	if (r == EMULATION_FAILED) {
+		if (vcpu->run->exit_reason == KVM_EXIT_SPP)
+			return EMULATE_USER_EXIT;
+
 		if (reexecute_instruction(vcpu, cr2, write_fault_to_spt,
 					emulation_type))
 			return EMULATE_DONE;
Signature · 1 line
2.17.2
maintainer
#34 lore raw
In reply to Yang Weijiang
Quoted 22 lines · Yang Weijiang

EPT-Based Sub-Page write Protection(SPP) allows Virtual Machine Monitor(VMM) specify write-permission for guest physical memory at a sub-page(128 byte) granularity. When SPP works, HW enforces write-access check for sub-pages within a protected 4KB page.

The feature targets to provide fine-grained memory protection for usages such as memory guard and VM introspection etc.

SPP is active when the "sub-page write protection" (bit 23) is 1 in Secondary VM-Execution Controls. The feature is backed with a Sub-Page Permission Table(SPPT), and subpage permission vector is stored in the leaf entry of SPPT. The root page is referenced via a Sub-Page Permission Table Pointer (SPPTP) in VMCS.

To enable SPP for guest memory, the guest page should be first mapped to a 4KB EPT entry, then set SPP bit 61 of the corresponding entry. While HW walks EPT, it traverses SPPT with the gpa to look up the sub-page permission vector within SPPT leaf entry. If the corresponding bit is set, write to sub-page is permitted, otherwise, SPP induced EPT violation is generated.

This patch serial passed SPP function test and selftest on Ice-Lake platform.

Quoted 8 lines

Please refer to the SPP introduction document in this patch set and Intel SDM for details:

Intel SDM:
https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf

SPP selftest patch:
https://lkml.org/lkml/2019/6/18/1197

On top of the changes I sent for the individual patches, please move vmx/spp.c to mmu/spp.c, and vmx/spp.h to spp.h (I've just sent a patch to create the mmu/ directory). Also, please include the selftest in this series.

Paolo

Quoted 85 lines

Patch 1: Documentation for SPP and related API. Patch 2: Add control flags for Sub-Page Protection(SPP). Patch 3: Add SPP Table setup functions.
Patch 4: Add functions to create/destroy SPP bitmap block. Patch 5: Introduce user-space SPP IOCTLs.
Patch 6: Set up SPP paging table at vmentry/vmexit. Patch 7: Enable Lazy mode SPP protection.
Patch 8: Handle SPP protected pages when VM memory changes. Patch 9: Add SPP protection check in emulation case.

Change logs:
V6 -> V7:
1. Configured all available protected pages once SPP induced vmexit happens since there's no PRESENT bit in SPPT leaf entry.
2. Changed SPP protection check flow in tdp_page_fault().
3. Code refactor and minior fixes.

V5 -> V6:
1. Added SPP protection patch for emulation cases per Jim's review.
2. Modified documentation and added API description per Jim's review.
3. Other minior changes suggested by Jim.

V4 -> V5:
1. Enable SPP support for Hugepage(1GB/2MB) to extend application.
2. Make SPP miss vm-exit handler as the unified place to set up SPPT.
3. If SPP protected pages are access-tracked or dirty-page-tracked, store SPP flag in reserved address bit, restore it in fast_page_fault() handler.
4. Move SPP specific functions to vmx/spp.c and vmx/spp.h
5. Rebased code to kernel v5.3
6. Other change suggested by KVM community.

V3 -> V4:
1. Modified documentation to make it consistent with patches.
2. Allocated SPPT root page in init_spp() instead of vmx_set_cr3() to avoid SPPT miss error.
3. Added back co-developers and sign-offs.

V2 -> V3:
1. Rebased patches to kernel 5.1 release
2. Deferred SPPT setup to EPT fault handler if the page is not available while set_subpage() is being called.
3. Added init IOCTL to reduce extra cost if SPP is not used.
4. Refactored patch structure, cleaned up cross referenced functions.
5. Added code to deal with memory swapping/migration/shrinker cases.

V2 -> V1:
1. Rebased to 4.20-rc1
2. Move VMCS change to a separated patch.
3. Code refine and Bug fix

Yang Weijiang (9):
Documentation: Introduce EPT based Subpage Protection and related
ioctls
vmx: spp: Add control flags for Sub-Page Protection(SPP)
mmu: spp: Add SPP Table setup functions
mmu: spp: Add functions to create/destroy SPP bitmap block
x86: spp: Introduce user-space SPP IOCTLs
vmx: spp: Set up SPP paging table at vmentry/vmexit
mmu: spp: Enable Lazy mode SPP protection
mmu: spp: Handle SPP protected pages when VM memory changes
x86: spp: Add SPP protection check in emulation.

 Documentation/virt/kvm/api.txt        |  46 ++
 Documentation/virtual/kvm/spp_kvm.txt | 180 +++++++
 arch/x86/include/asm/cpufeatures.h    |   1 +
 arch/x86/include/asm/kvm_host.h       |  10 +-
 arch/x86/include/asm/vmx.h            |  10 +
 arch/x86/include/uapi/asm/vmx.h       |   2 +
 arch/x86/kernel/cpu/intel.c           |   4 +
 arch/x86/kvm/mmu.c                    |  78 ++-
 arch/x86/kvm/mmu.h                    |   2 +
 arch/x86/kvm/vmx/capabilities.h       |   5 +
 arch/x86/kvm/vmx/spp.c                | 651 ++++++++++++++++++++++++++
 arch/x86/kvm/vmx/spp.h                |  28 ++
 arch/x86/kvm/vmx/vmx.c                | 113 +++++
 arch/x86/kvm/x86.c                    |  87 ++++
 include/uapi/linux/kvm.h              |  17 +
 15 files changed, 1232 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/virtual/kvm/spp_kvm.txt
 create mode 100644 arch/x86/kvm/vmx/spp.c
 create mode 100644 arch/x86/kvm/vmx/spp.h
#35 lore raw
Quoted 32 lines · Paolo Bonzini
Quoted 22 lines · Yang Weijiang

EPT-Based Sub-Page write Protection(SPP) allows Virtual Machine Monitor(VMM) specify write-permission for guest physical memory at a sub-page(128 byte) granularity. When SPP works, HW enforces write-access check for sub-pages within a protected 4KB page.

The feature targets to provide fine-grained memory protection for usages such as memory guard and VM introspection etc.

SPP is active when the "sub-page write protection" (bit 23) is 1 in Secondary VM-Execution Controls. The feature is backed with a Sub-Page Permission Table(SPPT), and subpage permission vector is stored in the leaf entry of SPPT. The root page is referenced via a Sub-Page Permission Table Pointer (SPPTP) in VMCS.

To enable SPP for guest memory, the guest page should be first mapped to a 4KB EPT entry, then set SPP bit 61 of the corresponding entry. While HW walks EPT, it traverses SPPT with the gpa to look up the sub-page permission vector within SPPT leaf entry. If the corresponding bit is set, write to sub-page is permitted, otherwise, SPP induced EPT violation is generated.

This patch serial passed SPP function test and selftest on Ice-Lake platform.

Quoted 8 lines

Please refer to the SPP introduction document in this patch set and Intel SDM for details:

Intel SDM:
https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf

SPP selftest patch:
https://lkml.org/lkml/2019/6/18/1197

Quoted 7 lines

On top of the changes I sent for the individual patches, please move vmx/spp.c to mmu/spp.c, and vmx/spp.h to spp.h (I've just sent a patch to create the mmu/ directory). Also, please include the selftest in this series.

Paolo

Thanks Paolo! Will follow it.