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