target-i386: Enhance SVM support
v2 cover target-i386: Enhance SVM support
These patches allow to run Jailhouse in emulated x86-64 mode under QEMU. AMD IOMMU only works with one additional hack, but that's a different story, and we can test these changes without it.
Change in v2:
- build fix for 32-bit hosts
- replaces NPT exitinfo magics with symbolic constants
Jan
Jan Kiszka (4):
target-i386: Add NMI interception to SVM
target-i386: Allow interrupt injection after STGI
target-i386: Mark cpu_vmexit noreturn
target-i386: Add NPT support
target/i386/cpu.c | 2 +- target/i386/cpu.h | 10 ++- target/i386/excp_helper.c | 216 +++++++++++++++++++++++++++++++++++++++++++++- target/i386/mem_helper.c | 6 +- target/i386/seg_helper.c | 1 + target/i386/svm.h | 14 +++ target/i386/svm_helper.c | 23 +++++ target/i386/translate.c | 3 +- 8 files changed, 266 insertions(+), 9 deletions(-)
Signature · 1 line
2.13.6
PATCH v2 1/4 target-i386: Add NMI interception to SVM
From: Jan Kiszka <[email protected]>
Check for SVM interception prior to injecting an NMI. Tested via the Jailhouse hypervisor.
--- target/i386/seg_helper.c | 1 + 1 file changed, 1 insertion(+)
target/i386/seg_helper.c +1 -0
@@ -1337,6 +1337,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) ret = true; }else if ((interrupt_request &CPU_INTERRUPT_NMI ) && !(env->hflags2 &HF2_NMI_MASK )) { +cpu_svm_check_intercept_param (env,SVM_EXIT_NMI ,0 ,0 ); cs->interrupt_request &= ~CPU_INTERRUPT_NMI ; env->hflags2 |=HF2_NMI_MASK ;do_interrupt_x86_hardirq (env,EXCP02_NMI ,1 );
Signature · 1 line
2.13.6
PATCH v2 2/4 target-i386: Allow interrupt injection after STGI
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
We need to terminate the translation block after STGI so that pending interrupts can be injected.
This fixes pending NMI injection for Jailhouse which uses "stgi; clgi" to open a brief injection window.
--- target/i386/translate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
target/i386/translate.c +2 -1
@@ -7450,8 +7450,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu)break ; }gen_update_cc_op (s); -gen_jmp_im (pc_start - s->cs_base);gen_helper_stgi (cpu_env); +gen_jmp_im (s->pc - s->cs_base); +gen_eob (s);break ;case 0xdd :/* CLGI */
Signature · 1 line
2.13.6
PATCH v2 3/4 target-i386: Mark cpu_vmexit noreturn
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
It calls cpu_loop_exit in system emulation mode (and should never be called in user emulation mode).
--- target/i386/cpu.h | 4 ++-- target/i386/svm_helper.c | 1 + 2 files changed, 3 insertions(+), 2 deletions(-)
target/i386/cpu.h +2 -2
@@ -1764,8 +1764,8 @@ void helper_lock_init(void);/* svm_helper.c */ void cpu_svm_check_intercept_param (CPUX86State *env1,uint32_t type,uint64_t param,uintptr_t retaddr); -void cpu_vmexit (CPUX86State *nenv,uint32_t exit_code,uint64_t exit_info_1, -uintptr_t retaddr); +void QEMU_NORETURN cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, +uint64_t exit_info_1,uintptr_t retaddr);void do_vmexit (CPUX86State *env,uint32_t exit_code,uint64_t exit_info_1);/* seg_helper.c */
target/i386/svm_helper.c +1 -0
@@ -62,6 +62,7 @@ void helper_invlpga(CPUX86State *env, int aflag)void cpu_vmexit (CPUX86State *nenv,uint32_t exit_code,uint64_t exit_info_1,uintptr_t retaddr) { +assert (0 ); } void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type,
Signature · 1 line
2.13.6
PATCH v2 4/4 target-i386: Add NPT support
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
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 hflags as well as 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.
--- target/i386/cpu.c | 2 +- target/i386/cpu.h | 6 ++ target/i386/excp_helper.c | 216 +++++++++++++++++++++++++++++++++++++++++++++- target/i386/mem_helper.c | 6 +- target/i386/svm.h | 14 +++ target/i386/svm_helper.c | 22 +++++ 6 files changed, 260 insertions(+), 6 deletions(-)
target/i386/cpu.c +1 -1
@@ -261,7 +261,7 @@ static void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1,#define TCG_EXT3_FEATURES (CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | \ CPUID_EXT3_CR8LEG | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A)#define TCG_EXT4_FEATURES 0 -#define TCG_SVM_FEATURES 0 +#define TCG_SVM_FEATURES CPUID_SVM_NPT#define TCG_KVM_FEATURES 0#define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP | \ CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX | \
target/i386/cpu.h +6 -0
@@ -177,6 +177,7 @@ typedef enum X86Seg {#define HF_IOBPT_SHIFT 24/* an io breakpoint enabled */ #define HF_MPX_EN_SHIFT 25/* MPX Enabled (CR4+XCR0+BNDCFGx) */ #define HF_MPX_IU_SHIFT 26/* BND registers in-use */ +#define HF_NPT_SHIFT 27/* Nested Paging enabled */ #define HF_CPL_MASK (3 << HF_CPL_SHIFT)#define HF_INHIBIT_IRQ_MASK (1 << HF_INHIBIT_IRQ_SHIFT)
@@ -202,6 +203,7 @@ typedef enum X86Seg {#define HF_IOBPT_MASK (1 << HF_IOBPT_SHIFT)#define HF_MPX_EN_MASK (1 << HF_MPX_EN_SHIFT)#define HF_MPX_IU_MASK (1 << HF_MPX_IU_SHIFT) +#define HF_NPT_MASK (1 << HF_NPT_SHIFT)/* hflags2 */
@@ -1201,12 +1203,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;
target/i386/excp_helper.c +214 -2
280 lines not on this page · raw message
target/i386/mem_helper.c +3 -3
@@ -202,13 +202,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); +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); -CPUX86State *env = &cpu->env; -raise_exception_err_ra (env, cs->exception_index, env->error_code, retaddr); } }
target/i386/svm.h +14 -0
@@ -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) +struct QEMU_PACKED vmcb_control_area {uint16_t intercept_cr_read;uint16_t intercept_cr_write;
target/i386/svm_helper.c +22 -0
@@ -124,6 +124,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;
@@ -206,6 +207,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->hflags |=HF_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 ;
@@ -616,6 +637,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->hflags &= ~HF_NPT_MASK ;/* Save the VM state in the vmcb */ svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.es),
Signature · 1 line
2.13.6
Re: [Qemu-devel] [PATCH v2 4/4] target-i386: Add NPT support
Quoted 5 lines · Jan Kiszka
target/i386/excp_helper.c +4
-0+static hwaddr get_hphys (CPUState *cs,hwaddr gphys,MMUAccessType access_type, +int *prot) +{ +CPUX86State *env = &X86_CPU (cs)->env;
target/i386/excp_helper.c int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, · line 173
Quoted 10 lines
target/i386/excp_helper.c +10
-0+uint64_t rsvd_mask =PG_HI_RSVD_MASK ; +uint64_t ptep, pte; +uint64_t exit_info_1 =0 ; +target_ulong pde_addr, pte_addr; +uint32_t page_offset; +int page_size; + +if (likely (!(env->hflags &HF_NPT_MASK ))) { +return gphys; + }
hflags are a somewhat limited resource. Can this go in hflags2?
Quoted 4 lines
target/i386/svm_helper.c +3
-0+ + env->nested_pg_mode =0 ; +if (env->cr[4 ] &CR4_PAE_MASK ) {
target/i386/svm_helper.c void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) · line 229
Quoted 10 lines
target/i386/svm_helper.c +10
-0+ 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 ; + } + } +
This needs to be migrated. You can put it in a subsection, conditional on hflags & HF_SVMI_MASK.
Also, do you need to flush the TLB unconditionally, even if CR0.PG is zero?
Otherwise looks good. I have queued patches 1-3, but hopefully this one can go in the next release too. Sorry for the delayed review.
Paolo
Re: [Qemu-devel] [PATCH v2 4/4] target-i386: Add NPT support
Quoted 19 lines · Paolo Bonzini
Quoted 5 lines · Jan Kiszka
target/i386/excp_helper.c +4
-0+static hwaddr get_hphys (CPUState *cs,hwaddr gphys,MMUAccessType access_type, +int *prot) +{ +CPUX86State *env = &X86_CPU (cs)->env;target/i386/excp_helper.c int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, · line 173
Quoted 10 lines
target/i386/excp_helper.c +10
-0+uint64_t rsvd_mask =PG_HI_RSVD_MASK ; +uint64_t ptep, pte; +uint64_t exit_info_1 =0 ; +target_ulong pde_addr, pte_addr; +uint32_t page_offset; +int page_size; + +if (likely (!(env->hflags &HF_NPT_MASK ))) { +return gphys; + }hflags are a somewhat limited resource. Can this go in hflags2?
Will have a look - I don't seen why not. Or is there any special semantical difference between both fields?
Quoted 17 lines
Quoted 4 lines
target/i386/svm_helper.c +3
-0+ + env->nested_pg_mode =0 ; +if (env->cr[4 ] &CR4_PAE_MASK ) {target/i386/svm_helper.c void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) · line 229
Quoted 10 lines
target/i386/svm_helper.c +10
-0+ 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 ; + } + } +This needs to be migrated. You can put it in a subsection, conditional on hflags &
HF_SVMI_MASK.
OK.
Quoted 2 lines
Also, do you need to flush the TLB unconditionally, even if CR0.PG is zero?
Cannot follow you here yet. What flush are you referring to?
Also, CR0.PG would not reflect if NPT is on, which now also contributes to our TLB.
Quoted 3 lines
Otherwise looks good. I have queued patches 1-3, but hopefully this one can go in the next release too. Sorry for the delayed review.
No problem.
Thanks,
Jan
Re: [Qemu-devel] [PATCH v2 4/4] target-i386: Add NPT support
Quoted 21 lines · Jan Kiszka
Quoted 19 lines · Paolo Bonzini
Quoted 5 lines · Jan Kiszka
target/i386/excp_helper.c +4
-0+static hwaddr get_hphys (CPUState *cs,hwaddr gphys,MMUAccessType access_type, +int *prot) +{ +CPUX86State *env = &X86_CPU (cs)->env;target/i386/excp_helper.c int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, · line 173
Quoted 10 lines
target/i386/excp_helper.c +10
-0+uint64_t rsvd_mask =PG_HI_RSVD_MASK ; +uint64_t ptep, pte; +uint64_t exit_info_1 =0 ; +target_ulong pde_addr, pte_addr; +uint32_t page_offset; +int page_size; + +if (likely (!(env->hflags &HF_NPT_MASK ))) { +return gphys; + }hflags are a somewhat limited resource. Can this go in hflags2?
Quoted 2 lines
Will have a look - I don't seen why not. Or is there any special semantical difference between both fields?
Yes, hflags become flags of the translation block, while hflags2 are just random processor state. If translate.c uses it you must use hflags, but here hflags2 should be safe.
Thanks,
Paolo
Quoted 36 lines
Quoted 17 lines
Quoted 4 lines
target/i386/svm_helper.c +3
-0+ + env->nested_pg_mode =0 ; +if (env->cr[4 ] &CR4_PAE_MASK ) {target/i386/svm_helper.c void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) · line 229
Quoted 10 lines
target/i386/svm_helper.c +10
-0+ 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 ; + } + } +This needs to be migrated. You can put it in a subsection, conditional on hflags &
HF_SVMI_MASK.OK.
Quoted 2 lines
Also, do you need to flush the TLB unconditionally, even if CR0.PG is zero?
Cannot follow you here yet. What flush are you referring to?
Also, CR0.PG would not reflect if NPT is on, which now also contributes to our TLB.
Quoted 3 lines
Otherwise looks good. I have queued patches 1-3, but hopefully this one can go in the next release too. Sorry for the delayed review.
No problem.
Thanks,
Jan
Re: [Qemu-devel] [PATCH v2 4/4] target-i386: Add NPT support
Quoted 22 lines · Paolo Bonzini
Quoted 21 lines · Jan Kiszka
Quoted 19 lines · Paolo Bonzini
Quoted 5 lines · Jan Kiszka
target/i386/excp_helper.c +4
-0+static hwaddr get_hphys (CPUState *cs,hwaddr gphys,MMUAccessType access_type, +int *prot) +{ +CPUX86State *env = &X86_CPU (cs)->env;target/i386/excp_helper.c int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, · line 173
Quoted 10 lines
target/i386/excp_helper.c +10
-0+uint64_t rsvd_mask =PG_HI_RSVD_MASK ; +uint64_t ptep, pte; +uint64_t exit_info_1 =0 ; +target_ulong pde_addr, pte_addr; +uint32_t page_offset; +int page_size; + +if (likely (!(env->hflags &HF_NPT_MASK ))) { +return gphys; + }hflags are a somewhat limited resource. Can this go in hflags2?
Quoted 6 lines
Quoted 2 lines
Will have a look - I don't seen why not. Or is there any special semantical difference between both fields?
Yes, hflags become flags of the translation block, while hflags2 are just random processor state. If
translate.cuses it you must use hflags, but here hflags2 should be safe.
Indeed. We only change it at vmentry/exit, and that is already a translation block barrier.
v2 is on the way.
Jan
This series seems to have some coding style problems. See output below for more information:
Hi,
This series seems to have some coding style problems. See output below for more information:
Type: series
Message-id: [email protected]
Subject: [Qemu-devel] [PATCH v2 0/4] target-i386: Enhance SVM support
=== TEST SCRIPT BEGIN ===
#!/bin/bash
BASE=base
n=1
total=$(git log --oneline $BASE.. | wc -l)
failed=0
git config --local diff.renamelimit 0
git config --local diff.renames True
git config --local diff.algorithm histogram
commits="$(git log --format=%H --reverse $BASE..)" for c in $commits; do
echo "Checking PATCH $n/$total: $(git log -n 1 --format=%s $c)..."
if ! git show $c --format=email | ./scripts/checkpatch.pl --mailback -; then
failed=1
echo
fi
n=$((n+1))done
exit $failed
=== TEST SCRIPT END ===
Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384 From https://github.com/patchew-project/qemu
* [new tag] patchew/[email protected] -> patchew/[email protected]
Switched to a new branch 'test'9cbe95251e target-i386: Add NPT support8880b9be5d target-i386: Mark cpu_vmexit noreturn 2913fc90bf target-i386: Allow interrupt injection after STGI 7c59f09518 target-i386: Add NMI interception to SVM
=== OUTPUT BEGIN ===
Checking PATCH 1/4: target-i386: Add NMI interception to SVM... Checking PATCH 2/4: target-i386: Allow interrupt injection after STGI... Checking PATCH 3/4: target-i386: Mark cpu_vmexit noreturn... Checking PATCH 4/4: target-i386: Add NPT support...
ERROR: braces {} are necessary for all arms of this statement #104: FILE: target/i386/excp_helper.c:184:
+ if (env->nested_pg_mode & SVM_NPT_LMA) {
[...]
+ } else
[...]
total: 1 errors, 0 warnings, 394 lines checked
Your patch has style problems, please review. If any of these errors are false positives report them to the maintainer, see CHECKPATCH in MAINTAINERS.
=== OUTPUT END ===
Test command exited with code: 1
---
Email generated automatically by Patchew [http://patchew.org/].
Please send your feedback to [email protected]
Quoted 16 lines · Jan Kiszka
These patches allow to run Jailhouse in emulated x86-64 mode under QEMU. AMD IOMMU only works with one additional hack, but that's a different story, and we can test these changes without it.
Change in v2:
- build fix for 32-bit hosts
- replaces NPT exitinfo magics with symbolic constantsJan
Jan Kiszka (4):
target-i386: Add NMI interception to SVM
target-i386: Allow interrupt injection after STGI
target-i386: Markcpu_vmexitnoreturn
target-i386: Add NPT support
Quoted 10 lines
target/i386/cpu.c | 2 +- target/i386/cpu.h | 10 ++- target/i386/excp_helper.c | 216 +++++++++++++++++++++++++++++++++++++++++++++- target/i386/mem_helper.c | 6 +- target/i386/seg_helper.c | 1 + target/i386/svm.h | 14 +++ target/i386/svm_helper.c | 23 +++++ target/i386/translate.c | 3 +- 8 files changed, 266 insertions(+), 9 deletions(-)
Ping on this series.
Jan
Signature · 2 lines
Siemens AG, Corporate Technology, CT RDA IOT SES-DE Corporate Competence Center Embedded Linux