diff mbox series

[4/5] Add KVM helper functions for AMD SVM

Message ID 20230421145746.5704-4-mdoucha@suse.cz
State Accepted
Headers show
Series [1/5] KVM: Add helper functions for accessing GDT/LDT | expand

Commit Message

Martin Doucha April 21, 2023, 2:57 p.m. UTC
Signed-off-by: Martin Doucha <mdoucha@suse.cz>
---
 doc/kvm-test-api.txt                       |  73 ++++++++++
 testcases/kernel/kvm/bootstrap_x86.S       |  79 +++++++++++
 testcases/kernel/kvm/bootstrap_x86_64.S    |  89 ++++++++++++
 testcases/kernel/kvm/include/kvm_x86.h     |  14 ++
 testcases/kernel/kvm/include/kvm_x86_svm.h | 157 +++++++++++++++++++++
 testcases/kernel/kvm/lib_x86.c             | 155 +++++++++++++++++++-
 6 files changed, 566 insertions(+), 1 deletion(-)
 create mode 100644 testcases/kernel/kvm/include/kvm_x86_svm.h

Comments

Petr Vorel May 2, 2023, 2:29 p.m. UTC | #1
Hi Martin,

Acked-by: Petr Vorel <pvorel@suse.cz>

Kind regards,
Petr
diff mbox series

Patch

diff --git a/doc/kvm-test-api.txt b/doc/kvm-test-api.txt
index 0aede5eea..812e12b38 100644
--- a/doc/kvm-test-api.txt
+++ b/doc/kvm-test-api.txt
@@ -390,6 +390,79 @@  Developer's Manual, Volume 3, Chapter 4 for explanation of the fields.
 See Intel(R) 64 and IA-32 Architectures Software Developer's Manual
 for documentation of standard and model-specific x86 registers.
 
+3.5 AMD SVM helper functions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`#include "kvm_test.h"` +
+`#include "kvm_x86.h"` +
+`#include "kvm_x86_svm.h"`
+
+The KVM guest library provides basic helper functions for creating and running
+nested virtual machines using the AMD SVM technology.
+
+.Example code to execute nested VM
+[source,c]
+-------------------------------------------------------------------------------
+int guest_main(void)
+{
+	...
+	return 0;
+}
+
+void main(void)
+{
+	struct kvm_svm_vcpu *vm;
+
+	kvm_init_svm();
+	vm = kvm_create_svm_vcpu(guest_main, 1);
+	kvm_svm_vmrun(vm);
+}
+-------------------------------------------------------------------------------
+
+- `int kvm_is_svm_supported(void)` - Returns non-zero value if the CPU
+  supports AMD SVM, otherwise returns 0.
+
+- `int kvm_get_svm_state(void)` - Returns non-zero value if SVM is currently
+  enabled, otherwise returns 0.
+
+- `void kvm_set_svm_state(int enabled)` - Enable or disable SVM according
+  to argument. If SVM is disabled by host or not supported, the test will exit
+  with `TCONF`.
+
+- `void kvm_init_svm(void)` - Enable and fully initialize SVM, including
+  allocating and setting up host save area VMCB. If SVM is disabled by host or
+  not supported, the test will exit with `TCONF`.
+
+- `struct kvm_vmcb *kvm_alloc_vmcb(void)` - Allocate new VMCB structure
+  with correct memory alignment and fill it with zeroes.
+
+- `void kvm_vmcb_set_intercept(struct kvm_vmcb *vmcb, unsigned int id, unsigned int state)` -
+  Set SVM intercept bit `id` to given `state`.
+
+- `void kvm_init_guest_vmcb(struct kvm_vmcb *vmcb, uint32_t asid, uint16_t ss, void *rsp, int (*guest_main)(void))` -
+  Initialize new SVM virtual machine. The `asid` parameter is the nested
+  page table ID. The `ss` and `rsp` parameters set the stack segment and stack
+  pointer values, respectively. The `guest_main` parameter sets the code entry
+  point of the virtual machine. All control registers, segment registers
+  (except stack segment register), GDTR and IDTR will be copied
+  from the current CPU state.
+
+- `struct kvm_svm_vcpu *kvm_create_svm_vcpu(int (*guest_main)(void), int alloc_stack)` -
+  Convenience function for allocating and initializing new SVM virtual CPU.
+  The `guest_main` parameter is passed to `kvm_init_guest_vmcb()`,
+  the `alloc_stack` parameter controls whether a new 8KB stack will be
+  allocated and registered in GDT. Interception will be enabled for `VMSAVE`
+  and `HLT` instructions. If you set `alloc_stack` to zero, you must configure
+  the stack segment register and stack pointer manually.
+
+- `void kvm_svm_vmrun(struct kvm_svm_vcpu *cpu)` - Start or continue execution
+  of a nested virtual machine. Beware that FPU state is not saved.  Do not use
+  floating point types or values in nested guest code. Also do not use
+  `tst_res()` or `tst_brk()` functions in nested guest code.
+
+See AMD64 Architecture Programmer's Manual Volume 2 for documentation
+of the Secure Virtual Machine (SVM) technology.
+
 4. KVM guest environment
 ------------------------
 
diff --git a/testcases/kernel/kvm/bootstrap_x86.S b/testcases/kernel/kvm/bootstrap_x86.S
index 89f73eba1..a39c6bea7 100644
--- a/testcases/kernel/kvm/bootstrap_x86.S
+++ b/testcases/kernel/kvm/bootstrap_x86.S
@@ -9,6 +9,8 @@ 
 .set RESULT_ADDRESS, 0xfffff000
 .set KVM_GDT_SIZE, 32
 
+.set MSR_VM_HSAVE_PA, 0xc0010117
+
 /*
  * This section will be allocated at address 0x1000 and
  * jumped to from the reset stub provided by kvm_run.
@@ -351,6 +353,83 @@  kvm_yield:
 	hlt
 	ret
 
+.global kvm_svm_guest_entry
+kvm_svm_guest_entry:
+	call *%eax
+1:	hlt
+	jmp 1b
+
+.global kvm_svm_vmrun
+kvm_svm_vmrun:
+	push %edi
+	mov 8(%esp), %edi
+	push %ebx
+	push %esi
+	push %ebp
+
+	clgi
+
+	/* Save full host state */
+	movl $MSR_VM_HSAVE_PA, %ecx
+	rdmsr
+	vmsave
+	push %eax
+
+	/* Load guest registers */
+	push %edi
+	movl (%edi), %eax
+	/* %eax is loaded by vmrun from VMCB */
+	movl 0x0c(%edi), %ebx
+	movl 0x14(%edi), %ecx
+	movl 0x1c(%edi), %edx
+	movl 0x2c(%edi), %esi
+	movl 0x34(%edi), %ebp
+	/* %esp is loaded by vmrun from VMCB */
+	movl 0x24(%edi), %edi
+
+	vmload
+	vmrun
+	vmsave
+
+	/* Clear guest register buffer */
+	push %edi
+	push %ecx
+	movl 8(%esp), %edi
+	addl $4, %edi
+	xorl %eax, %eax
+	mov $32, %ecx
+	pushfl
+	cld
+	rep stosl
+	popfl
+
+	/* Save guest registers */
+	pop %ecx
+	pop %eax
+	pop %edi
+	movl %ebx, 0x0c(%edi)
+	movl %ecx, 0x14(%edi)
+	movl %edx, 0x1c(%edi)
+	movl %eax, 0x24(%edi)
+	movl %esi, 0x2c(%edi)
+	movl %ebp, 0x34(%edi)
+	/* Copy %eax and %esp from VMCB */
+	movl (%edi), %esi
+	movl 0x5f8(%esi), %eax
+	movl %eax, 0x04(%edi)
+	movl 0x5d8(%esi), %eax
+	movl %eax, 0x3c(%edi)
+
+	pop %eax
+	vmload
+	stgi
+
+	pop %ebp
+	pop %esi
+	pop %ebx
+	pop %edi
+	ret
+
 
 .section .bss.pgtables, "aw", @nobits
 .global kvm_pagetable
diff --git a/testcases/kernel/kvm/bootstrap_x86_64.S b/testcases/kernel/kvm/bootstrap_x86_64.S
index e4d160b2e..b02dd4d92 100644
--- a/testcases/kernel/kvm/bootstrap_x86_64.S
+++ b/testcases/kernel/kvm/bootstrap_x86_64.S
@@ -10,6 +10,8 @@ 
 .set RESULT_ADDRESS, 0xfffff000
 .set KVM_GDT_SIZE, 32
 
+.set MSR_VM_HSAVE_PA, 0xc0010117
+
 /*
  * This section will be allocated at address 0x1000 and
  * jumped to from the reset stub provided by kvm_run.
@@ -474,6 +476,93 @@  kvm_yield:
 	hlt
 	ret
 
+.global kvm_svm_guest_entry
+kvm_svm_guest_entry:
+	call *%rax
+1:	hlt
+	jmp 1b
+
+.global kvm_svm_vmrun
+kvm_svm_vmrun:
+	pushq %rbx
+	pushq %rbp
+	pushq %r12
+	pushq %r13
+	pushq %r14
+	pushq %r15
+
+	clgi
+
+	/* Save full host state */
+	movq $MSR_VM_HSAVE_PA, %rcx
+	rdmsr
+	shlq $32, %rdx
+	orq %rdx, %rax
+	vmsave
+	pushq %rax
+
+	/* Load guest registers */
+	pushq %rdi
+	movq (%rdi), %rax
+	/* %rax is loaded by vmrun from VMCB */
+	movq 0x10(%rdi), %rbx
+	movq 0x18(%rdi), %rcx
+	movq 0x20(%rdi), %rdx
+	movq 0x30(%rdi), %rsi
+	movq 0x38(%rdi), %rbp
+	/* %rsp is loaded by vmrun from VMCB */
+	movq 0x48(%rdi), %r8
+	movq 0x50(%rdi), %r9
+	movq 0x58(%rdi), %r10
+	movq 0x60(%rdi), %r11
+	movq 0x68(%rdi), %r12
+	movq 0x70(%rdi), %r13
+	movq 0x78(%rdi), %r14
+	movq 0x80(%rdi), %r15
+	movq 0x28(%rdi), %rdi
+
+	vmload
+	vmrun
+	vmsave
+
+	/* Save guest registers */
+	movq %rdi, %rax
+	popq %rdi
+	movq %rbx, 0x10(%rdi)
+	movq %rcx, 0x18(%rdi)
+	movq %rdx, 0x20(%rdi)
+	/* %rax contains guest %rdi */
+	movq %rax, 0x28(%rdi)
+	movq %rsi, 0x30(%rdi)
+	movq %rbp, 0x38(%rdi)
+	movq %r8,  0x48(%rdi)
+	movq %r9,  0x50(%rdi)
+	movq %r10, 0x58(%rdi)
+	movq %r11, 0x60(%rdi)
+	movq %r12, 0x68(%rdi)
+	movq %r13, 0x70(%rdi)
+	movq %r14, 0x78(%rdi)
+	movq %r15, 0x80(%rdi)
+	/* copy guest %rax and %rsp from VMCB*/
+	movq (%rdi), %rsi
+	movq 0x5f8(%rsi), %rax
+	movq %rax, 0x08(%rdi)
+	movq 0x5d8(%rsi), %rax
+	movq %rax, 0x40(%rdi)
+
+	/* Reload host state */
+	popq %rax
+	vmload
+
+	stgi
+
+	popq %r15
+	popq %r14
+	popq %r13
+	popq %r12
+	popq %rbp
+	popq %rbx
+	retq
 
 .section .bss.pgtables, "aw", @nobits
 .global kvm_pagetable
diff --git a/testcases/kernel/kvm/include/kvm_x86.h b/testcases/kernel/kvm/include/kvm_x86.h
index a94f05451..bc36c0e0f 100644
--- a/testcases/kernel/kvm/include/kvm_x86.h
+++ b/testcases/kernel/kvm/include/kvm_x86.h
@@ -64,16 +64,25 @@ 
 /* CPUID constants */
 #define CPUID_GET_INPUT_RANGE 0x80000000
 #define CPUID_GET_EXT_FEATURES 0x80000001
+#define CPUID_GET_SVM_FEATURES 0x8000000a
 
 
 /* Model-specific CPU register constants */
 #define MSR_EFER 0xc0000080
+#define MSR_VM_CR 0xc0010114
+#define MSR_VM_HSAVE_PA 0xc0010117
 
 #define EFER_SCE (1 << 0)	/* SYSCALL/SYSRET instructions enabled */
 #define EFER_LME (1 << 8)	/* CPU is running in 64bit mode */
 #define EFER_LMA (1 << 10)	/* CPU uses 64bit memory paging (read-only) */
 #define EFER_NXE (1 << 11)	/* Execute disable bit active */
+#define EFER_SVME (1 << 12)	/* AMD SVM instructions enabled */
 
+#define VM_CR_DPD (1 << 0)
+#define VM_CR_R_INIT (1 << 1)
+#define VM_CR_DIS_A20M (1 << 2)
+#define VM_CR_LOCK (1 << 3)
+#define VM_CR_SVMDIS (1 << 4)
 
 /* Control register constants */
 #define CR4_VME (1 << 0)
@@ -162,6 +171,11 @@  struct kvm_sregs {
 	uint16_t cs, ds, es, fs, gs, ss;
 };
 
+struct kvm_regs64 {
+	uint64_t rax, rbx, rcx, rdx, rdi, rsi, rbp, rsp;
+	uint64_t r8, r9, r10, r11, r12, r13, r14, r15;
+};
+
 extern struct page_table_entry_pae kvm_pagetable[];
 extern struct intr_descriptor kvm_idt[X86_INTR_COUNT];
 extern struct segment_descriptor kvm_gdt[KVM_GDT_SIZE];
diff --git a/testcases/kernel/kvm/include/kvm_x86_svm.h b/testcases/kernel/kvm/include/kvm_x86_svm.h
new file mode 100644
index 000000000..965d1e716
--- /dev/null
+++ b/testcases/kernel/kvm/include/kvm_x86_svm.h
@@ -0,0 +1,157 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2023 SUSE LLC <mdoucha@suse.cz>
+ *
+ * x86-specific KVM helper functions and structures for AMD SVM
+ */
+
+#ifndef KVM_X86_SVM_H_
+#define KVM_X86_SVM_H_
+
+#include "kvm_x86.h"
+
+/* CPUID_GET_SVM_FEATURES flags returned in EDX */
+#define SVM_CPUID_NESTED_PAGING (1 << 0)
+#define SVM_CPUID_LBR_VIRT (1 << 1)
+#define SVM_CPUID_LOCK (1 << 2)
+#define SVM_CPUID_NRIP_SAVE (1 << 3)
+#define SVM_CPUID_TSC_RATE_MSR (1 << 4)
+#define SVM_CPUID_VMCB_CLEAN (1 << 5)
+#define SVM_CPUID_FLUSH_ASID (1 << 6)
+#define SVM_CPUID_DECODE_ASSIST (1 << 7)
+#define SVM_CPUID_PAUSE_FILTER (1 << 10)
+#define SVM_CPUID_PAUSE_THRESHOLD (1 << 12)
+#define SVM_CPUID_AVIC (1 << 13)
+#define SVM_CPUID_VMSAVE_VIRT (1 << 15)
+#define SVM_CPUID_VGIF (1 << 16)
+#define SVM_CPUID_GMET (1 << 17)
+#define SVM_CPUID_X2AVIC (1 << 18)
+#define SVM_CPUID_SSSCHECK (1 << 19)
+#define SVM_CPUID_SPEC_CTRL (1 << 20)
+#define SVM_CPUID_ROGPT (1 << 21)
+#define SVM_CPUID_HOST_MCE_OVERRIDE (1 << 23)
+#define SVM_CPUID_TLBI_CTL (1 << 24)
+#define SVM_CPUID_NMI_VIRT (1 << 25)
+#define SVM_CPUID_IBS_VIRT (1 << 26)
+
+/* SVM event intercept IDs */
+#define SVM_INTERCEPT_HLT 0x78
+#define SVM_INTERCEPT_VMRUN 0x80
+#define SVM_INTERCEPT_MAX 0x95
+
+/* SVM vmrun exit codes */
+#define SVM_EXIT_HLT 0x78
+#define SVM_EXIT_AVIC_NOACCEL 0x402
+#define SVM_EXIT_INVALID ((uint64_t)-1)
+
+/* SVM VMCB flags */
+#define SVM_INTR_AVIC (1 << 7)
+
+struct kvm_vmcb_descriptor {
+	uint16_t selector;
+	uint16_t attrib;
+	uint32_t limit;
+	uint64_t base;
+};
+
+struct kvm_vmcb {
+	/* VMCB control area */
+	uint8_t intercepts[20];
+	uint8_t reserved1[44];
+	uint64_t iopm_base_addr;
+	uint64_t msrpm_base_addr;
+	uint64_t tsc_offset;
+	uint32_t guest_asid;
+	uint32_t tlb_control;
+	uint8_t virtual_tpr;
+	uint8_t virtual_irq;
+	unsigned char virt_intr_prio: 4;
+	unsigned char virt_ignore_tpr: 4;
+	uint8_t virt_intr_ctl;
+	uint8_t virt_intr_vector;
+	uint8_t reserved2[3];
+	uint64_t interrupt_shadow;
+	uint64_t exitcode;
+	uint64_t exitinfo1;
+	uint64_t exitinfo2;
+	uint64_t exit_int_info;
+	uint64_t enable_nested_paging;
+	uint64_t avic_bar;
+	uint64_t ghcb_gpa;
+	uint64_t event_injection;
+	uint64_t nested_cr3;
+	uint64_t virt_ext;
+	uint32_t vmcb_clean;
+	uint8_t reserved3[4];
+	uint64_t next_rip;
+	uint8_t instr_len;
+	uint8_t instr_bytes[15];
+	uint64_t avic_backing_page;
+	uint8_t reserved4[8];
+	uint64_t avic_logical_ptr;
+	uint64_t avic_physical_ptr;
+	uint8_t reserved5[8];
+	uint64_t vmsa_pa;
+	uint64_t vmgexit_rax;
+	uint8_t vmgexit_cpl;
+	uint8_t reserved6[0x2e7];
+
+	/* VMCB state save area */
+	struct kvm_vmcb_descriptor es, cs, ss, ds, fs, gs;
+	struct kvm_vmcb_descriptor gdtr, ldtr, idtr, tr;
+	uint8_t reserved7[43];
+	uint8_t cpl;
+	uint8_t reserved8[4];
+	uint64_t efer;
+	uint8_t reserved9[112];
+	uint64_t cr4;
+	uint64_t cr3;
+	uint64_t cr0;
+	uint64_t dr7;
+	uint64_t dr6;
+	uint64_t rflags;
+	uint64_t rip;
+	uint8_t reserved10[88];
+	uint64_t rsp;
+	uint64_t s_cet;
+	uint64_t ssp;
+	uint64_t isst_addr;
+	uint64_t rax;
+	uint64_t star;
+	uint64_t lstar;
+	uint64_t cstar;
+	uint64_t sfmask;
+	uint64_t kernel_gs_base;
+	uint64_t sysenter_cs;
+	uint64_t sysenter_esp;
+	uint64_t sysenter_eip;
+	uint64_t cr2;
+	uint8_t reserved11[32];
+	uint64_t guest_pat;
+	uint8_t padding[0x990];
+};
+
+struct kvm_svm_vcpu {
+	struct kvm_vmcb *vmcb;
+	struct kvm_regs64 regs;
+};
+
+/* AMD SVM virtualization helper functions */
+int kvm_is_svm_supported(void);
+int kvm_get_svm_state(void);
+void kvm_set_svm_state(int enabled);
+
+void kvm_init_svm(void);	/* Fully initialize host SVM environment */
+struct kvm_vmcb *kvm_alloc_vmcb(void);
+void kvm_vmcb_copy_gdt_descriptor(struct kvm_vmcb_descriptor *dst,
+	unsigned int gdt_id);
+void kvm_vmcb_set_intercept(struct kvm_vmcb *vmcb, unsigned int id,
+	unsigned int state);
+void kvm_init_guest_vmcb(struct kvm_vmcb *vmcb, uint32_t asid, uint16_t ss,
+	void *rsp, int (*guest_main)(void));
+struct kvm_svm_vcpu *kvm_create_svm_vcpu(int (*guest_main)(void),
+	int alloc_stack);
+
+void kvm_svm_vmrun(struct kvm_svm_vcpu *cpu);
+
+#endif /* KVM_X86_SVM_H_ */
diff --git a/testcases/kernel/kvm/lib_x86.c b/testcases/kernel/kvm/lib_x86.c
index d206072ee..3e6656f11 100644
--- a/testcases/kernel/kvm/lib_x86.c
+++ b/testcases/kernel/kvm/lib_x86.c
@@ -5,7 +5,9 @@ 
  * x86-specific KVM helper functions
  */
 
-#include "kvm_x86.h"
+#include "kvm_x86_svm.h"
+
+void kvm_svm_guest_entry(void);
 
 struct kvm_interrupt_frame {
 	uintptr_t eip, cs, eflags, esp, ss;
@@ -240,3 +242,154 @@  uintptr_t kvm_get_interrupt_ip(const struct kvm_interrupt_frame *ifrm)
 {
 	return ifrm->eip;
 }
+
+int kvm_is_svm_supported(void)
+{
+	struct kvm_cpuid buf;
+
+	kvm_get_cpuid(CPUID_GET_INPUT_RANGE, 0, &buf);
+
+	if (buf.eax < CPUID_GET_EXT_FEATURES)
+		return 0;
+
+	kvm_get_cpuid(CPUID_GET_EXT_FEATURES, 0, &buf);
+	return buf.ecx & 0x4;
+}
+
+int kvm_get_svm_state(void)
+{
+	return kvm_rdmsr(MSR_EFER) & EFER_SVME;
+}
+
+void kvm_set_svm_state(int enabled)
+{
+	uint64_t value;
+
+	if (!kvm_is_svm_supported())
+		tst_brk(TCONF, "CPU does not support SVM");
+
+	if (kvm_rdmsr(MSR_VM_CR) & VM_CR_SVMDIS)
+		tst_brk(TCONF, "SVM is supported but disabled");
+
+	value = kvm_rdmsr(MSR_EFER);
+
+	if (enabled)
+		value |= EFER_SVME;
+	else
+		value &= ~EFER_SVME;
+
+	kvm_wrmsr(MSR_EFER, value);
+}
+
+struct kvm_vmcb *kvm_alloc_vmcb(void)
+{
+	struct kvm_vmcb *ret;
+
+	ret = tst_heap_alloc_aligned(sizeof(struct kvm_vmcb), PAGESIZE);
+	memset(ret, 0, sizeof(struct kvm_vmcb));
+	return ret;
+}
+
+void kvm_init_svm(void)
+{
+	kvm_set_svm_state(1);
+	kvm_wrmsr(MSR_VM_HSAVE_PA, (uintptr_t)kvm_alloc_vmcb());
+}
+
+void kvm_vmcb_copy_gdt_descriptor(struct kvm_vmcb_descriptor *dst,
+	unsigned int gdt_id)
+{
+	uint64_t baseaddr;
+	uint32_t limit;
+	unsigned int flags;
+
+	if (gdt_id >= KVM_GDT_SIZE)
+		tst_brk(TBROK, "GDT descriptor ID out of range");
+
+	kvm_parse_segment_descriptor(kvm_gdt + gdt_id, &baseaddr, &limit,
+		&flags);
+
+	if (!(flags & SEGFLAG_PRESENT)) {
+		memset(dst, 0, sizeof(struct kvm_vmcb_descriptor));
+		return;
+	}
+
+	if (flags & SEGFLAG_PAGE_LIMIT)
+		limit = (limit << 12) | 0xfff;
+
+	dst->selector = gdt_id << 3;
+	dst->attrib = flags;
+	dst->limit = limit;
+	dst->base = baseaddr;
+}
+
+void kvm_vmcb_set_intercept(struct kvm_vmcb *vmcb, unsigned int id,
+	unsigned int state)
+{
+	unsigned int addr = id / 8, bit = 1 << (id % 8);
+
+	if (id >= SVM_INTERCEPT_MAX)
+		tst_brk(TBROK, "Invalid SVM intercept ID");
+
+	if (state)
+		vmcb->intercepts[addr] |= bit;
+	else
+		vmcb->intercepts[addr] &= ~bit;
+}
+
+void kvm_init_guest_vmcb(struct kvm_vmcb *vmcb, uint32_t asid, uint16_t ss,
+	void *rsp, int (*guest_main)(void))
+{
+	struct kvm_cregs cregs;
+	struct kvm_sregs sregs;
+
+	kvm_read_cregs(&cregs);
+	kvm_read_sregs(&sregs);
+
+	kvm_vmcb_set_intercept(vmcb, SVM_INTERCEPT_VMRUN, 1);
+	kvm_vmcb_set_intercept(vmcb, SVM_INTERCEPT_HLT, 1);
+
+	kvm_vmcb_copy_gdt_descriptor(&vmcb->es, sregs.es >> 3);
+	kvm_vmcb_copy_gdt_descriptor(&vmcb->cs, sregs.cs >> 3);
+	kvm_vmcb_copy_gdt_descriptor(&vmcb->ss, ss);
+	kvm_vmcb_copy_gdt_descriptor(&vmcb->ds, sregs.ds >> 3);
+	kvm_vmcb_copy_gdt_descriptor(&vmcb->fs, sregs.fs >> 3);
+	kvm_vmcb_copy_gdt_descriptor(&vmcb->gs, sregs.gs >> 3);
+	vmcb->gdtr.base = (uintptr_t)kvm_gdt;
+	vmcb->gdtr.limit = (KVM_GDT_SIZE*sizeof(struct segment_descriptor)) - 1;
+	vmcb->idtr.base = (uintptr_t)kvm_idt;
+	vmcb->idtr.limit = (X86_INTR_COUNT*sizeof(struct intr_descriptor)) - 1;
+
+	vmcb->guest_asid = asid;
+	vmcb->efer = kvm_rdmsr(MSR_EFER);
+	vmcb->cr0 = cregs.cr0;
+	vmcb->cr3 = cregs.cr3;
+	vmcb->cr4 = cregs.cr4;
+	vmcb->rip = (uintptr_t)kvm_svm_guest_entry;
+	vmcb->rax = (uintptr_t)guest_main;
+	vmcb->rsp = (uintptr_t)rsp;
+	vmcb->rflags = 0x200;	/* Interrupts enabled */
+}
+
+struct kvm_svm_vcpu *kvm_create_svm_vcpu(int (*guest_main)(void),
+	int alloc_stack)
+{
+	uint16_t ss = 0;
+	char *stack = NULL;
+	struct kvm_vmcb *vmcb;
+	struct kvm_svm_vcpu *ret;
+
+	vmcb = kvm_alloc_vmcb();
+
+	if (alloc_stack) {
+		stack = tst_heap_alloc_aligned(2 * PAGESIZE, PAGESIZE);
+		ss = kvm_create_stack_descriptor(kvm_gdt, KVM_GDT_SIZE, stack);
+		stack += 2 * PAGESIZE;
+	}
+
+	kvm_init_guest_vmcb(vmcb, 1, ss, stack, guest_main);
+	ret = tst_heap_alloc(sizeof(struct kvm_svm_vcpu));
+	memset(ret, 0, sizeof(struct kvm_svm_vcpu));
+	ret->vmcb = vmcb;
+	return ret;
+}