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 + -
显示快捷键?