vlapic.c
来自「xen虚拟机源代码安装包」· C语言 代码 · 共 1,009 行 · 第 1/2 页
C
1,009 行
/* * vlapic.c: virtualize LAPIC for HVM vcpus. * * Copyright (c) 2004, Intel Corporation. * Copyright (c) 2006 Keir Fraser, XenSource Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307 USA. */#include <xen/config.h>#include <xen/types.h>#include <xen/mm.h>#include <xen/xmalloc.h>#include <xen/domain.h>#include <xen/domain_page.h>#include <xen/event.h>#include <xen/trace.h>#include <xen/lib.h>#include <xen/sched.h>#include <xen/numa.h>#include <asm/current.h>#include <asm/page.h>#include <asm/hvm/hvm.h>#include <asm/hvm/io.h>#include <asm/hvm/support.h>#include <asm/hvm/vmx/vmx.h>#include <public/hvm/ioreq.h>#include <public/hvm/params.h>#define VLAPIC_VERSION 0x00050014#define VLAPIC_LVT_NUM 6/* vlapic's frequence is 100 MHz */#define APIC_BUS_CYCLE_NS 10#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_TRIGGERstatic unsigned int vlapic_lvt_mask[VLAPIC_LVT_NUM] ={ /* LVTT */ LVT_MASK | APIC_LVT_TIMER_PERIODIC, /* LVTTHMR */ LVT_MASK | APIC_MODE_MASK, /* LVTPC */ LVT_MASK | APIC_MODE_MASK, /* LVT0-1 */ LINT_MASK, LINT_MASK, /* LVTERR */ LVT_MASK};/* Following could belong in apicdef.h */#define APIC_SHORT_MASK 0xc0000#define APIC_DEST_NOSHORT 0x0#define APIC_DEST_MASK 0x800#define vlapic_lvt_vector(vlapic, lvt_type) \ (vlapic_get_reg(vlapic, lvt_type) & APIC_VECTOR_MASK)#define vlapic_lvt_dm(vlapic, lvt_type) \ (vlapic_get_reg(vlapic, lvt_type) & APIC_MODE_MASK)#define vlapic_lvtt_period(vlapic) \ (vlapic_get_reg(vlapic, APIC_LVTT) & APIC_LVT_TIMER_PERIODIC)/* * Generic APIC bitmap vector update & search routines. */#define VEC_POS(v) ((v)%32)#define REG_POS(v) (((v)/32) * 0x10)#define vlapic_test_and_set_vector(vec, bitmap) \ test_and_set_bit(VEC_POS(vec), \ (unsigned long *)((bitmap) + REG_POS(vec)))#define vlapic_test_and_clear_vector(vec, bitmap) \ test_and_clear_bit(VEC_POS(vec), \ (unsigned long *)((bitmap) + REG_POS(vec)))#define vlapic_set_vector(vec, bitmap) \ set_bit(VEC_POS(vec), (unsigned long *)((bitmap) + REG_POS(vec)))#define vlapic_clear_vector(vec, bitmap) \ clear_bit(VEC_POS(vec), (unsigned long *)((bitmap) + REG_POS(vec)))static int vlapic_find_highest_vector(void *bitmap){ uint32_t *word = bitmap; int word_offset = MAX_VECTOR / 32; /* Work backwards through the bitmap (first 32-bit word in every four). */ while ( (word_offset != 0) && (word[(--word_offset)*4] == 0) ) continue; return (fls(word[word_offset*4]) - 1) + (word_offset * 32);}/* * IRR-specific bitmap update & search routines. */static int vlapic_test_and_set_irr(int vector, struct vlapic *vlapic){ return vlapic_test_and_set_vector(vector, &vlapic->regs->data[APIC_IRR]);}static void vlapic_clear_irr(int vector, struct vlapic *vlapic){ vlapic_clear_vector(vector, &vlapic->regs->data[APIC_IRR]);}static int vlapic_find_highest_irr(struct vlapic *vlapic){ return vlapic_find_highest_vector(&vlapic->regs->data[APIC_IRR]);}int vlapic_set_irq(struct vlapic *vlapic, uint8_t vec, uint8_t trig){ int ret; ret = !vlapic_test_and_set_irr(vec, vlapic); if ( trig ) vlapic_set_vector(vec, &vlapic->regs->data[APIC_TMR]); /* We may need to wake up target vcpu, besides set pending bit here */ return ret;}static int vlapic_find_highest_isr(struct vlapic *vlapic){ return vlapic_find_highest_vector(&vlapic->regs->data[APIC_ISR]);}uint32_t vlapic_get_ppr(struct vlapic *vlapic){ uint32_t tpr, isrv, ppr; int isr; tpr = vlapic_get_reg(vlapic, APIC_TASKPRI); isr = vlapic_find_highest_isr(vlapic); isrv = (isr != -1) ? isr : 0; if ( (tpr & 0xf0) >= (isrv & 0xf0) ) ppr = tpr & 0xff; else ppr = isrv & 0xf0; HVM_DBG_LOG(DBG_LEVEL_VLAPIC_INTERRUPT, "vlapic %p, ppr 0x%x, isr 0x%x, isrv 0x%x", vlapic, ppr, isr, isrv); return ppr;}int vlapic_match_logical_addr(struct vlapic *vlapic, uint8_t mda){ int result = 0; uint8_t logical_id; logical_id = GET_xAPIC_LOGICAL_ID(vlapic_get_reg(vlapic, APIC_LDR)); switch ( vlapic_get_reg(vlapic, 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: gdprintk(XENLOG_WARNING, "Bad DFR value for lapic of vcpu %d: %08x\n", vlapic_vcpu(vlapic)->vcpu_id, vlapic_get_reg(vlapic, APIC_DFR)); break; } return result;}static int vlapic_match_dest(struct vcpu *v, struct vlapic *source, int short_hand, int dest, int dest_mode){ int result = 0; struct vlapic *target = vcpu_vlapic(v); HVM_DBG_LOG(DBG_LEVEL_VLAPIC, "target %p, source %p, dest 0x%x, " "dest_mode 0x%x, short_hand 0x%x", target, source, dest, dest_mode, short_hand); switch ( short_hand ) { case APIC_DEST_NOSHORT: if ( dest_mode == 0 ) { /* Physical mode. */ if ( (dest == 0xFF) || (dest == VLAPIC_ID(target)) ) result = 1; } else { /* Logical mode. */ result = vlapic_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: gdprintk(XENLOG_WARNING, "Bad dest shorthand value %x\n", short_hand); break; } return result;}static int vlapic_vcpu_pause_async(struct vcpu *v){ vcpu_pause_nosync(v); if ( v->is_running ) { vcpu_unpause(v); return 0; } sync_vcpu_execstate(v); return 1;}static void vlapic_init_action(unsigned long _vcpu){ struct vcpu *v = (struct vcpu *)_vcpu; struct domain *d = v->domain; bool_t fpu_initialised; /* If the VCPU is not on its way down we have nothing to do. */ if ( !test_bit(_VPF_down, &v->pause_flags) ) return; if ( !vlapic_vcpu_pause_async(v) ) { tasklet_schedule(&vcpu_vlapic(v)->init_tasklet); return; } /* Reset necessary VCPU state. This does not include FPU state. */ domain_lock(d); fpu_initialised = v->fpu_initialised; vcpu_reset(v); v->fpu_initialised = fpu_initialised; vlapic_reset(vcpu_vlapic(v)); domain_unlock(d); vcpu_unpause(v);}static int vlapic_accept_init(struct vcpu *v){ /* Nothing to do if the VCPU is already reset. */ if ( !v->is_initialised ) return X86EMUL_OKAY; /* Asynchronously take the VCPU down and schedule reset work. */ hvm_vcpu_down(v); tasklet_schedule(&vcpu_vlapic(v)->init_tasklet); return X86EMUL_RETRY;}static int vlapic_accept_sipi(struct vcpu *v, int trampoline_vector){ /* If the VCPU is not on its way down we have nothing to do. */ if ( !test_bit(_VPF_down, &v->pause_flags) ) return X86EMUL_OKAY; if ( !vlapic_vcpu_pause_async(v) ) return X86EMUL_RETRY; hvm_vcpu_reset_state(v, trampoline_vector << 8, 0); vcpu_unpause(v); return X86EMUL_OKAY;}/* Add a pending IRQ into lapic. */static int vlapic_accept_irq(struct vcpu *v, int delivery_mode, int vector, int level, int trig_mode){ struct vlapic *vlapic = vcpu_vlapic(v); int rc = X86EMUL_OKAY; switch ( delivery_mode ) { case APIC_DM_FIXED: case APIC_DM_LOWEST: /* FIXME add logic for vcpu on reset */ if ( unlikely(!vlapic_enabled(vlapic)) ) break; if ( vlapic_test_and_set_irr(vector, vlapic) && trig_mode ) { HVM_DBG_LOG(DBG_LEVEL_VLAPIC, "level trig mode repeatedly for vector %d", vector); break; } if ( trig_mode ) { HVM_DBG_LOG(DBG_LEVEL_VLAPIC, "level trig mode for vector %d", vector); vlapic_set_vector(vector, &vlapic->regs->data[APIC_TMR]); } vcpu_kick(v); break; case APIC_DM_REMRD: gdprintk(XENLOG_WARNING, "Ignoring delivery mode 3\n"); break; case APIC_DM_SMI: gdprintk(XENLOG_WARNING, "Ignoring guest SMI\n"); break; case APIC_DM_NMI: if ( !test_and_set_bool(v->nmi_pending) ) vcpu_kick(v); break; case APIC_DM_INIT: /* No work on INIT de-assert for P4-type APIC. */ if ( trig_mode && !(level & APIC_INT_ASSERT) ) break; rc = vlapic_accept_init(v); break; case APIC_DM_STARTUP: rc = vlapic_accept_sipi(v, vector); break; default: gdprintk(XENLOG_ERR, "TODO: unsupported delivery mode %x\n", delivery_mode); domain_crash(v->domain); } return rc;}/* This function is used by both ioapic and lapic.The bitmap is for vcpu_id. */struct vlapic *apic_round_robin( struct domain *d, uint8_t vector, uint32_t bitmap){ int next, old; struct vlapic *target = NULL; old = next = d->arch.hvm_domain.irq.round_robin_prev_vcpu; do { if ( ++next == MAX_VIRT_CPUS ) next = 0; if ( (d->vcpu[next] == NULL) || !test_bit(next, &bitmap) ) continue; target = vcpu_vlapic(d->vcpu[next]); if ( vlapic_enabled(target) ) break; target = NULL; } while ( next != old ); d->arch.hvm_domain.irq.round_robin_prev_vcpu = next; return target;}void vlapic_EOI_set(struct vlapic *vlapic){ int vector = vlapic_find_highest_isr(vlapic); /* Some EOI writes may not have a matching to an in-service interrupt. */ if ( vector == -1 ) return; vlapic_clear_vector(vector, &vlapic->regs->data[APIC_ISR]); if ( vlapic_test_and_clear_vector(vector, &vlapic->regs->data[APIC_TMR]) ) vioapic_update_EOI(vlapic_domain(vlapic), vector); hvm_dpci_msi_eoi(current->domain, vector);}static int vlapic_ipi( struct vlapic *vlapic, uint32_t icr_low, uint32_t icr_high){ unsigned int dest = GET_xAPIC_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 vlapic *target; struct vcpu *v; uint32_t lpr_map = 0; int rc = X86EMUL_OKAY; HVM_DBG_LOG(DBG_LEVEL_VLAPIC, "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", icr_high, icr_low, short_hand, dest, trig_mode, level, dest_mode, delivery_mode, vector); for_each_vcpu ( vlapic_domain(vlapic), v ) { if ( vlapic_match_dest(v, vlapic, short_hand, dest, dest_mode) ) { if ( delivery_mode == APIC_DM_LOWEST ) __set_bit(v->vcpu_id, &lpr_map); else rc = vlapic_accept_irq(v, delivery_mode, vector, level, trig_mode); } if ( rc != X86EMUL_OKAY ) break; } if ( delivery_mode == APIC_DM_LOWEST ) { target = apic_round_robin(vlapic_domain(v), vector, lpr_map); if ( target != NULL ) rc = vlapic_accept_irq(vlapic_vcpu(target), delivery_mode, vector, level, trig_mode); } return rc;}static uint32_t vlapic_get_tmcct(struct vlapic *vlapic){ struct vcpu *v = current; uint32_t tmcct, tmict = vlapic_get_reg(vlapic, APIC_TMICT); uint64_t counter_passed; counter_passed = ((hvm_get_guest_time(v) - vlapic->timer_last_update) / APIC_BUS_CYCLE_NS / vlapic->hw.timer_divisor); tmcct = tmict - counter_passed; HVM_DBG_LOG(DBG_LEVEL_VLAPIC_TIMER, "timer initial count %d, timer current count %d, " "offset %"PRId64, tmict, tmcct, counter_passed); return tmcct;}static void vlapic_set_tdcr(struct vlapic *vlapic, unsigned int val){ /* Only bits 0, 1 and 3 are settable; others are MBZ. */ val &= 0xb; vlapic_set_reg(vlapic, APIC_TDCR, val); /* Update the demangled hw.timer_divisor. */ val = ((val & 3) | ((val & 8) >> 1)) + 1; vlapic->hw.timer_divisor = 1 << (val & 7); HVM_DBG_LOG(DBG_LEVEL_VLAPIC_TIMER, "timer_divisor: %d", vlapic->hw.timer_divisor);}static void vlapic_read_aligned( struct vlapic *vlapic, unsigned int offset, unsigned int *result){ switch ( offset ) { case APIC_PROCPRI:
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?