vlapic.c

来自「xen虚拟机源代码安装包」· C语言 代码 · 共 1,009 行 · 第 1/2 页

C
1,009
字号
        *result = vlapic_get_ppr(vlapic);        break;    case APIC_TMCCT: /* Timer CCR */        *result = vlapic_get_tmcct(vlapic);        break;    default:        *result = vlapic_get_reg(vlapic, offset);        break;    }}static int vlapic_read(    struct vcpu *v, unsigned long address,    unsigned long len, unsigned long *pval){    unsigned int alignment;    unsigned int tmp;    unsigned long result = 0;    struct vlapic *vlapic = vcpu_vlapic(v);    unsigned int offset = address - vlapic_base_address(vlapic);    if ( offset > (APIC_TDCR + 0x3) )        goto out;    alignment = offset & 0x3;    vlapic_read_aligned(vlapic, offset & ~0x3, &tmp);    switch ( len )    {    case 1:        result = *((unsigned char *)&tmp + alignment);        break;    case 2:        if ( alignment == 3 )            goto unaligned_exit_and_crash;        result = *(unsigned short *)((unsigned char *)&tmp + alignment);        break;    case 4:        if ( alignment != 0 )            goto unaligned_exit_and_crash;        result = *(unsigned int *)((unsigned char *)&tmp + alignment);        break;    default:        gdprintk(XENLOG_ERR, "Local APIC read with len=0x%lx, "                 "should be 4 instead.\n", len);        goto exit_and_crash;    }    HVM_DBG_LOG(DBG_LEVEL_VLAPIC, "offset 0x%x with length 0x%lx, "                "and the result is 0x%lx", offset, len, result); out:    *pval = result;    return X86EMUL_OKAY; unaligned_exit_and_crash:    gdprintk(XENLOG_ERR, "Unaligned LAPIC read len=0x%lx at offset=0x%x.\n",             len, offset); exit_and_crash:    domain_crash(v->domain);    return X86EMUL_OKAY;}void vlapic_pt_cb(struct vcpu *v, void *data){    *(s_time_t *)data = hvm_get_guest_time(v);}static int vlapic_write(struct vcpu *v, unsigned long address,                        unsigned long len, unsigned long val){    struct vlapic *vlapic = vcpu_vlapic(v);    unsigned int offset = address - vlapic_base_address(vlapic);    int rc = X86EMUL_OKAY;    if ( offset != 0xb0 )        HVM_DBG_LOG(DBG_LEVEL_VLAPIC,                    "offset 0x%x with length 0x%lx, and value is 0x%lx",                    offset, len, val);    /*     * According to the IA32 Manual, all accesses should be 32 bits.     * Some OSes do 8- or 16-byte accesses, however.     */    val = (uint32_t)val;    if ( len != 4 )    {        unsigned long tmp;        unsigned char alignment;        gdprintk(XENLOG_INFO, "Notice: Local APIC write with len = %lx\n",len);        alignment = offset & 0x3;        (void)vlapic_read(v, offset & ~0x3, 4, &tmp);        switch ( len )        {        case 1:            val = ((tmp & ~(0xff << (8*alignment))) |                   ((val & 0xff) << (8*alignment)));            break;        case 2:            if ( alignment & 1 )                goto unaligned_exit_and_crash;            val = ((tmp & ~(0xffff << (8*alignment))) |                   ((val & 0xffff) << (8*alignment)));            break;        default:            gdprintk(XENLOG_ERR, "Local APIC write with len = %lx, "                     "should be 4 instead\n", len);            goto exit_and_crash;        }    }    else if ( (offset & 0x3) != 0 )        goto unaligned_exit_and_crash;    offset &= ~0x3;    switch ( offset )    {    case APIC_TASKPRI:        vlapic_set_reg(vlapic, APIC_TASKPRI, val & 0xff);        break;    case APIC_EOI:        vlapic_EOI_set(vlapic);        break;    case APIC_LDR:        vlapic_set_reg(vlapic, APIC_LDR, val & APIC_LDR_MASK);        break;    case APIC_DFR:        vlapic_set_reg(vlapic, APIC_DFR, val | 0x0FFFFFFF);        break;    case APIC_SPIV:        vlapic_set_reg(vlapic, APIC_SPIV, val & 0x3ff);        if ( !(val & APIC_SPIV_APIC_ENABLED) )        {            int i;            uint32_t lvt_val;            vlapic->hw.disabled |= VLAPIC_SW_DISABLED;            for ( i = 0; i < VLAPIC_LVT_NUM; i++ )            {                lvt_val = vlapic_get_reg(vlapic, APIC_LVTT + 0x10 * i);                vlapic_set_reg(vlapic, APIC_LVTT + 0x10 * i,                               lvt_val | APIC_LVT_MASKED);            }        }        else            vlapic->hw.disabled &= ~VLAPIC_SW_DISABLED;        break;    case APIC_ESR:        /* Nothing to do. */        break;    case APIC_ICR:        val &= ~(1 << 12); /* always clear the pending bit */        rc = vlapic_ipi(vlapic, val, vlapic_get_reg(vlapic, APIC_ICR2));        if ( rc == X86EMUL_OKAY )            vlapic_set_reg(vlapic, APIC_ICR, val);        break;    case APIC_ICR2:        vlapic_set_reg(vlapic, APIC_ICR2, val & 0xff000000);        break;    case APIC_LVTT:         /* LVT Timer Reg */        vlapic->pt.irq = val & APIC_VECTOR_MASK;    case APIC_LVTTHMR:      /* LVT Thermal Monitor */    case APIC_LVTPC:        /* LVT Performance Counter */    case APIC_LVT0:         /* LVT LINT0 Reg */    case APIC_LVT1:         /* LVT Lint1 Reg */    case APIC_LVTERR:       /* LVT Error Reg */        if ( vlapic_sw_disabled(vlapic) )            val |= APIC_LVT_MASKED;        val &= vlapic_lvt_mask[(offset - APIC_LVTT) >> 4];        vlapic_set_reg(vlapic, offset, val);        break;    case APIC_TMICT:    {        uint64_t period = (uint64_t)APIC_BUS_CYCLE_NS *                            (uint32_t)val * vlapic->hw.timer_divisor;        vlapic_set_reg(vlapic, APIC_TMICT, val);        create_periodic_time(current, &vlapic->pt, period, vlapic->pt.irq,                             !vlapic_lvtt_period(vlapic), vlapic_pt_cb,                             &vlapic->timer_last_update);        vlapic->timer_last_update = vlapic->pt.last_plt_gtime;        HVM_DBG_LOG(DBG_LEVEL_VLAPIC,                    "bus cycle is %uns, "                    "initial count %lu, period %"PRIu64"ns",                    APIC_BUS_CYCLE_NS, val, period);    }    break;    case APIC_TDCR:        vlapic_set_tdcr(vlapic, val & 0xb);        HVM_DBG_LOG(DBG_LEVEL_VLAPIC_TIMER, "timer divisor is 0x%x",                    vlapic->hw.timer_divisor);        break;    default:        gdprintk(XENLOG_DEBUG,                 "Local APIC Write to read-only register 0x%x\n", offset);        break;    }    return rc; unaligned_exit_and_crash:    gdprintk(XENLOG_ERR, "Unaligned LAPIC write len=0x%lx at offset=0x%x.\n",             len, offset); exit_and_crash:    domain_crash(v->domain);    return rc;}static int vlapic_range(struct vcpu *v, unsigned long addr){    struct vlapic *vlapic = vcpu_vlapic(v);    unsigned long offset  = addr - vlapic_base_address(vlapic);    return (!vlapic_hw_disabled(vlapic) && (offset < PAGE_SIZE));}struct hvm_mmio_handler vlapic_mmio_handler = {    .check_handler = vlapic_range,    .read_handler = vlapic_read,    .write_handler = vlapic_write};void vlapic_msr_set(struct vlapic *vlapic, uint64_t value){    if ( (vlapic->hw.apic_base_msr ^ value) & MSR_IA32_APICBASE_ENABLE )    {        if ( value & MSR_IA32_APICBASE_ENABLE )        {            vlapic_reset(vlapic);            vlapic->hw.disabled &= ~VLAPIC_HW_DISABLED;        }        else        {            vlapic->hw.disabled |= VLAPIC_HW_DISABLED;        }    }    vlapic->hw.apic_base_msr = value;    vmx_vlapic_msr_changed(vlapic_vcpu(vlapic));    HVM_DBG_LOG(DBG_LEVEL_VLAPIC,                "apic base msr is 0x%016"PRIx64, vlapic->hw.apic_base_msr);}int vlapic_accept_pic_intr(struct vcpu *v){    struct vlapic *vlapic = vcpu_vlapic(v);    uint32_t lvt0 = vlapic_get_reg(vlapic, APIC_LVT0);    /*     * Only CPU0 is wired to the 8259A. INTA cycles occur if LINT0 is set up     * accept ExtInts, or if the LAPIC is disabled (so LINT0 behaves as INTR).     */    return ((v->vcpu_id == 0) &&            (((lvt0 & (APIC_MODE_MASK|APIC_LVT_MASKED)) == APIC_DM_EXTINT) ||             vlapic_hw_disabled(vlapic)));}int vlapic_has_pending_irq(struct vcpu *v){    struct vlapic *vlapic = vcpu_vlapic(v);    int irr, isr;    if ( !vlapic_enabled(vlapic) )        return -1;    irr = vlapic_find_highest_irr(vlapic);    if ( irr == -1 )        return -1;    isr = vlapic_find_highest_isr(vlapic);    isr = (isr != -1) ? isr : 0;    if ( (isr & 0xf0) >= (irr & 0xf0) )        return -1;    return irr;}int vlapic_ack_pending_irq(struct vcpu *v, int vector){    struct vlapic *vlapic = vcpu_vlapic(v);    vlapic_set_vector(vector, &vlapic->regs->data[APIC_ISR]);    vlapic_clear_irr(vector, vlapic);    return 1;}/* Reset the VLPAIC back to its power-on/reset state. */void vlapic_reset(struct vlapic *vlapic){    struct vcpu *v = vlapic_vcpu(vlapic);    int i;    vlapic_set_reg(vlapic, APIC_ID,  (v->vcpu_id * 2) << 24);    vlapic_set_reg(vlapic, APIC_LVR, VLAPIC_VERSION);    for ( i = 0; i < 8; i++ )    {        vlapic_set_reg(vlapic, APIC_IRR + 0x10 * i, 0);        vlapic_set_reg(vlapic, APIC_ISR + 0x10 * i, 0);        vlapic_set_reg(vlapic, APIC_TMR + 0x10 * i, 0);    }    vlapic_set_reg(vlapic, APIC_ICR,     0);    vlapic_set_reg(vlapic, APIC_ICR2,    0);    vlapic_set_reg(vlapic, APIC_LDR,     0);    vlapic_set_reg(vlapic, APIC_TASKPRI, 0);    vlapic_set_reg(vlapic, APIC_TMICT,   0);    vlapic_set_reg(vlapic, APIC_TMCCT,   0);    vlapic_set_tdcr(vlapic, 0);    vlapic_set_reg(vlapic, APIC_DFR, 0xffffffffU);    for ( i = 0; i < VLAPIC_LVT_NUM; i++ )        vlapic_set_reg(vlapic, APIC_LVTT + 0x10 * i, APIC_LVT_MASKED);    vlapic_set_reg(vlapic, APIC_SPIV, 0xff);    vlapic->hw.disabled |= VLAPIC_SW_DISABLED;    destroy_periodic_time(&vlapic->pt);}/* rearm the actimer if needed, after a HVM restore */static void lapic_rearm(struct vlapic *s){    unsigned long tmict = vlapic_get_reg(s, APIC_TMICT);    uint64_t period;    if ( (tmict = vlapic_get_reg(s, APIC_TMICT)) == 0 )        return;    period = ((uint64_t)APIC_BUS_CYCLE_NS *              (uint32_t)tmict * s->hw.timer_divisor);    s->pt.irq = vlapic_get_reg(s, APIC_LVTT) & APIC_VECTOR_MASK;    create_periodic_time(vlapic_vcpu(s), &s->pt, period, s->pt.irq,                         !vlapic_lvtt_period(s), vlapic_pt_cb,                         &s->timer_last_update);    s->timer_last_update = s->pt.last_plt_gtime;}static int lapic_save_hidden(struct domain *d, hvm_domain_context_t *h){    struct vcpu *v;    struct vlapic *s;    int rc = 0;    for_each_vcpu ( d, v )    {        s = vcpu_vlapic(v);        if ( (rc = hvm_save_entry(LAPIC, v->vcpu_id, h, &s->hw)) != 0 )            break;    }    return rc;}static int lapic_save_regs(struct domain *d, hvm_domain_context_t *h){    struct vcpu *v;    struct vlapic *s;    int rc = 0;    for_each_vcpu ( d, v )    {        s = vcpu_vlapic(v);        if ( (rc = hvm_save_entry(LAPIC_REGS, v->vcpu_id, h, s->regs)) != 0 )            break;    }    return rc;}static int lapic_load_hidden(struct domain *d, hvm_domain_context_t *h){    uint16_t vcpuid;    struct vcpu *v;    struct vlapic *s;        /* Which vlapic to load? */    vcpuid = hvm_load_instance(h);     if ( vcpuid > MAX_VIRT_CPUS || (v = d->vcpu[vcpuid]) == NULL )     {        gdprintk(XENLOG_ERR, "HVM restore: domain has no vlapic %u\n", vcpuid);        return -EINVAL;    }    s = vcpu_vlapic(v);        if ( hvm_load_entry(LAPIC, h, &s->hw) != 0 )         return -EINVAL;    vmx_vlapic_msr_changed(v);    return 0;}static int lapic_load_regs(struct domain *d, hvm_domain_context_t *h){    uint16_t vcpuid;    struct vcpu *v;    struct vlapic *s;        /* Which vlapic to load? */    vcpuid = hvm_load_instance(h);     if ( vcpuid > MAX_VIRT_CPUS || (v = d->vcpu[vcpuid]) == NULL )     {        gdprintk(XENLOG_ERR, "HVM restore: domain has no vlapic %u\n", vcpuid);        return -EINVAL;    }    s = vcpu_vlapic(v);        if ( hvm_load_entry(LAPIC_REGS, h, s->regs) != 0 )         return -EINVAL;    lapic_rearm(s);    return 0;}HVM_REGISTER_SAVE_RESTORE(LAPIC, lapic_save_hidden, lapic_load_hidden,                          1, HVMSR_PER_VCPU);HVM_REGISTER_SAVE_RESTORE(LAPIC_REGS, lapic_save_regs, lapic_load_regs,                          1, HVMSR_PER_VCPU);int vlapic_init(struct vcpu *v){    struct vlapic *vlapic = vcpu_vlapic(v);    unsigned int memflags = MEMF_node(vcpu_to_node(v));    HVM_DBG_LOG(DBG_LEVEL_VLAPIC, "%d", v->vcpu_id);    vlapic->pt.source = PTSRC_lapic;#ifdef __i386__    /* 32-bit VMX may be limited to 32-bit physical addresses. */    if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL )        memflags |= MEMF_bits(32);#endif    if (vlapic->regs_page == NULL)    {        vlapic->regs_page = alloc_domheap_page(NULL, memflags);        if ( vlapic->regs_page == NULL )        {            dprintk(XENLOG_ERR, "alloc vlapic regs error: %d/%d\n",                    v->domain->domain_id, v->vcpu_id);            return -ENOMEM;        }    }    if (vlapic->regs == NULL)     {        vlapic->regs = map_domain_page_global(page_to_mfn(vlapic->regs_page));        if ( vlapic->regs == NULL )        {            dprintk(XENLOG_ERR, "map vlapic regs error: %d/%d\n",                    v->domain->domain_id, v->vcpu_id);            return -ENOMEM;        }    }    clear_page(vlapic->regs);    vlapic_reset(vlapic);    vlapic->hw.apic_base_msr = (MSR_IA32_APICBASE_ENABLE |                                APIC_DEFAULT_PHYS_BASE);    if ( v->vcpu_id == 0 )        vlapic->hw.apic_base_msr |= MSR_IA32_APICBASE_BSP;    tasklet_init(&vlapic->init_tasklet, vlapic_init_action, (unsigned long)v);    return 0;}void vlapic_destroy(struct vcpu *v){    struct vlapic *vlapic = vcpu_vlapic(v);    tasklet_kill(&vlapic->init_tasklet);    destroy_periodic_time(&vlapic->pt);    unmap_domain_page_global(vlapic->regs);    free_domheap_page(vlapic->regs_page);}

⌨️ 快捷键说明

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