Skip to contentSkip to conversation

SVM support

PATCH In review Alexander Graf 5 participants 14 messages Aug 30, 2007 – Sep 18, 2007 Kbuild

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 SVM support

Hi,

after a lot of struggling I finally got everything working smoothly (special thanks to Fabrice and Avi), so I believe this patch is ready to be taken upstream.
CC_DST was never a problem, as everything I did in the eflags area already worked. I managed to clobber the segment attributes though, so that was the real problem here.
Nevertheless there is still a lot of functionality missing, whereas none of that is used in kvm by now, so that works already.

So there are still missing parts that I will list here:

- NPT support
- Everything related to device virtualisation
- The "Secure" part of the extension (would need TPM emulation for that)
- Debugging support (maybe it does work, I actually have never tried to debug a kvm virtualised machine)
- TSC_OFFSET
- ASID support
- Sanity checks
- Task switch and Ferr_Freeze Intercepts
- VMMCALL
- SMM support
- SVM-Lock

Most of these features are optional or not used by any software I know of today, so it does not hurt (yet).

I'm really sorry that this patch is so big. I already tried to make it as small as I could, but this feature is heavy.
In case it is too big, I can try to split it up into several patches.

Thanks a lot,

Alexander Graf

target-i386/helper2.c +11 -2
Index: qemu/target-i386/helper2.c
===================================================================
@@ -27,6 +27,7 @@
 
 #include "cpu.h"
 #include "exec-all.h"
+#include "svm.h"
 
 //#define DEBUG_MMU
 
@@ -111,6 +112,7 @@ CPUX86State *cpu_x86_init(void)
                                CPUID_CX8 | CPUID_PGE | CPUID_CMOV |
                                CPUID_PAT);
         env->pat = 0x0007040600070406ULL;
+        env->cpuid_ext3_features = CPUID_EXT3_SVM;
         env->cpuid_ext_features = CPUID_EXT_SSE3;
         env->cpuid_features |= CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | CPUID_PAE | CPUID_SEP;
         env->cpuid_features |= CPUID_APIC;
@@ -131,7 +133,7 @@ CPUX86State *cpu_x86_init(void)
         /* currently not enabled for std i386 because not fully tested */
         env->cpuid_ext2_features = (env->cpuid_features & 0x0183F3FF);
         env->cpuid_ext2_features |= CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX;
-        env->cpuid_xlevel = 0x80000008;
+        env->cpuid_xlevel = 0x8000000a;
 
         /* these features are needed for Win64 and aren't fully implemented */
         env->cpuid_features |= CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA;
@@ -160,6 +162,7 @@ void cpu_reset(CPUX86State *env)
 #ifdef CONFIG_SOFTMMU
     env->hflags |= HF_SOFTMMU_MASK;
 #endif
+    env->hflags |= HF_GIF_MASK;
 
     cpu_x86_update_cr0(env, 0x60000010);
     env->a20_mask = 0xffffffff;
@@ -863,7 +866,6 @@ int cpu_x86_handle_mmu_fault(CPUX86State
  do_fault_protect:
     error_code = PG_ERROR_P_MASK;
  do_fault:
-    env->cr[2] = addr;
     error_code |= (is_write << PG_ERROR_W_BIT);
     if (is_user)
         error_code |= PG_ERROR_U_MASK;
@@ -871,8 +873,15 @@ int cpu_x86_handle_mmu_fault(CPUX86State
         (env->efer & MSR_EFER_NXE) && 
         (env->cr[4] & CR4_PAE_MASK))
         error_code |= PG_ERROR_I_D_MASK;
+    if (INTERCEPTEDl(_exceptions, 1 << EXCP0E_PAGE)) {
+        stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), addr);
+    } else {
+        env->cr[2] = addr;
+    }
     env->error_code = error_code;
     env->exception_index = EXCP0E_PAGE;
+    if (INTERCEPTEDl(_exceptions, 1 << EXCP0E_PAGE)) // the VMM will handle this
+        return 2;
     return 1;
 }
 
target-i386/translate.c +178 -7

433 lines not on this page · raw message

target-i386/cpu.h +21 -0
Index: qemu/target-i386/cpu.h
===================================================================
@@ -46,6 +46,8 @@
 
 #include "softfloat.h"
 
+#include "svm.h"
+
 #if defined(__i386__) && !defined(CONFIG_SOFTMMU) && !defined(__APPLE__)
 #define USE_CODE_COPY
 #endif
@@ -84,6 +86,7 @@
 #define DESC_AVL_MASK   (1 << 20)
 #define DESC_P_MASK     (1 << 15)
 #define DESC_DPL_SHIFT  13
+#define DESC_DPL_MASK   (1 << DESC_DPL_SHIFT)
 #define DESC_S_MASK     (1 << 12)
 #define DESC_TYPE_SHIFT 8
 #define DESC_A_MASK     (1 << 8)
@@ -149,6 +152,8 @@
 #define HF_VM_SHIFT         17 /* must be same as eflags */
 #define HF_HALTED_SHIFT     18 /* CPU halted */
 #define HF_SMM_SHIFT        19 /* CPU in SMM mode */
+#define HF_GIF_SHIFT        20 /* if set CPU takes interrupts */
+#define HF_HIF_SHIFT        21 /* shadow copy of IF_MASK when in SVM */
 
 #define HF_CPL_MASK          (3 << HF_CPL_SHIFT)
 #define HF_SOFTMMU_MASK      (1 << HF_SOFTMMU_SHIFT)
@@ -166,6 +171,8 @@
 #define HF_OSFXSR_MASK       (1 << HF_OSFXSR_SHIFT)
 #define HF_HALTED_MASK       (1 << HF_HALTED_SHIFT)
 #define HF_SMM_MASK          (1 << HF_SMM_SHIFT)
+#define HF_GIF_MASK          (1 << HF_GIF_SHIFT)
+#define HF_HIF_MASK          (1 << HF_HIF_SHIFT)
 
 #define CR0_PE_MASK  (1 << 0)
 #define CR0_MP_MASK  (1 << 1)
@@ -249,6 +256,8 @@
 #define MSR_GSBASE                      0xc0000101
 #define MSR_KERNELGSBASE                0xc0000102
 
+#define MSR_VM_HSAVE_PA                 0xc0010117
+
 /* cpuid_features bits */
 #define CPUID_FP87 (1 << 0)
 #define CPUID_VME  (1 << 1)
@@ -283,6 +292,8 @@
 #define CPUID_EXT2_FFXSR   (1 << 25)
 #define CPUID_EXT2_LM      (1 << 29)
 
+#define CPUID_EXT3_SVM     (1 << 2)
+
 #define EXCP00_DIVZ	0
 #define EXCP01_SSTP	1
 #define EXCP02_NMI	2
@@ -489,6 +500,16 @@ typedef struct CPUX86State {
     uint32_t sysenter_eip;
     uint64_t efer;
     uint64_t star;
+
+    target_phys_addr_t vm_hsave;
+    target_phys_addr_t vm_vmcb;
+    uint64_t intercept;
+    uint16_t intercept_cr_read;
+    uint16_t intercept_cr_write;
+    uint16_t intercept_dr_read;
+    uint16_t intercept_dr_write;
+    uint32_t intercept_exceptions;
+
 #ifdef TARGET_X86_64
     target_ulong lstar;
     target_ulong cstar;
target-i386/op.c +74 -0

83 lines not on this page · raw message

target-i386/helper.c +472 -2

528 lines not on this page · raw message

target-i386/svm.h new file +373 -0

373 lines not on this page · raw message

cpu-exec.c +35 -3
Index: qemu/cpu-exec.c
===================================================================
@@ -104,6 +104,9 @@ static TranslationBlock *tb_find_slow(ta
         if (tb->pc == pc && 
             tb->page_addr[0] == phys_page1 &&
             tb->cs_base == cs_base && 
+#if defined(TARGET_I386)
+	    tb->intercept == env->intercept &&
+#endif
             tb->flags == flags) {
             /* check next page if needed */
             if (tb->page_addr[1] != -1) {
@@ -132,6 +135,9 @@ static TranslationBlock *tb_find_slow(ta
     tc_ptr = code_gen_ptr;
     tb->tc_ptr = tc_ptr;
     tb->cs_base = cs_base;
+#if defined(TARGET_I386)
+    tb->intercept = env->intercept;
+#endif
     tb->flags = flags;
     cpu_gen_code(env, tb, CODE_GEN_MAX_SIZE, &code_gen_size);
     code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
@@ -214,7 +220,11 @@ static inline TranslationBlock *tb_find_
 #endif
     tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)];
     if (__builtin_expect(!tb || tb->pc != pc || tb->cs_base != cs_base ||
-                         tb->flags != flags, 0)) {
+                         tb->flags != flags
+#if defined(TARGET_I386)
+			 || tb->intercept != env->intercept
+#endif
+			 , 0)) {
         tb = tb_find_slow(pc, cs_base, flags);
         /* Note: we do it here to avoid a gcc bug on Mac OS X when
            doing it in tb_find_slow */
@@ -373,7 +383,11 @@ int cpu_exec(CPUState *env1)
                 tmp_T0 = T0;
 #endif	    
                 interrupt_request = env->interrupt_request;
-                if (__builtin_expect(interrupt_request, 0)) {
+                if (__builtin_expect(interrupt_request, 0)
+#if defined(TARGET_I386)
+			&& env->hflags & HF_GIF_MASK
+#endif
+				) {
                     if (interrupt_request & CPU_INTERRUPT_DEBUG) {
                         env->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
                         env->exception_index = EXCP_DEBUG;
@@ -391,6 +405,7 @@ int cpu_exec(CPUState *env1)
 #if defined(TARGET_I386)
                     if ((interrupt_request & CPU_INTERRUPT_SMI) &&
                         !(env->hflags & HF_SMM_MASK)) {
+                        svm_check_intercept(SVM_EXIT_SMI);
                         env->interrupt_request &= ~CPU_INTERRUPT_SMI;
                         do_smm_enter();
 #if defined(__sparc__) && !defined(HOST_SOLARIS)
@@ -399,9 +414,10 @@ int cpu_exec(CPUState *env1)
                         T0 = 0;
 #endif
                     } else if ((interrupt_request & CPU_INTERRUPT_HARD) &&
-                        (env->eflags & IF_MASK) && 
+                        (env->eflags & IF_MASK || env->hflags & HF_HIF_MASK) && 
                         !(env->hflags & HF_INHIBIT_IRQ_MASK)) {
                         int intno;
+			svm_check_intercept(SVM_EXIT_INTR);
                         env->interrupt_request &= ~CPU_INTERRUPT_HARD;
                         intno = cpu_get_pic_interrupt(env);
                         if (loglevel & CPU_LOG_TB_IN_ASM) {
@@ -415,6 +431,22 @@ int cpu_exec(CPUState *env1)
 #else
                         T0 = 0;
 #endif
+                    } else if((interrupt_request & CPU_INTERRUPT_VIRQ) &&
+                        (env->eflags & IF_MASK) && !(env->hflags & HF_INHIBIT_IRQ_MASK)) {
+                         int intno;
+                         // FIXME: this should respect TPR
+                         svm_check_intercept(SVM_EXIT_VINTR);
+                         env->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
+                         stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl), ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl)) & ~V_IRQ_MASK);
+                         intno = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_vector));
+                         if (loglevel & CPU_LOG_TB_IN_ASM)
+                             fprintf(logfile, "Servicing virtual hardware INT=0x%02x\n", intno);
+	                 do_interrupt(intno, 0, 0, -1, 1);
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+                         tmp_T0 = 0;
+#else
+                         T0 = 0;
+#endif
                     }
 #elif defined(TARGET_PPC)
 #if 0
target-i386/exec.h +10 -0
Index: qemu/target-i386/exec.h
===================================================================
@@ -501,6 +501,15 @@ void update_fp_status(void);
 void helper_hlt(void);
 void helper_monitor(void);
 void helper_mwait(void);
+void helper_vmrun(target_ulong addr);
+void helper_vmmcall(void);
+void helper_vmload(target_ulong addr);
+void helper_vmsave(target_ulong addr);
+void helper_stgi(void);
+void helper_clgi(void);
+void helper_skinit(void);
+void helper_invlpga(void);
+void vmexit(uint64_t exit_code, uint64_t exit_info_1);
 
 extern const uint8_t parity_table[256];
 extern const uint8_t rclw_table[32];
@@ -588,3 +597,4 @@ static inline int cpu_halted(CPUState *e
     }
     return EXCP_HALTED;
 }
+
exec.c +5 -0
Index: qemu/exec.c
===================================================================
@@ -1281,6 +1281,11 @@ void cpu_abort(CPUState *env, const char
     vfprintf(stderr, fmt, ap);
     fprintf(stderr, "\n");
 #ifdef TARGET_I386
+    if(env->intercept & INTERCEPT_SVM_MASK) {
+	// most probably the virtual machine should not
+	// be shut down but rather caught by the VMM
+        vmexit(SVM_EXIT_SHUTDOWN, 0);
+    }
     cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU | X86_DUMP_CCOP);
 #else
     cpu_dump_state(env, stderr, fprintf, 0);
exec-all.h +1 -0
Index: qemu/exec-all.h
===================================================================
@@ -166,6 +166,7 @@ static inline int tlb_set_page(CPUState 
 typedef struct TranslationBlock {
     target_ulong pc;   /* simulated PC corresponding to this block (EIP + CS base) */
     target_ulong cs_base; /* CS base for this block */
+    uint64_t intercept; /* SVM intercept vector */
     unsigned int flags; /* flags defining in which context the code was generated */
     uint16_t size;      /* size of target code for this block (1 <=
                            size <= TARGET_PAGE_SIZE) */
cpu-all.h +1 -0
Index: qemu/cpu-all.h
===================================================================
@@ -715,6 +715,7 @@ extern int code_copy_enabled;
 #define CPU_INTERRUPT_HALT   0x20 /* CPU halt wanted */
 #define CPU_INTERRUPT_SMI    0x40 /* (x86 only) SMI interrupt pending */
 #define CPU_INTERRUPT_DEBUG  0x80 /* Debug event occured.  */
+#define CPU_INTERRUPT_VIRQ   0x100 /* virtual interrupt pending.  */
 
 void cpu_interrupt(CPUState *s, int mask);
 void cpu_reset_interrupt(CPUState *env, int mask);
#1 lore raw
Quoted 11 lines · Alexander Graf

Hi,

after a lot of struggling I finally got everything working smoothly (special thanks to Fabrice and Avi), so I believe this patch is ready to be taken upstream.
CC_DST was never a problem, as everything I did in the eflags area already worked. I managed to clobber the segment attributes though, so that was the real problem here.
Nevertheless there is still a lot of functionality missing, whereas none of that is used in kvm by now, so that works already.

Quoted 5 lines

So there are still missing parts that I will list here:

- NPT support
- Everything related to device virtualisation
- The "Secure" part of the extension (would need TPM emulation for that)

I backported Xen's TPM emulation to Qemu for exactly that purpose. I also started to implement skinit, but did not had the time to finish that work, yet. Please note that this patch requires a two line patch to the tpm-emulator to understand the localities.

Bernhard Kauer

Index: Makefile.target
===================================================================
RCS file: /sources/qemu/qemu/Makefile.target,v
retrieving revision 1.191

Makefile.target +1 -0
diff -u -r1.191 Makefile.target
@@ -429,6 +429,7 @@
 VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
 VL_OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o
 VL_OBJS+= usb-uhci.o smbus_eeprom.o vmmouse.o vmware_vga.o
+VL_OBJS+= tpm_tis.o
 CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
 endif
 ifeq ($(TARGET_BASE_ARCH), ppc)

Index: hw/pc.c
===================================================================
RCS file: /sources/qemu/qemu/hw/pc.c,v
retrieving revision 1.81

pc.c +3 -0
diff -u -r1.81 pc.c
@@ -914,6 +917,9 @@
     if (i440fx_state) {
         i440fx_init_memory_mappings(i440fx_state);
     }
+    
+    tpm_tis_init();
+    
 #if 0
     /* ??? Need to figure out some way for the user to
        specify SCSI devices.  */
tpm_tis.c new file +890 -0

890 lines not on this page · raw message

#2 lore raw
In reply to Alexander Graf

Just after I sent the patch I found a problem with the interrupt handling when a lot of interrupts occur. This patch fixes that and removes a small chunk of debugging code I forgot to remove.

Thanks,

Alexander Graf

target-i386/helper2.c +11 -2
Index: qemu/target-i386/helper2.c
===================================================================
@@ -27,6 +27,7 @@
 
 #include "cpu.h"
 #include "exec-all.h"
+#include "svm.h"
 
 //#define DEBUG_MMU
 
@@ -111,6 +112,7 @@ CPUX86State *cpu_x86_init(void)
                                CPUID_CX8 | CPUID_PGE | CPUID_CMOV |
                                CPUID_PAT);
         env->pat = 0x0007040600070406ULL;
+        env->cpuid_ext3_features = CPUID_EXT3_SVM;
         env->cpuid_ext_features = CPUID_EXT_SSE3;
         env->cpuid_features |= CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | CPUID_PAE | CPUID_SEP;
         env->cpuid_features |= CPUID_APIC;
@@ -131,7 +133,7 @@ CPUX86State *cpu_x86_init(void)
         /* currently not enabled for std i386 because not fully tested */
         env->cpuid_ext2_features = (env->cpuid_features & 0x0183F3FF);
         env->cpuid_ext2_features |= CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX;
-        env->cpuid_xlevel = 0x80000008;
+        env->cpuid_xlevel = 0x8000000a;
 
         /* these features are needed for Win64 and aren't fully implemented */
         env->cpuid_features |= CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA;
@@ -160,6 +162,7 @@ void cpu_reset(CPUX86State *env)
 #ifdef CONFIG_SOFTMMU
     env->hflags |= HF_SOFTMMU_MASK;
 #endif
+    env->hflags |= HF_GIF_MASK;
 
     cpu_x86_update_cr0(env, 0x60000010);
     env->a20_mask = 0xffffffff;
@@ -863,7 +866,6 @@ int cpu_x86_handle_mmu_fault(CPUX86State
  do_fault_protect:
     error_code = PG_ERROR_P_MASK;
  do_fault:
-    env->cr[2] = addr;
     error_code |= (is_write << PG_ERROR_W_BIT);
     if (is_user)
         error_code |= PG_ERROR_U_MASK;
@@ -871,8 +873,15 @@ int cpu_x86_handle_mmu_fault(CPUX86State
         (env->efer & MSR_EFER_NXE) && 
         (env->cr[4] & CR4_PAE_MASK))
         error_code |= PG_ERROR_I_D_MASK;
+    if (INTERCEPTEDl(_exceptions, 1 << EXCP0E_PAGE)) {
+        stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), addr);
+    } else {
+        env->cr[2] = addr;
+    }
     env->error_code = error_code;
     env->exception_index = EXCP0E_PAGE;
+    if (INTERCEPTEDl(_exceptions, 1 << EXCP0E_PAGE)) // the VMM will handle this
+        return 2;
     return 1;
 }
 
target-i386/translate.c +179 -7

440 lines not on this page · raw message

target-i386/cpu.h +21 -0
Index: qemu/target-i386/cpu.h
===================================================================
@@ -46,6 +46,8 @@
 
 #include "softfloat.h"
 
+#include "svm.h"
+
 #if defined(__i386__) && !defined(CONFIG_SOFTMMU) && !defined(__APPLE__)
 #define USE_CODE_COPY
 #endif
@@ -84,6 +86,7 @@
 #define DESC_AVL_MASK   (1 << 20)
 #define DESC_P_MASK     (1 << 15)
 #define DESC_DPL_SHIFT  13
+#define DESC_DPL_MASK   (1 << DESC_DPL_SHIFT)
 #define DESC_S_MASK     (1 << 12)
 #define DESC_TYPE_SHIFT 8
 #define DESC_A_MASK     (1 << 8)
@@ -149,6 +152,8 @@
 #define HF_VM_SHIFT         17 /* must be same as eflags */
 #define HF_HALTED_SHIFT     18 /* CPU halted */
 #define HF_SMM_SHIFT        19 /* CPU in SMM mode */
+#define HF_GIF_SHIFT        20 /* if set CPU takes interrupts */
+#define HF_HIF_SHIFT        21 /* shadow copy of IF_MASK when in SVM */
 
 #define HF_CPL_MASK          (3 << HF_CPL_SHIFT)
 #define HF_SOFTMMU_MASK      (1 << HF_SOFTMMU_SHIFT)
@@ -166,6 +171,8 @@
 #define HF_OSFXSR_MASK       (1 << HF_OSFXSR_SHIFT)
 #define HF_HALTED_MASK       (1 << HF_HALTED_SHIFT)
 #define HF_SMM_MASK          (1 << HF_SMM_SHIFT)
+#define HF_GIF_MASK          (1 << HF_GIF_SHIFT)
+#define HF_HIF_MASK          (1 << HF_HIF_SHIFT)
 
 #define CR0_PE_MASK  (1 << 0)
 #define CR0_MP_MASK  (1 << 1)
@@ -249,6 +256,8 @@
 #define MSR_GSBASE                      0xc0000101
 #define MSR_KERNELGSBASE                0xc0000102
 
+#define MSR_VM_HSAVE_PA                 0xc0010117
+
 /* cpuid_features bits */
 #define CPUID_FP87 (1 << 0)
 #define CPUID_VME  (1 << 1)
@@ -283,6 +292,8 @@
 #define CPUID_EXT2_FFXSR   (1 << 25)
 #define CPUID_EXT2_LM      (1 << 29)
 
+#define CPUID_EXT3_SVM     (1 << 2)
+
 #define EXCP00_DIVZ	0
 #define EXCP01_SSTP	1
 #define EXCP02_NMI	2
@@ -489,6 +500,16 @@ typedef struct CPUX86State {
     uint32_t sysenter_eip;
     uint64_t efer;
     uint64_t star;
+
+    target_phys_addr_t vm_hsave;
+    target_phys_addr_t vm_vmcb;
+    uint64_t intercept;
+    uint16_t intercept_cr_read;
+    uint16_t intercept_cr_write;
+    uint16_t intercept_dr_read;
+    uint16_t intercept_dr_write;
+    uint32_t intercept_exceptions;
+
 #ifdef TARGET_X86_64
     target_ulong lstar;
     target_ulong cstar;
target-i386/op.c +74 -0

83 lines not on this page · raw message

target-i386/helper.c +472 -2

528 lines not on this page · raw message

target-i386/svm.h new file +357 -0

357 lines not on this page · raw message

cpu-exec.c +34 -3
Index: qemu/cpu-exec.c
===================================================================
@@ -104,6 +104,9 @@ static TranslationBlock *tb_find_slow(ta
         if (tb->pc == pc && 
             tb->page_addr[0] == phys_page1 &&
             tb->cs_base == cs_base && 
+#if defined(TARGET_I386)
+	    tb->intercept == env->intercept &&
+#endif
             tb->flags == flags) {
             /* check next page if needed */
             if (tb->page_addr[1] != -1) {
@@ -132,6 +135,9 @@ static TranslationBlock *tb_find_slow(ta
     tc_ptr = code_gen_ptr;
     tb->tc_ptr = tc_ptr;
     tb->cs_base = cs_base;
+#if defined(TARGET_I386)
+    tb->intercept = env->intercept;
+#endif
     tb->flags = flags;
     cpu_gen_code(env, tb, CODE_GEN_MAX_SIZE, &code_gen_size);
     code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
@@ -214,7 +220,11 @@ static inline TranslationBlock *tb_find_
 #endif
     tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)];
     if (__builtin_expect(!tb || tb->pc != pc || tb->cs_base != cs_base ||
-                         tb->flags != flags, 0)) {
+                         tb->flags != flags
+#if defined(TARGET_I386)
+			 || tb->intercept != env->intercept
+#endif
+			 , 0)) {
         tb = tb_find_slow(pc, cs_base, flags);
         /* Note: we do it here to avoid a gcc bug on Mac OS X when
            doing it in tb_find_slow */
@@ -373,7 +383,11 @@ int cpu_exec(CPUState *env1)
                 tmp_T0 = T0;
 #endif	    
                 interrupt_request = env->interrupt_request;
-                if (__builtin_expect(interrupt_request, 0)) {
+                if (__builtin_expect(interrupt_request, 0)
+#if defined(TARGET_I386)
+			&& env->hflags & HF_GIF_MASK
+#endif
+				) {
                     if (interrupt_request & CPU_INTERRUPT_DEBUG) {
                         env->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
                         env->exception_index = EXCP_DEBUG;
@@ -391,6 +405,7 @@ int cpu_exec(CPUState *env1)
 #if defined(TARGET_I386)
                     if ((interrupt_request & CPU_INTERRUPT_SMI) &&
                         !(env->hflags & HF_SMM_MASK)) {
+                        svm_check_intercept(SVM_EXIT_SMI);
                         env->interrupt_request &= ~CPU_INTERRUPT_SMI;
                         do_smm_enter();
 #if defined(__sparc__) && !defined(HOST_SOLARIS)
@@ -399,9 +414,10 @@ int cpu_exec(CPUState *env1)
                         T0 = 0;
 #endif
                     } else if ((interrupt_request & CPU_INTERRUPT_HARD) &&
-                        (env->eflags & IF_MASK) && 
+                        (env->eflags & IF_MASK || env->hflags & HF_HIF_MASK) && 
                         !(env->hflags & HF_INHIBIT_IRQ_MASK)) {
                         int intno;
+			svm_check_intercept(SVM_EXIT_INTR);
                         env->interrupt_request &= ~CPU_INTERRUPT_HARD;
                         intno = cpu_get_pic_interrupt(env);
                         if (loglevel & CPU_LOG_TB_IN_ASM) {
@@ -415,6 +431,21 @@ int cpu_exec(CPUState *env1)
 #else
                         T0 = 0;
 #endif
+                    } else if((interrupt_request & CPU_INTERRUPT_VIRQ) &&
+                        (env->eflags & IF_MASK) && !(env->hflags & HF_INHIBIT_IRQ_MASK)) {
+                         int intno;
+                         // FIXME: this should respect TPR
+                         env->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
+                         stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl), ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl)) & ~V_IRQ_MASK);
+                         intno = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_vector));
+                         if (loglevel & CPU_LOG_TB_IN_ASM)
+                             fprintf(logfile, "Servicing virtual hardware INT=0x%02x\n", intno);
+	                 do_interrupt(intno, 0, 0, -1, 1);
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+                         tmp_T0 = 0;
+#else
+                         T0 = 0;
+#endif
                     }
 #elif defined(TARGET_PPC)
 #if 0
target-i386/exec.h +10 -0
Index: qemu/target-i386/exec.h
===================================================================
@@ -501,6 +501,15 @@ void update_fp_status(void);
 void helper_hlt(void);
 void helper_monitor(void);
 void helper_mwait(void);
+void helper_vmrun(target_ulong addr);
+void helper_vmmcall(void);
+void helper_vmload(target_ulong addr);
+void helper_vmsave(target_ulong addr);
+void helper_stgi(void);
+void helper_clgi(void);
+void helper_skinit(void);
+void helper_invlpga(void);
+void vmexit(uint64_t exit_code, uint64_t exit_info_1);
 
 extern const uint8_t parity_table[256];
 extern const uint8_t rclw_table[32];
@@ -588,3 +597,4 @@ static inline int cpu_halted(CPUState *e
     }
     return EXCP_HALTED;
 }
+
exec.c +5 -0
Index: qemu/exec.c
===================================================================
@@ -1281,6 +1281,11 @@ void cpu_abort(CPUState *env, const char
     vfprintf(stderr, fmt, ap);
     fprintf(stderr, "\n");
 #ifdef TARGET_I386
+    if(env->intercept & INTERCEPT_SVM_MASK) {
+	// most probably the virtual machine should not
+	// be shut down but rather caught by the VMM
+        vmexit(SVM_EXIT_SHUTDOWN, 0);
+    }
     cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU | X86_DUMP_CCOP);
 #else
     cpu_dump_state(env, stderr, fprintf, 0);
exec-all.h +1 -0
Index: qemu/exec-all.h
===================================================================
@@ -166,6 +166,7 @@ static inline int tlb_set_page(CPUState 
 typedef struct TranslationBlock {
     target_ulong pc;   /* simulated PC corresponding to this block (EIP + CS base) */
     target_ulong cs_base; /* CS base for this block */
+    uint64_t intercept; /* SVM intercept vector */
     unsigned int flags; /* flags defining in which context the code was generated */
     uint16_t size;      /* size of target code for this block (1 <=
                            size <= TARGET_PAGE_SIZE) */
cpu-all.h +1 -0
Index: qemu/cpu-all.h
===================================================================
@@ -715,6 +715,7 @@ extern int code_copy_enabled;
 #define CPU_INTERRUPT_HALT   0x20 /* CPU halt wanted */
 #define CPU_INTERRUPT_SMI    0x40 /* (x86 only) SMI interrupt pending */
 #define CPU_INTERRUPT_DEBUG  0x80 /* Debug event occured.  */
+#define CPU_INTERRUPT_VIRQ   0x100 /* virtual interrupt pending.  */
 
 void cpu_interrupt(CPUState *s, int mask);
 void cpu_reset_interrupt(CPUState *env, int mask);
#3 lore raw

Thanks to Michael Peter who tried the patch on an x86 host I found some compilation problems.
This updated patch addresses these issues and thus should compile on every platform for every target available.

Alexander Graf

target-i386/helper2.c +11 -3
Index: qemu/target-i386/helper2.c
===================================================================
@@ -27,6 +27,7 @@
 
 #include "cpu.h"
 #include "exec-all.h"
+#include "svm.h"
 
 //#define DEBUG_MMU
 
@@ -111,10 +112,11 @@ CPUX86State *cpu_x86_init(void)
                                CPUID_CX8 | CPUID_PGE | CPUID_CMOV |
                                CPUID_PAT);
         env->pat = 0x0007040600070406ULL;
+        env->cpuid_ext3_features = CPUID_EXT3_SVM;
         env->cpuid_ext_features = CPUID_EXT_SSE3;
         env->cpuid_features |= CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | CPUID_PAE | CPUID_SEP;
         env->cpuid_features |= CPUID_APIC;
-        env->cpuid_xlevel = 0;
+        env->cpuid_xlevel = 0x8000000a;
         {
             const char *model_id = "QEMU Virtual CPU version " QEMU_VERSION;
             int c, len, i;
@@ -131,7 +133,6 @@ CPUX86State *cpu_x86_init(void)
         /* currently not enabled for std i386 because not fully tested */
         env->cpuid_ext2_features = (env->cpuid_features & 0x0183F3FF);
         env->cpuid_ext2_features |= CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX;
-        env->cpuid_xlevel = 0x80000008;
 
         /* these features are needed for Win64 and aren't fully implemented */
         env->cpuid_features |= CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA;
@@ -160,6 +161,7 @@ void cpu_reset(CPUX86State *env)
 #ifdef CONFIG_SOFTMMU
     env->hflags |= HF_SOFTMMU_MASK;
 #endif
+    env->hflags |= HF_GIF_MASK;
 
     cpu_x86_update_cr0(env, 0x60000010);
     env->a20_mask = 0xffffffff;
@@ -863,7 +865,6 @@ int cpu_x86_handle_mmu_fault(CPUX86State
  do_fault_protect:
     error_code = PG_ERROR_P_MASK;
  do_fault:
-    env->cr[2] = addr;
     error_code |= (is_write << PG_ERROR_W_BIT);
     if (is_user)
         error_code |= PG_ERROR_U_MASK;
@@ -871,8 +872,15 @@ int cpu_x86_handle_mmu_fault(CPUX86State
         (env->efer & MSR_EFER_NXE) && 
         (env->cr[4] & CR4_PAE_MASK))
         error_code |= PG_ERROR_I_D_MASK;
+    if (INTERCEPTEDl(_exceptions, 1 << EXCP0E_PAGE)) {
+        stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), addr);
+    } else {
+        env->cr[2] = addr;
+    }
     env->error_code = error_code;
     env->exception_index = EXCP0E_PAGE;
+    if (INTERCEPTEDl(_exceptions, 1 << EXCP0E_PAGE)) // the VMM will handle this
+        return 2;
     return 1;
 }
 
target-i386/translate.c +177 -6

432 lines not on this page · raw message

target-i386/cpu.h +22 -0
Index: qemu/target-i386/cpu.h
===================================================================
@@ -46,6 +46,8 @@
 
 #include "softfloat.h"
 
+#include "svm.h"
+
 #if defined(__i386__) && !defined(CONFIG_SOFTMMU) && !defined(__APPLE__)
 #define USE_CODE_COPY
 #endif
@@ -84,6 +86,7 @@
 #define DESC_AVL_MASK   (1 << 20)
 #define DESC_P_MASK     (1 << 15)
 #define DESC_DPL_SHIFT  13
+#define DESC_DPL_MASK   (1 << DESC_DPL_SHIFT)
 #define DESC_S_MASK     (1 << 12)
 #define DESC_TYPE_SHIFT 8
 #define DESC_A_MASK     (1 << 8)
@@ -149,6 +152,8 @@
 #define HF_VM_SHIFT         17 /* must be same as eflags */
 #define HF_HALTED_SHIFT     18 /* CPU halted */
 #define HF_SMM_SHIFT        19 /* CPU in SMM mode */
+#define HF_GIF_SHIFT        20 /* if set CPU takes interrupts */
+#define HF_HIF_SHIFT        21 /* shadow copy of IF_MASK when in SVM */
 
 #define HF_CPL_MASK          (3 << HF_CPL_SHIFT)
 #define HF_SOFTMMU_MASK      (1 << HF_SOFTMMU_SHIFT)
@@ -166,6 +171,8 @@
 #define HF_OSFXSR_MASK       (1 << HF_OSFXSR_SHIFT)
 #define HF_HALTED_MASK       (1 << HF_HALTED_SHIFT)
 #define HF_SMM_MASK          (1 << HF_SMM_SHIFT)
+#define HF_GIF_MASK          (1 << HF_GIF_SHIFT)
+#define HF_HIF_MASK          (1 << HF_HIF_SHIFT)
 
 #define CR0_PE_MASK  (1 << 0)
 #define CR0_MP_MASK  (1 << 1)
@@ -249,6 +256,8 @@
 #define MSR_GSBASE                      0xc0000101
 #define MSR_KERNELGSBASE                0xc0000102
 
+#define MSR_VM_HSAVE_PA                 0xc0010117
+
 /* cpuid_features bits */
 #define CPUID_FP87 (1 << 0)
 #define CPUID_VME  (1 << 1)
@@ -283,6 +292,8 @@
 #define CPUID_EXT2_FFXSR   (1 << 25)
 #define CPUID_EXT2_LM      (1 << 29)
 
+#define CPUID_EXT3_SVM     (1 << 2)
+
 #define EXCP00_DIVZ	0
 #define EXCP01_SSTP	1
 #define EXCP02_NMI	2
@@ -489,6 +500,16 @@ typedef struct CPUX86State {
     uint32_t sysenter_eip;
     uint64_t efer;
     uint64_t star;
+
+    target_phys_addr_t vm_hsave;
+    target_phys_addr_t vm_vmcb;
+    uint64_t intercept;
+    uint16_t intercept_cr_read;
+    uint16_t intercept_cr_write;
+    uint16_t intercept_dr_read;
+    uint16_t intercept_dr_write;
+    uint32_t intercept_exceptions;
+
 #ifdef TARGET_X86_64
     target_ulong lstar;
     target_ulong cstar;
@@ -530,6 +551,7 @@ typedef struct CPUX86State {
     uint32_t cpuid_xlevel;
     uint32_t cpuid_model[12];
     uint32_t cpuid_ext2_features;
+    uint32_t cpuid_ext3_features;
     uint32_t cpuid_apic_id;
     
 #ifdef USE_KQEMU
target-i386/op.c +80 -2

103 lines not on this page · raw message

target-i386/helper.c +489 -2

545 lines not on this page · raw message

target-i386/svm.h new file +357 -0

357 lines not on this page · raw message

cpu-exec.c +34 -3
Index: qemu/cpu-exec.c
===================================================================
@@ -104,6 +104,9 @@ static TranslationBlock *tb_find_slow(ta
         if (tb->pc == pc && 
             tb->page_addr[0] == phys_page1 &&
             tb->cs_base == cs_base && 
+#if defined(TARGET_I386)
+	    tb->intercept == env->intercept &&
+#endif
             tb->flags == flags) {
             /* check next page if needed */
             if (tb->page_addr[1] != -1) {
@@ -132,6 +135,9 @@ static TranslationBlock *tb_find_slow(ta
     tc_ptr = code_gen_ptr;
     tb->tc_ptr = tc_ptr;
     tb->cs_base = cs_base;
+#if defined(TARGET_I386)
+    tb->intercept = env->intercept;
+#endif
     tb->flags = flags;
     cpu_gen_code(env, tb, CODE_GEN_MAX_SIZE, &code_gen_size);
     code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
@@ -214,7 +220,11 @@ static inline TranslationBlock *tb_find_
 #endif
     tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)];
     if (__builtin_expect(!tb || tb->pc != pc || tb->cs_base != cs_base ||
-                         tb->flags != flags, 0)) {
+                         tb->flags != flags
+#if defined(TARGET_I386)
+			 || tb->intercept != env->intercept
+#endif
+			 , 0)) {
         tb = tb_find_slow(pc, cs_base, flags);
         /* Note: we do it here to avoid a gcc bug on Mac OS X when
            doing it in tb_find_slow */
@@ -373,7 +383,11 @@ int cpu_exec(CPUState *env1)
                 tmp_T0 = T0;
 #endif	    
                 interrupt_request = env->interrupt_request;
-                if (__builtin_expect(interrupt_request, 0)) {
+                if (__builtin_expect(interrupt_request, 0)
+#if defined(TARGET_I386)
+			&& env->hflags & HF_GIF_MASK
+#endif
+				) {
                     if (interrupt_request & CPU_INTERRUPT_DEBUG) {
                         env->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
                         env->exception_index = EXCP_DEBUG;
@@ -391,6 +405,7 @@ int cpu_exec(CPUState *env1)
 #if defined(TARGET_I386)
                     if ((interrupt_request & CPU_INTERRUPT_SMI) &&
                         !(env->hflags & HF_SMM_MASK)) {
+                        svm_check_intercept(SVM_EXIT_SMI);
                         env->interrupt_request &= ~CPU_INTERRUPT_SMI;
                         do_smm_enter();
 #if defined(__sparc__) && !defined(HOST_SOLARIS)
@@ -399,9 +414,10 @@ int cpu_exec(CPUState *env1)
                         T0 = 0;
 #endif
                     } else if ((interrupt_request & CPU_INTERRUPT_HARD) &&
-                        (env->eflags & IF_MASK) && 
+                        (env->eflags & IF_MASK || env->hflags & HF_HIF_MASK) && 
                         !(env->hflags & HF_INHIBIT_IRQ_MASK)) {
                         int intno;
+			svm_check_intercept(SVM_EXIT_INTR);
                         env->interrupt_request &= ~CPU_INTERRUPT_HARD;
                         intno = cpu_get_pic_interrupt(env);
                         if (loglevel & CPU_LOG_TB_IN_ASM) {
@@ -415,6 +431,21 @@ int cpu_exec(CPUState *env1)
 #else
                         T0 = 0;
 #endif
+                    } else if((interrupt_request & CPU_INTERRUPT_VIRQ) &&
+                        (env->eflags & IF_MASK) && !(env->hflags & HF_INHIBIT_IRQ_MASK)) {
+                         int intno;
+                         // FIXME: this should respect TPR
+                         env->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
+                         stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl), ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl)) & ~V_IRQ_MASK);
+                         intno = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_vector));
+                         if (loglevel & CPU_LOG_TB_IN_ASM)
+                             fprintf(logfile, "Servicing virtual hardware INT=0x%02x\n", intno);
+	                 do_interrupt(intno, 0, 0, -1, 1);
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+                         tmp_T0 = 0;
+#else
+                         T0 = 0;
+#endif
                     }
 #elif defined(TARGET_PPC)
 #if 0
target-i386/exec.h +10 -0
Index: qemu/target-i386/exec.h
===================================================================
@@ -501,6 +501,15 @@ void update_fp_status(void);
 void helper_hlt(void);
 void helper_monitor(void);
 void helper_mwait(void);
+void helper_vmrun(target_ulong addr);
+void helper_vmmcall(void);
+void helper_vmload(target_ulong addr);
+void helper_vmsave(target_ulong addr);
+void helper_stgi(void);
+void helper_clgi(void);
+void helper_skinit(void);
+void helper_invlpga(void);
+void vmexit(uint64_t exit_code, uint64_t exit_info_1);
 
 extern const uint8_t parity_table[256];
 extern const uint8_t rclw_table[32];
@@ -588,3 +597,4 @@ static inline int cpu_halted(CPUState *e
     }
     return EXCP_HALTED;
 }
+
exec.c +5 -0
Index: qemu/exec.c
===================================================================
@@ -1281,6 +1281,11 @@ void cpu_abort(CPUState *env, const char
     vfprintf(stderr, fmt, ap);
     fprintf(stderr, "\n");
 #ifdef TARGET_I386
+    if(env->intercept & INTERCEPT_SVM_MASK) {
+	// most probably the virtual machine should not
+	// be shut down but rather caught by the VMM
+        vmexit(SVM_EXIT_SHUTDOWN, 0);
+    }
     cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU | X86_DUMP_CCOP);
 #else
     cpu_dump_state(env, stderr, fprintf, 0);
exec-all.h +1 -0
Index: qemu/exec-all.h
===================================================================
@@ -166,6 +166,7 @@ static inline int tlb_set_page(CPUState 
 typedef struct TranslationBlock {
     target_ulong pc;   /* simulated PC corresponding to this block (EIP + CS base) */
     target_ulong cs_base; /* CS base for this block */
+    uint64_t intercept; /* SVM intercept vector */
     unsigned int flags; /* flags defining in which context the code was generated */
     uint16_t size;      /* size of target code for this block (1 <=
                            size <= TARGET_PAGE_SIZE) */
cpu-all.h +1 -0
Index: qemu/cpu-all.h
===================================================================
@@ -715,6 +715,7 @@ extern int code_copy_enabled;
 #define CPU_INTERRUPT_HALT   0x20 /* CPU halt wanted */
 #define CPU_INTERRUPT_SMI    0x40 /* (x86 only) SMI interrupt pending */
 #define CPU_INTERRUPT_DEBUG  0x80 /* Debug event occured.  */
+#define CPU_INTERRUPT_VIRQ   0x100 /* virtual interrupt pending.  */
 
 void cpu_interrupt(CPUState *s, int mask);
 void cpu_reset_interrupt(CPUState *env, int mask);
#4 lore raw
Quoted 4 lines · Alexander Graf

Thanks to Michael Peter who tried the patch on an x86 host I found some compilation problems.
This updated patch addresses these issues and thus should compile on every platform for every target available.

Tried that on PPC/Linux:

libqemu.a(cpu-exec.o): In function cpu_x86_exec':
/home/ths/qemu/qemu-work/cpu-exec.c:439: undefined reference to ldl_phys' /home/ths/qemu/qemu-work/cpu-exec.c:439: undefined reference to stl_phys' /home/ths/qemu/qemu-work/cpu-exec.c:440: undefined reference to ldl_phys' libqemu.a(op.o): In function op_svm_check_intercept_io':
/home/ths/qemu/qemu-work/target-i386/op.c:1282: undefined reference to stq_phys' libqemu.a(translate-op.o): In function dyngen_code':
/home/ths/qemu/qemu-work/i386-linux-user/./op.h:4101: undefined reference to stq_phys' /home/ths/qemu/qemu-work/i386-linux-user/./op.h:4101: undefined reference to stq_phys'
collect2: ld returned 1 exit status
make[1]: *** [qemu-i386] Error 1
make: *** [subdir-i386-linux-user] Error 2

Thiemo

#5 lore raw
Quoted 24 lines · Thiemo Seufer
Quoted 5 lines · Alexander Graf

Thanks to Michael Peter who tried the patch on an x86 host I found some compilation problems.
This updated patch addresses these issues and thus should compile on every platform for every target available.

Tried that on PPC/Linux:

libqemu.a(cpu-exec.o): In function cpu_x86_exec':
/home/ths/qemu/qemu-work/cpu-exec.c:439: undefined reference to ldl_phys' /home/ths/qemu/qemu-work/cpu-exec.c:439: undefined reference to stl_phys' /home/ths/qemu/qemu-work/cpu-exec.c:440: undefined reference to ldl_phys' libqemu.a(op.o): In function op_svm_check_intercept_io':
/home/ths/qemu/qemu-work/target-i386/op.c:1282: undefined reference to stq_phys' libqemu.a(translate-op.o): In function dyngen_code':
/home/ths/qemu/qemu-work/i386-linux-user/./op.h:4101: undefined reference to stq_phys' /home/ths/qemu/qemu-work/i386-linux-user/./op.h:4101: undefined reference to stq_phys'
collect2: ld returned 1 exit status
make[1]: *** [qemu-i386] Error 1
make: *** [subdir-i386-linux-user] Error 2

Quoted 4 lines

Thiemo

Wow sorry about that, looks like I missed these.

Alex

target-i386/helper2.c +11 -3
Index: qemu-0.9.0.cvs/target-i386/helper2.c
===================================================================
@@ -27,6 +27,7 @@
 
 #include "cpu.h"
 #include "exec-all.h"
+#include "svm.h"
 
 //#define DEBUG_MMU
 
@@ -111,10 +112,11 @@ CPUX86State *cpu_x86_init(void)
                                CPUID_CX8 | CPUID_PGE | CPUID_CMOV |
                                CPUID_PAT);
         env->pat = 0x0007040600070406ULL;
+        env->cpuid_ext3_features = CPUID_EXT3_SVM;
         env->cpuid_ext_features = CPUID_EXT_SSE3;
         env->cpuid_features |= CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | CPUID_PAE | CPUID_SEP;
         env->cpuid_features |= CPUID_APIC;
-        env->cpuid_xlevel = 0;
+        env->cpuid_xlevel = 0x8000000a;
         {
             const char *model_id = "QEMU Virtual CPU version " QEMU_VERSION;
             int c, len, i;
@@ -131,7 +133,6 @@ CPUX86State *cpu_x86_init(void)
         /* currently not enabled for std i386 because not fully tested */
         env->cpuid_ext2_features = (env->cpuid_features & 0x0183F3FF);
         env->cpuid_ext2_features |= CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX;
-        env->cpuid_xlevel = 0x80000008;
 
         /* these features are needed for Win64 and aren't fully implemented */
         env->cpuid_features |= CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA;
@@ -160,6 +161,7 @@ void cpu_reset(CPUX86State *env)
 #ifdef CONFIG_SOFTMMU
     env->hflags |= HF_SOFTMMU_MASK;
 #endif
+    env->hflags |= HF_GIF_MASK;
 
     cpu_x86_update_cr0(env, 0x60000010);
     env->a20_mask = 0xffffffff;
@@ -863,7 +865,6 @@ int cpu_x86_handle_mmu_fault(CPUX86State
  do_fault_protect:
     error_code = PG_ERROR_P_MASK;
  do_fault:
-    env->cr[2] = addr;
     error_code |= (is_write << PG_ERROR_W_BIT);
     if (is_user)
         error_code |= PG_ERROR_U_MASK;
@@ -871,8 +872,15 @@ int cpu_x86_handle_mmu_fault(CPUX86State
         (env->efer & MSR_EFER_NXE) && 
         (env->cr[4] & CR4_PAE_MASK))
         error_code |= PG_ERROR_I_D_MASK;
+    if (INTERCEPTEDl(_exceptions, 1 << EXCP0E_PAGE)) {
+        stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), addr);
+    } else {
+        env->cr[2] = addr;
+    }
     env->error_code = error_code;
     env->exception_index = EXCP0E_PAGE;
+    if (INTERCEPTEDl(_exceptions, 1 << EXCP0E_PAGE)) // the VMM will handle this
+        return 2;
     return 1;
 }
 
target-i386/translate.c +179 -6

434 lines not on this page · raw message

target-i386/cpu.h +22 -0
Index: qemu-0.9.0.cvs/target-i386/cpu.h
===================================================================
@@ -46,6 +46,8 @@
 
 #include "softfloat.h"
 
+#include "svm.h"
+
 #if defined(__i386__) && !defined(CONFIG_SOFTMMU) && !defined(__APPLE__)
 #define USE_CODE_COPY
 #endif
@@ -84,6 +86,7 @@
 #define DESC_AVL_MASK   (1 << 20)
 #define DESC_P_MASK     (1 << 15)
 #define DESC_DPL_SHIFT  13
+#define DESC_DPL_MASK   (1 << DESC_DPL_SHIFT)
 #define DESC_S_MASK     (1 << 12)
 #define DESC_TYPE_SHIFT 8
 #define DESC_A_MASK     (1 << 8)
@@ -149,6 +152,8 @@
 #define HF_VM_SHIFT         17 /* must be same as eflags */
 #define HF_HALTED_SHIFT     18 /* CPU halted */
 #define HF_SMM_SHIFT        19 /* CPU in SMM mode */
+#define HF_GIF_SHIFT        20 /* if set CPU takes interrupts */
+#define HF_HIF_SHIFT        21 /* shadow copy of IF_MASK when in SVM */
 
 #define HF_CPL_MASK          (3 << HF_CPL_SHIFT)
 #define HF_SOFTMMU_MASK      (1 << HF_SOFTMMU_SHIFT)
@@ -166,6 +171,8 @@
 #define HF_OSFXSR_MASK       (1 << HF_OSFXSR_SHIFT)
 #define HF_HALTED_MASK       (1 << HF_HALTED_SHIFT)
 #define HF_SMM_MASK          (1 << HF_SMM_SHIFT)
+#define HF_GIF_MASK          (1 << HF_GIF_SHIFT)
+#define HF_HIF_MASK          (1 << HF_HIF_SHIFT)
 
 #define CR0_PE_MASK  (1 << 0)
 #define CR0_MP_MASK  (1 << 1)
@@ -249,6 +256,8 @@
 #define MSR_GSBASE                      0xc0000101
 #define MSR_KERNELGSBASE                0xc0000102
 
+#define MSR_VM_HSAVE_PA                 0xc0010117
+
 /* cpuid_features bits */
 #define CPUID_FP87 (1 << 0)
 #define CPUID_VME  (1 << 1)
@@ -283,6 +292,8 @@
 #define CPUID_EXT2_FFXSR   (1 << 25)
 #define CPUID_EXT2_LM      (1 << 29)
 
+#define CPUID_EXT3_SVM     (1 << 2)
+
 #define EXCP00_DIVZ	0
 #define EXCP01_SSTP	1
 #define EXCP02_NMI	2
@@ -489,6 +500,16 @@ typedef struct CPUX86State {
     uint32_t sysenter_eip;
     uint64_t efer;
     uint64_t star;
+
+    target_phys_addr_t vm_hsave;
+    target_phys_addr_t vm_vmcb;
+    uint64_t intercept;
+    uint16_t intercept_cr_read;
+    uint16_t intercept_cr_write;
+    uint16_t intercept_dr_read;
+    uint16_t intercept_dr_write;
+    uint32_t intercept_exceptions;
+
 #ifdef TARGET_X86_64
     target_ulong lstar;
     target_ulong cstar;
@@ -530,6 +551,7 @@ typedef struct CPUX86State {
     uint32_t cpuid_xlevel;
     uint32_t cpuid_model[12];
     uint32_t cpuid_ext2_features;
+    uint32_t cpuid_ext3_features;
     uint32_t cpuid_apic_id;
     
 #ifdef USE_KQEMU
target-i386/op.c +82 -2

105 lines not on this page · raw message

target-i386/helper.c +489 -2

545 lines not on this page · raw message

target-i386/svm.h new file +357 -0

357 lines not on this page · raw message

cpu-exec.c +36 -3
Index: qemu-0.9.0.cvs/cpu-exec.c
===================================================================
@@ -104,6 +104,9 @@ static TranslationBlock *tb_find_slow(ta
         if (tb->pc == pc && 
             tb->page_addr[0] == phys_page1 &&
             tb->cs_base == cs_base && 
+#if defined(TARGET_I386)
+	    tb->intercept == env->intercept &&
+#endif
             tb->flags == flags) {
             /* check next page if needed */
             if (tb->page_addr[1] != -1) {
@@ -132,6 +135,9 @@ static TranslationBlock *tb_find_slow(ta
     tc_ptr = code_gen_ptr;
     tb->tc_ptr = tc_ptr;
     tb->cs_base = cs_base;
+#if defined(TARGET_I386)
+    tb->intercept = env->intercept;
+#endif
     tb->flags = flags;
     cpu_gen_code(env, tb, CODE_GEN_MAX_SIZE, &code_gen_size);
     code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
@@ -218,7 +224,11 @@ static inline TranslationBlock *tb_find_
 #endif
     tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)];
     if (__builtin_expect(!tb || tb->pc != pc || tb->cs_base != cs_base ||
-                         tb->flags != flags, 0)) {
+                         tb->flags != flags
+#if defined(TARGET_I386)
+			 || tb->intercept != env->intercept
+#endif
+			 , 0)) {
         tb = tb_find_slow(pc, cs_base, flags);
         /* Note: we do it here to avoid a gcc bug on Mac OS X when
            doing it in tb_find_slow */
@@ -386,7 +396,11 @@ int cpu_exec(CPUState *env1)
                 tmp_T0 = T0;
 #endif	    
                 interrupt_request = env->interrupt_request;
-                if (__builtin_expect(interrupt_request, 0)) {
+                if (__builtin_expect(interrupt_request, 0)
+#if defined(TARGET_I386)
+			&& env->hflags & HF_GIF_MASK
+#endif
+				) {
                     if (interrupt_request & CPU_INTERRUPT_DEBUG) {
                         env->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
                         env->exception_index = EXCP_DEBUG;
@@ -404,6 +418,7 @@ int cpu_exec(CPUState *env1)
 #if defined(TARGET_I386)
                     if ((interrupt_request & CPU_INTERRUPT_SMI) &&
                         !(env->hflags & HF_SMM_MASK)) {
+                        svm_check_intercept(SVM_EXIT_SMI);
                         env->interrupt_request &= ~CPU_INTERRUPT_SMI;
                         do_smm_enter();
 #if defined(__sparc__) && !defined(HOST_SOLARIS)
@@ -412,9 +427,10 @@ int cpu_exec(CPUState *env1)
                         T0 = 0;
 #endif
                     } else if ((interrupt_request & CPU_INTERRUPT_HARD) &&
-                        (env->eflags & IF_MASK) && 
+                        (env->eflags & IF_MASK || env->hflags & HF_HIF_MASK) && 
                         !(env->hflags & HF_INHIBIT_IRQ_MASK)) {
                         int intno;
+			svm_check_intercept(SVM_EXIT_INTR);
                         env->interrupt_request &= ~CPU_INTERRUPT_HARD;
                         intno = cpu_get_pic_interrupt(env);
                         if (loglevel & CPU_LOG_TB_IN_ASM) {
@@ -428,6 +444,23 @@ int cpu_exec(CPUState *env1)
 #else
                         T0 = 0;
 #endif
+#if !defined(CONFIG_USER_ONLY)
+                    } else if((interrupt_request & CPU_INTERRUPT_VIRQ) &&
+                        (env->eflags & IF_MASK) && !(env->hflags & HF_INHIBIT_IRQ_MASK)) {
+                         int intno;
+                         // FIXME: this should respect TPR
+                         env->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
+                         stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl), ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl)) & ~V_IRQ_MASK);
+                         intno = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_vector));
+                         if (loglevel & CPU_LOG_TB_IN_ASM)
+                             fprintf(logfile, "Servicing virtual hardware INT=0x%02x\n", intno);
+	                 do_interrupt(intno, 0, 0, -1, 1);
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+                         tmp_T0 = 0;
+#else
+                         T0 = 0;
+#endif
+#endif
                     }
 #elif defined(TARGET_PPC)
 #if 0
target-i386/exec.h +10 -0
Index: qemu-0.9.0.cvs/target-i386/exec.h
===================================================================
@@ -502,6 +502,15 @@ void update_fp_status(void);
 void helper_hlt(void);
 void helper_monitor(void);
 void helper_mwait(void);
+void helper_vmrun(target_ulong addr);
+void helper_vmmcall(void);
+void helper_vmload(target_ulong addr);
+void helper_vmsave(target_ulong addr);
+void helper_stgi(void);
+void helper_clgi(void);
+void helper_skinit(void);
+void helper_invlpga(void);
+void vmexit(uint64_t exit_code, uint64_t exit_info_1);
 
 extern const uint8_t parity_table[256];
 extern const uint8_t rclw_table[32];
@@ -589,3 +598,4 @@ static inline int cpu_halted(CPUState *e
     }
     return EXCP_HALTED;
 }
+
exec.c +5 -0
Index: qemu-0.9.0.cvs/exec.c
===================================================================
@@ -1295,6 +1295,11 @@ void cpu_abort(CPUState *env, const char
     vfprintf(stderr, fmt, ap);
     fprintf(stderr, "\n");
 #ifdef TARGET_I386
+    if(env->intercept & INTERCEPT_SVM_MASK) {
+	// most probably the virtual machine should not
+	// be shut down but rather caught by the VMM
+        vmexit(SVM_EXIT_SHUTDOWN, 0);
+    }
     cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU | X86_DUMP_CCOP);
 #else
     cpu_dump_state(env, stderr, fprintf, 0);
exec-all.h +1 -0
Index: qemu-0.9.0.cvs/exec-all.h
===================================================================
@@ -166,6 +166,7 @@ static inline int tlb_set_page(CPUState 
 typedef struct TranslationBlock {
     target_ulong pc;   /* simulated PC corresponding to this block (EIP + CS base) */
     target_ulong cs_base; /* CS base for this block */
+    uint64_t intercept; /* SVM intercept vector */
     unsigned int flags; /* flags defining in which context the code was generated */
     uint16_t size;      /* size of target code for this block (1 <=
                            size <= TARGET_PAGE_SIZE) */
cpu-all.h +1 -0
Index: qemu-0.9.0.cvs/cpu-all.h
===================================================================
@@ -715,6 +715,7 @@ extern int code_copy_enabled;
 #define CPU_INTERRUPT_HALT   0x20 /* CPU halt wanted */
 #define CPU_INTERRUPT_SMI    0x40 /* (x86 only) SMI interrupt pending */
 #define CPU_INTERRUPT_DEBUG  0x80 /* Debug event occured.  */
+#define CPU_INTERRUPT_VIRQ   0x100 /* virtual interrupt pending.  */
 
 void cpu_interrupt(CPUState *s, int mask);
 void cpu_reset_interrupt(CPUState *env, int mask);
#6 lore raw
Quoted 9 lines · Alexander Graf
Quoted 8 lines · Thiemo Seufer
Quoted 5 lines · Alexander Graf

Thanks to Michael Peter who tried the patch on an x86 host I found some compilation problems.
This updated patch addresses these issues and thus should compile on every platform for every target available.

[...]

Quoted 4 lines
Quoted 3 lines

Wow sorry about that, looks like I missed these.

exec-all.h +2 -0
Index: qemu-0.9.0.cvs/exec-all.h
===================================================================
@@ -166,6 +166,7 @@ static inline int tlb_set_page(CPUState 
 typedef struct TranslationBlock {
     target_ulong pc;   /* simulated PC corresponding to this block (EIP
+ CS base) */
     target_ulong cs_base; /* CS base for this block */
+    uint64_t intercept; /* SVM intercept vector */
     unsigned int flags; /* flags defining in which context the code was

generated */
uint16_t size; /* size of target code for this block (1 <=
size <= TARGET_PAGE_SIZE) */

cpu-all.h +0 -0
Index: qemu-0.9.0.cvs/cpu-all.h
===================================================================
@@ -715,6 +715,7 @@ extern int code_copy_enabled;
 #define CPU_INTERRUPT_HALT   0x20 /* CPU halt wanted */
 #define CPU_INTERRUPT_SMI    0x40 /* (x86 only) SMI interrupt pending

*/
#define CPU_INTERRUPT_DEBUG 0x80 /* Debug event occured. */
+#define CPU_INTERRUPT_VIRQ 0x100 /* virtual interrupt pending. */

void cpu_interrupt(CPUState *s, int mask); void cpu_reset_interrupt(CPUState *env, int mask);

Those two patches look ugly to me as target specific features should never go in generic code or structures.
The CPU_INTERRUPT flags should just contain information about the emulator behavior, thus CPU_INTERRUPT_TIMER, CPU_INTERRUPT_SMI are to be removed. Target specific informations about the exception nature should go in the CPUState structure... Then, adding a CPU_INTERRUPT_VIRQ seems not a good idea at all: it's outside of the generic emulator scope and pointless for most targets.
For the same reason, the intercept field in the TB structure seems not acceptable, as TB specific target informations are already to be stored in the flags field. As intercept seems only to be a bit field, it should go, in a way or another, in tb flags. And as it seems that some interceptions are related with functions implemented in helpers (not micro-ops), you'd better check the interception in the helper at runtime, which would add no visible overhead (as calling a helper is slow compared to direct micro-ops execution), then you would not need to store those infos in the TB structure. This may even make the emulation run faster has you won't fill the TB cache with multiple translation of the same code each time the env->intercept changes, thus have chance to avoid many TB caches flushes.

Regards.

Signature · 2 lines
J. Mayer <[email protected]>
Never organized
#7 lore raw
Quoted 49 lines · J. Mayer
Quoted 11 lines · Alexander Graf
Quoted 9 lines · Thiemo Seufer
Quoted 6 lines · Alexander Graf

Thanks to Michael Peter who tried the patch on an x86 host I found some compilation problems.
This updated patch addresses these issues and thus should compile on every platform for every target available.

[...]

Quoted 4 lines
Quoted 2 lines

Wow sorry about that, looks like I missed these.

Index: qemu-0.9.0.cvs/exec-all.h
===================================================================

exec-all.h +2 -0
@@ -166,6 +166,7 @@ static inline int tlb_set_page(CPUState 
 typedef struct TranslationBlock {
     target_ulong pc;   /* simulated PC corresponding to this block (EIP
+ CS base) */
     target_ulong cs_base; /* CS base for this block */
+    uint64_t intercept; /* SVM intercept vector */
     unsigned int flags; /* flags defining in which context the code was

generated */
uint16_t size; /* size of target code for this block (1 <=
size <= TARGET_PAGE_SIZE) */
Index: qemu-0.9.0.cvs/cpu-all.h
===================================================================

cpu-all.h +1 -0
@@ -715,6 +715,7 @@ extern int code_copy_enabled;
 #define CPU_INTERRUPT_HALT   0x20 /* CPU halt wanted */
 #define CPU_INTERRUPT_SMI    0x40 /* (x86 only) SMI interrupt pending
 */
 #define CPU_INTERRUPT_DEBUG  0x80 /* Debug event occured.  */
+#define CPU_INTERRUPT_VIRQ   0x100 /* virtual interrupt pending.  */
 
 void cpu_interrupt(CPUState *s, int mask);

void cpu_reset_interrupt(CPUState *env, int mask);

Quoted 9 lines

Those two patches look ugly to me as target specific features should never go in generic code or structures.
The CPU_INTERRUPT flags should just contain information about the emulator behavior, thus CPU_INTERRUPT_TIMER, CPU_INTERRUPT_SMI are to be removed. Target specific informations about the exception nature should go in the CPUState structure... Then, adding a CPU_INTERRUPT_VIRQ seems not a good idea at all: it's outside of the generic emulator scope and pointless for most targets.

I don't see any practical reason not to do it this way. We could define a CPU_INTERRUPT_TARGET interrupt, that stores the information in a the target specific CPUState, but this would hit performance (marginally though) and does not improve the situation. I don't think that we should should forcefully try to seperate targets where we are not even close to running out of constants. If everyone on this list sees the situation as Jocelyn does, I would be fine with writing a patch that puts target specific interrupts to the specific targets.

Quoted 5 lines

For the same reason, the intercept field in the TB structure seems not acceptable, as TB specific target informations are already to be stored in the flags field. As intercept seems only to be a bit field, it should go, in a way or another, in tb flags. And as it seems that some

TB flags is a 32-bit bitfield, where several bits are already used. Currently SVM supports 45 intercepts and each combination can generate a different TB, as we do not want to reduce performance for the non-intercepted case. Think of the intercept field as flags2, that could be used by other virtualization implementations on other architectures too (though I do not know of any)

Quoted 6 lines

interceptions are related with functions implemented in helpers (not micro-ops), you'd better check the interception in the helper at runtime, which would add no visible overhead (as calling a helper is slow compared to direct micro-ops execution), then you would not need to store those infos in the TB structure. This may even make the emulation

This was the case in an earlier version of the patch. The current implementation you see is an optimization to make it running faster, as there is (nearly) no need for runtime detection.

Quoted 4 lines

run faster has you won't fill the TB cache with multiple translation of the same code each time the env->intercept changes, thus have chance to avoid many TB caches flushes.

This is actually intended, as I believe the emulated machine should run as fast as before when not using any interceptions

Quoted 3 lines

Regards.

Regards,

Alex

#8 lore raw
Quoted 59 lines · Alexander Graf
Quoted 49 lines · J. Mayer
Quoted 11 lines · Alexander Graf
Quoted 9 lines · Thiemo Seufer
Quoted 6 lines · Alexander Graf

Thanks to Michael Peter who tried the patch on an x86 host I found some compilation problems.
This updated patch addresses these issues and thus should compile on every platform for every target available.

[...]

Quoted 4 lines
Quoted 2 lines

Wow sorry about that, looks like I missed these.

Index: qemu-0.9.0.cvs/exec-all.h
===================================================================

exec-all.h +2 -0
@@ -166,6 +166,7 @@ static inline int tlb_set_page(CPUState 
 typedef struct TranslationBlock {
     target_ulong pc;   /* simulated PC corresponding to this block (EIP
+ CS base) */
     target_ulong cs_base; /* CS base for this block */
+    uint64_t intercept; /* SVM intercept vector */
     unsigned int flags; /* flags defining in which context the code was

generated */
uint16_t size; /* size of target code for this block (1 <=
size <= TARGET_PAGE_SIZE) */
Index: qemu-0.9.0.cvs/cpu-all.h
===================================================================

cpu-all.h +1 -0
@@ -715,6 +715,7 @@ extern int code_copy_enabled;
 #define CPU_INTERRUPT_HALT   0x20 /* CPU halt wanted */
 #define CPU_INTERRUPT_SMI    0x40 /* (x86 only) SMI interrupt pending
 */
 #define CPU_INTERRUPT_DEBUG  0x80 /* Debug event occured.  */
+#define CPU_INTERRUPT_VIRQ   0x100 /* virtual interrupt pending.  */
 
 void cpu_interrupt(CPUState *s, int mask);

void cpu_reset_interrupt(CPUState *env, int mask);

Quoted 9 lines

Those two patches look ugly to me as target specific features should never go in generic code or structures.
The CPU_INTERRUPT flags should just contain information about the emulator behavior, thus CPU_INTERRUPT_TIMER, CPU_INTERRUPT_SMI are to be removed. Target specific informations about the exception nature should go in the CPUState structure... Then, adding a CPU_INTERRUPT_VIRQ seems not a good idea at all: it's outside of the generic emulator scope and pointless for most targets.

Quoted 8 lines

I don't see any practical reason not to do it this way. We could define a CPU_INTERRUPT_TARGET interrupt, that stores the information in a the target specific CPUState, but this would hit performance (marginally though) and does not improve the situation. I don't think that we should should forcefully try to seperate targets where we are not even close to running out of constants. If everyone on this list sees the situation as Jocelyn does, I would be fine with writing a patch that puts target specific interrupts to the specific targets.

I was to do the same to add some functionality to the PowerPC target, long time ago, and Fabrice Bellard convinced me not to do and agreed it has been a bad idea to add the target specific CPU_INTERRUPT_xxx flags. Then I did manage the PowerPC target to use only generic CPU_INTERRUPT_xxx and put all other target specific informations in the CPUState structure. It absolutelly changes nothing in terms of functionality nor performance. The only thing is that it makes the generic parts simpler and could even allow the cpu_exec function to contain almost no target specific code, which would really great imho.

Quoted 11 lines
Quoted 5 lines

For the same reason, the intercept field in the TB structure seems not acceptable, as TB specific target informations are already to be stored in the flags field. As intercept seems only to be a bit field, it should go, in a way or another, in tb flags. And as it seems that some

TB flags is a 32-bit bitfield, where several bits are already used. Currently SVM supports 45 intercepts and each combination can generate a different TB, as we do not want to reduce performance for the non-intercepted case. Think of the intercept field as flags2, that could be used by other virtualization implementations on other architectures too (though I do not know of any)

So, why not make it generic and extend the flag field to as many bits as you need ? Intercept is x86 specific and has no meaning outside of this scope. Having more bits for flags is generic and may be useful for some other targets... The easy way is to convert flag to 64 bits, if you have enough bits. Another way is to make it a table, but this may need changes in all targets code...

Quoted 9 lines
Quoted 6 lines

interceptions are related with functions implemented in helpers (not micro-ops), you'd better check the interception in the helper at runtime, which would add no visible overhead (as calling a helper is slow compared to direct micro-ops execution), then you would not need to store those infos in the TB structure. This may even make the emulation

This was the case in an earlier version of the patch. The current implementation you see is an optimization to make it running faster, as there is (nearly) no need for runtime detection.

For all interceptions related to operations in helpers, it's not a actual optimisation. Things done in helpers are slow and not widely used (compared to the translated code, I mean), then adding a single test for interception in a helper may not hurt global performances. You may need TB flags only for operations you want to intercept that would be inlined in the translated code, then maybe reduce the number of bits needed without decreasing performanced. But, not knowing a lot about x86 VM specification I may be wrong...

Quoted 6 lines
Quoted 4 lines

run faster has you won't fill the TB cache with multiple translation of the same code each time the env->intercept changes, thus have chance to avoid many TB caches flushes.

This is actually intended, as I believe the emulated machine should run as fast as before when not using any interceptions

It depends if those flags are to be changed often or not, which depends on the hypervisor software in use I guess. If the flags never change, there'll be no TB flush, I agree...

Regards.

Signature · 1 line
Jocelyn Mayer <[email protected]>
#9 lore raw
Quoted 78 lines · Jocelyn Mayer
Quoted 66 lines · Alexander Graf
Quoted 54 lines · J. Mayer
Quoted 14 lines · Alexander Graf
Quoted 11 lines · Thiemo Seufer
Quoted 7 lines · Alexander Graf

Thanks to Michael Peter who tried the patch on an x86 host I found some compilation problems.
This updated patch addresses these issues and thus should compile on every platform for every target available.

[...]

Quoted 6 lines
Quoted 3 lines

Wow sorry about that, looks like I missed these.

Index: qemu-0.9.0.cvs/exec-all.h
===================================================================

exec-all.h +2 -0
@@ -166,6 +166,7 @@ static inline int tlb_set_page(CPUState 
 typedef struct TranslationBlock {
     target_ulong pc;   /* simulated PC corresponding to this block (EIP
+ CS base) */
     target_ulong cs_base; /* CS base for this block */
+    uint64_t intercept; /* SVM intercept vector */
     unsigned int flags; /* flags defining in which context the code was

generated */
uint16_t size; /* size of target code for this block (1 <=
size <= TARGET_PAGE_SIZE) */
Index: qemu-0.9.0.cvs/cpu-all.h
===================================================================

cpu-all.h +1 -0
@@ -715,6 +715,7 @@ extern int code_copy_enabled;
 #define CPU_INTERRUPT_HALT   0x20 /* CPU halt wanted */
 #define CPU_INTERRUPT_SMI    0x40 /* (x86 only) SMI interrupt pending
 */
 #define CPU_INTERRUPT_DEBUG  0x80 /* Debug event occured.  */
+#define CPU_INTERRUPT_VIRQ   0x100 /* virtual interrupt pending.  */
 
 void cpu_interrupt(CPUState *s, int mask);

void cpu_reset_interrupt(CPUState *env, int mask);

Quoted 10 lines

Those two patches look ugly to me as target specific features should never go in generic code or structures.
The CPU_INTERRUPT flags should just contain information about the emulator behavior, thus CPU_INTERRUPT_TIMER, CPU_INTERRUPT_SMI are to be removed. Target specific informations about the exception nature should go in the CPUState structure... Then, adding a CPU_INTERRUPT_VIRQ seems not a good idea at all: it's outside of the generic emulator scope and pointless for most targets.

Quoted 9 lines

I don't see any practical reason not to do it this way. We could define a CPU_INTERRUPT_TARGET interrupt, that stores the information in a the target specific CPUState, but this would hit performance (marginally though) and does not improve the situation. I don't think that we should should forcefully try to seperate targets where we are not even close to running out of constants. If everyone on this list sees the situation as Jocelyn does, I would be fine with writing a patch that puts target specific interrupts to the specific targets.

Quoted 10 lines

I was to do the same to add some functionality to the PowerPC target, long time ago, and Fabrice Bellard convinced me not to do and agreed it has been a bad idea to add the target specific CPU_INTERRUPT_xxx flags. Then I did manage the PowerPC target to use only generic CPU_INTERRUPT_xxx and put all other target specific informations in the CPUState structure. It absolutelly changes nothing in terms of functionality nor performance. The only thing is that it makes the generic parts simpler and could even allow the cpu_exec function to contain almost no target specific code, which would really great imho.

I can give that a try :-). Sounds reasonable for me.

Quoted 15 lines
Quoted 7 lines
Quoted 7 lines

For the same reason, the intercept field in the TB structure seems not acceptable, as TB specific target informations are already to be stored in the flags field. As intercept seems only to be a bit field, it should go, in a way or another, in tb flags. And as it seems that some

Quoted 7 lines

TB flags is a 32-bit bitfield, where several bits are already used. Currently SVM supports 45 intercepts and each combination can generate a different TB, as we do not want to reduce performance for the non-intercepted case. Think of the intercept field as flags2, that could be used by other virtualization implementations on other architectures too (though I do not know of any)

Quoted 7 lines

So, why not make it generic and extend the flag field to as many bits as you need ? Intercept is x86 specific and has no meaning outside of this scope. Having more bits for flags is generic and may be useful for some other targets... The easy way is to convert flag to 64 bits, if you have enough bits. Another way is to make it a table, but this may need changes in all targets code...

I think it might be quite a bad idea to extend flags until everything fits in there. I'm planning on implementing VMX as well and there are intercepts too so the meaning of the different fields change and I would not want that happening with the normal flags vector. What would you think of an #ifdef TARGET_I386? This way intercepts are x86 only and don't interfere with any other target architecture, but are still seperated from the flags (which is semantically correct as far as it goes for me).

Quoted 13 lines
Quoted 8 lines
Quoted 8 lines

interceptions are related with functions implemented in helpers (not micro-ops), you'd better check the interception in the helper at runtime, which would add no visible overhead (as calling a helper is slow compared to direct micro-ops execution), then you would not need to store those infos in the TB structure. This may even make the emulation

Quoted 4 lines

This was the case in an earlier version of the patch. The current implementation you see is an optimization to make it running faster, as there is (nearly) no need for runtime detection.

Quoted 10 lines

For all interceptions related to operations in helpers, it's not a actual optimisation. Things done in helpers are slow and not widely used (compared to the translated code, I mean), then adding a single test for interception in a helper may not hurt global performances. You may need TB flags only for operations you want to intercept that would be inlined in the translated code, then maybe reduce the number of bits needed without decreasing performanced. But, not knowing a lot about x86 VM specification I may be wrong...

I feel really uncomfortable with supporting hundreds of different ways of interception. It took me days to take all the intercept checks out of the helpers into the translate.c, as it is way easier to maintain and read that way. I do not like the idea of converting the intercept vector to something internal. It's quite nice to be able to use the very same constants as kvm does. This makes debugging really easy and you don't have to define everything twice. Additionally this way you can just set a variable without calculating anything, which does come in handy as well.

Basically the reason I really want to have everyting in translate.c is simplicity though.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

run faster has you won't fill the TB cache with multiple translation of the same code each time the env->intercept changes, thus have chance to avoid many TB caches flushes.

Quoted 8 lines
Quoted 3 lines

This is actually intended, as I believe the emulated machine should run as fast as before when not using any interceptions

It depends if those flags are to be changed often or not, which depends on the hypervisor software in use I guess. If the flags never change, there'll be no TB flush, I agree...

Usually (at least that's the case with kvm) you have two different sets of intercept flags. One for the real machine and one for the virtualized one. There is only one minor situation where that might change but that's not worth mentioning here. Nevertheless TBs point to physical memory (afaik) so since the virtual kernel is on a different page than the real one, we could not reuse TBs anyway so having the intercept information in the TB only helps in finding out which intercepts to check for as only a minority of the possible intercepts actually get used.

Regards,

Alex

#10 lore raw
Quoted 25 lines · Alexander Graf
Quoted 11 lines · Jocelyn Mayer
Quoted 11 lines

I don't see any practical reason not to do it this way. We could define a CPU_INTERRUPT_TARGET interrupt, that stores the information in a the target specific CPUState, but this would hit performance (marginally though) and does not improve the situation. I don't think that we should should forcefully try to seperate targets where we are not even close to running out of constants. If everyone on this list sees the situation as Jocelyn does, I would be fine with writing a patch that puts target specific interrupts to the specific targets.

Quoted 11 lines

I was to do the same to add some functionality to the PowerPC target, long time ago, and Fabrice Bellard convinced me not to do and agreed it has been a bad idea to add the target specific CPU_INTERRUPT_xxx flags. Then I did manage the PowerPC target to use only generic CPU_INTERRUPT_xxx and put all other target specific informations in the CPUState structure. It absolutelly changes nothing in terms of functionality nor performance. The only thing is that it makes the generic parts simpler and could even allow the cpu_exec function to contain almost no target specific code, which would really great imho.

Quoted 3 lines

I can give that a try :-). Sounds reasonable for me.

Oh well I just thought about this a bit more and while stumbling across CPU_INTERRUPT_FIQ which does the same thing one major problem came to me on that one: Priority. Real interrupts have priority over virtual interrupts so the VIRQs have to be processed after HARD IRQs, whereas SMIs and NMIs have to be taken care of before the HARD IRQs. It simply wouldn't work out to generalize the IRQs, just as it would not work with the VIRQ, as it has to be handled as a real IRQ but without accessing the APIC which has to be done for HARD IRQs.

I guess we're stuck with what's there now.

Greetings,

Alex

#11 lore raw
In reply to Alexander Graf
Quoted 102 lines · Alexander Graf
Quoted 78 lines · Jocelyn Mayer
Quoted 66 lines · Alexander Graf
Quoted 54 lines · J. Mayer
Quoted 14 lines · Alexander Graf
Quoted 11 lines · Thiemo Seufer
Quoted 7 lines · Alexander Graf

Thanks to Michael Peter who tried the patch on an x86 host I found some compilation problems.
This updated patch addresses these issues and thus should compile on every platform for every target available.

[...]

Quoted 6 lines
Quoted 3 lines

Wow sorry about that, looks like I missed these.

Index: qemu-0.9.0.cvs/exec-all.h
===================================================================

exec-all.h +2 -0
@@ -166,6 +166,7 @@ static inline int tlb_set_page(CPUState 
 typedef struct TranslationBlock {
     target_ulong pc;   /* simulated PC corresponding to this block (EIP
+ CS base) */
     target_ulong cs_base; /* CS base for this block */
+    uint64_t intercept; /* SVM intercept vector */
     unsigned int flags; /* flags defining in which context the code was

generated */
uint16_t size; /* size of target code for this block (1 <=
size <= TARGET_PAGE_SIZE) */
Index: qemu-0.9.0.cvs/cpu-all.h
===================================================================

cpu-all.h +1 -0
@@ -715,6 +715,7 @@ extern int code_copy_enabled;
 #define CPU_INTERRUPT_HALT   0x20 /* CPU halt wanted */
 #define CPU_INTERRUPT_SMI    0x40 /* (x86 only) SMI interrupt pending
 */
 #define CPU_INTERRUPT_DEBUG  0x80 /* Debug event occured.  */
+#define CPU_INTERRUPT_VIRQ   0x100 /* virtual interrupt pending.  */
 
 void cpu_interrupt(CPUState *s, int mask);

void cpu_reset_interrupt(CPUState *env, int mask);

Quoted 10 lines

Those two patches look ugly to me as target specific features should never go in generic code or structures.
The CPU_INTERRUPT flags should just contain information about the emulator behavior, thus CPU_INTERRUPT_TIMER, CPU_INTERRUPT_SMI are to be removed. Target specific informations about the exception nature should go in the CPUState structure... Then, adding a CPU_INTERRUPT_VIRQ seems not a good idea at all: it's outside of the generic emulator scope and pointless for most targets.

Quoted 9 lines

I don't see any practical reason not to do it this way. We could define a CPU_INTERRUPT_TARGET interrupt, that stores the information in a the target specific CPUState, but this would hit performance (marginally though) and does not improve the situation. I don't think that we should should forcefully try to seperate targets where we are not even close to running out of constants. If everyone on this list sees the situation as Jocelyn does, I would be fine with writing a patch that puts target specific interrupts to the specific targets.

Quoted 10 lines

I was to do the same to add some functionality to the PowerPC target, long time ago, and Fabrice Bellard convinced me not to do and agreed it has been a bad idea to add the target specific CPU_INTERRUPT_xxx flags. Then I did manage the PowerPC target to use only generic CPU_INTERRUPT_xxx and put all other target specific informations in the CPUState structure. It absolutelly changes nothing in terms of functionality nor performance. The only thing is that it makes the generic parts simpler and could even allow the cpu_exec function to contain almost no target specific code, which would really great imho.

I can give that a try :-). Sounds reasonable for me.

[Next message]
Oh well I just thought about this a bit more and while stumbling across CPU_INTERRUPT_FIQ which does the same thing one major problem came to me on that one: Priority. Real interrupts have priority over virtual interrupts so the VIRQs have to be processed after HARD IRQs, whereas SMIs and NMIs have to be taken care of before the HARD IRQs. It simply wouldn't work out to generalize the IRQs, just as it would not work with the VIRQ, as it has to be handled as a real IRQ but without accessing the APIC which has to be done for HARD IRQs.

Quoted 1 line

I guess we're stuck with what's there now.

Priorities are not an issue, you can take a look to the ppc_hw_interrupt function to see how the PowerPC emulation code manages priorities and masking between 10 different hardware interruption sources. The code could be better (I wanted to preserve existing code) but it's a solution that actually works... But looking better the x86 emulation code, I agree that if you don't want to change it too much, you have to add a flag. The other solution (and best, imho) would be to do the same as what has been done for PowerPC: just let the generic code in the cpu_exec loop and move the x86 specific code somewhere in target-i386 code. Going this way, I see no reason why the interruption code in the cpu_exec loop could not finally become:

                interrupt_request = env->interrupt_request;
                if (__builtin_expect(interrupt_request, 0)) {
                    if (interrupt_request & CPU_INTERRUPT_DEBUG) {
                        env->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
                        env->exception_index = EXCP_DEBUG;
                        cpu_loop_exit();
                    }
                    if (interrupt_request & CPU_INTERRUPT_HALT) {
                        env->interrupt_request &= ~CPU_INTERRUPT_HALT;
                        env->halted = 1;
                        env->exception_index = EXCP_HLT;
                        cpu_loop_exit();
                    }
                    if (interrupt_request & CPU_INTERRUPT_HARD) {
                        hw_interrupt(env);
                        if (env->pending_interrupts == 0)
                            env->interrupt_request &= ~CPU_INTERRUPT_HARD;
#if defined(__sparc__) && !defined(HOST_SOLARIS)
                        tmp_T0 = 0;
#else
                        T0 = 0;
#endif
                    }
                   /* Don't use the cached interupt_request value,
                      do_interrupt may have updated the EXITTB flag. */
                    if (interrupt_request & CPU_INTERRUPT_EXITTB) {
                        env->interrupt_request &= ~CPU_INTERRUPT_EXITTB;
                        /* ensure that no TB jump will be modified as
                           the program flow was changed */
#if defined(__sparc__) && !defined(HOST_SOLARIS)
                        tmp_T0 = 0;
#else
                        T0 = 0;
#endif
                    }
                    if (interrupt_request & CPU_INTERRUPT_EXIT) {
                        env->interrupt_request &= ~CPU_INTERRUPT_EXIT;
                        env->exception_index = EXCP_INTERRUPT;
                        cpu_loop_exit();
                    }
                }

All the targets specific tricks could be done in the hw_interrupt routine. And the generic code would become much more readable. But this needs some works (not so much) and intensive tests... And I guess nobody feels like taking this risk right now ;-)
But I think this will have to be done someday...

Quoted 23 lines
Quoted 15 lines
Quoted 7 lines
Quoted 7 lines

For the same reason, the intercept field in the TB structure seems not acceptable, as TB specific target informations are already to be stored in the flags field. As intercept seems only to be a bit field, it should go, in a way or another, in tb flags. And as it seems that some

Quoted 7 lines

TB flags is a 32-bit bitfield, where several bits are already used. Currently SVM supports 45 intercepts and each combination can generate a different TB, as we do not want to reduce performance for the non-intercepted case. Think of the intercept field as flags2, that could be used by other virtualization implementations on other architectures too (though I do not know of any)

Quoted 7 lines

So, why not make it generic and extend the flag field to as many bits as you need ? Intercept is x86 specific and has no meaning outside of this scope. Having more bits for flags is generic and may be useful for some other targets... The easy way is to convert flag to 64 bits, if you have enough bits. Another way is to make it a table, but this may need changes in all targets code...

Quoted 8 lines

I think it might be quite a bad idea to extend flags until everything fits in there. I'm planning on implementing VMX as well and there are intercepts too so the meaning of the different fields change and I would not want that happening with the normal flags vector. What would you think of an #ifdef TARGET_I386? This way intercepts are x86 only and don't interfere with any other target architecture, but are still seperated from the flags (which is semantically correct as far as it goes for me).

I don't consider as a problem that one bit in the TB flags field can have different meanings.
This field is something opaque from the generic code. I actually have the same thing in
the PowerPC emulation (even if it's not fully done for now): TB flags are just an
selection of the Machine State Register bits. Some of those bits can have different
meanings depending on the PowerPC implementation (ie CPU model). But the only thing important is to know if the current CPUState flags are equal to the
TB flags, not the actual meaning of the bits. As the MSR is 32 bits on 32 bits
PowerPC implementations, I have no hesitation to use those bits directly even if their actual meaning, from the CPU "point of view" changes. Then, imho, you should not hesitate to put those intercept bits in a generic field...

Quoted 33 lines
Quoted 13 lines
Quoted 8 lines
Quoted 8 lines

interceptions are related with functions implemented in helpers (not micro-ops), you'd better check the interception in the helper at runtime, which would add no visible overhead (as calling a helper is slow compared to direct micro-ops execution), then you would not need to store those infos in the TB structure. This may even make the emulation

Quoted 4 lines

This was the case in an earlier version of the patch. The current implementation you see is an optimization to make it running faster, as there is (nearly) no need for runtime detection.

Quoted 10 lines

For all interceptions related to operations in helpers, it's not a actual optimisation. Things done in helpers are slow and not widely used (compared to the translated code, I mean), then adding a single test for interception in a helper may not hurt global performances. You may need TB flags only for operations you want to intercept that would be inlined in the translated code, then maybe reduce the number of bits needed without decreasing performanced. But, not knowing a lot about x86 VM specification I may be wrong...

I feel really uncomfortable with supporting hundreds of different ways of interception. It took me days to take all the intercept checks out of the helpers into the translate.c, as it is way easier to maintain and read that way. I do not like the idea of converting the intercept vector to something internal. It's quite nice to be able to use the very same constants as kvm does. This makes debugging really easy and you don't have to define everything twice. Additionally this way you can just set a variable without calculating anything, which does come in handy as well.

Quoted 2 lines

Basically the reason I really want to have everyting in translate.c is simplicity though.

OK, I understand that using the same code for all interceptions can make your coding
far easier...

Quoted 14 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

run faster has you won't fill the TB cache with multiple translation of the same code each time the env->intercept changes, thus have chance to avoid many TB caches flushes.

Quoted 8 lines
Quoted 3 lines

This is actually intended, as I believe the emulated machine should run as fast as before when not using any interceptions

It depends if those flags are to be changed often or not, which depends on the hypervisor software in use I guess. If the flags never change, there'll be no TB flush, I agree...

Quoted 8 lines

Usually (at least that's the case with kvm) you have two different sets of intercept flags. One for the real machine and one for the virtualized one. There is only one minor situation where that might change but that's not worth mentioning here. Nevertheless TBs point to physical memory (afaik) so since the virtual kernel is on a different page than the real one, we could not reuse TBs anyway so having the intercept information in the TB only helps in finding out which intercepts to check for as only a minority of the possible intercepts actually get used.

So as far as the hypervisor do not provide shared routines to be used by the OS,
you should never have any "collisison" between translated blocks in different environments ?
I guess you need a complete TB flush when switching between the virtualized environments.
Then, OK, the interception flags should never lead to fill the TB cache with multiple translations
of the same blocks...

Regards.

Signature · 2 lines
J. Mayer <[email protected]>
Never organized
#12 lore raw
Quoted 43 lines · J. Mayer

Going this way, I see no reason why the interruption code in the cpu_exec loop could not finally become:

                interrupt_request = env->interrupt_request;
                if (__builtin_expect(interrupt_request, 0)) {
                    if (interrupt_request & CPU_INTERRUPT_DEBUG) {
                        env->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
                        env->exception_index = EXCP_DEBUG;
                        cpu_loop_exit();
                    }
                    if (interrupt_request & CPU_INTERRUPT_HALT) {
                        env->interrupt_request &= ~CPU_INTERRUPT_HALT;
                        env->halted = 1;
                        env->exception_index = EXCP_HLT;
                        cpu_loop_exit();
                    }
                    if (interrupt_request & CPU_INTERRUPT_HARD) {
                        hw_interrupt(env);
                        if (env->pending_interrupts == 0)
                            env->interrupt_request &= ~CPU_INTERRUPT_HARD;
#if defined(__sparc__) && !defined(HOST_SOLARIS)
                        tmp_T0 = 0;
#else
                        T0 = 0;
#endif
                    }
                   /* Don't use the cached interupt_request value,
                      do_interrupt may have updated the EXITTB flag. */
                    if (interrupt_request & CPU_INTERRUPT_EXITTB) {
                        env->interrupt_request &= ~CPU_INTERRUPT_EXITTB;
                        /* ensure that no TB jump will be modified as
                           the program flow was changed */
#if defined(__sparc__) && !defined(HOST_SOLARIS)
                        tmp_T0 = 0;
#else
                        T0 = 0;
#endif
                    }
                    if (interrupt_request & CPU_INTERRUPT_EXIT) {
                        env->interrupt_request &= ~CPU_INTERRUPT_EXIT;
                        env->exception_index = EXCP_INTERRUPT;
                        cpu_loop_exit();
                    }
                }
Quoted 4 lines

All the targets specific tricks could be done in the hw_interrupt routine. And the generic code would become much more readable. But this needs some works (not so much) and intensive tests... And I guess nobody feels like taking this risk right now ;-)
But I think this will have to be done someday...

I vote for this. There are some other places where we could use the same API:
cpu_dump_state()
cpu_list()
cpu_register()

#13 lore raw
In reply to Alexander Graf
Quoted 25 lines · Alexander Graf
Quoted 24 lines · Thiemo Seufer
Quoted 5 lines · Alexander Graf

Thanks to Michael Peter who tried the patch on an x86 host I found some compilation problems.
This updated patch addresses these issues and thus should compile on every platform for every target available.

Tried that on PPC/Linux:

libqemu.a(cpu-exec.o): In function cpu_x86_exec':
/home/ths/qemu/qemu-work/cpu-exec.c:439: undefined reference to ldl_phys' /home/ths/qemu/qemu-work/cpu-exec.c:439: undefined reference to stl_phys' /home/ths/qemu/qemu-work/cpu-exec.c:440: undefined reference to ldl_phys' libqemu.a(op.o): In function op_svm_check_intercept_io':
/home/ths/qemu/qemu-work/target-i386/op.c:1282: undefined reference to stq_phys' libqemu.a(translate-op.o): In function dyngen_code':
/home/ths/qemu/qemu-work/i386-linux-user/./op.h:4101: undefined reference to stq_phys' /home/ths/qemu/qemu-work/i386-linux-user/./op.h:4101: undefined reference to stq_phys'
collect2: ld returned 1 exit status
make[1]: *** [qemu-i386] Error 1
make: *** [subdir-i386-linux-user] Error 2

Quoted 5 lines
Quoted 4 lines

Thiemo

Wow sorry about that, looks like I missed these.

I worked a bit on the patch to remove some coding style issues and data type mismatches which caused compiler warnings. The result is attached and seems to work reasonably well for me.

That said, J. Mayers comments point out worthwile improvements, so I don't commit it for now. Please incorporate my changes in your patch, and look into improving it further.

Thiemo

target-i386/helper2.c +12 -3
Index: qemu-work/target-i386/helper2.c
===================================================================
@@ -27,6 +27,7 @@
 
 #include "cpu.h"
 #include "exec-all.h"
+#include "svm.h"
 
 //#define DEBUG_MMU
 
@@ -111,10 +112,11 @@
                                CPUID_CX8 | CPUID_PGE | CPUID_CMOV |
                                CPUID_PAT);
         env->pat = 0x0007040600070406ULL;
+        env->cpuid_ext3_features = CPUID_EXT3_SVM;
         env->cpuid_ext_features = CPUID_EXT_SSE3;
         env->cpuid_features |= CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | CPUID_PAE | CPUID_SEP;
         env->cpuid_features |= CPUID_APIC;
-        env->cpuid_xlevel = 0x80000006;
+        env->cpuid_xlevel = 0x8000000e;
         {
             const char *model_id = "QEMU Virtual CPU version " QEMU_VERSION;
             int c, len, i;
@@ -131,7 +133,6 @@
         /* currently not enabled for std i386 because not fully tested */
         env->cpuid_ext2_features = (env->cpuid_features & 0x0183F3FF);
         env->cpuid_ext2_features |= CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX;
-        env->cpuid_xlevel = 0x80000008;
 
         /* these features are needed for Win64 and aren't fully implemented */
         env->cpuid_features |= CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA;
@@ -162,6 +163,7 @@
 #ifdef CONFIG_SOFTMMU
     env->hflags |= HF_SOFTMMU_MASK;
 #endif
+    env->hflags |= HF_GIF_MASK;
 
     cpu_x86_update_cr0(env, 0x60000010);
     env->a20_mask = 0xffffffff;
@@ -865,7 +867,6 @@
  do_fault_protect:
     error_code = PG_ERROR_P_MASK;
  do_fault:
-    env->cr[2] = addr;
     error_code |= (is_write << PG_ERROR_W_BIT);
     if (is_user)
         error_code |= PG_ERROR_U_MASK;
@@ -873,8 +874,16 @@
         (env->efer & MSR_EFER_NXE) &&
         (env->cr[4] & CR4_PAE_MASK))
         error_code |= PG_ERROR_I_D_MASK;
+    if (INTERCEPTEDl(_exceptions, 1 << EXCP0E_PAGE)) {
+        stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), addr);
+    } else {
+        env->cr[2] = addr;
+    }
     env->error_code = error_code;
     env->exception_index = EXCP0E_PAGE;
+    /* the VMM will handle this */
+    if (INTERCEPTEDl(_exceptions, 1 << EXCP0E_PAGE))
+        return 2;
     return 1;
 }
 
target-i386/translate.c +240 -5

494 lines not on this page · raw message

target-i386/cpu.h +22 -0
Index: qemu-work/target-i386/cpu.h
===================================================================
@@ -46,6 +46,8 @@
 
 #include "softfloat.h"
 
+#include "svm.h"
+
 #if defined(__i386__) && !defined(CONFIG_SOFTMMU) && !defined(__APPLE__)
 #define USE_CODE_COPY
 #endif
@@ -84,6 +86,7 @@
 #define DESC_AVL_MASK   (1 << 20)
 #define DESC_P_MASK     (1 << 15)
 #define DESC_DPL_SHIFT  13
+#define DESC_DPL_MASK   (1 << DESC_DPL_SHIFT)
 #define DESC_S_MASK     (1 << 12)
 #define DESC_TYPE_SHIFT 8
 #define DESC_A_MASK     (1 << 8)
@@ -149,6 +152,8 @@
 #define HF_VM_SHIFT         17 /* must be same as eflags */
 #define HF_HALTED_SHIFT     18 /* CPU halted */
 #define HF_SMM_SHIFT        19 /* CPU in SMM mode */
+#define HF_GIF_SHIFT        20 /* if set CPU takes interrupts */
+#define HF_HIF_SHIFT        21 /* shadow copy of IF_MASK when in SVM */
 
 #define HF_CPL_MASK          (3 << HF_CPL_SHIFT)
 #define HF_SOFTMMU_MASK      (1 << HF_SOFTMMU_SHIFT)
@@ -166,6 +171,8 @@
 #define HF_OSFXSR_MASK       (1 << HF_OSFXSR_SHIFT)
 #define HF_HALTED_MASK       (1 << HF_HALTED_SHIFT)
 #define HF_SMM_MASK          (1 << HF_SMM_SHIFT)
+#define HF_GIF_MASK          (1 << HF_GIF_SHIFT)
+#define HF_HIF_MASK          (1 << HF_HIF_SHIFT)
 
 #define CR0_PE_MASK  (1 << 0)
 #define CR0_MP_MASK  (1 << 1)
@@ -249,6 +256,8 @@
 #define MSR_GSBASE                      0xc0000101
 #define MSR_KERNELGSBASE                0xc0000102
 
+#define MSR_VM_HSAVE_PA                 0xc0010117
+
 /* cpuid_features bits */
 #define CPUID_FP87 (1 << 0)
 #define CPUID_VME  (1 << 1)
@@ -283,6 +292,8 @@
 #define CPUID_EXT2_FFXSR   (1 << 25)
 #define CPUID_EXT2_LM      (1 << 29)
 
+#define CPUID_EXT3_SVM     (1 << 2)
+
 #define EXCP00_DIVZ	0
 #define EXCP01_SSTP	1
 #define EXCP02_NMI	2
@@ -489,6 +500,16 @@
     uint32_t sysenter_eip;
     uint64_t efer;
     uint64_t star;
+
+    target_phys_addr_t vm_hsave;
+    target_phys_addr_t vm_vmcb;
+    uint64_t intercept;
+    uint16_t intercept_cr_read;
+    uint16_t intercept_cr_write;
+    uint16_t intercept_dr_read;
+    uint16_t intercept_dr_write;
+    uint32_t intercept_exceptions;
+
 #ifdef TARGET_X86_64
     target_ulong lstar;
     target_ulong cstar;
@@ -530,6 +551,7 @@
     uint32_t cpuid_xlevel;
     uint32_t cpuid_model[12];
     uint32_t cpuid_ext2_features;
+    uint32_t cpuid_ext3_features;
     uint32_t cpuid_apic_id;
 
 #ifdef USE_KQEMU
target-i386/op.c +85 -3

114 lines not on this page · raw message

target-i386/helper.c +518 -2

569 lines not on this page · raw message

target-i386/svm.h new file +356 -0

356 lines not on this page · raw message

cpu-exec.c +37 -3
Index: qemu-work/cpu-exec.c
===================================================================
@@ -104,6 +104,9 @@
         if (tb->pc == pc &&
             tb->page_addr[0] == phys_page1 &&
             tb->cs_base == cs_base &&
+#if defined(TARGET_I386)
+            tb->intercept == env->intercept &&
+#endif
             tb->flags == flags) {
             /* check next page if needed */
             if (tb->page_addr[1] != -1) {
@@ -132,6 +135,9 @@
     tc_ptr = code_gen_ptr;
     tb->tc_ptr = tc_ptr;
     tb->cs_base = cs_base;
+#if defined(TARGET_I386)
+    tb->intercept = env->intercept;
+#endif
     tb->flags = flags;
     cpu_gen_code(env, tb, CODE_GEN_MAX_SIZE, &code_gen_size);
     code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
@@ -214,7 +220,11 @@
 #endif
     tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)];
     if (__builtin_expect(!tb || tb->pc != pc || tb->cs_base != cs_base ||
-                         tb->flags != flags, 0)) {
+                         tb->flags != flags
+#if defined(TARGET_I386)
+			 || tb->intercept != env->intercept
+#endif
+			 , 0)) {
         tb = tb_find_slow(pc, cs_base, flags);
         /* Note: we do it here to avoid a gcc bug on Mac OS X when
            doing it in tb_find_slow */
@@ -373,7 +383,11 @@
                 tmp_T0 = T0;
 #endif
                 interrupt_request = env->interrupt_request;
-                if (__builtin_expect(interrupt_request, 0)) {
+                if (__builtin_expect(interrupt_request, 0)
+#if defined(TARGET_I386)
+			&& env->hflags & HF_GIF_MASK
+#endif
+				) {
                     if (interrupt_request & CPU_INTERRUPT_DEBUG) {
                         env->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
                         env->exception_index = EXCP_DEBUG;
@@ -391,6 +405,7 @@
 #if defined(TARGET_I386)
                     if ((interrupt_request & CPU_INTERRUPT_SMI) &&
                         !(env->hflags & HF_SMM_MASK)) {
+                        svm_check_intercept(SVM_EXIT_SMI);
                         env->interrupt_request &= ~CPU_INTERRUPT_SMI;
                         do_smm_enter();
 #if defined(__sparc__) && !defined(HOST_SOLARIS)
@@ -399,9 +414,10 @@
                         T0 = 0;
 #endif
                     } else if ((interrupt_request & CPU_INTERRUPT_HARD) &&
-                        (env->eflags & IF_MASK) &&
+                        (env->eflags & IF_MASK || env->hflags & HF_HIF_MASK) &&
                         !(env->hflags & HF_INHIBIT_IRQ_MASK)) {
                         int intno;
+                        svm_check_intercept(SVM_EXIT_INTR);
                         env->interrupt_request &= ~CPU_INTERRUPT_HARD;
                         intno = cpu_get_pic_interrupt(env);
                         if (loglevel & CPU_LOG_TB_IN_ASM) {
@@ -415,6 +431,24 @@
 #else
                         T0 = 0;
 #endif
+#if !defined(CONFIG_USER_ONLY)
+                    } else if ((interrupt_request & CPU_INTERRUPT_VIRQ) &&
+                        (env->eflags & IF_MASK) && !(env->hflags & HF_INHIBIT_IRQ_MASK)) {
+                         int intno;
+                         /* FIXME: this should respect TPR */
+                         env->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
+                         stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl),
+                                  ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl)) & ~V_IRQ_MASK);
+                         intno = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_vector));
+                         if (loglevel & CPU_LOG_TB_IN_ASM)
+                             fprintf(logfile, "Servicing virtual hardware INT=0x%02x\n", intno);
+	                 do_interrupt(intno, 0, 0, -1, 1);
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+                         tmp_T0 = 0;
+#else
+                         T0 = 0;
+#endif
+#endif
                     }
 #elif defined(TARGET_PPC)
 #if 0
target-i386/exec.h +10 -0
Index: qemu-work/target-i386/exec.h
===================================================================
@@ -502,6 +502,15 @@
 void helper_hlt(void);
 void helper_monitor(void);
 void helper_mwait(void);
+void helper_vmrun(target_ulong addr);
+void helper_vmmcall(void);
+void helper_vmload(target_ulong addr);
+void helper_vmsave(target_ulong addr);
+void helper_stgi(void);
+void helper_clgi(void);
+void helper_skinit(void);
+void helper_invlpga(void);
+void vmexit(uint64_t exit_code, uint64_t exit_info_1);
 
 extern const uint8_t parity_table[256];
 extern const uint8_t rclw_table[32];
@@ -589,3 +598,4 @@
     }
     return EXCP_HALTED;
 }
+
exec.c +5 -0
Index: qemu-work/exec.c
===================================================================
@@ -1292,6 +1292,11 @@
     vfprintf(stderr, fmt, ap);
     fprintf(stderr, "\n");
 #ifdef TARGET_I386
+    if(env->intercept & INTERCEPT_SVM_MASK) {
+	/* most probably the virtual machine should not
+	   be shut down but rather caught by the VMM */
+        vmexit(SVM_EXIT_SHUTDOWN, 0);
+    }
     cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU | X86_DUMP_CCOP);
 #else
     cpu_dump_state(env, stderr, fprintf, 0);
exec-all.h +1 -0
Index: qemu-work/exec-all.h
===================================================================
@@ -166,6 +166,7 @@
 typedef struct TranslationBlock {
     target_ulong pc;   /* simulated PC corresponding to this block (EIP + CS base) */
     target_ulong cs_base; /* CS base for this block */
+    uint64_t intercept; /* SVM intercept vector */
     unsigned int flags; /* flags defining in which context the code was generated */
     uint16_t size;      /* size of target code for this block (1 <=
                            size <= TARGET_PAGE_SIZE) */
cpu-all.h +1 -0
Index: qemu-work/cpu-all.h
===================================================================
@@ -716,6 +716,7 @@
 #define CPU_INTERRUPT_HALT   0x20 /* CPU halt wanted */
 #define CPU_INTERRUPT_SMI    0x40 /* (x86 only) SMI interrupt pending */
 #define CPU_INTERRUPT_DEBUG  0x80 /* Debug event occured.  */
+#define CPU_INTERRUPT_VIRQ   0x100 /* virtual interrupt pending.  */
 
 void cpu_interrupt(CPUState *s, int mask);
 void cpu_reset_interrupt(CPUState *env, int mask);