git: 24100ad5de39 - stable/13 - bhyve: add support for MTRR

From: Emmanuel Vadot <manu_at_FreeBSD.org>
Date: Fri, 28 Jan 2022 10:34:34 UTC
The branch stable/13 has been updated by manu:

URL: https://cgit.FreeBSD.org/src/commit/?id=24100ad5de3933fc95726f33d9b6df964b779f53

commit 24100ad5de3933fc95726f33d9b6df964b779f53
Author:     Corvin Köhne <CorvinK@beckhoff.com>
AuthorDate: 2022-01-14 09:58:48 +0000
Commit:     Emmanuel Vadot <manu@FreeBSD.org>
CommitDate: 2022-01-28 07:10:56 +0000

    bhyve: add support for MTRR
    
    Some guests or driver might depend on MTRR to work properly. E.g. the
    nvidia gpu driver won't work without MTRR.
    
    Reviewed by:    markj
    MFC after:      2 weeks
    Sponsored by:   Beckhoff Automation GmbH & Co. KG
    Differential Revision:  https://reviews.freebsd.org/D33333
    
    (cherry picked from commit 6171e026be11824495cebe8baf559af673a8e533)
---
 sys/amd64/vmm/amd/svm_msr.c   | 16 ++++++---
 sys/amd64/vmm/amd/svm_softc.h |  3 ++
 sys/amd64/vmm/intel/vmx.h     |  2 ++
 sys/amd64/vmm/intel/vmx_msr.c | 17 +++++----
 sys/amd64/vmm/x86.c           | 82 +++++++++++++++++++++++++++++++++++++++++++
 sys/amd64/vmm/x86.h           | 20 +++++++++++
 6 files changed, 130 insertions(+), 10 deletions(-)

diff --git a/sys/amd64/vmm/amd/svm_msr.c b/sys/amd64/vmm/amd/svm_msr.c
index 12046de4dbb9..1a22f16cf48e 100644
--- a/sys/amd64/vmm/amd/svm_msr.c
+++ b/sys/amd64/vmm/amd/svm_msr.c
@@ -120,9 +120,14 @@ svm_rdmsr(struct svm_softc *sc, int vcpu, u_int num, uint64_t *result,
 		break;
 	case MSR_MTRRcap:
 	case MSR_MTRRdefType:
-	case MSR_MTRR4kBase ... MSR_MTRR4kBase + 8:
+	case MSR_MTRR4kBase ... MSR_MTRR4kBase + 7:
 	case MSR_MTRR16kBase ... MSR_MTRR16kBase + 1:
 	case MSR_MTRR64kBase:
+	case MSR_MTRRVarBase ... MSR_MTRRVarBase + (VMM_MTRR_VAR_MAX * 2) - 1:
+		if (vm_rdmtrr(&sc->mtrr[vcpu], num, result) != 0) {
+			vm_inject_gp(sc->vm, vcpu);
+		}
+		break;
 	case MSR_SYSCFG:
 	case MSR_AMDK8_IPM:
 	case MSR_EXTFEATURES:
@@ -146,12 +151,15 @@ svm_wrmsr(struct svm_softc *sc, int vcpu, u_int num, uint64_t val, bool *retu)
 	case MSR_MCG_STATUS:
 		break;		/* ignore writes */
 	case MSR_MTRRcap:
-		vm_inject_gp(sc->vm, vcpu);
-		break;
 	case MSR_MTRRdefType:
-	case MSR_MTRR4kBase ... MSR_MTRR4kBase + 8:
+	case MSR_MTRR4kBase ... MSR_MTRR4kBase + 7:
 	case MSR_MTRR16kBase ... MSR_MTRR16kBase + 1:
 	case MSR_MTRR64kBase:
+	case MSR_MTRRVarBase ... MSR_MTRRVarBase + (VMM_MTRR_VAR_MAX * 2) - 1:
+		if (vm_wrmtrr(&sc->mtrr[vcpu], num, val) != 0) {
+			vm_inject_gp(sc->vm, vcpu);
+		}
+		break;
 	case MSR_SYSCFG:
 		break;		/* Ignore writes */
 	case MSR_AMDK8_IPM:
diff --git a/sys/amd64/vmm/amd/svm_softc.h b/sys/amd64/vmm/amd/svm_softc.h
index 8735353bb472..5f6a267617d2 100644
--- a/sys/amd64/vmm/amd/svm_softc.h
+++ b/sys/amd64/vmm/amd/svm_softc.h
@@ -31,6 +31,8 @@
 #ifndef _SVM_SOFTC_H_
 #define _SVM_SOFTC_H_
 
+#include "x86.h"
+
 #define SVM_IO_BITMAP_SIZE	(3 * PAGE_SIZE)
 #define SVM_MSR_BITMAP_SIZE	(2 * PAGE_SIZE)
 
@@ -64,6 +66,7 @@ struct svm_softc {
 	uint8_t		*iopm_bitmap;    /* shared by all vcpus */
 	uint8_t		*msr_bitmap;    /* shared by all vcpus */
 	struct vm	*vm;
+	struct vm_mtrr  mtrr[VM_MAXCPU];
 };
 
 CTASSERT((offsetof(struct svm_softc, nptp) & PAGE_MASK) == 0);
diff --git a/sys/amd64/vmm/intel/vmx.h b/sys/amd64/vmm/intel/vmx.h
index 57499a3a6869..81e508e30d3d 100644
--- a/sys/amd64/vmm/intel/vmx.h
+++ b/sys/amd64/vmm/intel/vmx.h
@@ -32,6 +32,7 @@
 #define	_VMX_H_
 
 #include "vmcs.h"
+#include "x86.h"
 
 struct pmap;
 
@@ -134,6 +135,7 @@ struct vmx {
 	uint64_t	eptp;
 	struct vm	*vm;
 	long		eptgen[MAXCPU];		/* cached pmap->pm_eptgen */
+	struct vm_mtrr  mtrr[VM_MAXCPU];
 };
 CTASSERT((offsetof(struct vmx, vmcs) & PAGE_MASK) == 0);
 CTASSERT((offsetof(struct vmx, msr_bitmap) & PAGE_MASK) == 0);
diff --git a/sys/amd64/vmm/intel/vmx_msr.c b/sys/amd64/vmm/intel/vmx_msr.c
index db9e659bab5b..a135518cb1c3 100644
--- a/sys/amd64/vmm/intel/vmx_msr.c
+++ b/sys/amd64/vmm/intel/vmx_msr.c
@@ -425,10 +425,13 @@ vmx_rdmsr(struct vmx *vmx, int vcpuid, u_int num, uint64_t *val, bool *retu)
 		break;
 	case MSR_MTRRcap:
 	case MSR_MTRRdefType:
-	case MSR_MTRR4kBase ... MSR_MTRR4kBase + 8:
+	case MSR_MTRR4kBase ... MSR_MTRR4kBase + 7:
 	case MSR_MTRR16kBase ... MSR_MTRR16kBase + 1:
 	case MSR_MTRR64kBase:
-		*val = 0;
+	case MSR_MTRRVarBase ... MSR_MTRRVarBase + (VMM_MTRR_VAR_MAX * 2) - 1:
+		if (vm_rdmtrr(&vmx->mtrr[vcpuid], num, val) != 0) {
+			vm_inject_gp(vmx->vm, vcpuid);
+		}
 		break;
 	case MSR_IA32_MISC_ENABLE:
 		*val = misc_enable;
@@ -465,13 +468,15 @@ vmx_wrmsr(struct vmx *vmx, int vcpuid, u_int num, uint64_t val, bool *retu)
 	case MSR_MCG_STATUS:
 		break;		/* ignore writes */
 	case MSR_MTRRcap:
-		vm_inject_gp(vmx->vm, vcpuid);
-		break;
 	case MSR_MTRRdefType:
-	case MSR_MTRR4kBase ... MSR_MTRR4kBase + 8:
+	case MSR_MTRR4kBase ... MSR_MTRR4kBase + 7:
 	case MSR_MTRR16kBase ... MSR_MTRR16kBase + 1:
 	case MSR_MTRR64kBase:
-		break;		/* Ignore writes */
+	case MSR_MTRRVarBase ... MSR_MTRRVarBase + (VMM_MTRR_VAR_MAX * 2) - 1:
+		if (vm_wrmtrr(&vmx->mtrr[vcpuid], num, val) != 0) {
+			vm_inject_gp(vmx->vm, vcpuid);
+		}
+		break;
 	case MSR_IA32_MISC_ENABLE:
 		changed = val ^ misc_enable;
 		/*
diff --git a/sys/amd64/vmm/x86.c b/sys/amd64/vmm/x86.c
index c43a3c870211..c97cb91af4f6 100644
--- a/sys/amd64/vmm/x86.c
+++ b/sys/amd64/vmm/x86.c
@@ -648,3 +648,85 @@ vm_cpuid_capability(struct vm *vm, int vcpuid, enum vm_cpuid_capability cap)
 	}
 	return (rv);
 }
+
+int
+vm_rdmtrr(struct vm_mtrr *mtrr, u_int num, uint64_t *val)
+{
+	switch (num) {
+	case MSR_MTRRcap:
+		*val = MTRR_CAP_WC | MTRR_CAP_FIXED | VMM_MTRR_VAR_MAX;
+		break;
+	case MSR_MTRRdefType:
+		*val = mtrr->def_type;
+		break;
+	case MSR_MTRR4kBase ... MSR_MTRR4kBase + 7:
+		*val = mtrr->fixed4k[num - MSR_MTRR4kBase];
+		break;
+	case MSR_MTRR16kBase ... MSR_MTRR16kBase + 1:
+		*val = mtrr->fixed16k[num - MSR_MTRR16kBase];
+		break;
+	case MSR_MTRR64kBase:
+		*val = mtrr->fixed64k;
+		break;
+	case MSR_MTRRVarBase ... MSR_MTRRVarBase + (VMM_MTRR_VAR_MAX * 2) - 1: {
+		u_int offset = num - MSR_MTRRVarBase;
+		if (offset % 2 == 0) {
+			*val = mtrr->var[offset / 2].base;
+		} else {
+			*val = mtrr->var[offset / 2].mask;
+		}
+		break;
+	}
+	default:
+		return (-1);
+	}
+
+	return (0);
+}
+
+int
+vm_wrmtrr(struct vm_mtrr *mtrr, u_int num, uint64_t val)
+{
+	switch (num) {
+	case MSR_MTRRcap:
+		/* MTRRCAP is read only */
+		return (-1);
+	case MSR_MTRRdefType:
+		if (val & ~VMM_MTRR_DEF_MASK) {
+			/* generate #GP on writes to reserved fields */
+			return (-1);
+		}
+		mtrr->def_type = val;
+		break;
+	case MSR_MTRR4kBase ... MSR_MTRR4kBase + 7:
+		mtrr->fixed4k[num - MSR_MTRR4kBase] = val;
+		break;
+	case MSR_MTRR16kBase ... MSR_MTRR16kBase + 1:
+		mtrr->fixed16k[num - MSR_MTRR16kBase] = val;
+		break;
+	case MSR_MTRR64kBase:
+		mtrr->fixed64k = val;
+		break;
+	case MSR_MTRRVarBase ... MSR_MTRRVarBase + (VMM_MTRR_VAR_MAX * 2) - 1: {
+		u_int offset = num - MSR_MTRRVarBase;
+		if (offset % 2 == 0) {
+			if (val & ~VMM_MTRR_PHYSBASE_MASK) {
+				/* generate #GP on writes to reserved fields */
+				return (-1);
+			}
+			mtrr->var[offset / 2].base = val;
+		} else {
+			if (val & ~VMM_MTRR_PHYSMASK_MASK) {
+				/* generate #GP on writes to reserved fields */
+				return (-1);
+			}
+			mtrr->var[offset / 2].mask = val;
+		}
+		break;
+	}
+	default:
+		return (-1);
+	}
+
+	return (0);
+}
diff --git a/sys/amd64/vmm/x86.h b/sys/amd64/vmm/x86.h
index 7c8fccf78f28..318f0b5cf871 100644
--- a/sys/amd64/vmm/x86.h
+++ b/sys/amd64/vmm/x86.h
@@ -80,4 +80,24 @@ enum vm_cpuid_capability {
  * and 'false' otherwise.
  */
 bool vm_cpuid_capability(struct vm *vm, int vcpuid, enum vm_cpuid_capability);
+
+#define VMM_MTRR_VAR_MAX 10
+#define VMM_MTRR_DEF_MASK \
+	(MTRR_DEF_ENABLE | MTRR_DEF_FIXED_ENABLE | MTRR_DEF_TYPE)
+#define VMM_MTRR_PHYSBASE_MASK (MTRR_PHYSBASE_PHYSBASE | MTRR_PHYSBASE_TYPE)
+#define VMM_MTRR_PHYSMASK_MASK (MTRR_PHYSMASK_PHYSMASK | MTRR_PHYSMASK_VALID)
+struct vm_mtrr {
+	uint64_t def_type;
+	uint64_t fixed4k[8];
+	uint64_t fixed16k[2];
+	uint64_t fixed64k;
+	struct {
+		uint64_t base;
+		uint64_t mask;
+	} var[VMM_MTRR_VAR_MAX];
+};
+
+int vm_rdmtrr(struct vm_mtrr *mtrr, u_int num, uint64_t *val);
+int vm_wrmtrr(struct vm_mtrr *mtrr, u_int num, uint64_t val);
+
 #endif