Skip to content
Snippets Groups Projects
x86.c 147 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		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;
    
    	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);
    
    
    	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);
    
    		r = -ENOMEM;
    		dirty_bitmap = vmalloc(n);
    		if (!dirty_bitmap)
    			goto out;
    		memset(dirty_bitmap, 0, n);
    
    		r = -ENOMEM;
    		slots = kzalloc(sizeof(struct kvm_memslots), GFP_KERNEL);
    		if (!slots) {
    			vfree(dirty_bitmap);
    			goto out;
    		}
    
    		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);
    
    
    		r = -EFAULT;
    		if (copy_to_user(log->dirty_bitmap, dirty_bitmap, n)) {
    			vfree(dirty_bitmap);
    			goto out;
    		}
    		vfree(dirty_bitmap);
    	} else {
    		r = -EFAULT;
    		if (clear_user(log->dirty_bitmap, n))
    			goto out;
    
    	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_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_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_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))
    
    			goto out;
    		r = -ENXIO;
    		if (!kvm->arch.vpit)
    			goto out;
    
    		r = kvm_vm_ioctl_set_pit(kvm, &u.ps);
    
    	case KVM_GET_PIT2: {
    		r = -ENXIO;
    		if (!kvm->arch.vpit)
    			goto out;
    		r = kvm_vm_ioctl_get_pit2(kvm, &u.ps2);
    		if (r)
    			goto out;
    		r = -EFAULT;
    		if (copy_to_user(argp, &u.ps2, sizeof(u.ps2)))
    			goto out;
    		r = 0;
    		break;
    	}
    	case KVM_SET_PIT2: {
    		r = -EFAULT;
    		if (copy_from_user(&u.ps2, argp, sizeof(u.ps2)))
    			goto out;
    		r = -ENXIO;
    		if (!kvm->arch.vpit)
    			goto out;
    		r = kvm_vm_ioctl_set_pit2(kvm, &u.ps2);
    		if (r)
    			goto out;
    		r = 0;
    		break;
    	}
    
    	case KVM_REINJECT_CONTROL: {
    		struct kvm_reinject_control control;
    		r =  -EFAULT;
    		if (copy_from_user(&control, argp, sizeof(control)))
    			goto out;
    		r = kvm_vm_ioctl_reinject(kvm, &control);
    		if (r)
    			goto out;
    		r = 0;
    		break;
    	}
    
    	case KVM_XEN_HVM_CONFIG: {
    		r = -EFAULT;
    		if (copy_from_user(&kvm->arch.xen_hvm_config, argp,
    				   sizeof(struct kvm_xen_hvm_config)))
    			goto out;
    		r = -EINVAL;
    		if (kvm->arch.xen_hvm_config.flags)
    			goto out;
    		r = 0;
    		break;
    	}
    
    	case KVM_SET_CLOCK: {
    		struct kvm_clock_data user_ns;
    		u64 now_ns;
    		s64 delta;
    
    		r = -EFAULT;
    		if (copy_from_user(&user_ns, argp, sizeof(user_ns)))
    			goto out;
    
    		r = -EINVAL;
    		if (user_ns.flags)
    			goto out;
    
    		r = 0;
    
    		now_ns = get_kernel_ns();
    
    		delta = user_ns.clock - now_ns;
    		kvm->arch.kvmclock_offset = delta;
    		break;
    	}
    	case KVM_GET_CLOCK: {
    		struct kvm_clock_data user_ns;
    		u64 now_ns;
    
    
    		now_ns = get_kernel_ns();
    
    		user_ns.clock = kvm->arch.kvmclock_offset + now_ns;
    		user_ns.flags = 0;
    
    		r = -EFAULT;
    		if (copy_to_user(argp, &user_ns, sizeof(user_ns)))
    			goto out;
    		r = 0;
    		break;
    	}
    
    
    static void kvm_init_msr_list(void)
    
    	/* skip the first msrs in the list. KVM-specific */
    	for (i = j = KVM_SAVE_MSRS_BEGIN; i < ARRAY_SIZE(msrs_to_save); i++) {
    
    		if (rdmsr_safe(msrs_to_save[i], &dummy[0], &dummy[1]) < 0)
    			continue;
    		if (j < i)
    			msrs_to_save[j] = msrs_to_save[i];
    		j++;
    	}
    	num_msrs_to_save = j;
    }
    
    
    static int vcpu_mmio_write(struct kvm_vcpu *vcpu, gpa_t addr, int len,
    			   const void *v)
    
    	if (vcpu->arch.apic &&
    	    !kvm_iodevice_write(&vcpu->arch.apic->dev, addr, len, v))
    		return 0;
    
    	return kvm_io_bus_write(vcpu->kvm, KVM_MMIO_BUS, addr, len, v);
    
    static int vcpu_mmio_read(struct kvm_vcpu *vcpu, gpa_t addr, int len, void *v)
    
    	if (vcpu->arch.apic &&
    	    !kvm_iodevice_read(&vcpu->arch.apic->dev, addr, len, v))
    		return 0;
    
    	return kvm_io_bus_read(vcpu->kvm, KVM_MMIO_BUS, addr, len, v);
    
    static void kvm_set_segment(struct kvm_vcpu *vcpu,
    			struct kvm_segment *var, int seg)
    {
    	kvm_x86_ops->set_segment(vcpu, var, seg);
    }
    
    void kvm_get_segment(struct kvm_vcpu *vcpu,
    		     struct kvm_segment *var, int seg)
    {
    	kvm_x86_ops->get_segment(vcpu, var, seg);
    }
    
    
    static gpa_t translate_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access)
    {
    	return gpa;
    }
    
    
    static gpa_t translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access)
    {
    	gpa_t t_gpa;
    	u32 error;
    
    	BUG_ON(!mmu_is_nested(vcpu));
    
    	/* NPT walks are always user-walks */
    	access |= PFERR_USER_MASK;
    	t_gpa  = vcpu->arch.mmu.gva_to_gpa(vcpu, gpa, access, &error);
    	if (t_gpa == UNMAPPED_GVA)
    		vcpu->arch.fault.error_code |= PFERR_NESTED_MASK;
    
    	return t_gpa;
    }
    
    
    gpa_t kvm_mmu_gva_to_gpa_read(struct kvm_vcpu *vcpu, gva_t gva, u32 *error)
    {
    	u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;
    
    	return vcpu->arch.walk_mmu->gva_to_gpa(vcpu, gva, access, error);
    
    }
    
     gpa_t kvm_mmu_gva_to_gpa_fetch(struct kvm_vcpu *vcpu, gva_t gva, u32 *error)
    {
    	u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;
    	access |= PFERR_FETCH_MASK;
    
    	return vcpu->arch.walk_mmu->gva_to_gpa(vcpu, gva, access, error);
    
    }
    
    gpa_t kvm_mmu_gva_to_gpa_write(struct kvm_vcpu *vcpu, gva_t gva, u32 *error)
    {
    	u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;
    	access |= PFERR_WRITE_MASK;
    
    	return vcpu->arch.walk_mmu->gva_to_gpa(vcpu, gva, access, error);
    
    }
    
    /* uses this to access any guest's mapped memory without checking CPL */
    gpa_t kvm_mmu_gva_to_gpa_system(struct kvm_vcpu *vcpu, gva_t gva, u32 *error)
    {
    
    	return vcpu->arch.walk_mmu->gva_to_gpa(vcpu, gva, 0, error);
    
    }
    
    static int kvm_read_guest_virt_helper(gva_t addr, void *val, unsigned int bytes,
    				      struct kvm_vcpu *vcpu, u32 access,
    				      u32 *error)
    
    	int r = X86EMUL_CONTINUE;
    
    		gpa_t gpa = vcpu->arch.walk_mmu->gva_to_gpa(vcpu, addr, access,
    							    error);
    
    		unsigned offset = addr & (PAGE_SIZE-1);
    
    		unsigned toread = min(bytes, (unsigned)PAGE_SIZE - offset);
    
    		if (gpa == UNMAPPED_GVA) {
    			r = X86EMUL_PROPAGATE_FAULT;
    			goto out;
    		}
    
    		ret = kvm_read_guest(vcpu->kvm, gpa, data, toread);
    
    		if (ret < 0) {
    
    		bytes -= toread;
    		data += toread;
    		addr += toread;
    
    /* used for instruction fetching */
    static int kvm_fetch_guest_virt(gva_t addr, void *val, unsigned int bytes,
    				struct kvm_vcpu *vcpu, u32 *error)
    {
    	u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;
    	return kvm_read_guest_virt_helper(addr, val, bytes, vcpu,
    					  access | PFERR_FETCH_MASK, error);
    }
    
    static int kvm_read_guest_virt(gva_t addr, void *val, unsigned int bytes,
    			       struct kvm_vcpu *vcpu, u32 *error)
    {
    	u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;
    	return kvm_read_guest_virt_helper(addr, val, bytes, vcpu, access,
    					  error);
    }
    
    static int kvm_read_guest_virt_system(gva_t addr, void *val, unsigned int bytes,
    			       struct kvm_vcpu *vcpu, u32 *error)
    {
    	return kvm_read_guest_virt_helper(addr, val, bytes, vcpu, 0, error);
    }
    
    
    static int kvm_write_guest_virt_system(gva_t addr, void *val,
    
    {
    	void *data = val;
    	int r = X86EMUL_CONTINUE;
    
    	while (bytes) {
    
    		gpa_t gpa =  vcpu->arch.walk_mmu->gva_to_gpa(vcpu, addr,
    							     PFERR_WRITE_MASK,
    							     error);
    
    		unsigned offset = addr & (PAGE_SIZE-1);
    		unsigned towrite = min(bytes, (unsigned)PAGE_SIZE - offset);
    		int ret;
    
    		if (gpa == UNMAPPED_GVA) {
    			r = X86EMUL_PROPAGATE_FAULT;
    			goto out;
    		}
    		ret = kvm_write_guest(vcpu->kvm, gpa, data, towrite);
    		if (ret < 0) {
    
    			goto out;
    		}
    
    		bytes -= towrite;
    		data += towrite;
    		addr += towrite;
    	}
    out:
    	return r;
    }
    
    
    static int emulator_read_emulated(unsigned long addr,
    				  void *val,
    				  unsigned int bytes,
    
    				  struct kvm_vcpu *vcpu)
    {
    	gpa_t                 gpa;
    
    	if (vcpu->mmio_read_completed) {
    		memcpy(val, vcpu->mmio_data, bytes);
    
    Avi Kivity's avatar
    Avi Kivity committed
    		trace_kvm_mmio(KVM_TRACE_MMIO_READ, bytes,
    			       vcpu->mmio_phys_addr, *(u64 *)val);
    
    		vcpu->mmio_read_completed = 0;
    		return X86EMUL_CONTINUE;
    	}
    
    
    	gpa = kvm_mmu_gva_to_gpa_read(vcpu, addr, error_code);
    
    		return X86EMUL_PROPAGATE_FAULT;
    
    
    	/* For APIC access vmexit */
    	if ((gpa & PAGE_MASK) == APIC_DEFAULT_PHYS_BASE)
    		goto mmio;
    
    
    	if (kvm_read_guest_virt(addr, val, bytes, vcpu, NULL)
    
    		return X86EMUL_CONTINUE;
    
    mmio:
    	/*
    	 * Is this MMIO handled locally?
    	 */
    
    Avi Kivity's avatar
    Avi Kivity committed
    	if (!vcpu_mmio_read(vcpu, gpa, bytes, val)) {
    		trace_kvm_mmio(KVM_TRACE_MMIO_READ, bytes, gpa, *(u64 *)val);
    
    Avi Kivity's avatar
    Avi Kivity committed
    
    	trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, bytes, gpa, 0);
    
    	vcpu->run->exit_reason = KVM_EXIT_MMIO;
    	vcpu->run->mmio.phys_addr = vcpu->mmio_phys_addr = gpa;
    	vcpu->run->mmio.len = vcpu->mmio_size = bytes;
    	vcpu->run->mmio.is_write = vcpu->mmio_is_write = 0;
    
    int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa,
    
    			  const void *val, int bytes)
    
    {
    	int ret;
    
    	ret = kvm_write_guest(vcpu->kvm, gpa, val, bytes);
    
    	kvm_mmu_pte_write(vcpu, gpa, val, bytes, 1);
    
    	return 1;
    }
    
    static int emulator_write_emulated_onepage(unsigned long addr,
    					   const void *val,
    					   unsigned int bytes,
    
    	gpa = kvm_mmu_gva_to_gpa_write(vcpu, addr, error_code);
    
    		return X86EMUL_PROPAGATE_FAULT;
    
    	/* For APIC access vmexit */
    	if ((gpa & PAGE_MASK) == APIC_DEFAULT_PHYS_BASE)
    		goto mmio;
    
    	if (emulator_write_phys(vcpu, gpa, val, bytes))
    		return X86EMUL_CONTINUE;
    
    mmio:
    
    Avi Kivity's avatar
    Avi Kivity committed
    	trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, *(u64 *)val);
    
    	if (!vcpu_mmio_write(vcpu, gpa, bytes, val))
    
    		return X86EMUL_CONTINUE;
    
    	vcpu->mmio_needed = 1;
    
    	vcpu->run->exit_reason = KVM_EXIT_MMIO;
    	vcpu->run->mmio.phys_addr = vcpu->mmio_phys_addr = gpa;
    	vcpu->run->mmio.len = vcpu->mmio_size = bytes;
    	vcpu->run->mmio.is_write = vcpu->mmio_is_write = 1;
    	memcpy(vcpu->run->mmio.data, val, bytes);
    
    
    	return X86EMUL_CONTINUE;
    }
    
    int emulator_write_emulated(unsigned long addr,
    
    {
    	/* Crossing a page boundary? */
    	if (((addr + bytes - 1) ^ addr) & PAGE_MASK) {
    		int rc, now;
    
    		now = -addr & ~PAGE_MASK;
    
    		rc = emulator_write_emulated_onepage(addr, val, now, error_code,
    						     vcpu);
    
    		if (rc != X86EMUL_CONTINUE)
    			return rc;
    		addr += now;
    		val += now;
    		bytes -= now;
    	}
    
    	return emulator_write_emulated_onepage(addr, val, bytes, error_code,
    					       vcpu);
    
    #define CMPXCHG_TYPE(t, ptr, old, new) \
    	(cmpxchg((t *)(ptr), *(t *)(old), *(t *)(new)) == *(t *)(old))
    
    #ifdef CONFIG_X86_64
    #  define CMPXCHG64(ptr, old, new) CMPXCHG_TYPE(u64, ptr, old, new)
    #else
    #  define CMPXCHG64(ptr, old, new) \
    
    	(cmpxchg64((u64 *)(ptr), *(u64 *)(old), *(u64 *)(new)) == *(u64 *)(old))
    
    static int emulator_cmpxchg_emulated(unsigned long addr,
    				     const void *old,
    				     const void *new,
    				     unsigned int bytes,
    
    	gpa_t gpa;
    	struct page *page;
    	char *kaddr;
    	bool exchanged;
    
    	/* guests cmpxchg8b have to be emulated atomically */
    	if (bytes > 8 || (bytes & (bytes - 1)))
    		goto emul_write;
    
    	gpa = kvm_mmu_gva_to_gpa_write(vcpu, addr, NULL);
    
    	if (gpa == UNMAPPED_GVA ||
    	    (gpa & PAGE_MASK) == APIC_DEFAULT_PHYS_BASE)
    		goto emul_write;
    
    	if (((gpa + bytes - 1) & PAGE_MASK) != (gpa & PAGE_MASK))
    		goto emul_write;
    
    	page = gfn_to_page(vcpu->kvm, gpa >> PAGE_SHIFT);
    
    	if (is_error_page(page)) {
    		kvm_release_page_clean(page);
    		goto emul_write;
    	}
    
    	kaddr = kmap_atomic(page, KM_USER0);
    	kaddr += offset_in_page(gpa);
    	switch (bytes) {
    	case 1:
    		exchanged = CMPXCHG_TYPE(u8, kaddr, old, new);
    		break;
    	case 2:
    		exchanged = CMPXCHG_TYPE(u16, kaddr, old, new);
    		break;
    	case 4:
    		exchanged = CMPXCHG_TYPE(u32, kaddr, old, new);
    		break;
    	case 8:
    		exchanged = CMPXCHG64(kaddr, old, new);
    		break;
    	default:
    		BUG();
    
    	kunmap_atomic(kaddr, KM_USER0);
    	kvm_release_page_dirty(page);
    
    	if (!exchanged)
    		return X86EMUL_CMPXCHG_FAILED;
    
    
    	kvm_mmu_pte_write(vcpu, gpa, new, bytes, 1);
    
    	return X86EMUL_CONTINUE;
    
    emul_write:
    
    	printk_once(KERN_WARNING "kvm: emulating exchange as write\n");
    
    	return emulator_write_emulated(addr, new, bytes, error_code, vcpu);
    
    static int kernel_pio(struct kvm_vcpu *vcpu, void *pd)
    {
    	/* TODO: String I/O for in kernel device */
    	int r;
    
    	if (vcpu->arch.pio.in)
    		r = kvm_io_bus_read(vcpu->kvm, KVM_PIO_BUS, vcpu->arch.pio.port,
    				    vcpu->arch.pio.size, pd);
    	else
    		r = kvm_io_bus_write(vcpu->kvm, KVM_PIO_BUS,
    				     vcpu->arch.pio.port, vcpu->arch.pio.size,
    				     pd);
    	return r;
    }
    
    
    static int emulator_pio_in_emulated(int size, unsigned short port, void *val,
    			     unsigned int count, struct kvm_vcpu *vcpu)
    {
    
    Avi Kivity's avatar
    Avi Kivity committed
    	trace_kvm_pio(0, port, size, 1);
    
    
    	vcpu->arch.pio.port = port;
    	vcpu->arch.pio.in = 1;
    
    	vcpu->arch.pio.count  = count;
    
    	vcpu->arch.pio.size = size;
    
    	if (!kernel_pio(vcpu, vcpu->arch.pio_data)) {
    	data_avail:
    		memcpy(val, vcpu->arch.pio_data, size * count);
    
    		return 1;
    	}
    
    	vcpu->run->exit_reason = KVM_EXIT_IO;
    	vcpu->run->io.direction = KVM_EXIT_IO_IN;
    	vcpu->run->io.size = size;
    	vcpu->run->io.data_offset = KVM_PIO_PAGE_OFFSET * PAGE_SIZE;
    	vcpu->run->io.count = count;
    	vcpu->run->io.port = port;
    
    	return 0;
    }
    
    static int emulator_pio_out_emulated(int size, unsigned short port,
    			      const void *val, unsigned int count,
    			      struct kvm_vcpu *vcpu)
    {
    
    Avi Kivity's avatar
    Avi Kivity committed
    	trace_kvm_pio(1, port, size, 1);
    
    
    	vcpu->arch.pio.port = port;
    	vcpu->arch.pio.in = 0;
    
    	vcpu->arch.pio.count = count;
    
    	vcpu->arch.pio.size = size;
    
    	memcpy(vcpu->arch.pio_data, val, size * count);
    
    	if (!kernel_pio(vcpu, vcpu->arch.pio_data)) {
    
    		return 1;
    	}
    
    	vcpu->run->exit_reason = KVM_EXIT_IO;
    	vcpu->run->io.direction = KVM_EXIT_IO_OUT;
    	vcpu->run->io.size = size;
    	vcpu->run->io.data_offset = KVM_PIO_PAGE_OFFSET * PAGE_SIZE;
    	vcpu->run->io.count = count;
    	vcpu->run->io.port = port;
    
    	return 0;
    }
    
    
    static unsigned long get_segment_base(struct kvm_vcpu *vcpu, int seg)
    {
    	return kvm_x86_ops->get_segment_base(vcpu, seg);
    }
    
    int emulate_invlpg(struct kvm_vcpu *vcpu, gva_t address)
    {
    
    Marcelo Tosatti's avatar
    Marcelo Tosatti committed
    	kvm_mmu_invlpg(vcpu, address);
    
    int kvm_emulate_wbinvd(struct kvm_vcpu *vcpu)
    {
    	if (!need_emulate_wbinvd(vcpu))
    		return X86EMUL_CONTINUE;
    
    	if (kvm_x86_ops->has_wbinvd_exit()) {
    		smp_call_function_many(vcpu->arch.wbinvd_dirty_mask,
    				wbinvd_ipi, NULL, 1);
    		cpumask_clear(vcpu->arch.wbinvd_dirty_mask);
    	}
    	wbinvd();
    	return X86EMUL_CONTINUE;
    }
    EXPORT_SYMBOL_GPL(kvm_emulate_wbinvd);
    
    
    int emulate_clts(struct kvm_vcpu *vcpu)
    {
    
    	kvm_x86_ops->set_cr0(vcpu, kvm_read_cr0_bits(vcpu, ~X86_CR0_TS));
    
    Avi Kivity's avatar
    Avi Kivity committed
    	kvm_x86_ops->fpu_activate(vcpu);
    
    int emulator_get_dr(int dr, unsigned long *dest, struct kvm_vcpu *vcpu)
    
    	return _kvm_get_dr(vcpu, dr, dest);
    
    int emulator_set_dr(int dr, unsigned long value, struct kvm_vcpu *vcpu)
    
    static u64 mk_cr_64(u64 curr_cr, u32 new_val)
    
    	return (curr_cr & ~((1ULL << 32) - 1)) | new_val;
    
    static unsigned long emulator_get_cr(int cr, struct kvm_vcpu *vcpu)
    
    	unsigned long value;
    
    	switch (cr) {
    	case 0:
    		value = kvm_read_cr0(vcpu);
    		break;
    	case 2:
    		value = vcpu->arch.cr2;
    		break;
    	case 3:
    		value = vcpu->arch.cr3;
    		break;
    	case 4:
    		value = kvm_read_cr4(vcpu);
    		break;
    	case 8:
    		value = kvm_get_cr8(vcpu);
    		break;
    	default:
    		vcpu_printf(vcpu, "%s: unexpected cr %u\n", __func__, cr);
    		return 0;
    	}
    
    	return value;
    }
    
    
    static int emulator_set_cr(int cr, unsigned long val, struct kvm_vcpu *vcpu)
    
    		res = kvm_set_cr0(vcpu, mk_cr_64(kvm_read_cr0(vcpu), val));
    
    		break;
    	case 2:
    		vcpu->arch.cr2 = val;
    		break;
    	case 3: