⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lapic.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	}	return tmcct;}static u32 __apic_read(struct kvm_lapic *apic, unsigned int offset){	u32 val = 0;	if (offset >= LAPIC_MMIO_LENGTH)		return 0;	switch (offset) {	case APIC_ARBPRI:		printk(KERN_WARNING "Access APIC ARBPRI register "		       "which is for P6\n");		break;	case APIC_TMCCT:	/* Timer CCR */		val = apic_get_tmcct(apic);		break;	default:		apic_update_ppr(apic);		val = apic_get_reg(apic, offset);		break;	}	return val;}static void apic_mmio_read(struct kvm_io_device *this,			   gpa_t address, int len, void *data){	struct kvm_lapic *apic = (struct kvm_lapic *)this->private;	unsigned int offset = address - apic->base_address;	unsigned char alignment = offset & 0xf;	u32 result;	if ((alignment + len) > 4) {		printk(KERN_ERR "KVM_APIC_READ: alignment error %lx %d",		       (unsigned long)address, len);		return;	}	result = __apic_read(apic, offset & ~0xf);	switch (len) {	case 1:	case 2:	case 4:		memcpy(data, (char *)&result + alignment, len);		break;	default:		printk(KERN_ERR "Local APIC read with len = %x, "		       "should be 1,2, or 4 instead\n", len);		break;	}}static void update_divide_count(struct kvm_lapic *apic){	u32 tmp1, tmp2, tdcr;	tdcr = apic_get_reg(apic, APIC_TDCR);	tmp1 = tdcr & 0xf;	tmp2 = ((tmp1 & 0x3) | ((tmp1 & 0x8) >> 1)) + 1;	apic->timer.divide_count = 0x1 << (tmp2 & 0x7);	apic_debug("timer divide count is 0x%x\n",				   apic->timer.divide_count);}static void start_apic_timer(struct kvm_lapic *apic){	ktime_t now = apic->timer.dev.base->get_time();	apic->timer.last_update = now;	apic->timer.period = apic_get_reg(apic, APIC_TMICT) *		    APIC_BUS_CYCLE_NS * apic->timer.divide_count;	atomic_set(&apic->timer.pending, 0);	hrtimer_start(&apic->timer.dev,		      ktime_add_ns(now, apic->timer.period),		      HRTIMER_MODE_ABS);	apic_debug("%s: bus cycle is %" PRId64 "ns, now 0x%016"			   PRIx64 ", "			   "timer initial count 0x%x, period %lldns, "			   "expire @ 0x%016" PRIx64 ".\n", __FUNCTION__,			   APIC_BUS_CYCLE_NS, ktime_to_ns(now),			   apic_get_reg(apic, APIC_TMICT),			   apic->timer.period,			   ktime_to_ns(ktime_add_ns(now,					apic->timer.period)));}static void apic_mmio_write(struct kvm_io_device *this,			    gpa_t address, int len, const void *data){	struct kvm_lapic *apic = (struct kvm_lapic *)this->private;	unsigned int offset = address - apic->base_address;	unsigned char alignment = offset & 0xf;	u32 val;	/*	 * APIC register must be aligned on 128-bits boundary.	 * 32/64/128 bits registers must be accessed thru 32 bits.	 * Refer SDM 8.4.1	 */	if (len != 4 || alignment) {		if (printk_ratelimit())			printk(KERN_ERR "apic write: bad size=%d %lx\n",			       len, (long)address);		return;	}	val = *(u32 *) data;	/* too common printing */	if (offset != APIC_EOI)		apic_debug("%s: offset 0x%x with length 0x%x, and value is "			   "0x%x\n", __FUNCTION__, offset, len, val);	offset &= 0xff0;	switch (offset) {	case APIC_ID:		/* Local APIC ID */		apic_set_reg(apic, APIC_ID, val);		break;	case APIC_TASKPRI:		apic_set_tpr(apic, val & 0xff);		break;	case APIC_EOI:		apic_set_eoi(apic);		break;	case APIC_LDR:		apic_set_reg(apic, APIC_LDR, val & APIC_LDR_MASK);		break;	case APIC_DFR:		apic_set_reg(apic, APIC_DFR, val | 0x0FFFFFFF);		break;	case APIC_SPIV:		apic_set_reg(apic, APIC_SPIV, val & 0x3ff);		if (!(val & APIC_SPIV_APIC_ENABLED)) {			int i;			u32 lvt_val;			for (i = 0; i < APIC_LVT_NUM; i++) {				lvt_val = apic_get_reg(apic,						       APIC_LVTT + 0x10 * i);				apic_set_reg(apic, APIC_LVTT + 0x10 * i,					     lvt_val | APIC_LVT_MASKED);			}			atomic_set(&apic->timer.pending, 0);		}		break;	case APIC_ICR:		/* No delay here, so we always clear the pending bit */		apic_set_reg(apic, APIC_ICR, val & ~(1 << 12));		apic_send_ipi(apic);		break;	case APIC_ICR2:		apic_set_reg(apic, APIC_ICR2, val & 0xff000000);		break;	case APIC_LVTT:	case APIC_LVTTHMR:	case APIC_LVTPC:	case APIC_LVT0:	case APIC_LVT1:	case APIC_LVTERR:		/* TODO: Check vector */		if (!apic_sw_enabled(apic))			val |= APIC_LVT_MASKED;		val &= apic_lvt_mask[(offset - APIC_LVTT) >> 4];		apic_set_reg(apic, offset, val);		break;	case APIC_TMICT:		hrtimer_cancel(&apic->timer.dev);		apic_set_reg(apic, APIC_TMICT, val);		start_apic_timer(apic);		return;	case APIC_TDCR:		if (val & 4)			printk(KERN_ERR "KVM_WRITE:TDCR %x\n", val);		apic_set_reg(apic, APIC_TDCR, val);		update_divide_count(apic);		break;	default:		apic_debug("Local APIC Write to read-only register %x\n",			   offset);		break;	}}static int apic_mmio_range(struct kvm_io_device *this, gpa_t addr){	struct kvm_lapic *apic = (struct kvm_lapic *)this->private;	int ret = 0;	if (apic_hw_enabled(apic) &&	    (addr >= apic->base_address) &&	    (addr < (apic->base_address + LAPIC_MMIO_LENGTH)))		ret = 1;	return ret;}void kvm_free_apic(struct kvm_lapic *apic){	if (!apic)		return;	hrtimer_cancel(&apic->timer.dev);	if (apic->regs_page) {		__free_page(apic->regs_page);		apic->regs_page = 0;	}	kfree(apic);}/* *---------------------------------------------------------------------- * LAPIC interface *---------------------------------------------------------------------- */void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8){	struct kvm_lapic *apic = (struct kvm_lapic *)vcpu->apic;	if (!apic)		return;	apic_set_tpr(apic, ((cr8 & 0x0f) << 4));}u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu){	struct kvm_lapic *apic = (struct kvm_lapic *)vcpu->apic;	u64 tpr;	if (!apic)		return 0;	tpr = (u64) apic_get_reg(apic, APIC_TASKPRI);	return (tpr & 0xf0) >> 4;}EXPORT_SYMBOL_GPL(kvm_lapic_get_cr8);void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value){	struct kvm_lapic *apic = (struct kvm_lapic *)vcpu->apic;	if (!apic) {		value |= MSR_IA32_APICBASE_BSP;		vcpu->apic_base = value;		return;	}	if (apic->vcpu->vcpu_id)		value &= ~MSR_IA32_APICBASE_BSP;	vcpu->apic_base = value;	apic->base_address = apic->vcpu->apic_base &			     MSR_IA32_APICBASE_BASE;	/* with FSB delivery interrupt, we can restart APIC functionality */	apic_debug("apic base msr is 0x%016" PRIx64 ", and base address is "		   "0x%lx.\n", apic->apic_base, apic->base_address);}u64 kvm_lapic_get_base(struct kvm_vcpu *vcpu){	return vcpu->apic_base;}EXPORT_SYMBOL_GPL(kvm_lapic_get_base);void kvm_lapic_reset(struct kvm_vcpu *vcpu){	struct kvm_lapic *apic;	int i;	apic_debug("%s\n", __FUNCTION__);	ASSERT(vcpu);	apic = vcpu->apic;	ASSERT(apic != NULL);	/* Stop the timer in case it's a reset to an active apic */	hrtimer_cancel(&apic->timer.dev);	apic_set_reg(apic, APIC_ID, vcpu->vcpu_id << 24);	apic_set_reg(apic, APIC_LVR, APIC_VERSION);	for (i = 0; i < APIC_LVT_NUM; i++)		apic_set_reg(apic, APIC_LVTT + 0x10 * i, APIC_LVT_MASKED);	apic_set_reg(apic, APIC_LVT0,		     SET_APIC_DELIVERY_MODE(0, APIC_MODE_EXTINT));	apic_set_reg(apic, APIC_DFR, 0xffffffffU);	apic_set_reg(apic, APIC_SPIV, 0xff);	apic_set_reg(apic, APIC_TASKPRI, 0);	apic_set_reg(apic, APIC_LDR, 0);	apic_set_reg(apic, APIC_ESR, 0);	apic_set_reg(apic, APIC_ICR, 0);	apic_set_reg(apic, APIC_ICR2, 0);	apic_set_reg(apic, APIC_TDCR, 0);	apic_set_reg(apic, APIC_TMICT, 0);	for (i = 0; i < 8; i++) {		apic_set_reg(apic, APIC_IRR + 0x10 * i, 0);		apic_set_reg(apic, APIC_ISR + 0x10 * i, 0);		apic_set_reg(apic, APIC_TMR + 0x10 * i, 0);	}	update_divide_count(apic);	atomic_set(&apic->timer.pending, 0);	if (vcpu->vcpu_id == 0)		vcpu->apic_base |= MSR_IA32_APICBASE_BSP;	apic_update_ppr(apic);	apic_debug(KERN_INFO "%s: vcpu=%p, id=%d, base_msr="		   "0x%016" PRIx64 ", base_address=0x%0lx.\n", __FUNCTION__,		   vcpu, kvm_apic_id(apic),		   vcpu->apic_base, apic->base_address);}EXPORT_SYMBOL_GPL(kvm_lapic_reset);int kvm_lapic_enabled(struct kvm_vcpu *vcpu){	struct kvm_lapic *apic = (struct kvm_lapic *)vcpu->apic;	int ret = 0;	if (!apic)		return 0;	ret = apic_enabled(apic);	return ret;}EXPORT_SYMBOL_GPL(kvm_lapic_enabled);/* *---------------------------------------------------------------------- * timer interface *---------------------------------------------------------------------- *//* TODO: make sure __apic_timer_fn runs in current pCPU */static int __apic_timer_fn(struct kvm_lapic *apic){	int result = 0;	wait_queue_head_t *q = &apic->vcpu->wq;	atomic_inc(&apic->timer.pending);	if (waitqueue_active(q))	{		apic->vcpu->mp_state = VCPU_MP_STATE_RUNNABLE;		wake_up_interruptible(q);	}	if (apic_lvtt_period(apic)) {		result = 1;		apic->timer.dev.expires = ktime_add_ns(					apic->timer.dev.expires,					apic->timer.period);	}	return result;}static int __inject_apic_timer_irq(struct kvm_lapic *apic){	int vector;	vector = apic_lvt_vector(apic, APIC_LVTT);	return __apic_accept_irq(apic, APIC_DM_FIXED, vector, 1, 0);}static enum hrtimer_restart apic_timer_fn(struct hrtimer *data){	struct kvm_lapic *apic;	int restart_timer = 0;	apic = container_of(data, struct kvm_lapic, timer.dev);	restart_timer = __apic_timer_fn(apic);	if (restart_timer)		return HRTIMER_RESTART;	else		return HRTIMER_NORESTART;}int kvm_create_lapic(struct kvm_vcpu *vcpu){	struct kvm_lapic *apic;	ASSERT(vcpu != NULL);	apic_debug("apic_init %d\n", vcpu->vcpu_id);	apic = kzalloc(sizeof(*apic), GFP_KERNEL);	if (!apic)		goto nomem;	vcpu->apic = apic;	apic->regs_page = alloc_page(GFP_KERNEL);	if (apic->regs_page == NULL) {		printk(KERN_ERR "malloc apic regs error for vcpu %x\n",		       vcpu->vcpu_id);		goto nomem;	}	apic->regs = page_address(apic->regs_page);	memset(apic->regs, 0, PAGE_SIZE);	apic->vcpu = vcpu;	hrtimer_init(&apic->timer.dev, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);	apic->timer.dev.function = apic_timer_fn;	apic->base_address = APIC_DEFAULT_PHYS_BASE;	vcpu->apic_base = APIC_DEFAULT_PHYS_BASE;	kvm_lapic_reset(vcpu);	apic->dev.read = apic_mmio_read;	apic->dev.write = apic_mmio_write;	apic->dev.in_range = apic_mmio_range;	apic->dev.private = apic;	return 0;nomem:	kvm_free_apic(apic);	return -ENOMEM;}EXPORT_SYMBOL_GPL(kvm_create_lapic);int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu){	struct kvm_lapic *apic = vcpu->apic;	int highest_irr;	if (!apic || !apic_enabled(apic))		return -1;	apic_update_ppr(apic);	highest_irr = apic_find_highest_irr(apic);	if ((highest_irr == -1) ||	    ((highest_irr & 0xF0) <= apic_get_reg(apic, APIC_PROCPRI)))		return -1;	return highest_irr;}int kvm_apic_accept_pic_intr(struct kvm_vcpu *vcpu){	u32 lvt0 = apic_get_reg(vcpu->apic, APIC_LVT0);	int r = 0;	if (vcpu->vcpu_id == 0) {		if (!apic_hw_enabled(vcpu->apic))			r = 1;		if ((lvt0 & APIC_LVT_MASKED) == 0 &&		    GET_APIC_DELIVERY_MODE(lvt0) == APIC_MODE_EXTINT)			r = 1;	}	return r;}void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu){	struct kvm_lapic *apic = vcpu->apic;	if (apic && apic_lvt_enabled(apic, APIC_LVTT) &&		atomic_read(&apic->timer.pending) > 0) {		if (__inject_apic_timer_irq(apic))			atomic_dec(&apic->timer.pending);	}}void kvm_apic_timer_intr_post(struct kvm_vcpu *vcpu, int vec){	struct kvm_lapic *apic = vcpu->apic;	if (apic && apic_lvt_vector(apic, APIC_LVTT) == vec)		apic->timer.last_update = ktime_add_ns(				apic->timer.last_update,				apic->timer.period);}int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu){	int vector = kvm_apic_has_interrupt(vcpu);	struct kvm_lapic *apic = vcpu->apic;	if (vector == -1)		return -1;	apic_set_vector(vector, apic->regs + APIC_ISR);	apic_update_ppr(apic);	apic_clear_irr(vector, apic);	return vector;}void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu){	struct kvm_lapic *apic = vcpu->apic;	apic->base_address = vcpu->apic_base &			     MSR_IA32_APICBASE_BASE;	apic_set_reg(apic, APIC_LVR, APIC_VERSION);	apic_update_ppr(apic);	hrtimer_cancel(&apic->timer.dev);	update_divide_count(apic);	start_apic_timer(apic);}void kvm_migrate_apic_timer(struct kvm_vcpu *vcpu){	struct kvm_lapic *apic = vcpu->apic;	struct hrtimer *timer;	if (!apic)		return;	timer = &apic->timer.dev;	if (hrtimer_cancel(timer))		hrtimer_start(timer, timer->expires, HRTIMER_MODE_ABS);}EXPORT_SYMBOL_GPL(kvm_migrate_apic_timer);

⌨️ 快捷键说明

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