📄 lapic.c
字号:
} return tmcct;}static u32 __apic_read(struct kvm_lapic *apic, unsigned int offset){ u32 val = 0; if (offset >= LAPIC_MMIO_LENGTH) return 0; switch (offset) { case APIC_ARBPRI: printk(KERN_WARNING "Access APIC ARBPRI register " "which is for P6\n"); break; case APIC_TMCCT: /* Timer CCR */ val = apic_get_tmcct(apic); break; default: apic_update_ppr(apic); val = apic_get_reg(apic, offset); break; } return val;}static void apic_mmio_read(struct kvm_io_device *this, gpa_t address, int len, void *data){ struct kvm_lapic *apic = (struct kvm_lapic *)this->private; unsigned int offset = address - apic->base_address; unsigned char alignment = offset & 0xf; u32 result; if ((alignment + len) > 4) { printk(KERN_ERR "KVM_APIC_READ: alignment error %lx %d", (unsigned long)address, len); return; } result = __apic_read(apic, offset & ~0xf); switch (len) { case 1: case 2: case 4: memcpy(data, (char *)&result + alignment, len); break; default: printk(KERN_ERR "Local APIC read with len = %x, " "should be 1,2, or 4 instead\n", len); break; }}static void update_divide_count(struct kvm_lapic *apic){ u32 tmp1, tmp2, tdcr; tdcr = apic_get_reg(apic, APIC_TDCR); tmp1 = tdcr & 0xf; tmp2 = ((tmp1 & 0x3) | ((tmp1 & 0x8) >> 1)) + 1; apic->timer.divide_count = 0x1 << (tmp2 & 0x7); apic_debug("timer divide count is 0x%x\n", apic->timer.divide_count);}static void start_apic_timer(struct kvm_lapic *apic){ ktime_t now = apic->timer.dev.base->get_time(); apic->timer.last_update = now; apic->timer.period = apic_get_reg(apic, APIC_TMICT) * APIC_BUS_CYCLE_NS * apic->timer.divide_count; atomic_set(&apic->timer.pending, 0); hrtimer_start(&apic->timer.dev, ktime_add_ns(now, apic->timer.period), HRTIMER_MODE_ABS); apic_debug("%s: bus cycle is %" PRId64 "ns, now 0x%016" PRIx64 ", " "timer initial count 0x%x, period %lldns, " "expire @ 0x%016" PRIx64 ".\n", __FUNCTION__, APIC_BUS_CYCLE_NS, ktime_to_ns(now), apic_get_reg(apic, APIC_TMICT), apic->timer.period, ktime_to_ns(ktime_add_ns(now, apic->timer.period)));}static void apic_mmio_write(struct kvm_io_device *this, gpa_t address, int len, const void *data){ struct kvm_lapic *apic = (struct kvm_lapic *)this->private; unsigned int offset = address - apic->base_address; unsigned char alignment = offset & 0xf; u32 val; /* * APIC register must be aligned on 128-bits boundary. * 32/64/128 bits registers must be accessed thru 32 bits. * Refer SDM 8.4.1 */ if (len != 4 || alignment) { if (printk_ratelimit()) printk(KERN_ERR "apic write: bad size=%d %lx\n", len, (long)address); return; } val = *(u32 *) data; /* too common printing */ if (offset != APIC_EOI) apic_debug("%s: offset 0x%x with length 0x%x, and value is " "0x%x\n", __FUNCTION__, offset, len, val); offset &= 0xff0; switch (offset) { case APIC_ID: /* Local APIC ID */ apic_set_reg(apic, APIC_ID, val); break; case APIC_TASKPRI: apic_set_tpr(apic, val & 0xff); break; case APIC_EOI: apic_set_eoi(apic); break; case APIC_LDR: apic_set_reg(apic, APIC_LDR, val & APIC_LDR_MASK); break; case APIC_DFR: apic_set_reg(apic, APIC_DFR, val | 0x0FFFFFFF); break; case APIC_SPIV: apic_set_reg(apic, APIC_SPIV, val & 0x3ff); if (!(val & APIC_SPIV_APIC_ENABLED)) { int i; u32 lvt_val; for (i = 0; i < APIC_LVT_NUM; i++) { lvt_val = apic_get_reg(apic, APIC_LVTT + 0x10 * i); apic_set_reg(apic, APIC_LVTT + 0x10 * i, lvt_val | APIC_LVT_MASKED); } atomic_set(&apic->timer.pending, 0); } break; case APIC_ICR: /* No delay here, so we always clear the pending bit */ apic_set_reg(apic, APIC_ICR, val & ~(1 << 12)); apic_send_ipi(apic); break; case APIC_ICR2: apic_set_reg(apic, APIC_ICR2, val & 0xff000000); break; case APIC_LVTT: case APIC_LVTTHMR: case APIC_LVTPC: case APIC_LVT0: case APIC_LVT1: case APIC_LVTERR: /* TODO: Check vector */ if (!apic_sw_enabled(apic)) val |= APIC_LVT_MASKED; val &= apic_lvt_mask[(offset - APIC_LVTT) >> 4]; apic_set_reg(apic, offset, val); break; case APIC_TMICT: hrtimer_cancel(&apic->timer.dev); apic_set_reg(apic, APIC_TMICT, val); start_apic_timer(apic); return; case APIC_TDCR: if (val & 4) printk(KERN_ERR "KVM_WRITE:TDCR %x\n", val); apic_set_reg(apic, APIC_TDCR, val); update_divide_count(apic); break; default: apic_debug("Local APIC Write to read-only register %x\n", offset); break; }}static int apic_mmio_range(struct kvm_io_device *this, gpa_t addr){ struct kvm_lapic *apic = (struct kvm_lapic *)this->private; int ret = 0; if (apic_hw_enabled(apic) && (addr >= apic->base_address) && (addr < (apic->base_address + LAPIC_MMIO_LENGTH))) ret = 1; return ret;}void kvm_free_apic(struct kvm_lapic *apic){ if (!apic) return; hrtimer_cancel(&apic->timer.dev); if (apic->regs_page) { __free_page(apic->regs_page); apic->regs_page = 0; } kfree(apic);}/* *---------------------------------------------------------------------- * LAPIC interface *---------------------------------------------------------------------- */void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8){ struct kvm_lapic *apic = (struct kvm_lapic *)vcpu->apic; if (!apic) return; apic_set_tpr(apic, ((cr8 & 0x0f) << 4));}u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu){ struct kvm_lapic *apic = (struct kvm_lapic *)vcpu->apic; u64 tpr; if (!apic) return 0; tpr = (u64) apic_get_reg(apic, APIC_TASKPRI); return (tpr & 0xf0) >> 4;}EXPORT_SYMBOL_GPL(kvm_lapic_get_cr8);void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value){ struct kvm_lapic *apic = (struct kvm_lapic *)vcpu->apic; if (!apic) { value |= MSR_IA32_APICBASE_BSP; vcpu->apic_base = value; return; } if (apic->vcpu->vcpu_id) value &= ~MSR_IA32_APICBASE_BSP; vcpu->apic_base = value; apic->base_address = apic->vcpu->apic_base & MSR_IA32_APICBASE_BASE; /* with FSB delivery interrupt, we can restart APIC functionality */ apic_debug("apic base msr is 0x%016" PRIx64 ", and base address is " "0x%lx.\n", apic->apic_base, apic->base_address);}u64 kvm_lapic_get_base(struct kvm_vcpu *vcpu){ return vcpu->apic_base;}EXPORT_SYMBOL_GPL(kvm_lapic_get_base);void kvm_lapic_reset(struct kvm_vcpu *vcpu){ struct kvm_lapic *apic; int i; apic_debug("%s\n", __FUNCTION__); ASSERT(vcpu); apic = vcpu->apic; ASSERT(apic != NULL); /* Stop the timer in case it's a reset to an active apic */ hrtimer_cancel(&apic->timer.dev); apic_set_reg(apic, APIC_ID, vcpu->vcpu_id << 24); apic_set_reg(apic, APIC_LVR, APIC_VERSION); for (i = 0; i < APIC_LVT_NUM; i++) apic_set_reg(apic, APIC_LVTT + 0x10 * i, APIC_LVT_MASKED); apic_set_reg(apic, APIC_LVT0, SET_APIC_DELIVERY_MODE(0, APIC_MODE_EXTINT)); apic_set_reg(apic, APIC_DFR, 0xffffffffU); apic_set_reg(apic, APIC_SPIV, 0xff); apic_set_reg(apic, APIC_TASKPRI, 0); apic_set_reg(apic, APIC_LDR, 0); apic_set_reg(apic, APIC_ESR, 0); apic_set_reg(apic, APIC_ICR, 0); apic_set_reg(apic, APIC_ICR2, 0); apic_set_reg(apic, APIC_TDCR, 0); apic_set_reg(apic, APIC_TMICT, 0); for (i = 0; i < 8; i++) { apic_set_reg(apic, APIC_IRR + 0x10 * i, 0); apic_set_reg(apic, APIC_ISR + 0x10 * i, 0); apic_set_reg(apic, APIC_TMR + 0x10 * i, 0); } update_divide_count(apic); atomic_set(&apic->timer.pending, 0); if (vcpu->vcpu_id == 0) vcpu->apic_base |= MSR_IA32_APICBASE_BSP; apic_update_ppr(apic); apic_debug(KERN_INFO "%s: vcpu=%p, id=%d, base_msr=" "0x%016" PRIx64 ", base_address=0x%0lx.\n", __FUNCTION__, vcpu, kvm_apic_id(apic), vcpu->apic_base, apic->base_address);}EXPORT_SYMBOL_GPL(kvm_lapic_reset);int kvm_lapic_enabled(struct kvm_vcpu *vcpu){ struct kvm_lapic *apic = (struct kvm_lapic *)vcpu->apic; int ret = 0; if (!apic) return 0; ret = apic_enabled(apic); return ret;}EXPORT_SYMBOL_GPL(kvm_lapic_enabled);/* *---------------------------------------------------------------------- * timer interface *---------------------------------------------------------------------- *//* TODO: make sure __apic_timer_fn runs in current pCPU */static int __apic_timer_fn(struct kvm_lapic *apic){ int result = 0; wait_queue_head_t *q = &apic->vcpu->wq; atomic_inc(&apic->timer.pending); if (waitqueue_active(q)) { apic->vcpu->mp_state = VCPU_MP_STATE_RUNNABLE; wake_up_interruptible(q); } if (apic_lvtt_period(apic)) { result = 1; apic->timer.dev.expires = ktime_add_ns( apic->timer.dev.expires, apic->timer.period); } return result;}static int __inject_apic_timer_irq(struct kvm_lapic *apic){ int vector; vector = apic_lvt_vector(apic, APIC_LVTT); return __apic_accept_irq(apic, APIC_DM_FIXED, vector, 1, 0);}static enum hrtimer_restart apic_timer_fn(struct hrtimer *data){ struct kvm_lapic *apic; int restart_timer = 0; apic = container_of(data, struct kvm_lapic, timer.dev); restart_timer = __apic_timer_fn(apic); if (restart_timer) return HRTIMER_RESTART; else return HRTIMER_NORESTART;}int kvm_create_lapic(struct kvm_vcpu *vcpu){ struct kvm_lapic *apic; ASSERT(vcpu != NULL); apic_debug("apic_init %d\n", vcpu->vcpu_id); apic = kzalloc(sizeof(*apic), GFP_KERNEL); if (!apic) goto nomem; vcpu->apic = apic; apic->regs_page = alloc_page(GFP_KERNEL); if (apic->regs_page == NULL) { printk(KERN_ERR "malloc apic regs error for vcpu %x\n", vcpu->vcpu_id); goto nomem; } apic->regs = page_address(apic->regs_page); memset(apic->regs, 0, PAGE_SIZE); apic->vcpu = vcpu; hrtimer_init(&apic->timer.dev, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); apic->timer.dev.function = apic_timer_fn; apic->base_address = APIC_DEFAULT_PHYS_BASE; vcpu->apic_base = APIC_DEFAULT_PHYS_BASE; kvm_lapic_reset(vcpu); apic->dev.read = apic_mmio_read; apic->dev.write = apic_mmio_write; apic->dev.in_range = apic_mmio_range; apic->dev.private = apic; return 0;nomem: kvm_free_apic(apic); return -ENOMEM;}EXPORT_SYMBOL_GPL(kvm_create_lapic);int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu){ struct kvm_lapic *apic = vcpu->apic; int highest_irr; if (!apic || !apic_enabled(apic)) return -1; apic_update_ppr(apic); highest_irr = apic_find_highest_irr(apic); if ((highest_irr == -1) || ((highest_irr & 0xF0) <= apic_get_reg(apic, APIC_PROCPRI))) return -1; return highest_irr;}int kvm_apic_accept_pic_intr(struct kvm_vcpu *vcpu){ u32 lvt0 = apic_get_reg(vcpu->apic, APIC_LVT0); int r = 0; if (vcpu->vcpu_id == 0) { if (!apic_hw_enabled(vcpu->apic)) r = 1; if ((lvt0 & APIC_LVT_MASKED) == 0 && GET_APIC_DELIVERY_MODE(lvt0) == APIC_MODE_EXTINT) r = 1; } return r;}void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu){ struct kvm_lapic *apic = vcpu->apic; if (apic && apic_lvt_enabled(apic, APIC_LVTT) && atomic_read(&apic->timer.pending) > 0) { if (__inject_apic_timer_irq(apic)) atomic_dec(&apic->timer.pending); }}void kvm_apic_timer_intr_post(struct kvm_vcpu *vcpu, int vec){ struct kvm_lapic *apic = vcpu->apic; if (apic && apic_lvt_vector(apic, APIC_LVTT) == vec) apic->timer.last_update = ktime_add_ns( apic->timer.last_update, apic->timer.period);}int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu){ int vector = kvm_apic_has_interrupt(vcpu); struct kvm_lapic *apic = vcpu->apic; if (vector == -1) return -1; apic_set_vector(vector, apic->regs + APIC_ISR); apic_update_ppr(apic); apic_clear_irr(vector, apic); return vector;}void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu){ struct kvm_lapic *apic = vcpu->apic; apic->base_address = vcpu->apic_base & MSR_IA32_APICBASE_BASE; apic_set_reg(apic, APIC_LVR, APIC_VERSION); apic_update_ppr(apic); hrtimer_cancel(&apic->timer.dev); update_divide_count(apic); start_apic_timer(apic);}void kvm_migrate_apic_timer(struct kvm_vcpu *vcpu){ struct kvm_lapic *apic = vcpu->apic; struct hrtimer *timer; if (!apic) return; timer = &apic->timer.dev; if (hrtimer_cancel(timer)) hrtimer_start(timer, timer->expires, HRTIMER_MODE_ABS);}EXPORT_SYMBOL_GPL(kvm_migrate_apic_timer);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -