target-i386: Add NPT support

This implements NPT suport for SVM by hooking into
x86_cpu_handle_mmu_fault where it reads the stage-1 page table. Whether
we need to perform this 2nd stage translation, and how, is decided
during vmrun and stored in hflags2, along with nested_cr3 and
nested_pg_mode.

As get_hphys performs a direct cpu_vmexit in case of NPT faults, we need
retaddr in that function. To avoid changing the signature of
cpu_handle_mmu_fault, this passes the value from tlb_fill to get_hphys
via the CPU state.

This was tested successfully via the Jailhouse hypervisor.

Backports commit fe441054bb3f0c75ff23335790342c0408e11c3a from qemu
This commit is contained in:
Jan Kiszka 2018-07-03 19:52:46 -04:00 committed by Lioncash
parent f5698ff9a5
commit f5f1d9f86b
No known key found for this signature in database
GPG Key ID: 4E3C3CC1031BA9C7
4 changed files with 45 additions and 3 deletions

View File

@ -192,6 +192,7 @@
#define HF2_VINTR_SHIFT 3 /* value of V_INTR_MASKING bit */
#define HF2_SMM_INSIDE_NMI_SHIFT 4 /* CPU serving SMI nested inside NMI */
#define HF2_MPX_PR_SHIFT 5 /* BNDCFGx.BNDPRESERVE */
#define HF2_NPT_SHIFT 6 /* Nested Paging enabled */
#define HF2_GIF_MASK (1 << HF2_GIF_SHIFT)
#define HF2_HIF_MASK (1 << HF2_HIF_SHIFT)
@ -199,6 +200,7 @@
#define HF2_VINTR_MASK (1 << HF2_VINTR_SHIFT)
#define HF2_SMM_INSIDE_NMI_MASK (1 << HF2_SMM_INSIDE_NMI_SHIFT)
#define HF2_MPX_PR_MASK (1 << HF2_MPX_PR_SHIFT)
#define HF2_NPT_MASK (1 << HF2_NPT_SHIFT)
#define CR0_PE_SHIFT 0
#define CR0_MP_SHIFT 1
@ -1208,12 +1210,16 @@ typedef struct CPUX86State {
uint16_t intercept_dr_read;
uint16_t intercept_dr_write;
uint32_t intercept_exceptions;
uint64_t nested_cr3;
uint32_t nested_pg_mode;
uint8_t v_tpr;
/* KVM states, automatically cleared on reset */
uint8_t nmi_injected;
uint8_t nmi_pending;
uintptr_t retaddr;
/* Fields up to this point are cleared by a CPU reset */
struct {} end_reset_fields;

View File

@ -204,13 +204,13 @@ void helper_boundl(CPUX86State *env, target_ulong a0, int v)
void tlb_fill(CPUState *cs, target_ulong addr, int size,
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
{
X86CPU *cpu = X86_CPU(cs->uc, cs);
CPUX86State *env = &cpu->env;
int ret;
env->retaddr = retaddr;
ret = x86_cpu_handle_mmu_fault(cs, addr, size, access_type, mmu_idx);
if (ret) {
X86CPU *cpu = X86_CPU(cs->uc, cs);
CPUX86State *env = &cpu->env;
raise_exception_err_ra(env, cs->exception_index, env->error_code, retaddr);
}
}

View File

@ -130,6 +130,20 @@
#define SVM_CR0_SELECTIVE_MASK (1 << 3 | 1) /* TS and MP */
#define SVM_NPT_ENABLED (1 << 0)
#define SVM_NPT_PAE (1 << 0)
#define SVM_NPT_LMA (1 << 1)
#define SVM_NPT_NXE (1 << 2)
#define SVM_NPTEXIT_P (1ULL << 0)
#define SVM_NPTEXIT_RW (1ULL << 1)
#define SVM_NPTEXIT_US (1ULL << 2)
#define SVM_NPTEXIT_RSVD (1ULL << 3)
#define SVM_NPTEXIT_ID (1ULL << 4)
#define SVM_NPTEXIT_GPA (1ULL << 32)
#define SVM_NPTEXIT_GPT (1ULL << 33)
QEMU_PACK( struct vmcb_control_area {
uint16_t intercept_cr_read;
uint16_t intercept_cr_write;

View File

@ -125,6 +125,7 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend)
{
CPUState *cs = CPU(x86_env_get_cpu(env));
target_ulong addr;
uint64_t nested_ctl;
uint32_t event_inj;
uint32_t int_ctl;
@ -207,6 +208,26 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend)
control.intercept_exceptions
));
nested_ctl = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
control.nested_ctl));
if (nested_ctl & SVM_NPT_ENABLED) {
env->nested_cr3 = x86_ldq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb,
control.nested_cr3));
env->hflags2 |= HF2_NPT_MASK;
env->nested_pg_mode = 0;
if (env->cr[4] & CR4_PAE_MASK) {
env->nested_pg_mode |= SVM_NPT_PAE;
}
if (env->hflags & HF_LMA_MASK) {
env->nested_pg_mode |= SVM_NPT_LMA;
}
if (env->efer & MSR_EFER_NXE) {
env->nested_pg_mode |= SVM_NPT_NXE;
}
}
/* enable intercepts */
env->hflags |= HF_SVMI_MASK;
@ -605,6 +626,7 @@ void do_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1)
x86_stl_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, control.int_state), 0);
}
env->hflags2 &= ~HF2_NPT_MASK;
/* Save the VM state in the vmcb */
svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.es),