kvm_main.c

来自「linux 内核源代码」· C语言 代码 · 共 2,825 行 · 第 1/5 页

C
2,825
字号
	mutex_lock(&vcpu->kvm->lock);	/*	 * Does the new cr3 value map to physical memory? (Note, we	 * catch an invalid cr3 even in real-mode, because it would	 * cause trouble later on when we turn on paging anyway.)	 *	 * A real CPU would silently accept an invalid cr3 and would	 * attempt to use it - with largely undefined (and often hard	 * to debug) behavior on the guest side.	 */	if (unlikely(!gfn_to_memslot(vcpu->kvm, cr3 >> PAGE_SHIFT)))		inject_gp(vcpu);	else {		vcpu->cr3 = cr3;		vcpu->mmu.new_cr3(vcpu);	}	mutex_unlock(&vcpu->kvm->lock);}EXPORT_SYMBOL_GPL(set_cr3);void set_cr8(struct kvm_vcpu *vcpu, unsigned long cr8){	if (cr8 & CR8_RESERVED_BITS) {		printk(KERN_DEBUG "set_cr8: #GP, reserved bits 0x%lx\n", cr8);		inject_gp(vcpu);		return;	}	if (irqchip_in_kernel(vcpu->kvm))		kvm_lapic_set_tpr(vcpu, cr8);	else		vcpu->cr8 = cr8;}EXPORT_SYMBOL_GPL(set_cr8);unsigned long get_cr8(struct kvm_vcpu *vcpu){	if (irqchip_in_kernel(vcpu->kvm))		return kvm_lapic_get_cr8(vcpu);	else		return vcpu->cr8;}EXPORT_SYMBOL_GPL(get_cr8);u64 kvm_get_apic_base(struct kvm_vcpu *vcpu){	if (irqchip_in_kernel(vcpu->kvm))		return vcpu->apic_base;	else		return vcpu->apic_base;}EXPORT_SYMBOL_GPL(kvm_get_apic_base);void kvm_set_apic_base(struct kvm_vcpu *vcpu, u64 data){	/* TODO: reserve bits check */	if (irqchip_in_kernel(vcpu->kvm))		kvm_lapic_set_base(vcpu, data);	else		vcpu->apic_base = data;}EXPORT_SYMBOL_GPL(kvm_set_apic_base);void fx_init(struct kvm_vcpu *vcpu){	unsigned after_mxcsr_mask;	/* Initialize guest FPU by resetting ours and saving into guest's */	preempt_disable();	fx_save(&vcpu->host_fx_image);	fpu_init();	fx_save(&vcpu->guest_fx_image);	fx_restore(&vcpu->host_fx_image);	preempt_enable();	vcpu->cr0 |= X86_CR0_ET;	after_mxcsr_mask = offsetof(struct i387_fxsave_struct, st_space);	vcpu->guest_fx_image.mxcsr = 0x1f80;	memset((void *)&vcpu->guest_fx_image + after_mxcsr_mask,	       0, sizeof(struct i387_fxsave_struct) - after_mxcsr_mask);}EXPORT_SYMBOL_GPL(fx_init);/* * Allocate some memory and give it an address in the guest physical address * space. * * Discontiguous memory is allowed, mostly for framebuffers. */static int kvm_vm_ioctl_set_memory_region(struct kvm *kvm,					  struct kvm_memory_region *mem){	int r;	gfn_t base_gfn;	unsigned long npages;	unsigned long i;	struct kvm_memory_slot *memslot;	struct kvm_memory_slot old, new;	r = -EINVAL;	/* General sanity checks */	if (mem->memory_size & (PAGE_SIZE - 1))		goto out;	if (mem->guest_phys_addr & (PAGE_SIZE - 1))		goto out;	if (mem->slot >= KVM_MEMORY_SLOTS)		goto out;	if (mem->guest_phys_addr + mem->memory_size < mem->guest_phys_addr)		goto out;	memslot = &kvm->memslots[mem->slot];	base_gfn = mem->guest_phys_addr >> PAGE_SHIFT;	npages = mem->memory_size >> PAGE_SHIFT;	if (!npages)		mem->flags &= ~KVM_MEM_LOG_DIRTY_PAGES;	mutex_lock(&kvm->lock);	new = old = *memslot;	new.base_gfn = base_gfn;	new.npages = npages;	new.flags = mem->flags;	/* Disallow changing a memory slot's size. */	r = -EINVAL;	if (npages && old.npages && npages != old.npages)		goto out_unlock;	/* Check for overlaps */	r = -EEXIST;	for (i = 0; i < KVM_MEMORY_SLOTS; ++i) {		struct kvm_memory_slot *s = &kvm->memslots[i];		if (s == memslot)			continue;		if (!((base_gfn + npages <= s->base_gfn) ||		      (base_gfn >= s->base_gfn + s->npages)))			goto out_unlock;	}	/* Deallocate if slot is being removed */	if (!npages)		new.phys_mem = NULL;	/* Free page dirty bitmap if unneeded */	if (!(new.flags & KVM_MEM_LOG_DIRTY_PAGES))		new.dirty_bitmap = NULL;	r = -ENOMEM;	/* Allocate if a slot is being created */	if (npages && !new.phys_mem) {		new.phys_mem = vmalloc(npages * sizeof(struct page *));		if (!new.phys_mem)			goto out_unlock;		memset(new.phys_mem, 0, npages * sizeof(struct page *));		for (i = 0; i < npages; ++i) {			new.phys_mem[i] = alloc_page(GFP_HIGHUSER						     | __GFP_ZERO);			if (!new.phys_mem[i])				goto out_unlock;			set_page_private(new.phys_mem[i],0);		}	}	/* Allocate page dirty bitmap if needed */	if ((new.flags & KVM_MEM_LOG_DIRTY_PAGES) && !new.dirty_bitmap) {		unsigned dirty_bytes = ALIGN(npages, BITS_PER_LONG) / 8;		new.dirty_bitmap = vmalloc(dirty_bytes);		if (!new.dirty_bitmap)			goto out_unlock;		memset(new.dirty_bitmap, 0, dirty_bytes);	}	if (mem->slot >= kvm->nmemslots)		kvm->nmemslots = mem->slot + 1;	*memslot = new;	kvm_mmu_slot_remove_write_access(kvm, mem->slot);	kvm_flush_remote_tlbs(kvm);	mutex_unlock(&kvm->lock);	kvm_free_physmem_slot(&old, &new);	return 0;out_unlock:	mutex_unlock(&kvm->lock);	kvm_free_physmem_slot(&new, &old);out:	return r;}/* * Get (and clear) the dirty memory log for a memory slot. */static int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,				      struct kvm_dirty_log *log){	struct kvm_memory_slot *memslot;	int r, i;	int n;	unsigned long any = 0;	mutex_lock(&kvm->lock);	r = -EINVAL;	if (log->slot >= KVM_MEMORY_SLOTS)		goto out;	memslot = &kvm->memslots[log->slot];	r = -ENOENT;	if (!memslot->dirty_bitmap)		goto out;	n = ALIGN(memslot->npages, BITS_PER_LONG) / 8;	for (i = 0; !any && i < n/sizeof(long); ++i)		any = memslot->dirty_bitmap[i];	r = -EFAULT;	if (copy_to_user(log->dirty_bitmap, memslot->dirty_bitmap, n))		goto out;	/* If nothing is dirty, don't bother messing with page tables. */	if (any) {		kvm_mmu_slot_remove_write_access(kvm, log->slot);		kvm_flush_remote_tlbs(kvm);		memset(memslot->dirty_bitmap, 0, n);	}	r = 0;out:	mutex_unlock(&kvm->lock);	return r;}/* * Set a new alias region.  Aliases map a portion of physical memory into * another portion.  This is useful for memory windows, for example the PC * VGA region. */static int kvm_vm_ioctl_set_memory_alias(struct kvm *kvm,					 struct kvm_memory_alias *alias){	int r, n;	struct kvm_mem_alias *p;	r = -EINVAL;	/* General sanity checks */	if (alias->memory_size & (PAGE_SIZE - 1))		goto out;	if (alias->guest_phys_addr & (PAGE_SIZE - 1))		goto out;	if (alias->slot >= KVM_ALIAS_SLOTS)		goto out;	if (alias->guest_phys_addr + alias->memory_size	    < alias->guest_phys_addr)		goto out;	if (alias->target_phys_addr + alias->memory_size	    < alias->target_phys_addr)		goto out;	mutex_lock(&kvm->lock);	p = &kvm->aliases[alias->slot];	p->base_gfn = alias->guest_phys_addr >> PAGE_SHIFT;	p->npages = alias->memory_size >> PAGE_SHIFT;	p->target_gfn = alias->target_phys_addr >> PAGE_SHIFT;	for (n = KVM_ALIAS_SLOTS; n > 0; --n)		if (kvm->aliases[n - 1].npages)			break;	kvm->naliases = n;	kvm_mmu_zap_all(kvm);	mutex_unlock(&kvm->lock);	return 0;out:	return r;}static int kvm_vm_ioctl_get_irqchip(struct kvm *kvm, struct kvm_irqchip *chip){	int r;	r = 0;	switch (chip->chip_id) {	case KVM_IRQCHIP_PIC_MASTER:		memcpy (&chip->chip.pic,			&pic_irqchip(kvm)->pics[0],			sizeof(struct kvm_pic_state));		break;	case KVM_IRQCHIP_PIC_SLAVE:		memcpy (&chip->chip.pic,			&pic_irqchip(kvm)->pics[1],			sizeof(struct kvm_pic_state));		break;	case KVM_IRQCHIP_IOAPIC:		memcpy (&chip->chip.ioapic,			ioapic_irqchip(kvm),			sizeof(struct kvm_ioapic_state));		break;	default:		r = -EINVAL;		break;	}	return r;}static int kvm_vm_ioctl_set_irqchip(struct kvm *kvm, struct kvm_irqchip *chip){	int r;	r = 0;	switch (chip->chip_id) {	case KVM_IRQCHIP_PIC_MASTER:		memcpy (&pic_irqchip(kvm)->pics[0],			&chip->chip.pic,			sizeof(struct kvm_pic_state));		break;	case KVM_IRQCHIP_PIC_SLAVE:		memcpy (&pic_irqchip(kvm)->pics[1],			&chip->chip.pic,			sizeof(struct kvm_pic_state));		break;	case KVM_IRQCHIP_IOAPIC:		memcpy (ioapic_irqchip(kvm),			&chip->chip.ioapic,			sizeof(struct kvm_ioapic_state));		break;	default:		r = -EINVAL;		break;	}	kvm_pic_update_irq(pic_irqchip(kvm));	return r;}static gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn){	int i;	struct kvm_mem_alias *alias;	for (i = 0; i < kvm->naliases; ++i) {		alias = &kvm->aliases[i];		if (gfn >= alias->base_gfn		    && gfn < alias->base_gfn + alias->npages)			return alias->target_gfn + gfn - alias->base_gfn;	}	return gfn;}static struct kvm_memory_slot *__gfn_to_memslot(struct kvm *kvm, gfn_t gfn){	int i;	for (i = 0; i < kvm->nmemslots; ++i) {		struct kvm_memory_slot *memslot = &kvm->memslots[i];		if (gfn >= memslot->base_gfn		    && gfn < memslot->base_gfn + memslot->npages)			return memslot;	}	return NULL;}struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn){	gfn = unalias_gfn(kvm, gfn);	return __gfn_to_memslot(kvm, gfn);}struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn){	struct kvm_memory_slot *slot;	gfn = unalias_gfn(kvm, gfn);	slot = __gfn_to_memslot(kvm, gfn);	if (!slot)		return NULL;	return slot->phys_mem[gfn - slot->base_gfn];}EXPORT_SYMBOL_GPL(gfn_to_page);/* WARNING: Does not work on aliased pages. */void mark_page_dirty(struct kvm *kvm, gfn_t gfn){	struct kvm_memory_slot *memslot;	memslot = __gfn_to_memslot(kvm, gfn);	if (memslot && memslot->dirty_bitmap) {		unsigned long rel_gfn = gfn - memslot->base_gfn;		/* avoid RMW */		if (!test_bit(rel_gfn, memslot->dirty_bitmap))			set_bit(rel_gfn, memslot->dirty_bitmap);	}}int emulator_read_std(unsigned long addr,			     void *val,			     unsigned int bytes,			     struct kvm_vcpu *vcpu){	void *data = val;	while (bytes) {		gpa_t gpa = vcpu->mmu.gva_to_gpa(vcpu, addr);		unsigned offset = addr & (PAGE_SIZE-1);		unsigned tocopy = min(bytes, (unsigned)PAGE_SIZE - offset);		unsigned long pfn;		struct page *page;		void *page_virt;		if (gpa == UNMAPPED_GVA)			return X86EMUL_PROPAGATE_FAULT;		pfn = gpa >> PAGE_SHIFT;		page = gfn_to_page(vcpu->kvm, pfn);		if (!page)			return X86EMUL_UNHANDLEABLE;		page_virt = kmap_atomic(page, KM_USER0);		memcpy(data, page_virt + offset, tocopy);		kunmap_atomic(page_virt, KM_USER0);		bytes -= tocopy;		data += tocopy;		addr += tocopy;	}	return X86EMUL_CONTINUE;}EXPORT_SYMBOL_GPL(emulator_read_std);static int emulator_write_std(unsigned long addr,			      const void *val,			      unsigned int bytes,			      struct kvm_vcpu *vcpu){	pr_unimpl(vcpu, "emulator_write_std: addr %lx n %d\n", addr, bytes);	return X86EMUL_UNHANDLEABLE;}/* * Only apic need an MMIO device hook, so shortcut now.. */static struct kvm_io_device *vcpu_find_pervcpu_dev(struct kvm_vcpu *vcpu,						gpa_t addr){	struct kvm_io_device *dev;	if (vcpu->apic) {		dev = &vcpu->apic->dev;		if (dev->in_range(dev, addr))			return dev;	}	return NULL;}static struct kvm_io_device *vcpu_find_mmio_dev(struct kvm_vcpu *vcpu,						gpa_t addr){	struct kvm_io_device *dev;	dev = vcpu_find_pervcpu_dev(vcpu, addr);	if (dev == NULL)		dev = kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr);	return dev;}static struct kvm_io_device *vcpu_find_pio_dev(struct kvm_vcpu *vcpu,					       gpa_t addr){	return kvm_io_bus_find_dev(&vcpu->kvm->pio_bus, addr);}static int emulator_read_emulated(unsigned long addr,				  void *val,				  unsigned int bytes,				  struct kvm_vcpu *vcpu){	struct kvm_io_device *mmio_dev;	gpa_t                 gpa;	if (vcpu->mmio_read_completed) {		memcpy(val, vcpu->mmio_data, bytes);		vcpu->mmio_read_completed = 0;		return X86EMUL_CONTINUE;	} else if (emulator_read_std(addr, val, bytes, vcpu)		   == X86EMUL_CONTINUE)		return X86EMUL_CONTINUE;	gpa = vcpu->mmu.gva_to_gpa(vcpu, addr);	if (gpa == UNMAPPED_GVA)		return X86EMUL_PROPAGATE_FAULT;	/*	 * Is this MMIO handled locally?	 */	mmio_dev = vcpu_find_mmio_dev(vcpu, gpa);	if (mmio_dev) {		kvm_iodevice_read(mmio_dev, gpa, bytes, val);		return X86EMUL_CONTINUE;	}	vcpu->mmio_needed = 1;	vcpu->mmio_phys_addr = gpa;	vcpu->mmio_size = bytes;	vcpu->mmio_is_write = 0;	return X86EMUL_UNHANDLEABLE;}static int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa,			       const void *val, int bytes){	struct page *page;	void *virt;	if (((gpa + bytes - 1) >> PAGE_SHIFT) != (gpa >> PAGE_SHIFT))		return 0;	page = gfn_to_page(vcpu->kvm, gpa >> PAGE_SHIFT);	if (!page)		return 0;	mark_page_dirty(vcpu->kvm, gpa >> PAGE_SHIFT);	virt = kmap_atomic(page, KM_USER0);	kvm_mmu_pte_write(vcpu, gpa, val, bytes);	memcpy(virt + offset_in_page(gpa), val, bytes);	kunmap_atomic(virt, KM_USER0);	return 1;}static int emulator_write_emulated_onepage(unsigned long addr,					   const void *val,					   unsigned int bytes,					   struct kvm_vcpu *vcpu){	struct kvm_io_device *mmio_dev;	gpa_t                 gpa = vcpu->mmu.gva_to_gpa(vcpu, addr);	if (gpa == UNMAPPED_GVA) {		kvm_x86_ops->inject_page_fault(vcpu, addr, 2);		return X86EMUL_PROPAGATE_FAULT;	}	if (emulator_write_phys(vcpu, gpa, val, bytes))		return X86EMUL_CONTINUE;	/*	 * Is this MMIO handled locally?	 */	mmio_dev = vcpu_find_mmio_dev(vcpu, gpa);	if (mmio_dev) {		kvm_iodevice_write(mmio_dev, gpa, bytes, val);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?