--- v4
+++ v5
@@ -1,198 +1,40 @@
-SEV-SNP VMs can ask the hypervisor to change the page state in the RMP
-table to be private or shared using the Page State Change MSR protocol
-as defined in the GHCB specification.
+The #NPT error code is a 64-bit value but the trace prints only the
+lower 32-bits. Some of the fault error code (e.g PFERR_GUEST_FINAL_MASK)
+are available in the upper 32-bits.
-Before changing the page state in the RMP entry, we lookup the page in
-the TDP to make sure that there is a valid mapping for it. If the mapping
-exist then try to find a workable page level between the TDP and RMP for
-the page. If the page is not mapped in the TDP, then create a fault such
-that it gets mapped before we change the page state in the RMP entry.
-
+Cc: <stable@kernel.org>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
- arch/x86/include/asm/sev-common.h | 3 +
- arch/x86/kvm/svm/sev.c | 141 ++++++++++++++++++++++++++++++
- 2 files changed, 144 insertions(+)
+ arch/x86/kvm/trace.h | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
-diff --git a/arch/x86/include/asm/sev-common.h b/arch/x86/include/asm/sev-common.h
-index 6990d5a9d73c..2561413cb316 100644
---- a/arch/x86/include/asm/sev-common.h
-+++ b/arch/x86/include/asm/sev-common.h
-@@ -81,6 +81,9 @@
+diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h
+index b484141ea15b..1c360e07856f 100644
+--- a/arch/x86/kvm/trace.h
++++ b/arch/x86/kvm/trace.h
+@@ -365,12 +365,12 @@ TRACE_EVENT(kvm_inj_exception,
+ * Tracepoint for page fault.
+ */
+ TRACE_EVENT(kvm_page_fault,
+- TP_PROTO(unsigned long fault_address, unsigned int error_code),
++ TP_PROTO(unsigned long fault_address, u64 error_code),
+ TP_ARGS(fault_address, error_code),
- #define GHCB_MSR_PSC_RESP 0x015
- #define GHCB_MSR_PSC_ERROR_POS 32
-+#define GHCB_MSR_PSC_ERROR_MASK GENMASK_ULL(31, 0)
-+#define GHCB_MSR_PSC_RSVD_POS 12
-+#define GHCB_MSR_PSC_RSVD_MASK GENMASK_ULL(19, 0)
- #define GHCB_MSR_PSC_RESP_VAL(val) ((val) >> GHCB_MSR_PSC_ERROR_POS)
+ TP_STRUCT__entry(
+ __field( unsigned long, fault_address )
+- __field( unsigned int, error_code )
++ __field( u64, error_code )
+ ),
- /* GHCB Hypervisor Feature Request */
-diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
-index 3af5d1ad41bf..68d275b2a660 100644
---- a/arch/x86/kvm/svm/sev.c
-+++ b/arch/x86/kvm/svm/sev.c
-@@ -28,6 +28,7 @@
- #include "svm_ops.h"
- #include "cpuid.h"
- #include "trace.h"
-+#include "mmu.h"
+ TP_fast_assign(
+@@ -378,7 +378,7 @@ TRACE_EVENT(kvm_page_fault,
+ __entry->error_code = error_code;
+ ),
- #define __ex(x) __kvm_handle_fault_on_reboot(x)
-
-@@ -2843,6 +2844,127 @@ static void set_ghcb_msr(struct vcpu_svm *svm, u64 value)
- svm->vmcb->control.ghcb_gpa = value;
- }
-
-+static int snp_rmptable_psmash(struct kvm_vcpu *vcpu, kvm_pfn_t pfn)
-+{
-+ pfn = pfn & ~(KVM_PAGES_PER_HPAGE(PG_LEVEL_2M) - 1);
-+
-+ return psmash(pfn_to_page(pfn));
-+}
-+
-+static int snp_make_page_shared(struct kvm_vcpu *vcpu, gpa_t gpa, kvm_pfn_t pfn, int level)
-+{
-+ struct rmpupdate val;
-+ int rc, rmp_level;
-+ struct rmpentry *e;
-+
-+ e = snp_lookup_page_in_rmptable(pfn_to_page(pfn), &rmp_level);
-+ if (!e)
-+ return -EINVAL;
-+
-+ if (!rmpentry_assigned(e))
-+ return 0;
-+
-+ /* Log if the entry is validated */
-+ if (rmpentry_validated(e))
-+ pr_warn_ratelimited("Remove RMP entry for a validated gpa 0x%llx\n", gpa);
-+
-+ /*
-+ * Is the page part of an existing 2M RMP entry ? Split the 2MB into multiple
-+ * of 4K-page before making the memory shared.
-+ */
-+ if ((level == PG_LEVEL_4K) && (rmp_level == PG_LEVEL_2M)) {
-+ rc = snp_rmptable_psmash(vcpu, pfn);
-+ if (rc)
-+ return rc;
-+ }
-+
-+ memset(&val, 0, sizeof(val));
-+ val.pagesize = X86_TO_RMP_PG_LEVEL(level);
-+ return rmpupdate(pfn_to_page(pfn), &val);
-+}
-+
-+static int snp_make_page_private(struct kvm_vcpu *vcpu, gpa_t gpa, kvm_pfn_t pfn, int level)
-+{
-+ struct kvm_sev_info *sev = &to_kvm_svm(vcpu->kvm)->sev_info;
-+ struct rmpupdate val;
-+ struct rmpentry *e;
-+ int rmp_level;
-+
-+ e = snp_lookup_page_in_rmptable(pfn_to_page(pfn), &rmp_level);
-+ if (!e)
-+ return -EINVAL;
-+
-+ /* Log if the entry is validated */
-+ if (rmpentry_validated(e))
-+ pr_warn_ratelimited("Asked to make a pre-validated gpa %llx private\n", gpa);
-+
-+ memset(&val, 0, sizeof(val));
-+ val.gpa = gpa;
-+ val.asid = sev->asid;
-+ val.pagesize = X86_TO_RMP_PG_LEVEL(level);
-+ val.assigned = true;
-+
-+ return rmpupdate(pfn_to_page(pfn), &val);
-+}
-+
-+static int __snp_handle_psc(struct kvm_vcpu *vcpu, int op, gpa_t gpa, int level)
-+{
-+ struct kvm *kvm = vcpu->kvm;
-+ int rc, tdp_level;
-+ kvm_pfn_t pfn;
-+ gpa_t gpa_end;
-+
-+ gpa_end = gpa + page_level_size(level);
-+
-+ while (gpa < gpa_end) {
-+ /*
-+ * Get the pfn and level for the gpa from the nested page table.
-+ *
-+ * If the TDP walk failed, then its safe to say that we don't have a valid
-+ * mapping for the gpa in the nested page table. Create a fault to map the
-+ * page is nested page table.
-+ */
-+ if (!kvm_mmu_get_tdp_walk(vcpu, gpa, &pfn, &tdp_level)) {
-+ pfn = kvm_mmu_map_tdp_page(vcpu, gpa, PFERR_USER_MASK, level);
-+ if (is_error_noslot_pfn(pfn))
-+ goto out;
-+
-+ if (!kvm_mmu_get_tdp_walk(vcpu, gpa, &pfn, &tdp_level))
-+ goto out;
-+ }
-+
-+ /* Adjust the level so that we don't go higher than the backing page level */
-+ level = min_t(size_t, level, tdp_level);
-+
-+ write_lock(&kvm->mmu_lock);
-+
-+ switch (op) {
-+ case SNP_PAGE_STATE_SHARED:
-+ rc = snp_make_page_shared(vcpu, gpa, pfn, level);
-+ break;
-+ case SNP_PAGE_STATE_PRIVATE:
-+ rc = snp_make_page_private(vcpu, gpa, pfn, level);
-+ break;
-+ default:
-+ rc = -EINVAL;
-+ break;
-+ }
-+
-+ write_unlock(&kvm->mmu_lock);
-+
-+ if (rc) {
-+ pr_err_ratelimited("Error op %d gpa %llx pfn %llx level %d rc %d\n",
-+ op, gpa, pfn, level, rc);
-+ goto out;
-+ }
-+
-+ gpa = gpa + page_level_size(level);
-+ }
-+
-+out:
-+ return rc;
-+}
-+
- static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)
- {
- struct vmcb_control_area *control = &svm->vmcb->control;
-@@ -2941,6 +3063,25 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)
- GHCB_MSR_INFO_POS);
- break;
- }
-+ case GHCB_MSR_PSC_REQ: {
-+ gfn_t gfn;
-+ int ret;
-+ u8 op;
-+
-+ gfn = get_ghcb_msr_bits(svm, GHCB_MSR_PSC_GFN_MASK, GHCB_MSR_PSC_GFN_POS);
-+ op = get_ghcb_msr_bits(svm, GHCB_MSR_PSC_OP_MASK, GHCB_MSR_PSC_OP_POS);
-+
-+ ret = __snp_handle_psc(vcpu, op, gfn_to_gpa(gfn), PG_LEVEL_4K);
-+
-+ /* If failed to change the state then spec requires to return all F's */
-+ if (ret)
-+ ret = -1;
-+
-+ set_ghcb_msr_bits(svm, ret, GHCB_MSR_PSC_ERROR_MASK, GHCB_MSR_PSC_ERROR_POS);
-+ set_ghcb_msr_bits(svm, 0, GHCB_MSR_PSC_RSVD_MASK, GHCB_MSR_PSC_RSVD_POS);
-+ set_ghcb_msr_bits(svm, GHCB_MSR_PSC_RESP, GHCB_MSR_INFO_MASK, GHCB_MSR_INFO_POS);
-+ break;
-+ }
- case GHCB_MSR_TERM_REQ: {
- u64 reason_set, reason_code;
+- TP_printk("address %lx error_code %x",
++ TP_printk("address %lx error_code %llx",
+ __entry->fault_address, __entry->error_code)
+ );
--
2.17.1