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

📄 lapic.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Local APIC virtualization * * Copyright (C) 2006 Qumranet, Inc. * Copyright (C) 2007 Novell * Copyright (C) 2007 Intel * * Authors: *   Dor Laor <dor.laor@qumranet.com> *   Gregory Haskins <ghaskins@novell.com> *   Yaozu (Eddie) Dong <eddie.dong@intel.com> * * Based on Xen 3.1 code, Copyright (c) 2004, Intel Corporation. * * This work is licensed under the terms of the GNU GPL, version 2.  See * the COPYING file in the top-level directory. */#include "kvm.h"#include <linux/kvm.h>#include <linux/mm.h>#include <linux/highmem.h>#include <linux/smp.h>#include <linux/hrtimer.h>#include <linux/io.h>#include <linux/module.h>#include <asm/processor.h>#include <asm/msr.h>#include <asm/page.h>#include <asm/current.h>#include <asm/apicdef.h>#include <asm/atomic.h>#include <asm/div64.h>#include "irq.h"#define PRId64 "d"#define PRIx64 "llx"#define PRIu64 "u"#define PRIo64 "o"#define APIC_BUS_CYCLE_NS 1/* #define apic_debug(fmt,arg...) printk(KERN_WARNING fmt,##arg) */#define apic_debug(fmt, arg...)#define APIC_LVT_NUM			6/* 14 is the version for Xeon and Pentium 8.4.8*/#define APIC_VERSION			(0x14UL | ((APIC_LVT_NUM - 1) << 16))#define LAPIC_MMIO_LENGTH		(1 << 12)/* followed define is not in apicdef.h */#define APIC_SHORT_MASK			0xc0000#define APIC_DEST_NOSHORT		0x0#define APIC_DEST_MASK			0x800#define MAX_APIC_VECTOR			256#define VEC_POS(v) ((v) & (32 - 1))#define REG_POS(v) (((v) >> 5) << 4)static inline u32 apic_get_reg(struct kvm_lapic *apic, int reg_off){	return *((u32 *) (apic->regs + reg_off));}static inline void apic_set_reg(struct kvm_lapic *apic, int reg_off, u32 val){	*((u32 *) (apic->regs + reg_off)) = val;}static inline int apic_test_and_set_vector(int vec, void *bitmap){	return test_and_set_bit(VEC_POS(vec), (bitmap) + REG_POS(vec));}static inline int apic_test_and_clear_vector(int vec, void *bitmap){	return test_and_clear_bit(VEC_POS(vec), (bitmap) + REG_POS(vec));}static inline void apic_set_vector(int vec, void *bitmap){	set_bit(VEC_POS(vec), (bitmap) + REG_POS(vec));}static inline void apic_clear_vector(int vec, void *bitmap){	clear_bit(VEC_POS(vec), (bitmap) + REG_POS(vec));}static inline int apic_hw_enabled(struct kvm_lapic *apic){	return (apic)->vcpu->apic_base & MSR_IA32_APICBASE_ENABLE;}static inline int  apic_sw_enabled(struct kvm_lapic *apic){	return apic_get_reg(apic, APIC_SPIV) & APIC_SPIV_APIC_ENABLED;}static inline int apic_enabled(struct kvm_lapic *apic){	return apic_sw_enabled(apic) &&	apic_hw_enabled(apic);}#define LVT_MASK	\	(APIC_LVT_MASKED | APIC_SEND_PENDING | APIC_VECTOR_MASK)#define LINT_MASK	\	(LVT_MASK | APIC_MODE_MASK | APIC_INPUT_POLARITY | \	 APIC_LVT_REMOTE_IRR | APIC_LVT_LEVEL_TRIGGER)static inline int kvm_apic_id(struct kvm_lapic *apic){	return (apic_get_reg(apic, APIC_ID) >> 24) & 0xff;}static inline int apic_lvt_enabled(struct kvm_lapic *apic, int lvt_type){	return !(apic_get_reg(apic, lvt_type) & APIC_LVT_MASKED);}static inline int apic_lvt_vector(struct kvm_lapic *apic, int lvt_type){	return apic_get_reg(apic, lvt_type) & APIC_VECTOR_MASK;}static inline int apic_lvtt_period(struct kvm_lapic *apic){	return apic_get_reg(apic, APIC_LVTT) & APIC_LVT_TIMER_PERIODIC;}static unsigned int apic_lvt_mask[APIC_LVT_NUM] = {	LVT_MASK | APIC_LVT_TIMER_PERIODIC,	/* LVTT */	LVT_MASK | APIC_MODE_MASK,	/* LVTTHMR */	LVT_MASK | APIC_MODE_MASK,	/* LVTPC */	LINT_MASK, LINT_MASK,	/* LVT0-1 */	LVT_MASK		/* LVTERR */};static int find_highest_vector(void *bitmap){	u32 *word = bitmap;	int word_offset = MAX_APIC_VECTOR >> 5;	while ((word_offset != 0) && (word[(--word_offset) << 2] == 0))		continue;	if (likely(!word_offset && !word[0]))		return -1;	else		return fls(word[word_offset << 2]) - 1 + (word_offset << 5);}static inline int apic_test_and_set_irr(int vec, struct kvm_lapic *apic){	return apic_test_and_set_vector(vec, apic->regs + APIC_IRR);}static inline void apic_clear_irr(int vec, struct kvm_lapic *apic){	apic_clear_vector(vec, apic->regs + APIC_IRR);}static inline int apic_find_highest_irr(struct kvm_lapic *apic){	int result;	result = find_highest_vector(apic->regs + APIC_IRR);	ASSERT(result == -1 || result >= 16);	return result;}int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu){	struct kvm_lapic *apic = (struct kvm_lapic *)vcpu->apic;	int highest_irr;	if (!apic)		return 0;	highest_irr = apic_find_highest_irr(apic);	return highest_irr;}EXPORT_SYMBOL_GPL(kvm_lapic_find_highest_irr);int kvm_apic_set_irq(struct kvm_lapic *apic, u8 vec, u8 trig){	if (!apic_test_and_set_irr(vec, apic)) {		/* a new pending irq is set in IRR */		if (trig)			apic_set_vector(vec, apic->regs + APIC_TMR);		else			apic_clear_vector(vec, apic->regs + APIC_TMR);		kvm_vcpu_kick(apic->vcpu);		return 1;	}	return 0;}static inline int apic_find_highest_isr(struct kvm_lapic *apic){	int result;	result = find_highest_vector(apic->regs + APIC_ISR);	ASSERT(result == -1 || result >= 16);	return result;}static void apic_update_ppr(struct kvm_lapic *apic){	u32 tpr, isrv, ppr;	int isr;	tpr = apic_get_reg(apic, APIC_TASKPRI);	isr = apic_find_highest_isr(apic);	isrv = (isr != -1) ? isr : 0;	if ((tpr & 0xf0) >= (isrv & 0xf0))		ppr = tpr & 0xff;	else		ppr = isrv & 0xf0;	apic_debug("vlapic %p, ppr 0x%x, isr 0x%x, isrv 0x%x",		   apic, ppr, isr, isrv);	apic_set_reg(apic, APIC_PROCPRI, ppr);}static void apic_set_tpr(struct kvm_lapic *apic, u32 tpr){	apic_set_reg(apic, APIC_TASKPRI, tpr);	apic_update_ppr(apic);}int kvm_apic_match_physical_addr(struct kvm_lapic *apic, u16 dest){	return kvm_apic_id(apic) == dest;}int kvm_apic_match_logical_addr(struct kvm_lapic *apic, u8 mda){	int result = 0;	u8 logical_id;	logical_id = GET_APIC_LOGICAL_ID(apic_get_reg(apic, APIC_LDR));	switch (apic_get_reg(apic, APIC_DFR)) {	case APIC_DFR_FLAT:		if (logical_id & mda)			result = 1;		break;	case APIC_DFR_CLUSTER:		if (((logical_id >> 4) == (mda >> 0x4))		    && (logical_id & mda & 0xf))			result = 1;		break;	default:		printk(KERN_WARNING "Bad DFR vcpu %d: %08x\n",		       apic->vcpu->vcpu_id, apic_get_reg(apic, APIC_DFR));		break;	}	return result;}static int apic_match_dest(struct kvm_vcpu *vcpu, struct kvm_lapic *source,			   int short_hand, int dest, int dest_mode){	int result = 0;	struct kvm_lapic *target = vcpu->apic;	apic_debug("target %p, source %p, dest 0x%x, "		   "dest_mode 0x%x, short_hand 0x%x",		   target, source, dest, dest_mode, short_hand);	ASSERT(!target);	switch (short_hand) {	case APIC_DEST_NOSHORT:		if (dest_mode == 0) {			/* Physical mode. */			if ((dest == 0xFF) || (dest == kvm_apic_id(target)))				result = 1;		} else			/* Logical mode. */			result = kvm_apic_match_logical_addr(target, dest);		break;	case APIC_DEST_SELF:		if (target == source)			result = 1;		break;	case APIC_DEST_ALLINC:		result = 1;		break;	case APIC_DEST_ALLBUT:		if (target != source)			result = 1;		break;	default:		printk(KERN_WARNING "Bad dest shorthand value %x\n",		       short_hand);		break;	}	return result;}/* * Add a pending IRQ into lapic. * Return 1 if successfully added and 0 if discarded. */static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,			     int vector, int level, int trig_mode){	int orig_irr, result = 0;	struct kvm_vcpu *vcpu = apic->vcpu;	switch (delivery_mode) {	case APIC_DM_FIXED:	case APIC_DM_LOWEST:		/* FIXME add logic for vcpu on reset */		if (unlikely(!apic_enabled(apic)))			break;		orig_irr = apic_test_and_set_irr(vector, apic);		if (orig_irr && trig_mode) {			apic_debug("level trig mode repeatedly for vector %d",				   vector);			break;		}		if (trig_mode) {			apic_debug("level trig mode for vector %d", vector);			apic_set_vector(vector, apic->regs + APIC_TMR);		} else			apic_clear_vector(vector, apic->regs + APIC_TMR);		if (vcpu->mp_state == VCPU_MP_STATE_RUNNABLE)			kvm_vcpu_kick(vcpu);		else if (vcpu->mp_state == VCPU_MP_STATE_HALTED) {			vcpu->mp_state = VCPU_MP_STATE_RUNNABLE;			if (waitqueue_active(&vcpu->wq))				wake_up_interruptible(&vcpu->wq);		}		result = (orig_irr == 0);		break;	case APIC_DM_REMRD:		printk(KERN_DEBUG "Ignoring delivery mode 3\n");		break;	case APIC_DM_SMI:		printk(KERN_DEBUG "Ignoring guest SMI\n");		break;	case APIC_DM_NMI:		printk(KERN_DEBUG "Ignoring guest NMI\n");		break;	case APIC_DM_INIT:		if (level) {			if (vcpu->mp_state == VCPU_MP_STATE_RUNNABLE)				printk(KERN_DEBUG				       "INIT on a runnable vcpu %d\n",				       vcpu->vcpu_id);			vcpu->mp_state = VCPU_MP_STATE_INIT_RECEIVED;			kvm_vcpu_kick(vcpu);		} else {			printk(KERN_DEBUG			       "Ignoring de-assert INIT to vcpu %d\n",			       vcpu->vcpu_id);		}		break;	case APIC_DM_STARTUP:		printk(KERN_DEBUG "SIPI to vcpu %d vector 0x%02x\n",		       vcpu->vcpu_id, vector);		if (vcpu->mp_state == VCPU_MP_STATE_INIT_RECEIVED) {			vcpu->sipi_vector = vector;			vcpu->mp_state = VCPU_MP_STATE_SIPI_RECEIVED;			if (waitqueue_active(&vcpu->wq))				wake_up_interruptible(&vcpu->wq);		}		break;	default:		printk(KERN_ERR "TODO: unsupported delivery mode %x\n",		       delivery_mode);		break;	}	return result;}struct kvm_lapic *kvm_apic_round_robin(struct kvm *kvm, u8 vector,				       unsigned long bitmap){	int vcpu_id;	int last;	int next;	struct kvm_lapic *apic;	last = kvm->round_robin_prev_vcpu;	next = last;	do {		if (++next == KVM_MAX_VCPUS)			next = 0;		if (kvm->vcpus[next] == NULL || !test_bit(next, &bitmap))			continue;		apic = kvm->vcpus[next]->apic;		if (apic && apic_enabled(apic))			break;		apic = NULL;	} while (next != last);	kvm->round_robin_prev_vcpu = next;	if (!apic) {		vcpu_id = ffs(bitmap) - 1;		if (vcpu_id < 0) {			vcpu_id = 0;			printk(KERN_DEBUG "vcpu not ready for apic_round_robin\n");		}		apic = kvm->vcpus[vcpu_id]->apic;	}	return apic;}static void apic_set_eoi(struct kvm_lapic *apic){	int vector = apic_find_highest_isr(apic);	/*	 * Not every write EOI will has corresponding ISR,	 * one example is when Kernel check timer on setup_IO_APIC	 */	if (vector == -1)		return;	apic_clear_vector(vector, apic->regs + APIC_ISR);	apic_update_ppr(apic);	if (apic_test_and_clear_vector(vector, apic->regs + APIC_TMR))		kvm_ioapic_update_eoi(apic->vcpu->kvm, vector);}static void apic_send_ipi(struct kvm_lapic *apic){	u32 icr_low = apic_get_reg(apic, APIC_ICR);	u32 icr_high = apic_get_reg(apic, APIC_ICR2);	unsigned int dest = GET_APIC_DEST_FIELD(icr_high);	unsigned int short_hand = icr_low & APIC_SHORT_MASK;	unsigned int trig_mode = icr_low & APIC_INT_LEVELTRIG;	unsigned int level = icr_low & APIC_INT_ASSERT;	unsigned int dest_mode = icr_low & APIC_DEST_MASK;	unsigned int delivery_mode = icr_low & APIC_MODE_MASK;	unsigned int vector = icr_low & APIC_VECTOR_MASK;	struct kvm_lapic *target;	struct kvm_vcpu *vcpu;	unsigned long lpr_map = 0;	int i;	apic_debug("icr_high 0x%x, icr_low 0x%x, "		   "short_hand 0x%x, dest 0x%x, trig_mode 0x%x, level 0x%x, "		   "dest_mode 0x%x, delivery_mode 0x%x, vector 0x%x\n",		   icr_high, icr_low, short_hand, dest,		   trig_mode, level, dest_mode, delivery_mode, vector);	for (i = 0; i < KVM_MAX_VCPUS; i++) {		vcpu = apic->vcpu->kvm->vcpus[i];		if (!vcpu)			continue;		if (vcpu->apic &&		    apic_match_dest(vcpu, apic, short_hand, dest, dest_mode)) {			if (delivery_mode == APIC_DM_LOWEST)				set_bit(vcpu->vcpu_id, &lpr_map);			else				__apic_accept_irq(vcpu->apic, delivery_mode,						  vector, level, trig_mode);		}	}	if (delivery_mode == APIC_DM_LOWEST) {		target = kvm_apic_round_robin(vcpu->kvm, vector, lpr_map);		if (target != NULL)			__apic_accept_irq(target, delivery_mode,					  vector, level, trig_mode);	}}static u32 apic_get_tmcct(struct kvm_lapic *apic){	u64 counter_passed;	ktime_t passed, now;	u32 tmcct;	ASSERT(apic != NULL);	now = apic->timer.dev.base->get_time();	tmcct = apic_get_reg(apic, APIC_TMICT);	/* if initial count is 0, current count should also be 0 */	if (tmcct == 0)		return 0;	if (unlikely(ktime_to_ns(now) <=		ktime_to_ns(apic->timer.last_update))) {		/* Wrap around */		passed = ktime_add(( {				    (ktime_t) {				    .tv64 = KTIME_MAX -				    (apic->timer.last_update).tv64}; }				   ), now);		apic_debug("time elapsed\n");	} else		passed = ktime_sub(now, apic->timer.last_update);	counter_passed = div64_64(ktime_to_ns(passed),				  (APIC_BUS_CYCLE_NS * apic->timer.divide_count));	if (counter_passed > tmcct) {		if (unlikely(!apic_lvtt_period(apic))) {			/* one-shot timers stick at 0 until reset */			tmcct = 0;		} else {			/*			 * periodic timers reset to APIC_TMICT when they			 * hit 0. The while loop simulates this happening N			 * times. (counter_passed %= tmcct) would also work,			 * but might be slower or not work on 32-bit??			 */			while (counter_passed > tmcct)				counter_passed -= tmcct;			tmcct -= counter_passed;		}	} else {		tmcct -= counter_passed;

⌨️ 快捷键说明

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