GMET (Guest Mode Execution Trap) has two effects: it disables
the U bit check when the guest is in user mode, and enables
the SMEP check when the guest is in kernel mode.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/cpu.h | 2 ++
target/i386/svm.h | 1 +
target/i386/cpu.c | 2 +-
target/i386/tcg/system/excp_helper.c | 20 ++++++++++++++++----
target/i386/tcg/system/svm_helper.c | 4 ++++
5 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 0b539155c40..82b7d1a52e4 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -354,6 +354,7 @@ typedef enum X86Seg {
#define PG_MODE_NXE (1 << 2)
#define PG_MODE_PSE (1 << 3)
#define PG_MODE_LA57 (1 << 4)
+#define PG_MODE_SVM_GMET (1 << 5)
#define PG_MODE_SVM_MASK MAKE_64BIT_MASK(0, 15)
/* Bits of CR4 that do not affect the NPT page format. */
@@ -879,6 +880,7 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w);
#define CPUID_SVM_AVIC (1U << 13)
#define CPUID_SVM_V_VMSAVE_VMLOAD (1U << 15)
#define CPUID_SVM_VGIF (1U << 16)
+#define CPUID_SVM_GMET (1U << 17)
#define CPUID_SVM_VNMI (1U << 25)
#define CPUID_SVM_SVME_ADDR_CHK (1U << 28)
diff --git a/target/i386/svm.h b/target/i386/svm.h
index 1bd78447306..e05a66ecc43 100644
--- a/target/i386/svm.h
+++ b/target/i386/svm.h
@@ -140,6 +140,7 @@
#define SVM_CR0_SELECTIVE_MASK (1 << 3 | 1) /* TS and MP */
#define SVM_NPT_ENABLED (1 << 0)
+#define SVM_GMET_ENABLED (1 << 3)
#define SVM_NPTEXIT_GPA (1ULL << 32)
#define SVM_NPTEXIT_GPT (1ULL << 33)
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index d02f6f0653e..ec80649658c 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -960,7 +960,7 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1,
#else
#define CPUID_SVM_KERNEL_FEATURES 0
#endif
-#define TCG_SVM_FEATURES (CPUID_SVM_NPT | CPUID_SVM_VGIF | \
+#define TCG_SVM_FEATURES (CPUID_SVM_NPT | CPUID_SVM_VGIF | CPUID_SVM_GMET | \
CPUID_SVM_SVME_ADDR_CHK | CPUID_SVM_KERNEL_FEATURES)
#define TCG_KVM_FEATURES 0
diff --git a/target/i386/tcg/system/excp_helper.c b/target/i386/tcg/system/excp_helper.c
index d7ea77c8558..bd99e8416f5 100644
--- a/target/i386/tcg/system/excp_helper.c
+++ b/target/i386/tcg/system/excp_helper.c
@@ -371,7 +371,9 @@ do_check_protect_pse36:
ptep ^= PG_NX_MASK;
/* can the page can be put in the TLB? prot will tell us */
- if (is_user && !(ptep & PG_USER_MASK)) {
+
+ /* GMET disables checks to the U bit other than the SMEP check below. */
+ if (is_user && !(ptep & PG_USER_MASK) && !(pg_mode & PG_MODE_SVM_GMET)) {
goto do_fault_protect;
}
@@ -384,7 +386,7 @@ do_check_protect_pse36:
}
if (!(ptep & PG_NX_MASK) &&
(is_user ||
- !((pg_mode & PG_MODE_SMEP) && (ptep & PG_USER_MASK)))) {
+ !((pg_mode & (PG_MODE_SMEP | PG_MODE_SVM_GMET)) && (ptep & PG_USER_MASK)))) {
prot |= PAGE_EXEC;
}
@@ -543,6 +545,17 @@ static G_NORETURN void raise_stage2(CPUX86State *env, TranslateFault *err,
cpu_vmexit(env, SVM_EXIT_NPF, exit_info_1, retaddr);
}
+static int cpu_mmu_index_svm(CPUX86State *env)
+{
+ unsigned pl = env->hflags & HF_CPL_MASK;
+ int mmu_index_32 = (env->nested_pg_mode & PG_MODE_LMA) ? 0 : 1;
+ int mmu_index_base =
+ pl < 3 && (env->nested_pg_mode & PG_MODE_SVM_GMET)
+ ? MMU_KNOSMAP64_IDX : MMU_USER64_IDX;
+
+ return mmu_index_base + mmu_index_32;
+}
+
static bool get_physical_address(CPUX86State *env, vaddr addr,
MMUAccessType access_type, int mmu_idx,
TranslateResult *out, TranslateFault *err,
@@ -562,8 +575,7 @@ static bool get_physical_address(CPUX86State *env, vaddr addr,
if (likely(use_stage2)) {
in.cr3 = env->nested_cr3;
in.pg_mode = env->nested_pg_mode;
- in.mmu_idx =
- env->nested_pg_mode & PG_MODE_LMA ? MMU_USER64_IDX : MMU_USER32_IDX;
+ in.mmu_idx = cpu_mmu_index_svm(env);
in.ptw_idx = MMU_PHYS_IDX;
if (!mmu_translate(env, &in, out, err, ra)) {
diff --git a/target/i386/tcg/system/svm_helper.c b/target/i386/tcg/system/svm_helper.c
index d5ffabc2f4d..14e46f00bb6 100644
--- a/target/i386/tcg/system/svm_helper.c
+++ b/target/i386/tcg/system/svm_helper.c
@@ -296,6 +296,10 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend)
env->hflags2 |= HF2_NPT_MASK;
env->nested_pg_mode = get_pg_mode(env) & PG_MODE_SVM_MASK;
+ if ((nested_ctl & SVM_GMET_ENABLED) &&
+ (env->features[FEAT_SVM] & CPUID_SVM_GMET)) {
+ env->nested_pg_mode |= PG_MODE_SVM_GMET;
+ }
tlb_flush_by_mmuidx(cs, 1 << MMU_NESTED_IDX);
}
--
2.53.0