?? svm.c
字號(hào):
/* * Kernel-based Virtual Machine driver for Linux * * AMD SVM support * * Copyright (C) 2006 Qumranet, Inc. * * Authors: * Yaniv Kamay <yaniv@qumranet.com> * Avi Kivity <avi@qumranet.com> * * This work is licensed under the terms of the GNU GPL, version 2. See * the COPYING file in the top-level directory. * */#include "kvm_svm.h"#include "x86_emulate.h"#include "irq.h"#include <linux/module.h>#include <linux/kernel.h>#include <linux/vmalloc.h>#include <linux/highmem.h>#include <linux/sched.h>#include <asm/desc.h>MODULE_AUTHOR("Qumranet");MODULE_LICENSE("GPL");#define IOPM_ALLOC_ORDER 2#define MSRPM_ALLOC_ORDER 1#define DB_VECTOR 1#define UD_VECTOR 6#define GP_VECTOR 13#define DR7_GD_MASK (1 << 13)#define DR6_BD_MASK (1 << 13)#define SEG_TYPE_LDT 2#define SEG_TYPE_BUSY_TSS16 3#define KVM_EFER_LMA (1 << 10)#define KVM_EFER_LME (1 << 8)#define SVM_FEATURE_NPT (1 << 0)#define SVM_FEATURE_LBRV (1 << 1)#define SVM_DEATURE_SVML (1 << 2)static void kvm_reput_irq(struct vcpu_svm *svm);static inline struct vcpu_svm *to_svm(struct kvm_vcpu *vcpu){ return container_of(vcpu, struct vcpu_svm, vcpu);}unsigned long iopm_base;unsigned long msrpm_base;struct kvm_ldttss_desc { u16 limit0; u16 base0; unsigned base1 : 8, type : 5, dpl : 2, p : 1; unsigned limit1 : 4, zero0 : 3, g : 1, base2 : 8; u32 base3; u32 zero1;} __attribute__((packed));struct svm_cpu_data { int cpu; u64 asid_generation; u32 max_asid; u32 next_asid; struct kvm_ldttss_desc *tss_desc; struct page *save_area;};static DEFINE_PER_CPU(struct svm_cpu_data *, svm_data);static uint32_t svm_features;struct svm_init_data { int cpu; int r;};static u32 msrpm_ranges[] = {0, 0xc0000000, 0xc0010000};#define NUM_MSR_MAPS ARRAY_SIZE(msrpm_ranges)#define MSRS_RANGE_SIZE 2048#define MSRS_IN_RANGE (MSRS_RANGE_SIZE * 8 / 2)#define MAX_INST_SIZE 15static inline u32 svm_has(u32 feat){ return svm_features & feat;}static inline u8 pop_irq(struct kvm_vcpu *vcpu){ int word_index = __ffs(vcpu->irq_summary); int bit_index = __ffs(vcpu->irq_pending[word_index]); int irq = word_index * BITS_PER_LONG + bit_index; clear_bit(bit_index, &vcpu->irq_pending[word_index]); if (!vcpu->irq_pending[word_index]) clear_bit(word_index, &vcpu->irq_summary); return irq;}static inline void push_irq(struct kvm_vcpu *vcpu, u8 irq){ set_bit(irq, vcpu->irq_pending); set_bit(irq / BITS_PER_LONG, &vcpu->irq_summary);}static inline void clgi(void){ asm volatile (SVM_CLGI);}static inline void stgi(void){ asm volatile (SVM_STGI);}static inline void invlpga(unsigned long addr, u32 asid){ asm volatile (SVM_INVLPGA :: "a"(addr), "c"(asid));}static inline unsigned long kvm_read_cr2(void){ unsigned long cr2; asm volatile ("mov %%cr2, %0" : "=r" (cr2)); return cr2;}static inline void kvm_write_cr2(unsigned long val){ asm volatile ("mov %0, %%cr2" :: "r" (val));}static inline unsigned long read_dr6(void){ unsigned long dr6; asm volatile ("mov %%dr6, %0" : "=r" (dr6)); return dr6;}static inline void write_dr6(unsigned long val){ asm volatile ("mov %0, %%dr6" :: "r" (val));}static inline unsigned long read_dr7(void){ unsigned long dr7; asm volatile ("mov %%dr7, %0" : "=r" (dr7)); return dr7;}static inline void write_dr7(unsigned long val){ asm volatile ("mov %0, %%dr7" :: "r" (val));}static inline void force_new_asid(struct kvm_vcpu *vcpu){ to_svm(vcpu)->asid_generation--;}static inline void flush_guest_tlb(struct kvm_vcpu *vcpu){ force_new_asid(vcpu);}static void svm_set_efer(struct kvm_vcpu *vcpu, u64 efer){ if (!(efer & KVM_EFER_LMA)) efer &= ~KVM_EFER_LME; to_svm(vcpu)->vmcb->save.efer = efer | MSR_EFER_SVME_MASK; vcpu->shadow_efer = efer;}static void svm_inject_gp(struct kvm_vcpu *vcpu, unsigned error_code){ struct vcpu_svm *svm = to_svm(vcpu); svm->vmcb->control.event_inj = SVM_EVTINJ_VALID | SVM_EVTINJ_VALID_ERR | SVM_EVTINJ_TYPE_EXEPT | GP_VECTOR; svm->vmcb->control.event_inj_err = error_code;}static void inject_ud(struct kvm_vcpu *vcpu){ to_svm(vcpu)->vmcb->control.event_inj = SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_EXEPT | UD_VECTOR;}static int is_page_fault(uint32_t info){ info &= SVM_EVTINJ_VEC_MASK | SVM_EVTINJ_TYPE_MASK | SVM_EVTINJ_VALID; return info == (PF_VECTOR | SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_EXEPT);}static int is_external_interrupt(u32 info){ info &= SVM_EVTINJ_TYPE_MASK | SVM_EVTINJ_VALID; return info == (SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_INTR);}static void skip_emulated_instruction(struct kvm_vcpu *vcpu){ struct vcpu_svm *svm = to_svm(vcpu); if (!svm->next_rip) { printk(KERN_DEBUG "%s: NOP\n", __FUNCTION__); return; } if (svm->next_rip - svm->vmcb->save.rip > MAX_INST_SIZE) { printk(KERN_ERR "%s: ip 0x%llx next 0x%llx\n", __FUNCTION__, svm->vmcb->save.rip, svm->next_rip); } vcpu->rip = svm->vmcb->save.rip = svm->next_rip; svm->vmcb->control.int_state &= ~SVM_INTERRUPT_SHADOW_MASK; vcpu->interrupt_window_open = 1;}static int has_svm(void){ uint32_t eax, ebx, ecx, edx; if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) { printk(KERN_INFO "has_svm: not amd\n"); return 0; } cpuid(0x80000000, &eax, &ebx, &ecx, &edx); if (eax < SVM_CPUID_FUNC) { printk(KERN_INFO "has_svm: can't execute cpuid_8000000a\n"); return 0; } cpuid(0x80000001, &eax, &ebx, &ecx, &edx); if (!(ecx & (1 << SVM_CPUID_FEATURE_SHIFT))) { printk(KERN_DEBUG "has_svm: svm not available\n"); return 0; } return 1;}static void svm_hardware_disable(void *garbage){ struct svm_cpu_data *svm_data = per_cpu(svm_data, raw_smp_processor_id()); if (svm_data) { uint64_t efer; wrmsrl(MSR_VM_HSAVE_PA, 0); rdmsrl(MSR_EFER, efer); wrmsrl(MSR_EFER, efer & ~MSR_EFER_SVME_MASK); per_cpu(svm_data, raw_smp_processor_id()) = NULL; __free_page(svm_data->save_area); kfree(svm_data); }}static void svm_hardware_enable(void *garbage){ struct svm_cpu_data *svm_data; uint64_t efer;#ifdef CONFIG_X86_64 struct desc_ptr gdt_descr;#else struct Xgt_desc_struct gdt_descr;#endif struct desc_struct *gdt; int me = raw_smp_processor_id(); if (!has_svm()) { printk(KERN_ERR "svm_cpu_init: err EOPNOTSUPP on %d\n", me); return; } svm_data = per_cpu(svm_data, me); if (!svm_data) { printk(KERN_ERR "svm_cpu_init: svm_data is NULL on %d\n", me); return; } svm_data->asid_generation = 1; svm_data->max_asid = cpuid_ebx(SVM_CPUID_FUNC) - 1; svm_data->next_asid = svm_data->max_asid + 1; svm_features = cpuid_edx(SVM_CPUID_FUNC); asm volatile ( "sgdt %0" : "=m"(gdt_descr) ); gdt = (struct desc_struct *)gdt_descr.address; svm_data->tss_desc = (struct kvm_ldttss_desc *)(gdt + GDT_ENTRY_TSS); rdmsrl(MSR_EFER, efer); wrmsrl(MSR_EFER, efer | MSR_EFER_SVME_MASK); wrmsrl(MSR_VM_HSAVE_PA, page_to_pfn(svm_data->save_area) << PAGE_SHIFT);}static int svm_cpu_init(int cpu){ struct svm_cpu_data *svm_data; int r; svm_data = kzalloc(sizeof(struct svm_cpu_data), GFP_KERNEL); if (!svm_data) return -ENOMEM; svm_data->cpu = cpu; svm_data->save_area = alloc_page(GFP_KERNEL); r = -ENOMEM; if (!svm_data->save_area) goto err_1; per_cpu(svm_data, cpu) = svm_data; return 0;err_1: kfree(svm_data); return r;}static void set_msr_interception(u32 *msrpm, unsigned msr, int read, int write){ int i; for (i = 0; i < NUM_MSR_MAPS; i++) { if (msr >= msrpm_ranges[i] && msr < msrpm_ranges[i] + MSRS_IN_RANGE) { u32 msr_offset = (i * MSRS_IN_RANGE + msr - msrpm_ranges[i]) * 2; u32 *base = msrpm + (msr_offset / 32); u32 msr_shift = msr_offset % 32; u32 mask = ((write) ? 0 : 2) | ((read) ? 0 : 1); *base = (*base & ~(0x3 << msr_shift)) | (mask << msr_shift); return; } } BUG();}static __init int svm_hardware_setup(void){ int cpu; struct page *iopm_pages; struct page *msrpm_pages; void *iopm_va, *msrpm_va; int r; iopm_pages = alloc_pages(GFP_KERNEL, IOPM_ALLOC_ORDER); if (!iopm_pages) return -ENOMEM; iopm_va = page_address(iopm_pages); memset(iopm_va, 0xff, PAGE_SIZE * (1 << IOPM_ALLOC_ORDER)); clear_bit(0x80, iopm_va); /* allow direct access to PC debug port */ iopm_base = page_to_pfn(iopm_pages) << PAGE_SHIFT; msrpm_pages = alloc_pages(GFP_KERNEL, MSRPM_ALLOC_ORDER); r = -ENOMEM; if (!msrpm_pages) goto err_1; msrpm_va = page_address(msrpm_pages); memset(msrpm_va, 0xff, PAGE_SIZE * (1 << MSRPM_ALLOC_ORDER)); msrpm_base = page_to_pfn(msrpm_pages) << PAGE_SHIFT;#ifdef CONFIG_X86_64 set_msr_interception(msrpm_va, MSR_GS_BASE, 1, 1); set_msr_interception(msrpm_va, MSR_FS_BASE, 1, 1); set_msr_interception(msrpm_va, MSR_KERNEL_GS_BASE, 1, 1); set_msr_interception(msrpm_va, MSR_LSTAR, 1, 1); set_msr_interception(msrpm_va, MSR_CSTAR, 1, 1); set_msr_interception(msrpm_va, MSR_SYSCALL_MASK, 1, 1);#endif set_msr_interception(msrpm_va, MSR_K6_STAR, 1, 1); set_msr_interception(msrpm_va, MSR_IA32_SYSENTER_CS, 1, 1); set_msr_interception(msrpm_va, MSR_IA32_SYSENTER_ESP, 1, 1); set_msr_interception(msrpm_va, MSR_IA32_SYSENTER_EIP, 1, 1); for_each_online_cpu(cpu) { r = svm_cpu_init(cpu); if (r) goto err_2; } return 0;err_2: __free_pages(msrpm_pages, MSRPM_ALLOC_ORDER); msrpm_base = 0;err_1: __free_pages(iopm_pages, IOPM_ALLOC_ORDER); iopm_base = 0; return r;}static __exit void svm_hardware_unsetup(void){ __free_pages(pfn_to_page(msrpm_base >> PAGE_SHIFT), MSRPM_ALLOC_ORDER); __free_pages(pfn_to_page(iopm_base >> PAGE_SHIFT), IOPM_ALLOC_ORDER); iopm_base = msrpm_base = 0;}static void init_seg(struct vmcb_seg *seg){ seg->selector = 0; seg->attrib = SVM_SELECTOR_P_MASK | SVM_SELECTOR_S_MASK | SVM_SELECTOR_WRITE_MASK; /* Read/Write Data Segment */ seg->limit = 0xffff; seg->base = 0;}static void init_sys_seg(struct vmcb_seg *seg, uint32_t type){ seg->selector = 0; seg->attrib = SVM_SELECTOR_P_MASK | type; seg->limit = 0xffff; seg->base = 0;}static void init_vmcb(struct vmcb *vmcb){ struct vmcb_control_area *control = &vmcb->control; struct vmcb_save_area *save = &vmcb->save; control->intercept_cr_read = INTERCEPT_CR0_MASK | INTERCEPT_CR3_MASK | INTERCEPT_CR4_MASK; control->intercept_cr_write = INTERCEPT_CR0_MASK | INTERCEPT_CR3_MASK | INTERCEPT_CR4_MASK; control->intercept_dr_read = INTERCEPT_DR0_MASK | INTERCEPT_DR1_MASK | INTERCEPT_DR2_MASK | INTERCEPT_DR3_MASK; control->intercept_dr_write = INTERCEPT_DR0_MASK | INTERCEPT_DR1_MASK | INTERCEPT_DR2_MASK | INTERCEPT_DR3_MASK | INTERCEPT_DR5_MASK | INTERCEPT_DR7_MASK; control->intercept_exceptions = 1 << PF_VECTOR; control->intercept = (1ULL << INTERCEPT_INTR) | (1ULL << INTERCEPT_NMI) | (1ULL << INTERCEPT_SMI) | /* * selective cr0 intercept bug? * 0: 0f 22 d8 mov %eax,%cr3 * 3: 0f 20 c0 mov %cr0,%eax * 6: 0d 00 00 00 80 or $0x80000000,%eax * b: 0f 22 c0 mov %eax,%cr0 * set cr3 ->interception * get cr0 ->interception * set cr0 -> no interception */ /* (1ULL << INTERCEPT_SELECTIVE_CR0) | */ (1ULL << INTERCEPT_CPUID) | (1ULL << INTERCEPT_INVD) | (1ULL << INTERCEPT_HLT) | (1ULL << INTERCEPT_INVLPGA) | (1ULL << INTERCEPT_IOIO_PROT) | (1ULL << INTERCEPT_MSR_PROT) | (1ULL << INTERCEPT_TASK_SWITCH) | (1ULL << INTERCEPT_SHUTDOWN) | (1ULL << INTERCEPT_VMRUN) | (1ULL << INTERCEPT_VMMCALL) | (1ULL << INTERCEPT_VMLOAD) | (1ULL << INTERCEPT_VMSAVE) | (1ULL << INTERCEPT_STGI) | (1ULL << INTERCEPT_CLGI) | (1ULL << INTERCEPT_SKINIT) | (1ULL << INTERCEPT_WBINVD) | (1ULL << INTERCEPT_MONITOR) | (1ULL << INTERCEPT_MWAIT); control->iopm_base_pa = iopm_base; control->msrpm_base_pa = msrpm_base; control->tsc_offset = 0; control->int_ctl = V_INTR_MASKING_MASK; init_seg(&save->es); init_seg(&save->ss); init_seg(&save->ds); init_seg(&save->fs); init_seg(&save->gs); save->cs.selector = 0xf000; /* Executable/Readable Code Segment */ save->cs.attrib = SVM_SELECTOR_READ_MASK | SVM_SELECTOR_P_MASK | SVM_SELECTOR_S_MASK | SVM_SELECTOR_CODE_MASK; save->cs.limit = 0xffff; /* * cs.base should really be 0xffff0000, but vmx can't handle that, so * be consistent with it. * * Replace when we have real mode working for vmx. */ save->cs.base = 0xf0000; save->gdtr.limit = 0xffff; save->idtr.limit = 0xffff; init_sys_seg(&save->ldtr, SEG_TYPE_LDT); init_sys_seg(&save->tr, SEG_TYPE_BUSY_TSS16); save->efer = MSR_EFER_SVME_MASK; save->dr6 = 0xffff0ff0; save->dr7 = 0x400; save->rflags = 2; save->rip = 0x0000fff0; /* * cr0 val on cpu init should be 0x60000010, we enable cpu * cache by default. the orderly way is to enable cache in bios. */ save->cr0 = 0x00000010 | X86_CR0_PG | X86_CR0_WP; save->cr4 = X86_CR4_PAE; /* rdx = ?? */}static void svm_vcpu_reset(struct kvm_vcpu *vcpu){ struct vcpu_svm *svm = to_svm(vcpu); init_vmcb(svm->vmcb); if (vcpu->vcpu_id != 0) { svm->vmcb->save.rip = 0; svm->vmcb->save.cs.base = svm->vcpu.sipi_vector << 12; svm->vmcb->save.cs.selector = svm->vcpu.sipi_vector << 8; }}static struct kvm_vcpu *svm_create_vcpu(struct kvm *kvm, unsigned int id){ struct vcpu_svm *svm; struct page *page; int err; svm = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL); if (!svm) { err = -ENOMEM; goto out; }
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -