Skip to content
Snippets Groups Projects
x86.c 132 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	r = -E2BIG;
    	if (nent >= cpuid->nent)
    		goto out_free;
    
    
    	r = -EFAULT;
    	if (copy_to_user(entries, cpuid_entries,
    
    			 nent * sizeof(struct kvm_cpuid_entry2)))
    
    		goto out_free;
    	cpuid->nent = nent;
    	r = 0;
    
    out_free:
    	vfree(cpuid_entries);
    out:
    	return r;
    }
    
    
    static int kvm_vcpu_ioctl_get_lapic(struct kvm_vcpu *vcpu,
    				    struct kvm_lapic_state *s)
    {
    	vcpu_load(vcpu);
    
    	memcpy(s->regs, vcpu->arch.apic->regs, sizeof *s);
    
    	vcpu_put(vcpu);
    
    	return 0;
    }
    
    static int kvm_vcpu_ioctl_set_lapic(struct kvm_vcpu *vcpu,
    				    struct kvm_lapic_state *s)
    {
    	vcpu_load(vcpu);
    
    	memcpy(vcpu->arch.apic->regs, s->regs, sizeof *s);
    
    	kvm_apic_post_state_restore(vcpu);
    
    static int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu,
    				    struct kvm_interrupt *irq)
    {
    	if (irq->irq < 0 || irq->irq >= 256)
    		return -EINVAL;
    	if (irqchip_in_kernel(vcpu->kvm))
    		return -ENXIO;
    	vcpu_load(vcpu);
    
    
    	kvm_queue_interrupt(vcpu, irq->irq, false);
    
    static int kvm_vcpu_ioctl_nmi(struct kvm_vcpu *vcpu)
    {
    	vcpu_load(vcpu);
    	kvm_inject_nmi(vcpu);
    	vcpu_put(vcpu);
    
    	return 0;
    }
    
    
    static int vcpu_ioctl_tpr_access_reporting(struct kvm_vcpu *vcpu,
    					   struct kvm_tpr_access_ctl *tac)
    {
    	if (tac->flags)
    		return -EINVAL;
    	vcpu->arch.tpr_access_reporting = !!tac->enabled;
    	return 0;
    }
    
    
    Huang Ying's avatar
    Huang Ying committed
    static int kvm_vcpu_ioctl_x86_setup_mce(struct kvm_vcpu *vcpu,
    					u64 mcg_cap)
    {
    	int r;
    	unsigned bank_num = mcg_cap & 0xff, bank;
    
    	r = -EINVAL;
    
    	if (!bank_num || bank_num >= KVM_MAX_MCE_BANKS)
    
    Huang Ying's avatar
    Huang Ying committed
    		goto out;
    	if (mcg_cap & ~(KVM_MCE_CAP_SUPPORTED | 0xff | 0xff0000))
    		goto out;
    	r = 0;
    	vcpu->arch.mcg_cap = mcg_cap;
    	/* Init IA32_MCG_CTL to all 1s */
    	if (mcg_cap & MCG_CTL_P)
    		vcpu->arch.mcg_ctl = ~(u64)0;
    	/* Init IA32_MCi_CTL to all 1s */
    	for (bank = 0; bank < bank_num; bank++)
    		vcpu->arch.mce_banks[bank*4] = ~(u64)0;
    out:
    	return r;
    }
    
    static int kvm_vcpu_ioctl_x86_set_mce(struct kvm_vcpu *vcpu,
    				      struct kvm_x86_mce *mce)
    {
    	u64 mcg_cap = vcpu->arch.mcg_cap;
    	unsigned bank_num = mcg_cap & 0xff;
    	u64 *banks = vcpu->arch.mce_banks;
    
    	if (mce->bank >= bank_num || !(mce->status & MCI_STATUS_VAL))
    		return -EINVAL;
    	/*
    	 * if IA32_MCG_CTL is not all 1s, the uncorrected error
    	 * reporting is disabled
    	 */
    	if ((mce->status & MCI_STATUS_UC) && (mcg_cap & MCG_CTL_P) &&
    	    vcpu->arch.mcg_ctl != ~(u64)0)
    		return 0;
    	banks += 4 * mce->bank;
    	/*
    	 * if IA32_MCi_CTL is not all 1s, the uncorrected error
    	 * reporting is disabled for the bank
    	 */
    	if ((mce->status & MCI_STATUS_UC) && banks[0] != ~(u64)0)
    		return 0;
    	if (mce->status & MCI_STATUS_UC) {
    		if ((vcpu->arch.mcg_status & MCG_STATUS_MCIP) ||
    
    		    !kvm_read_cr4_bits(vcpu, X86_CR4_MCE)) {
    
    Huang Ying's avatar
    Huang Ying committed
    			printk(KERN_DEBUG "kvm: set_mce: "
    			       "injects mce exception while "
    			       "previous one is in progress!\n");
    			set_bit(KVM_REQ_TRIPLE_FAULT, &vcpu->requests);
    			return 0;
    		}
    		if (banks[1] & MCI_STATUS_VAL)
    			mce->status |= MCI_STATUS_OVER;
    		banks[2] = mce->addr;
    		banks[3] = mce->misc;
    		vcpu->arch.mcg_status = mce->mcg_status;
    		banks[1] = mce->status;
    		kvm_queue_exception(vcpu, MC_VECTOR);
    	} else if (!(banks[1] & MCI_STATUS_VAL)
    		   || !(banks[1] & MCI_STATUS_UC)) {
    		if (banks[1] & MCI_STATUS_VAL)
    			mce->status |= MCI_STATUS_OVER;
    		banks[2] = mce->addr;
    		banks[3] = mce->misc;
    		banks[1] = mce->status;
    	} else
    		banks[1] |= MCI_STATUS_OVER;
    	return 0;
    }
    
    
    static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu,
    					       struct kvm_vcpu_events *events)
    {
    	vcpu_load(vcpu);
    
    
    	events->exception.injected =
    		vcpu->arch.exception.pending &&
    		!kvm_exception_is_soft(vcpu->arch.exception.nr);
    
    	events->exception.nr = vcpu->arch.exception.nr;
    	events->exception.has_error_code = vcpu->arch.exception.has_error_code;
    	events->exception.error_code = vcpu->arch.exception.error_code;
    
    
    	events->interrupt.injected =
    		vcpu->arch.interrupt.pending && !vcpu->arch.interrupt.soft;
    
    	events->interrupt.nr = vcpu->arch.interrupt.nr;
    
    	events->interrupt.soft = 0;
    
    	events->interrupt.shadow =
    		kvm_x86_ops->get_interrupt_shadow(vcpu,
    			KVM_X86_SHADOW_INT_MOV_SS | KVM_X86_SHADOW_INT_STI);
    
    
    	events->nmi.injected = vcpu->arch.nmi_injected;
    	events->nmi.pending = vcpu->arch.nmi_pending;
    	events->nmi.masked = kvm_x86_ops->get_nmi_mask(vcpu);
    
    	events->sipi_vector = vcpu->arch.sipi_vector;
    
    
    	events->flags = (KVM_VCPUEVENT_VALID_NMI_PENDING
    
    			 | KVM_VCPUEVENT_VALID_SIPI_VECTOR
    			 | KVM_VCPUEVENT_VALID_SHADOW);
    
    
    	vcpu_put(vcpu);
    }
    
    static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
    					      struct kvm_vcpu_events *events)
    {
    
    	if (events->flags & ~(KVM_VCPUEVENT_VALID_NMI_PENDING
    
    			      | KVM_VCPUEVENT_VALID_SIPI_VECTOR
    			      | KVM_VCPUEVENT_VALID_SHADOW))
    
    		return -EINVAL;
    
    	vcpu_load(vcpu);
    
    	vcpu->arch.exception.pending = events->exception.injected;
    	vcpu->arch.exception.nr = events->exception.nr;
    	vcpu->arch.exception.has_error_code = events->exception.has_error_code;
    	vcpu->arch.exception.error_code = events->exception.error_code;
    
    	vcpu->arch.interrupt.pending = events->interrupt.injected;
    	vcpu->arch.interrupt.nr = events->interrupt.nr;
    	vcpu->arch.interrupt.soft = events->interrupt.soft;
    	if (vcpu->arch.interrupt.pending && irqchip_in_kernel(vcpu->kvm))
    		kvm_pic_clear_isr_ack(vcpu->kvm);
    
    	if (events->flags & KVM_VCPUEVENT_VALID_SHADOW)
    		kvm_x86_ops->set_interrupt_shadow(vcpu,
    						  events->interrupt.shadow);
    
    
    	vcpu->arch.nmi_injected = events->nmi.injected;
    
    	if (events->flags & KVM_VCPUEVENT_VALID_NMI_PENDING)
    		vcpu->arch.nmi_pending = events->nmi.pending;
    
    	kvm_x86_ops->set_nmi_mask(vcpu, events->nmi.masked);
    
    
    	if (events->flags & KVM_VCPUEVENT_VALID_SIPI_VECTOR)
    		vcpu->arch.sipi_vector = events->sipi_vector;
    
    static void kvm_vcpu_ioctl_x86_get_debugregs(struct kvm_vcpu *vcpu,
    					     struct kvm_debugregs *dbgregs)
    {
    	vcpu_load(vcpu);
    
    	memcpy(dbgregs->db, vcpu->arch.db, sizeof(vcpu->arch.db));
    	dbgregs->dr6 = vcpu->arch.dr6;
    	dbgregs->dr7 = vcpu->arch.dr7;
    	dbgregs->flags = 0;
    
    	vcpu_put(vcpu);
    }
    
    static int kvm_vcpu_ioctl_x86_set_debugregs(struct kvm_vcpu *vcpu,
    					    struct kvm_debugregs *dbgregs)
    {
    	if (dbgregs->flags)
    		return -EINVAL;
    
    	vcpu_load(vcpu);
    
    	memcpy(vcpu->arch.db, dbgregs->db, sizeof(vcpu->arch.db));
    	vcpu->arch.dr6 = dbgregs->dr6;
    	vcpu->arch.dr7 = dbgregs->dr7;
    
    	vcpu_put(vcpu);
    
    	return 0;
    }
    
    
    long kvm_arch_vcpu_ioctl(struct file *filp,
    			 unsigned int ioctl, unsigned long arg)
    {
    	struct kvm_vcpu *vcpu = filp->private_data;
    	void __user *argp = (void __user *)arg;
    	int r;
    
    	struct kvm_lapic_state *lapic = NULL;
    
    
    	switch (ioctl) {
    	case KVM_GET_LAPIC: {
    
    		lapic = kzalloc(sizeof(struct kvm_lapic_state), GFP_KERNEL);
    
    		r = -ENOMEM;
    		if (!lapic)
    			goto out;
    		r = kvm_vcpu_ioctl_get_lapic(vcpu, lapic);
    
    		if (r)
    			goto out;
    		r = -EFAULT;
    
    		if (copy_to_user(argp, lapic, sizeof(struct kvm_lapic_state)))
    
    			goto out;
    		r = 0;
    		break;
    	}
    	case KVM_SET_LAPIC: {
    
    		lapic = kmalloc(sizeof(struct kvm_lapic_state), GFP_KERNEL);
    		r = -ENOMEM;
    		if (!lapic)
    			goto out;
    
    		r = -EFAULT;
    
    		if (copy_from_user(lapic, argp, sizeof(struct kvm_lapic_state)))
    
    		r = kvm_vcpu_ioctl_set_lapic(vcpu, lapic);
    
    		if (r)
    			goto out;
    		r = 0;
    		break;
    	}
    
    	case KVM_INTERRUPT: {
    		struct kvm_interrupt irq;
    
    		r = -EFAULT;
    		if (copy_from_user(&irq, argp, sizeof irq))
    			goto out;
    		r = kvm_vcpu_ioctl_interrupt(vcpu, &irq);
    		if (r)
    			goto out;
    		r = 0;
    		break;
    	}
    
    	case KVM_NMI: {
    		r = kvm_vcpu_ioctl_nmi(vcpu);
    		if (r)
    			goto out;
    		r = 0;
    		break;
    	}
    
    	case KVM_SET_CPUID: {
    		struct kvm_cpuid __user *cpuid_arg = argp;
    		struct kvm_cpuid cpuid;
    
    		r = -EFAULT;
    		if (copy_from_user(&cpuid, cpuid_arg, sizeof cpuid))
    			goto out;
    		r = kvm_vcpu_ioctl_set_cpuid(vcpu, &cpuid, cpuid_arg->entries);
    		if (r)
    			goto out;
    		break;
    	}
    
    	case KVM_SET_CPUID2: {
    		struct kvm_cpuid2 __user *cpuid_arg = argp;
    		struct kvm_cpuid2 cpuid;
    
    		r = -EFAULT;
    		if (copy_from_user(&cpuid, cpuid_arg, sizeof cpuid))
    			goto out;
    		r = kvm_vcpu_ioctl_set_cpuid2(vcpu, &cpuid,
    
    					      cpuid_arg->entries);
    
    		if (r)
    			goto out;
    		break;
    	}
    	case KVM_GET_CPUID2: {
    		struct kvm_cpuid2 __user *cpuid_arg = argp;
    		struct kvm_cpuid2 cpuid;
    
    		r = -EFAULT;
    		if (copy_from_user(&cpuid, cpuid_arg, sizeof cpuid))
    			goto out;
    		r = kvm_vcpu_ioctl_get_cpuid2(vcpu, &cpuid,
    
    					      cpuid_arg->entries);
    
    		if (r)
    			goto out;
    		r = -EFAULT;
    		if (copy_to_user(cpuid_arg, &cpuid, sizeof cpuid))
    			goto out;
    		r = 0;
    		break;
    	}
    
    	case KVM_GET_MSRS:
    		r = msr_io(vcpu, argp, kvm_get_msr, 1);
    		break;
    	case KVM_SET_MSRS:
    		r = msr_io(vcpu, argp, do_set_msr, 0);
    		break;
    
    	case KVM_TPR_ACCESS_REPORTING: {
    		struct kvm_tpr_access_ctl tac;
    
    		r = -EFAULT;
    		if (copy_from_user(&tac, argp, sizeof tac))
    			goto out;
    		r = vcpu_ioctl_tpr_access_reporting(vcpu, &tac);
    		if (r)
    			goto out;
    		r = -EFAULT;
    		if (copy_to_user(argp, &tac, sizeof tac))
    			goto out;
    		r = 0;
    		break;
    	};
    
    	case KVM_SET_VAPIC_ADDR: {
    		struct kvm_vapic_addr va;
    
    		r = -EINVAL;
    		if (!irqchip_in_kernel(vcpu->kvm))
    			goto out;
    		r = -EFAULT;
    		if (copy_from_user(&va, argp, sizeof va))
    			goto out;
    		r = 0;
    		kvm_lapic_set_vapic_addr(vcpu, va.vapic_addr);
    		break;
    	}
    
    Huang Ying's avatar
    Huang Ying committed
    	case KVM_X86_SETUP_MCE: {
    		u64 mcg_cap;
    
    		r = -EFAULT;
    		if (copy_from_user(&mcg_cap, argp, sizeof mcg_cap))
    			goto out;
    		r = kvm_vcpu_ioctl_x86_setup_mce(vcpu, mcg_cap);
    		break;
    	}
    	case KVM_X86_SET_MCE: {
    		struct kvm_x86_mce mce;
    
    		r = -EFAULT;
    		if (copy_from_user(&mce, argp, sizeof mce))
    			goto out;
    		r = kvm_vcpu_ioctl_x86_set_mce(vcpu, &mce);
    		break;
    	}
    
    	case KVM_GET_VCPU_EVENTS: {
    		struct kvm_vcpu_events events;
    
    		kvm_vcpu_ioctl_x86_get_vcpu_events(vcpu, &events);
    
    		r = -EFAULT;
    		if (copy_to_user(argp, &events, sizeof(struct kvm_vcpu_events)))
    			break;
    		r = 0;
    		break;
    	}
    	case KVM_SET_VCPU_EVENTS: {
    		struct kvm_vcpu_events events;
    
    		r = -EFAULT;
    		if (copy_from_user(&events, argp, sizeof(struct kvm_vcpu_events)))
    			break;
    
    		r = kvm_vcpu_ioctl_x86_set_vcpu_events(vcpu, &events);
    		break;
    	}
    
    	case KVM_GET_DEBUGREGS: {
    		struct kvm_debugregs dbgregs;
    
    		kvm_vcpu_ioctl_x86_get_debugregs(vcpu, &dbgregs);
    
    		r = -EFAULT;
    		if (copy_to_user(argp, &dbgregs,
    				 sizeof(struct kvm_debugregs)))
    			break;
    		r = 0;
    		break;
    	}
    	case KVM_SET_DEBUGREGS: {
    		struct kvm_debugregs dbgregs;
    
    		r = -EFAULT;
    		if (copy_from_user(&dbgregs, argp,
    				   sizeof(struct kvm_debugregs)))
    			break;
    
    		r = kvm_vcpu_ioctl_x86_set_debugregs(vcpu, &dbgregs);
    		break;
    	}
    
    	default:
    		r = -EINVAL;
    	}
    out:
    
    static int kvm_vm_ioctl_set_tss_addr(struct kvm *kvm, unsigned long addr)
    {
    	int ret;
    
    	if (addr > (unsigned int)(-3 * PAGE_SIZE))
    		return -1;
    	ret = kvm_x86_ops->set_tss_addr(kvm, addr);
    	return ret;
    }
    
    
    static int kvm_vm_ioctl_set_identity_map_addr(struct kvm *kvm,
    					      u64 ident_addr)
    {
    	kvm->arch.ept_identity_map_addr = ident_addr;
    	return 0;
    }
    
    
    static int kvm_vm_ioctl_set_nr_mmu_pages(struct kvm *kvm,
    					  u32 kvm_nr_mmu_pages)
    {
    	if (kvm_nr_mmu_pages < KVM_MIN_ALLOC_MMU_PAGES)
    		return -EINVAL;
    
    
    	mutex_lock(&kvm->slots_lock);
    
    	spin_lock(&kvm->mmu_lock);
    
    
    	kvm_mmu_change_mmu_pages(kvm, kvm_nr_mmu_pages);
    
    	kvm->arch.n_requested_mmu_pages = kvm_nr_mmu_pages;
    
    	spin_unlock(&kvm->mmu_lock);
    
    	mutex_unlock(&kvm->slots_lock);
    
    	return 0;
    }
    
    static int kvm_vm_ioctl_get_nr_mmu_pages(struct kvm *kvm)
    {
    
    	return kvm->arch.n_alloc_mmu_pages;
    
    gfn_t unalias_gfn_instantiation(struct kvm *kvm, gfn_t gfn)
    {
    	int i;
    	struct kvm_mem_alias *alias;
    	struct kvm_mem_aliases *aliases;
    
    
    	aliases = kvm_aliases(kvm);
    
    
    	for (i = 0; i < aliases->naliases; ++i) {
    		alias = &aliases->aliases[i];
    		if (alias->flags & KVM_ALIAS_INVALID)
    			continue;
    		if (gfn >= alias->base_gfn
    		    && gfn < alias->base_gfn + alias->npages)
    			return alias->target_gfn + gfn - alias->base_gfn;
    	}
    	return gfn;
    }
    
    
    gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
    {
    	int i;
    	struct kvm_mem_alias *alias;
    
    	struct kvm_mem_aliases *aliases;
    
    
    	aliases = kvm_aliases(kvm);
    
    	for (i = 0; i < aliases->naliases; ++i) {
    		alias = &aliases->aliases[i];
    
    		if (gfn >= alias->base_gfn
    		    && gfn < alias->base_gfn + alias->npages)
    			return alias->target_gfn + gfn - alias->base_gfn;
    	}
    	return gfn;
    }
    
    
    /*
     * 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;
    
    	struct kvm_mem_aliases *aliases, *old_aliases;
    
    
    	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;
    
    
    	r = -ENOMEM;
    	aliases = kzalloc(sizeof(struct kvm_mem_aliases), GFP_KERNEL);
    	if (!aliases)
    		goto out;
    
    
    	mutex_lock(&kvm->slots_lock);
    
    	/* invalidate any gfn reference in case of deletion/shrinking */
    	memcpy(aliases, kvm->arch.aliases, sizeof(struct kvm_mem_aliases));
    	aliases->aliases[alias->slot].flags |= KVM_ALIAS_INVALID;
    	old_aliases = kvm->arch.aliases;
    	rcu_assign_pointer(kvm->arch.aliases, aliases);
    	synchronize_srcu_expedited(&kvm->srcu);
    	kvm_mmu_zap_all(kvm);
    	kfree(old_aliases);
    
    	r = -ENOMEM;
    	aliases = kzalloc(sizeof(struct kvm_mem_aliases), GFP_KERNEL);
    	if (!aliases)
    		goto out_unlock;
    
    	memcpy(aliases, kvm->arch.aliases, sizeof(struct kvm_mem_aliases));
    
    
    	p = &aliases->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;
    
    	p->flags &= ~(KVM_ALIAS_INVALID);
    
    
    	for (n = KVM_ALIAS_SLOTS; n > 0; --n)
    
    		if (aliases->aliases[n - 1].npages)
    
    	aliases->naliases = n;
    
    	old_aliases = kvm->arch.aliases;
    	rcu_assign_pointer(kvm->arch.aliases, aliases);
    	synchronize_srcu_expedited(&kvm->srcu);
    	kfree(old_aliases);
    	r = 0;
    
    	mutex_unlock(&kvm->slots_lock);
    
    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:
    
    		r = kvm_get_ioapic(kvm, &chip->chip.ioapic);
    
    		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:
    
    		raw_spin_lock(&pic_irqchip(kvm)->lock);
    
    		memcpy(&pic_irqchip(kvm)->pics[0],
    			&chip->chip.pic,
    			sizeof(struct kvm_pic_state));
    
    		raw_spin_unlock(&pic_irqchip(kvm)->lock);
    
    		break;
    	case KVM_IRQCHIP_PIC_SLAVE:
    
    		raw_spin_lock(&pic_irqchip(kvm)->lock);
    
    		memcpy(&pic_irqchip(kvm)->pics[1],
    			&chip->chip.pic,
    			sizeof(struct kvm_pic_state));
    
    		raw_spin_unlock(&pic_irqchip(kvm)->lock);
    
    		break;
    	case KVM_IRQCHIP_IOAPIC:
    
    		r = kvm_set_ioapic(kvm, &chip->chip.ioapic);
    
    		break;
    	default:
    		r = -EINVAL;
    		break;
    	}
    	kvm_pic_update_irq(pic_irqchip(kvm));
    	return r;
    }
    
    
    static int kvm_vm_ioctl_get_pit(struct kvm *kvm, struct kvm_pit_state *ps)
    {
    	int r = 0;
    
    
    	mutex_lock(&kvm->arch.vpit->pit_state.lock);
    
    	memcpy(ps, &kvm->arch.vpit->pit_state, sizeof(struct kvm_pit_state));
    
    	mutex_unlock(&kvm->arch.vpit->pit_state.lock);
    
    	return r;
    }
    
    static int kvm_vm_ioctl_set_pit(struct kvm *kvm, struct kvm_pit_state *ps)
    {
    	int r = 0;
    
    
    	mutex_lock(&kvm->arch.vpit->pit_state.lock);
    
    	memcpy(&kvm->arch.vpit->pit_state, ps, sizeof(struct kvm_pit_state));
    
    	kvm_pit_load_count(kvm, 0, ps->channels[0].count, 0);
    	mutex_unlock(&kvm->arch.vpit->pit_state.lock);
    	return r;
    }
    
    static int kvm_vm_ioctl_get_pit2(struct kvm *kvm, struct kvm_pit_state2 *ps)
    {
    	int r = 0;
    
    	mutex_lock(&kvm->arch.vpit->pit_state.lock);
    	memcpy(ps->channels, &kvm->arch.vpit->pit_state.channels,
    		sizeof(ps->channels));
    	ps->flags = kvm->arch.vpit->pit_state.flags;
    	mutex_unlock(&kvm->arch.vpit->pit_state.lock);
    	return r;
    }
    
    static int kvm_vm_ioctl_set_pit2(struct kvm *kvm, struct kvm_pit_state2 *ps)
    {
    	int r = 0, start = 0;
    	u32 prev_legacy, cur_legacy;
    	mutex_lock(&kvm->arch.vpit->pit_state.lock);
    	prev_legacy = kvm->arch.vpit->pit_state.flags & KVM_PIT_FLAGS_HPET_LEGACY;
    	cur_legacy = ps->flags & KVM_PIT_FLAGS_HPET_LEGACY;
    	if (!prev_legacy && cur_legacy)
    		start = 1;
    	memcpy(&kvm->arch.vpit->pit_state.channels, &ps->channels,
    	       sizeof(kvm->arch.vpit->pit_state.channels));
    	kvm->arch.vpit->pit_state.flags = ps->flags;
    	kvm_pit_load_count(kvm, 0, kvm->arch.vpit->pit_state.channels[0].count, start);
    
    	mutex_unlock(&kvm->arch.vpit->pit_state.lock);
    
    static int kvm_vm_ioctl_reinject(struct kvm *kvm,
    				 struct kvm_reinject_control *control)
    {
    	if (!kvm->arch.vpit)
    		return -ENXIO;
    
    	mutex_lock(&kvm->arch.vpit->pit_state.lock);
    
    	kvm->arch.vpit->pit_state.pit_timer.reinject = control->pit_reinject;
    
    	mutex_unlock(&kvm->arch.vpit->pit_state.lock);
    
    /*
     * Get (and clear) the dirty memory log for a memory slot.
     */
    int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
    				      struct kvm_dirty_log *log)
    {
    
    	unsigned long is_dirty = 0;
    	unsigned long *dirty_bitmap = NULL;
    
    	mutex_lock(&kvm->slots_lock);
    
    	r = -EINVAL;
    	if (log->slot >= KVM_MEMORY_SLOTS)
    		goto out;
    
    	memslot = &kvm->memslots->memslots[log->slot];
    	r = -ENOENT;
    	if (!memslot->dirty_bitmap)
    		goto out;
    
    
    	n = kvm_dirty_bitmap_bytes(memslot);
    
    
    	r = -ENOMEM;
    	dirty_bitmap = vmalloc(n);
    	if (!dirty_bitmap)
    
    	memset(dirty_bitmap, 0, n);
    
    	for (i = 0; !is_dirty && i < n/sizeof(long); i++)
    		is_dirty = memslot->dirty_bitmap[i];
    
    
    	/* If nothing is dirty, don't bother messing with page tables. */
    	if (is_dirty) {
    
    		struct kvm_memslots *slots, *old_slots;
    
    
    		spin_lock(&kvm->mmu_lock);
    
    		kvm_mmu_slot_remove_write_access(kvm, log->slot);
    
    		spin_unlock(&kvm->mmu_lock);
    
    
    		slots = kzalloc(sizeof(struct kvm_memslots), GFP_KERNEL);
    		if (!slots)
    			goto out_free;
    
    		memcpy(slots, kvm->memslots, sizeof(struct kvm_memslots));
    		slots->memslots[log->slot].dirty_bitmap = dirty_bitmap;
    
    		old_slots = kvm->memslots;
    		rcu_assign_pointer(kvm->memslots, slots);
    		synchronize_srcu_expedited(&kvm->srcu);
    		dirty_bitmap = old_slots->memslots[log->slot].dirty_bitmap;
    		kfree(old_slots);
    
    	if (copy_to_user(log->dirty_bitmap, dirty_bitmap, n))
    		r = -EFAULT;
    out_free:
    	vfree(dirty_bitmap);
    
    	mutex_unlock(&kvm->slots_lock);
    
    long kvm_arch_vm_ioctl(struct file *filp,
    		       unsigned int ioctl, unsigned long arg)
    {
    	struct kvm *kvm = filp->private_data;
    	void __user *argp = (void __user *)arg;
    
    	int r = -ENOTTY;
    
    	/*
    	 * This union makes it completely explicit to gcc-3.x
    	 * that these two variables' stack usage should be
    	 * combined, not added together.
    	 */
    	union {
    		struct kvm_pit_state ps;
    
    		struct kvm_pit_state2 ps2;
    
    		struct kvm_memory_alias alias;
    
    		struct kvm_pit_config pit_config;
    
    
    	switch (ioctl) {
    	case KVM_SET_TSS_ADDR:
    		r = kvm_vm_ioctl_set_tss_addr(kvm, arg);
    		if (r < 0)
    			goto out;
    		break;
    
    	case KVM_SET_IDENTITY_MAP_ADDR: {
    		u64 ident_addr;
    
    		r = -EFAULT;
    		if (copy_from_user(&ident_addr, argp, sizeof ident_addr))
    			goto out;
    		r = kvm_vm_ioctl_set_identity_map_addr(kvm, ident_addr);
    		if (r < 0)
    			goto out;
    		break;
    	}
    
    	case KVM_SET_MEMORY_REGION: {
    		struct kvm_memory_region kvm_mem;
    		struct kvm_userspace_memory_region kvm_userspace_mem;
    
    		r = -EFAULT;
    		if (copy_from_user(&kvm_mem, argp, sizeof kvm_mem))
    			goto out;
    		kvm_userspace_mem.slot = kvm_mem.slot;
    		kvm_userspace_mem.flags = kvm_mem.flags;
    		kvm_userspace_mem.guest_phys_addr = kvm_mem.guest_phys_addr;
    		kvm_userspace_mem.memory_size = kvm_mem.memory_size;
    		r = kvm_vm_ioctl_set_memory_region(kvm, &kvm_userspace_mem, 0);
    		if (r)
    			goto out;
    		break;
    	}
    	case KVM_SET_NR_MMU_PAGES:
    		r = kvm_vm_ioctl_set_nr_mmu_pages(kvm, arg);
    		if (r)
    			goto out;
    		break;
    	case KVM_GET_NR_MMU_PAGES:
    		r = kvm_vm_ioctl_get_nr_mmu_pages(kvm);
    		break;
    
    	case KVM_SET_MEMORY_ALIAS:
    
    		if (copy_from_user(&u.alias, argp, sizeof(struct kvm_memory_alias)))
    
    		r = kvm_vm_ioctl_set_memory_alias(kvm, &u.alias);
    
    		if (r)
    			goto out;
    		break;
    
    	case KVM_CREATE_IRQCHIP: {
    		struct kvm_pic *vpic;
    
    		mutex_lock(&kvm->lock);
    		r = -EEXIST;
    		if (kvm->arch.vpic)
    			goto create_irqchip_unlock;
    
    		vpic = kvm_create_pic(kvm);
    		if (vpic) {
    
    			r = kvm_ioapic_init(kvm);
    			if (r) {
    
    				kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS,
    							  &vpic->dev);
    
    				kfree(vpic);
    				goto create_irqchip_unlock;
    
    			goto create_irqchip_unlock;
    		smp_wmb();
    		kvm->arch.vpic = vpic;
    		smp_wmb();
    
    		r = kvm_setup_default_irq_routing(kvm);
    		if (r) {
    
    			mutex_lock(&kvm->irq_lock);
    
    			kvm_ioapic_destroy(kvm);
    			kvm_destroy_pic(kvm);
    
    			mutex_unlock(&kvm->irq_lock);
    
    	create_irqchip_unlock:
    		mutex_unlock(&kvm->lock);
    
    Sheng Yang's avatar
    Sheng Yang committed
    	case KVM_CREATE_PIT:
    
    		u.pit_config.flags = KVM_PIT_SPEAKER_DUMMY;
    		goto create_pit;
    	case KVM_CREATE_PIT2:
    		r = -EFAULT;
    		if (copy_from_user(&u.pit_config, argp,
    				   sizeof(struct kvm_pit_config)))
    			goto out;
    	create_pit:
    
    		mutex_lock(&kvm->slots_lock);
    
    		r = -EEXIST;
    		if (kvm->arch.vpit)
    			goto create_pit_unlock;
    
    Sheng Yang's avatar
    Sheng Yang committed
    		r = -ENOMEM;
    
    		kvm->arch.vpit = kvm_create_pit(kvm, u.pit_config.flags);
    
    Sheng Yang's avatar
    Sheng Yang committed
    		if (kvm->arch.vpit)
    			r = 0;
    
    	create_pit_unlock:
    
    		mutex_unlock(&kvm->slots_lock);
    
    Sheng Yang's avatar
    Sheng Yang committed
    		break;
    
    	case KVM_IRQ_LINE_STATUS:
    
    	case KVM_IRQ_LINE: {
    		struct kvm_irq_level irq_event;
    
    		r = -EFAULT;
    		if (copy_from_user(&irq_event, argp, sizeof irq_event))
    			goto out;
    
    		if (irqchip_in_kernel(kvm)) {
    
    			__s32 status;
    			status = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
    					irq_event.irq, irq_event.level);
    			if (ioctl == KVM_IRQ_LINE_STATUS) {
    
    				irq_event.status = status;
    				if (copy_to_user(argp, &irq_event,
    							sizeof irq_event))
    					goto out;
    			}
    
    			r = 0;
    		}
    		break;
    	}
    	case KVM_GET_IRQCHIP: {
    		/* 0: PIC master, 1: PIC slave, 2: IOAPIC */
    
    		struct kvm_irqchip *chip = kmalloc(sizeof(*chip), GFP_KERNEL);
    
    		r = -EFAULT;
    		if (copy_from_user(chip, argp, sizeof *chip))
    			goto get_irqchip_out;
    
    		r = -ENXIO;
    		if (!irqchip_in_kernel(kvm))
    
    			goto get_irqchip_out;
    		r = kvm_vm_ioctl_get_irqchip(kvm, chip);
    
    			goto get_irqchip_out;
    
    		if (copy_to_user(argp, chip, sizeof *chip))
    			goto get_irqchip_out;
    
    	get_irqchip_out:
    		kfree(chip);
    		if (r)
    			goto out;
    
    		break;
    	}
    	case KVM_SET_IRQCHIP: {
    		/* 0: PIC master, 1: PIC slave, 2: IOAPIC */
    
    		struct kvm_irqchip *chip = kmalloc(sizeof(*chip), GFP_KERNEL);
    
    		r = -EFAULT;
    		if (copy_from_user(chip, argp, sizeof *chip))
    			goto set_irqchip_out;
    
    		r = -ENXIO;
    		if (!irqchip_in_kernel(kvm))
    
    			goto set_irqchip_out;
    		r = kvm_vm_ioctl_set_irqchip(kvm, chip);
    
    			goto set_irqchip_out;
    
    	set_irqchip_out:
    		kfree(chip);
    		if (r)
    			goto out;
    
    	case KVM_GET_PIT: {
    		r = -EFAULT;
    
    		if (copy_from_user(&u.ps, argp, sizeof(struct kvm_pit_state)))
    
    			goto out;
    		r = -ENXIO;
    		if (!kvm->arch.vpit)
    			goto out;
    
    		r = kvm_vm_ioctl_get_pit(kvm, &u.ps);
    
    		if (r)
    			goto out;
    		r = -EFAULT;
    
    		if (copy_to_user(argp, &u.ps, sizeof(struct kvm_pit_state)))
    
    			goto out;
    		r = 0;
    		break;
    	}
    	case KVM_SET_PIT: {
    		r = -EFAULT;
    
    		if (copy_from_user(&u.ps, argp, sizeof u.ps))