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 + -
显示快捷键?