|  | /* @TAG(OTHER_GPL) */ | 
|  |  | 
|  | /* | 
|  | * Local APIC virtualization | 
|  | * | 
|  | * Copyright (C) 2006 Qumranet, Inc. | 
|  | * Copyright (C) 2007 Novell | 
|  | * Copyright (C) 2007 Intel | 
|  | * Copyright 2009 Red Hat, Inc. and/or its affiliates. | 
|  | * | 
|  | * Authors: | 
|  | *   Dor Laor <dor.laor@qumranet.com> | 
|  | *   Gregory Haskins <ghaskins@novell.com> | 
|  | *   Yaozu (Eddie) Dong <eddie.dong@intel.com> | 
|  | * | 
|  | * Based on Xen 3.1 code, Copyright (c) 2004, Intel Corporation. | 
|  | * | 
|  | * This work is licensed under the terms of the GNU GPL, version 2.  See | 
|  | * the COPYING file in the top-level directory. | 
|  | */ | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <utils/util.h> | 
|  |  | 
|  | #include "vmm/debug.h" | 
|  |  | 
|  | #include "vmm/processor/lapic.h" | 
|  | #include "vmm/processor/apicdef.h" | 
|  | #include "vmm/processor/msr.h" | 
|  | #include "vmm/mmio.h" | 
|  |  | 
|  | #define APIC_BUS_CYCLE_NS 1 | 
|  |  | 
|  | #define APIC_DEBUG 0 | 
|  | #define apic_debug(lvl,...) do{ if(lvl < APIC_DEBUG){printf(__VA_ARGS__);fflush(stdout);}}while (0) | 
|  |  | 
|  | #define APIC_LVT_NUM            6 | 
|  | /* 14 is the version for Xeon and Pentium 8.4.8*/ | 
|  | #define APIC_VERSION            (0x14UL | ((APIC_LVT_NUM - 1) << 16)) | 
|  | #define LAPIC_MMIO_LENGTH       (BIT(12)) | 
|  | /* followed define is not in apicdef.h */ | 
|  | #define APIC_SHORT_MASK         0xc0000 | 
|  | #define APIC_DEST_NOSHORT       0x0 | 
|  | #define APIC_DEST_MASK          0x800 | 
|  | #define MAX_APIC_VECTOR         256 | 
|  | #define APIC_VECTORS_PER_REG        32 | 
|  |  | 
|  | inline static int pic_get_interrupt(vmm_t *vmm) | 
|  | { | 
|  | return vmm->plat_callbacks.get_interrupt(); | 
|  | } | 
|  |  | 
|  | inline static int pic_has_interrupt(vmm_t *vmm) | 
|  | { | 
|  | return vmm->plat_callbacks.has_interrupt(); | 
|  | } | 
|  |  | 
|  | struct vmm_lapic_irq { | 
|  | uint32_t vector; | 
|  | uint32_t delivery_mode; | 
|  | uint32_t dest_mode; | 
|  | uint32_t level; | 
|  | uint32_t trig_mode; | 
|  | uint32_t shorthand; | 
|  | uint32_t dest_id; | 
|  | }; | 
|  |  | 
|  | /* Generic bit operations; TODO move these elsewhere */ | 
|  | static inline int fls(int x) | 
|  | { | 
|  | int r = 32; | 
|  |  | 
|  | if (!x) | 
|  | return 0; | 
|  |  | 
|  | if (!(x & 0xffff0000u)) { | 
|  | x <<= 16; | 
|  | r -= 16; | 
|  | } | 
|  | if (!(x & 0xff000000u)) { | 
|  | x <<= 8; | 
|  | r -= 8; | 
|  | } | 
|  | if (!(x & 0xf0000000u)) { | 
|  | x <<= 4; | 
|  | r -= 4; | 
|  | } | 
|  | if (!(x & 0xc0000000u)) { | 
|  | x <<= 2; | 
|  | r -= 2; | 
|  | } | 
|  | if (!(x & 0x80000000u)) { | 
|  | x <<= 1; | 
|  | r -= 1; | 
|  | } | 
|  | return r; | 
|  | } | 
|  |  | 
|  | static uint32_t hweight32(unsigned int w) | 
|  | { | 
|  | uint32_t res = w - ((w >> 1) & 0x55555555); | 
|  | res = (res & 0x33333333) + ((res >> 2) & 0x33333333); | 
|  | res = (res + (res >> 4)) & 0x0F0F0F0F; | 
|  | res = res + (res >> 8); | 
|  | return (res + (res >> 16)) & 0x000000FF; | 
|  | } | 
|  | /* End generic bit ops */ | 
|  |  | 
|  | void vmm_lapic_reset(vmm_vcpu_t *vcpu); | 
|  |  | 
|  | // Returns whether the irq delivery mode is lowest prio | 
|  | inline static bool vmm_is_dm_lowest_prio(struct vmm_lapic_irq *irq) | 
|  | { | 
|  | return irq->delivery_mode == APIC_DM_LOWEST; | 
|  | } | 
|  |  | 
|  | // Access register field | 
|  | static inline void apic_set_reg(vmm_lapic_t *apic, int reg_off, uint32_t val) | 
|  | { | 
|  | *((uint32_t *) (apic->regs + reg_off)) = val; | 
|  | } | 
|  |  | 
|  | static inline uint32_t vmm_apic_get_reg(vmm_lapic_t *apic, int reg_off) | 
|  | { | 
|  | return *((uint32_t *) (apic->regs + reg_off)); | 
|  | } | 
|  |  | 
|  | static inline int apic_test_vector(int vec, void *bitmap) | 
|  | { | 
|  | return ((1UL << (vec & 31)) & ((uint32_t *)bitmap)[vec >> 5]) != 0; | 
|  | } | 
|  |  | 
|  | bool vmm_apic_pending_eoi(vmm_vcpu_t *vcpu, int vector) | 
|  | { | 
|  | vmm_lapic_t *apic = vcpu->lapic; | 
|  |  | 
|  | return apic_test_vector(vector, apic->regs + APIC_ISR) || | 
|  | apic_test_vector(vector, apic->regs + APIC_IRR); | 
|  | } | 
|  |  | 
|  | static inline void apic_set_vector(int vec, void *bitmap) | 
|  | { | 
|  | ((uint32_t *)bitmap)[vec >> 5] |= 1UL << (vec & 31); | 
|  | } | 
|  |  | 
|  | static inline void apic_clear_vector(int vec, void *bitmap) | 
|  | { | 
|  | ((uint32_t *)bitmap)[vec >> 5] &= ~(1UL << (vec & 31)); | 
|  | } | 
|  |  | 
|  | static inline int vmm_apic_sw_enabled(vmm_lapic_t *apic) | 
|  | { | 
|  | return vmm_apic_get_reg(apic, APIC_SPIV) & APIC_SPIV_APIC_ENABLED; | 
|  | } | 
|  |  | 
|  | static inline int vmm_apic_hw_enabled(vmm_lapic_t *apic) | 
|  | { | 
|  | return apic->apic_base & MSR_IA32_APICBASE_ENABLE; | 
|  | } | 
|  |  | 
|  | inline int vmm_apic_enabled(vmm_lapic_t *apic) | 
|  | { | 
|  | return vmm_apic_sw_enabled(apic) && vmm_apic_hw_enabled(apic); | 
|  | } | 
|  |  | 
|  | #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_TRIGGER) | 
|  |  | 
|  | static inline int vmm_apic_id(vmm_lapic_t *apic) | 
|  | { | 
|  | return (vmm_apic_get_reg(apic, APIC_ID) >> 24) & 0xff; | 
|  | } | 
|  |  | 
|  | static inline void apic_set_spiv(vmm_lapic_t *apic, uint32_t val) | 
|  | { | 
|  | apic_set_reg(apic, APIC_SPIV, val); | 
|  | } | 
|  |  | 
|  | static const unsigned int apic_lvt_mask[APIC_LVT_NUM] = { | 
|  | LVT_MASK ,      /* part LVTT mask, timer mode mask added at runtime */ | 
|  | LVT_MASK | APIC_MODE_MASK,  /* LVTTHMR */ | 
|  | LVT_MASK | APIC_MODE_MASK,  /* LVTPC */ | 
|  | LINT_MASK, LINT_MASK,   /* LVT0-1 */ | 
|  | LVT_MASK        /* LVTERR */ | 
|  | }; | 
|  |  | 
|  | static inline void vmm_apic_set_id(vmm_lapic_t *apic, uint8_t id) | 
|  | { | 
|  | apic_set_reg(apic, APIC_ID, id << 24); | 
|  | } | 
|  |  | 
|  | static inline void vmm_apic_set_ldr(vmm_lapic_t *apic, uint32_t id) | 
|  | { | 
|  | apic_set_reg(apic, APIC_LDR, id); | 
|  | } | 
|  |  | 
|  | static inline int apic_lvt_enabled(vmm_lapic_t *apic, int lvt_type) | 
|  | { | 
|  | return !(vmm_apic_get_reg(apic, lvt_type) & APIC_LVT_MASKED); | 
|  | } | 
|  |  | 
|  | static inline int apic_lvt_vector(vmm_lapic_t *apic, int lvt_type) | 
|  | { | 
|  | return vmm_apic_get_reg(apic, lvt_type) & APIC_VECTOR_MASK; | 
|  | } | 
|  |  | 
|  | static inline int vmm_vcpu_is_bsp(vmm_vcpu_t *vcpu) | 
|  | { | 
|  | return vcpu->vcpu_id == BOOT_VCPU; | 
|  | } | 
|  |  | 
|  | static inline int apic_lvt_nmi_mode(uint32_t lvt_val) | 
|  | { | 
|  | return (lvt_val & (APIC_MODE_MASK | APIC_LVT_MASKED)) == APIC_DM_NMI; | 
|  | } | 
|  |  | 
|  | int vmm_apic_compare_prio(vmm_vcpu_t *vcpu1, vmm_vcpu_t *vcpu2) | 
|  | { | 
|  | return vcpu1->lapic->arb_prio - vcpu2->lapic->arb_prio; | 
|  | } | 
|  |  | 
|  | static void UNUSED dump_vector(const char *name, void *bitmap) | 
|  | { | 
|  | int vec; | 
|  | uint32_t *reg = bitmap; | 
|  |  | 
|  | printf("%s = 0x", name); | 
|  |  | 
|  | for (vec = MAX_APIC_VECTOR - APIC_VECTORS_PER_REG; | 
|  | vec >= 0; vec -= APIC_VECTORS_PER_REG) { | 
|  | printf("%08x", reg[vec >> 5]); | 
|  | } | 
|  |  | 
|  | printf("\n"); | 
|  | } | 
|  |  | 
|  | static int find_highest_vector(void *bitmap) | 
|  | { | 
|  | int vec; | 
|  | uint32_t *reg = bitmap; | 
|  |  | 
|  | for (vec = MAX_APIC_VECTOR - APIC_VECTORS_PER_REG; | 
|  | vec >= 0; vec -= APIC_VECTORS_PER_REG) { | 
|  | if (reg[vec >> 5]) | 
|  | return fls(reg[vec >> 5]) - 1 + vec; | 
|  | } | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static uint8_t UNUSED count_vectors(void *bitmap) | 
|  | { | 
|  | int vec; | 
|  | uint32_t *reg = bitmap; | 
|  | uint8_t count = 0; | 
|  |  | 
|  | for (vec = 0; vec < MAX_APIC_VECTOR; vec += APIC_VECTORS_PER_REG) { | 
|  | count += hweight32(reg[vec >> 5]); | 
|  | } | 
|  |  | 
|  | return count; | 
|  | } | 
|  |  | 
|  | static inline int apic_search_irr(vmm_lapic_t *apic) | 
|  | { | 
|  | return find_highest_vector(apic->regs + APIC_IRR); | 
|  | } | 
|  |  | 
|  | static inline int apic_find_highest_irr(vmm_lapic_t *apic) | 
|  | { | 
|  | int result; | 
|  |  | 
|  | if (!apic->irr_pending) | 
|  | return -1; | 
|  |  | 
|  | result = apic_search_irr(apic); | 
|  | assert(result == -1 || result >= 16); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static inline void apic_set_irr(int vec, vmm_lapic_t *apic) | 
|  | { | 
|  | if (vec != 0x30) { | 
|  | apic_debug(5, "!settting irr 0x%x\n", vec); | 
|  | } | 
|  |  | 
|  | apic->irr_pending = true; | 
|  | apic_set_vector(vec, apic->regs + APIC_IRR); | 
|  | } | 
|  |  | 
|  | static inline void apic_clear_irr(int vec, vmm_lapic_t *apic) | 
|  | { | 
|  | apic_clear_vector(vec, apic->regs + APIC_IRR); | 
|  |  | 
|  | vec = apic_search_irr(apic); | 
|  | apic->irr_pending = (vec != -1); | 
|  | } | 
|  |  | 
|  | static inline void apic_set_isr(int vec, vmm_lapic_t *apic) | 
|  | { | 
|  | if (apic_test_vector(vec, apic->regs + APIC_ISR)) { | 
|  | return; | 
|  | } | 
|  | apic_set_vector(vec, apic->regs + APIC_ISR); | 
|  |  | 
|  | ++apic->isr_count; | 
|  | /* | 
|  | * ISR (in service register) bit is set when injecting an interrupt. | 
|  | * The highest vector is injected. Thus the latest bit set matches | 
|  | * the highest bit in ISR. | 
|  | */ | 
|  | } | 
|  |  | 
|  | static inline int apic_find_highest_isr(vmm_lapic_t *apic) | 
|  | { | 
|  | int result; | 
|  |  | 
|  | /* | 
|  | * Note that isr_count is always 1, and highest_isr_cache | 
|  | * is always -1, with APIC virtualization enabled. | 
|  | */ | 
|  | if (!apic->isr_count) | 
|  | return -1; | 
|  | if (apic->highest_isr_cache != -1) | 
|  | return apic->highest_isr_cache; | 
|  |  | 
|  | result = find_highest_vector(apic->regs + APIC_ISR); | 
|  | assert(result == -1 || result >= 16); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static inline void apic_clear_isr(int vec, vmm_lapic_t *apic) | 
|  | { | 
|  | if (!apic_test_vector(vec, apic->regs + APIC_ISR)) { | 
|  | return; | 
|  | } | 
|  | apic_clear_vector(vec, apic->regs + APIC_ISR); | 
|  |  | 
|  | --apic->isr_count; | 
|  | apic->highest_isr_cache = -1; | 
|  | } | 
|  |  | 
|  | int vmm_lapic_find_highest_irr(vmm_vcpu_t *vcpu) | 
|  | { | 
|  | int highest_irr; | 
|  |  | 
|  | highest_irr = apic_find_highest_irr(vcpu->lapic); | 
|  |  | 
|  | return highest_irr; | 
|  | } | 
|  |  | 
|  | static int __apic_accept_irq(vmm_vcpu_t *vcpu, int delivery_mode, | 
|  | int vector, int level, int trig_mode, | 
|  | unsigned long *dest_map); | 
|  |  | 
|  | int vmm_apic_set_irq(vmm_vcpu_t *vcpu, struct vmm_lapic_irq *irq, | 
|  | unsigned long *dest_map) | 
|  | { | 
|  | return __apic_accept_irq(vcpu, irq->delivery_mode, irq->vector, | 
|  | irq->level, irq->trig_mode, dest_map); | 
|  | } | 
|  |  | 
|  | void vmm_apic_update_tmr(vmm_vcpu_t *vcpu, uint32_t *tmr) | 
|  | { | 
|  | vmm_lapic_t *apic = vcpu->lapic; | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < 8; i++) | 
|  | apic_set_reg(apic, APIC_TMR + 0x10 * i, tmr[i]); | 
|  | } | 
|  |  | 
|  | static void apic_update_ppr(vmm_vcpu_t *vcpu) | 
|  | { | 
|  | uint32_t tpr, isrv, ppr, old_ppr; | 
|  | int isr; | 
|  | vmm_lapic_t *apic = vcpu->lapic; | 
|  |  | 
|  | old_ppr = vmm_apic_get_reg(apic, APIC_PROCPRI); | 
|  | tpr = vmm_apic_get_reg(apic, APIC_TASKPRI); | 
|  | isr = apic_find_highest_isr(apic); | 
|  | isrv = (isr != -1) ? isr : 0; | 
|  |  | 
|  | if ((tpr & 0xf0) >= (isrv & 0xf0)) | 
|  | ppr = tpr & 0xff; | 
|  | else | 
|  | ppr = isrv & 0xf0; | 
|  |  | 
|  | apic_debug(6, "vlapic %p, ppr 0x%x, isr 0x%x, isrv 0x%x\n", | 
|  | apic, ppr, isr, isrv); | 
|  |  | 
|  | if (old_ppr != ppr) { | 
|  | apic_set_reg(apic, APIC_PROCPRI, ppr); | 
|  | if (ppr < old_ppr) { | 
|  | /* Might have unmasked some pending interrupts */ | 
|  | vmm_vcpu_accept_interrupt(vcpu); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void apic_set_tpr(vmm_vcpu_t *vcpu, uint32_t tpr) | 
|  | { | 
|  | apic_set_reg(vcpu->lapic, APIC_TASKPRI, tpr); | 
|  | apic_debug(3, "vcpu %d lapic TPR set to %d\n", vcpu->vcpu_id, tpr); | 
|  | apic_update_ppr(vcpu); | 
|  | } | 
|  |  | 
|  | int vmm_apic_match_physical_addr(vmm_lapic_t *apic, uint16_t dest) | 
|  | { | 
|  | return dest == 0xff || vmm_apic_id(apic) == dest; | 
|  | } | 
|  |  | 
|  | int vmm_apic_match_logical_addr(vmm_lapic_t *apic, uint8_t mda) | 
|  | { | 
|  | int result = 0; | 
|  | uint32_t logical_id; | 
|  |  | 
|  | logical_id = GET_APIC_LOGICAL_ID(vmm_apic_get_reg(apic, APIC_LDR)); | 
|  |  | 
|  | switch (vmm_apic_get_reg(apic, 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: | 
|  | apic_debug(1, "Bad DFR: %08x\n", vmm_apic_get_reg(apic, APIC_DFR)); | 
|  | break; | 
|  | } | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | int vmm_apic_match_dest(vmm_vcpu_t *vcpu, vmm_lapic_t *source, | 
|  | int short_hand, int dest, int dest_mode) | 
|  | { | 
|  | int result = 0; | 
|  | vmm_lapic_t *target = vcpu->lapic; | 
|  |  | 
|  | assert(target); | 
|  | switch (short_hand) { | 
|  | case APIC_DEST_NOSHORT: | 
|  | if (dest_mode == 0) | 
|  | /* Physical mode. */ | 
|  | result = vmm_apic_match_physical_addr(target, dest); | 
|  | else | 
|  | /* Logical mode. */ | 
|  | result = vmm_apic_match_logical_addr(target, dest); | 
|  | break; | 
|  | case APIC_DEST_SELF: | 
|  | result = (target == source); | 
|  | break; | 
|  | case APIC_DEST_ALLINC: | 
|  | result = 1; | 
|  | break; | 
|  | case APIC_DEST_ALLBUT: | 
|  | result = (target != source); | 
|  | break; | 
|  | default: | 
|  | apic_debug(2, "apic: Bad dest shorthand value %x\n", | 
|  | short_hand); | 
|  | break; | 
|  | } | 
|  |  | 
|  | apic_debug(4, "target %p, source %p, dest 0x%x, " | 
|  | "dest_mode 0x%x, short_hand 0x%x", | 
|  | target, source, dest, dest_mode, short_hand); | 
|  | if (result) { | 
|  | apic_debug(4, " MATCH\n"); | 
|  | } else { | 
|  | apic_debug(4, "\n"); | 
|  | } | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | int vmm_irq_delivery_to_apic(vmm_vcpu_t *src_vcpu, struct vmm_lapic_irq *irq, unsigned long *dest_map) | 
|  | { | 
|  | int i, r = -1; | 
|  | vmm_lapic_t *src = src_vcpu->lapic; | 
|  | vmm_t *vmm = src_vcpu->vmm; | 
|  |  | 
|  | vmm_vcpu_t *lowest = NULL; | 
|  |  | 
|  | if (irq->shorthand == APIC_DEST_SELF) { | 
|  | return vmm_apic_set_irq(src_vcpu, irq, dest_map); | 
|  | } | 
|  |  | 
|  | for (i = 0; i < vmm->num_vcpus; i++) { | 
|  | vmm_vcpu_t *dest_vcpu = &vmm->vcpus[i]; | 
|  |  | 
|  | if (!vmm_apic_hw_enabled(dest_vcpu->lapic)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (!vmm_apic_match_dest(dest_vcpu, src, irq->shorthand, | 
|  | irq->dest_id, irq->dest_mode)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (!vmm_is_dm_lowest_prio(irq)) { | 
|  | // Normal delivery | 
|  | if (r < 0) { | 
|  | r = 0; | 
|  | } | 
|  | r += vmm_apic_set_irq(dest_vcpu, irq, dest_map); | 
|  | } else if (vmm_apic_enabled(dest_vcpu->lapic)) { | 
|  | // Pick vcpu with lowest priority to deliver to | 
|  | if (!lowest) { | 
|  | lowest = dest_vcpu; | 
|  | } else if (vmm_apic_compare_prio(dest_vcpu, lowest) < 0) { | 
|  | lowest = dest_vcpu; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (lowest) { | 
|  | r = vmm_apic_set_irq(lowest, irq, dest_map); | 
|  | } | 
|  |  | 
|  | return r; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Add a pending IRQ into lapic. | 
|  | * Return 1 if successfully added and 0 if discarded. | 
|  | */ | 
|  | static int __apic_accept_irq(vmm_vcpu_t *vcpu, int delivery_mode, | 
|  | int vector, int level, int trig_mode, | 
|  | unsigned long *dest_map) | 
|  | { | 
|  | int result = 0; | 
|  | vmm_lapic_t *apic = vcpu->lapic; | 
|  |  | 
|  | switch (delivery_mode) { | 
|  | case APIC_DM_LOWEST: | 
|  | apic->arb_prio++; | 
|  | case APIC_DM_FIXED: | 
|  | /* FIXME add logic for vcpu on reset */ | 
|  | if (!vmm_apic_enabled(apic)) { | 
|  | break; | 
|  | } | 
|  | apic_debug(4, "####fixed ipi 0x%x to vcpu %d\n", vector, vcpu->vcpu_id); | 
|  |  | 
|  | result = 1; | 
|  | apic_set_irr(vector, apic); | 
|  | vmm_vcpu_accept_interrupt(vcpu); | 
|  | break; | 
|  |  | 
|  | case APIC_DM_NMI: | 
|  | case APIC_DM_REMRD: | 
|  | result = 1; | 
|  | vmm_vcpu_accept_interrupt(vcpu); | 
|  | break; | 
|  |  | 
|  | case APIC_DM_SMI: | 
|  | apic_debug(2, "Ignoring guest SMI\n"); | 
|  | break; | 
|  |  | 
|  | case APIC_DM_INIT: | 
|  | apic_debug(2, "Got init ipi on vcpu %d\n", vcpu->vcpu_id); | 
|  | if (!trig_mode || level) { | 
|  | if (apic->state == LAPIC_STATE_RUN) { | 
|  | /* Already running, ignore inits */ | 
|  | break; | 
|  | } | 
|  | result = 1; | 
|  | vmm_lapic_reset(vcpu); | 
|  | apic->arb_prio = vmm_apic_id(apic); | 
|  | apic->state = LAPIC_STATE_WAITSIPI; | 
|  | } else { | 
|  | apic_debug(2, "Ignoring de-assert INIT to vcpu %d\n", | 
|  | vcpu->vcpu_id); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case APIC_DM_STARTUP: | 
|  | if (apic->state != LAPIC_STATE_WAITSIPI) { | 
|  | apic_debug(1, "Received SIPI while processor was not in wait for SIPI state\n"); | 
|  | } else { | 
|  | apic_debug(2, "SIPI to vcpu %d vector 0x%02x\n", | 
|  | vcpu->vcpu_id, vector); | 
|  | result = 1; | 
|  | apic->sipi_vector = vector; | 
|  | apic->state = LAPIC_STATE_RUN; | 
|  |  | 
|  | /* Start the VCPU thread. */ | 
|  | vmm_start_ap_vcpu(vcpu, vector); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case APIC_DM_EXTINT: | 
|  | /* extints are handled by vmm_apic_consume_extints */ | 
|  | printf("extint should not come to this function. vcpu %d\n", vcpu->vcpu_id); | 
|  | assert(0); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | printf("TODO: unsupported lapic ipi delivery mode %x", delivery_mode); | 
|  | assert(0); | 
|  | break; | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static int apic_set_eoi(vmm_vcpu_t *vcpu) | 
|  | { | 
|  | vmm_lapic_t *apic = vcpu->lapic; | 
|  | int vector = apic_find_highest_isr(apic); | 
|  |  | 
|  | /* | 
|  | * Not every write EOI will has corresponding ISR, | 
|  | * one example is when Kernel check timer on setup_IO_APIC | 
|  | */ | 
|  | if (vector == -1) | 
|  | return vector; | 
|  |  | 
|  | apic_clear_isr(vector, apic); | 
|  | apic_update_ppr(vcpu); | 
|  |  | 
|  | /* If another interrupt is pending, raise it */ | 
|  | vmm_vcpu_accept_interrupt(vcpu); | 
|  |  | 
|  | return vector; | 
|  | } | 
|  |  | 
|  | static void apic_send_ipi(vmm_vcpu_t *vcpu) | 
|  | { | 
|  | vmm_lapic_t *apic = vcpu->lapic; | 
|  | uint32_t icr_low = vmm_apic_get_reg(apic, APIC_ICR); | 
|  | uint32_t icr_high = vmm_apic_get_reg(apic, APIC_ICR2); | 
|  | struct vmm_lapic_irq irq; | 
|  |  | 
|  | irq.vector = icr_low & APIC_VECTOR_MASK; | 
|  | irq.delivery_mode = icr_low & APIC_MODE_MASK; | 
|  | irq.dest_mode = icr_low & APIC_DEST_MASK; | 
|  | irq.level = icr_low & APIC_INT_ASSERT; | 
|  | irq.trig_mode = icr_low & APIC_INT_LEVELTRIG; | 
|  | irq.shorthand = icr_low & APIC_SHORT_MASK; | 
|  | irq.dest_id = GET_APIC_DEST_FIELD(icr_high); | 
|  |  | 
|  | apic_debug(3, "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\n", | 
|  | icr_high, icr_low, irq.shorthand, irq.dest_id, | 
|  | irq.trig_mode, irq.level, irq.dest_mode, irq.delivery_mode, | 
|  | irq.vector); | 
|  |  | 
|  | vmm_irq_delivery_to_apic(vcpu, &irq, NULL); | 
|  | } | 
|  |  | 
|  | static uint32_t __apic_read(vmm_lapic_t *apic, unsigned int offset) | 
|  | { | 
|  | uint32_t val = 0; | 
|  |  | 
|  | if (offset >= LAPIC_MMIO_LENGTH) | 
|  | return 0; | 
|  |  | 
|  | switch (offset) { | 
|  | case APIC_ID: | 
|  | val = vmm_apic_id(apic) << 24; | 
|  | break; | 
|  | case APIC_ARBPRI: | 
|  | apic_debug(2, "Access APIC ARBPRI register which is for P6\n"); | 
|  | break; | 
|  |  | 
|  | case APIC_TMCCT:    /* Timer CCR */ | 
|  | break; | 
|  | case APIC_PROCPRI: | 
|  | val = vmm_apic_get_reg(apic, offset); | 
|  | break; | 
|  | default: | 
|  | val = vmm_apic_get_reg(apic, offset); | 
|  | break; | 
|  | } | 
|  |  | 
|  | return val; | 
|  | } | 
|  |  | 
|  | static void apic_manage_nmi_watchdog(vmm_lapic_t *apic, uint32_t lvt0_val) | 
|  | { | 
|  | int nmi_wd_enabled = apic_lvt_nmi_mode(vmm_apic_get_reg(apic, APIC_LVT0)); | 
|  |  | 
|  | if (apic_lvt_nmi_mode(lvt0_val)) { | 
|  | if (!nmi_wd_enabled) { | 
|  | apic_debug(4, "Receive NMI setting on APIC_LVT0 \n"); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static int apic_reg_write(vmm_vcpu_t *vcpu, uint32_t reg, uint32_t val) | 
|  | { | 
|  | vmm_lapic_t *apic = vcpu->lapic; | 
|  | int ret = 0; | 
|  |  | 
|  | switch (reg) { | 
|  | case APIC_ID:       /* Local APIC ID */ | 
|  | vmm_apic_set_id(apic, val >> 24); | 
|  | break; | 
|  |  | 
|  | case APIC_TASKPRI: | 
|  | apic_set_tpr(vcpu, val & 0xff); | 
|  | break; | 
|  |  | 
|  | case APIC_EOI: | 
|  | apic_set_eoi(vcpu); | 
|  | break; | 
|  |  | 
|  | case APIC_LDR: | 
|  | vmm_apic_set_ldr(apic, val & APIC_LDR_MASK); | 
|  | break; | 
|  |  | 
|  | case APIC_DFR: | 
|  | apic_set_reg(apic, APIC_DFR, val | 0x0FFFFFFF); | 
|  | break; | 
|  |  | 
|  | case APIC_SPIV: { | 
|  | uint32_t mask = 0x3ff; | 
|  | if (vmm_apic_get_reg(apic, APIC_LVR) & APIC_LVR_DIRECTED_EOI) | 
|  | mask |= APIC_SPIV_DIRECTED_EOI; | 
|  | apic_set_spiv(apic, val & mask); | 
|  | if (!(val & APIC_SPIV_APIC_ENABLED)) { | 
|  | int i; | 
|  | uint32_t lvt_val; | 
|  |  | 
|  | for (i = 0; i < APIC_LVT_NUM; i++) { | 
|  | lvt_val = vmm_apic_get_reg(apic, | 
|  | APIC_LVTT + 0x10 * i); | 
|  | apic_set_reg(apic, APIC_LVTT + 0x10 * i, | 
|  | lvt_val | APIC_LVT_MASKED); | 
|  | } | 
|  | //    atomic_set(&apic->lapic_timer.pending, 0); | 
|  |  | 
|  | } | 
|  | break; | 
|  | } | 
|  | case APIC_ICR: | 
|  | /* No delay here, so we always clear the pending bit */ | 
|  | apic_set_reg(apic, APIC_ICR, val & ~(BIT(12))); | 
|  | apic_send_ipi(vcpu); | 
|  | break; | 
|  |  | 
|  | case APIC_ICR2: | 
|  | val &= 0xff000000; | 
|  | apic_set_reg(apic, APIC_ICR2, val); | 
|  | break; | 
|  |  | 
|  | case APIC_LVT0: | 
|  | apic_manage_nmi_watchdog(apic, val); | 
|  | case APIC_LVTTHMR: | 
|  | case APIC_LVTPC: | 
|  | case APIC_LVT1: | 
|  | case APIC_LVTERR: | 
|  | /* TODO: Check vector */ | 
|  | if (!vmm_apic_sw_enabled(apic)) | 
|  | val |= APIC_LVT_MASKED; | 
|  |  | 
|  | val &= apic_lvt_mask[(reg - APIC_LVTT) >> 4]; | 
|  | apic_set_reg(apic, reg, val); | 
|  |  | 
|  | break; | 
|  |  | 
|  | case APIC_LVTT: | 
|  | apic_set_reg(apic, APIC_LVTT, val); | 
|  | break; | 
|  |  | 
|  | case APIC_TMICT: | 
|  | apic_set_reg(apic, APIC_TMICT, val); | 
|  | break; | 
|  |  | 
|  | case APIC_TDCR: | 
|  | apic_set_reg(apic, APIC_TDCR, val); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | ret = 1; | 
|  | break; | 
|  | } | 
|  | if (ret) | 
|  | apic_debug(2, "Local APIC Write to read-only register %x\n", reg); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | void vmm_apic_mmio_write(vmm_vcpu_t *vcpu, void *cookie, uint32_t offset, | 
|  | int len, const uint32_t data) | 
|  | { | 
|  | (void)cookie; | 
|  |  | 
|  | /* | 
|  | * 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 || (offset & 0xf)) { | 
|  | apic_debug(1, "apic write: bad size=%d %x\n", len, offset); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* too common printing */ | 
|  | if (offset != APIC_EOI) | 
|  | apic_debug(6, "lapic mmio write at %s: offset 0x%x with length 0x%x, and value is " | 
|  | "0x%x\n", __func__, offset, len, data); | 
|  |  | 
|  | apic_reg_write(vcpu, offset & 0xff0, data); | 
|  | } | 
|  |  | 
|  | static int apic_reg_read(vmm_lapic_t *apic, uint32_t offset, int len, | 
|  | void *data) | 
|  | { | 
|  | unsigned char alignment = offset & 0xf; | 
|  | uint32_t result; | 
|  | /* this bitmask has a bit cleared for each reserved register */ | 
|  | static const uint64_t rmask = 0x43ff01ffffffe70cULL; | 
|  |  | 
|  | if ((alignment + len) > 4) { | 
|  | apic_debug(2, "APIC READ: alignment error %x %d\n", | 
|  | offset, len); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | if (offset > 0x3f0 || !(rmask & (1ULL << (offset >> 4)))) { | 
|  | apic_debug(2, "APIC_READ: read reserved register %x\n", | 
|  | offset); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | result = __apic_read(apic, offset & ~0xf); | 
|  |  | 
|  | switch (len) { | 
|  | case 1: | 
|  | case 2: | 
|  | case 4: | 
|  | memcpy(data, (char *)&result + alignment, len); | 
|  | break; | 
|  | default: | 
|  | apic_debug(2, "Local APIC read with len = %x, " | 
|  | "should be 1,2, or 4 instead\n", len); | 
|  | break; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void vmm_apic_mmio_read(vmm_vcpu_t *vcpu, void *cookie, uint32_t offset, | 
|  | int len, uint32_t *data) | 
|  | { | 
|  | vmm_lapic_t *apic = vcpu->lapic; | 
|  | (void)cookie; | 
|  |  | 
|  | apic_reg_read(apic, offset, len, data); | 
|  |  | 
|  | apic_debug(6, "lapic mmio read on vcpu %d, reg %08x = %08x\n", vcpu->vcpu_id, offset, *data); | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | void vmm_free_lapic(vmm_vcpu_t *vcpu) | 
|  | { | 
|  | vmm_lapic_t *apic = vcpu->lapic; | 
|  |  | 
|  | if (!apic) | 
|  | return; | 
|  |  | 
|  | if (apic->regs) { | 
|  | free(apic->regs); | 
|  | } | 
|  |  | 
|  | free(apic); | 
|  | } | 
|  |  | 
|  | void vmm_lapic_set_base_msr(vmm_vcpu_t *vcpu, uint32_t value) | 
|  | { | 
|  | apic_debug(2, "IA32_APIC_BASE MSR set to %08x on vcpu %d\n", value, vcpu->vcpu_id); | 
|  |  | 
|  | if (!(value & MSR_IA32_APICBASE_ENABLE)) { | 
|  | printf("Warning! Local apic has been disabled by MSR on vcpu %d. " | 
|  | "This will probably not work!\n", vcpu->vcpu_id); | 
|  | } | 
|  |  | 
|  | vcpu->lapic->apic_base = value; | 
|  | } | 
|  |  | 
|  | uint32_t vmm_lapic_get_base_msr(vmm_vcpu_t *vcpu) | 
|  | { | 
|  | uint32_t value = vcpu->lapic->apic_base; | 
|  |  | 
|  | if (vmm_vcpu_is_bsp(vcpu)) { | 
|  | value |= MSR_IA32_APICBASE_BSP; | 
|  | } else { | 
|  | value &= ~MSR_IA32_APICBASE_BSP; | 
|  | } | 
|  |  | 
|  | apic_debug(2, "Read from IA32_APIC_BASE MSR returns %08x on vcpu %d\n", value, vcpu->vcpu_id); | 
|  |  | 
|  | return value; | 
|  | } | 
|  |  | 
|  | void vmm_lapic_reset(vmm_vcpu_t *vcpu) | 
|  | { | 
|  | vmm_lapic_t *apic; | 
|  | int i; | 
|  |  | 
|  | apic_debug(4, "%s\n", __func__); | 
|  |  | 
|  | assert(vcpu); | 
|  | apic = vcpu->lapic; | 
|  | assert(apic != NULL); | 
|  |  | 
|  | /* Stop the timer in case it's a reset to an active apic */ | 
|  |  | 
|  | vmm_apic_set_id(apic, vcpu->vcpu_id); /* In agreement with ACPI code */ | 
|  | 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_DFR, 0xffffffffU); | 
|  | apic_set_spiv(apic, 0xff); | 
|  | apic_set_reg(apic, APIC_TASKPRI, 0); | 
|  | vmm_apic_set_ldr(apic, 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); | 
|  | } | 
|  | apic->irr_pending = 0; | 
|  | apic->isr_count = 0; | 
|  | apic->highest_isr_cache = -1; | 
|  | apic_update_ppr(vcpu); | 
|  |  | 
|  | vcpu->lapic->arb_prio = 0; | 
|  |  | 
|  | apic_debug(4, "%s: vcpu=%p, id=%d, base_msr=" | 
|  | "0x%016x\n", __func__, | 
|  | vcpu, vmm_apic_id(apic), | 
|  | apic->apic_base); | 
|  |  | 
|  | if (vcpu->vcpu_id == BOOT_VCPU) { | 
|  | /* Bootstrap boot vcpu lapic in virtual wire mode */ | 
|  | apic_set_reg(apic, APIC_LVT0, | 
|  | SET_APIC_DELIVERY_MODE(0, APIC_MODE_EXTINT)); | 
|  | apic_set_reg(apic, APIC_SPIV, APIC_SPIV_APIC_ENABLED); | 
|  |  | 
|  | assert(vmm_apic_sw_enabled(apic)); | 
|  | } else { | 
|  | apic_set_reg(apic, APIC_SPIV, 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | int vmm_create_lapic(vmm_vcpu_t *vcpu, int enabled) | 
|  | { | 
|  | vmm_lapic_t *apic; | 
|  |  | 
|  | assert(vcpu != NULL); | 
|  | apic_debug(2, "apic_init %d\n", vcpu->vcpu_id); | 
|  |  | 
|  | apic = malloc(sizeof(*apic)); | 
|  | if (!apic) | 
|  | goto nomem; | 
|  |  | 
|  | vcpu->lapic = apic; | 
|  |  | 
|  | apic->regs = malloc(sizeof(struct local_apic_regs)); // TODO this is a page; allocate a page | 
|  | if (!apic->regs) { | 
|  | printf("malloc apic regs error for vcpu %x\n", | 
|  | vcpu->vcpu_id); | 
|  | goto nomem_free_apic; | 
|  | } | 
|  |  | 
|  | if (enabled) { | 
|  | vmm_lapic_set_base_msr(vcpu, APIC_DEFAULT_PHYS_BASE | MSR_IA32_APICBASE_ENABLE); | 
|  | } else { | 
|  | vmm_lapic_set_base_msr(vcpu, APIC_DEFAULT_PHYS_BASE); | 
|  | } | 
|  |  | 
|  | /* mainly init registers */ | 
|  | vmm_lapic_reset(vcpu); | 
|  |  | 
|  | return 0; | 
|  | nomem_free_apic: | 
|  | free(apic); | 
|  | nomem: | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* Return 1 if this vcpu should accept a PIC interrupt */ | 
|  | int vmm_apic_accept_pic_intr(vmm_vcpu_t *vcpu) | 
|  | { | 
|  | vmm_lapic_t *apic = vcpu->lapic; | 
|  | uint32_t lvt0 = vmm_apic_get_reg(apic, APIC_LVT0); | 
|  |  | 
|  | return ((lvt0 & APIC_LVT_MASKED) == 0 && | 
|  | GET_APIC_DELIVERY_MODE(lvt0) == APIC_MODE_EXTINT && | 
|  | vmm_apic_sw_enabled(vcpu->lapic)); | 
|  | } | 
|  |  | 
|  | /* Service an interrupt */ | 
|  | int vmm_apic_get_interrupt(vmm_vcpu_t *vcpu) | 
|  | { | 
|  | vmm_lapic_t *apic = vcpu->lapic; | 
|  | int vector = vmm_apic_has_interrupt(vcpu); | 
|  |  | 
|  | if (vector == 1) { | 
|  | return pic_get_interrupt(vcpu->vmm); | 
|  | } else if (vector == -1) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | apic_set_isr(vector, apic); | 
|  | apic_update_ppr(vcpu); | 
|  | apic_clear_irr(vector, apic); | 
|  | return vector; | 
|  | } | 
|  |  | 
|  | /* Return which vector is next up for servicing */ | 
|  | int vmm_apic_has_interrupt(vmm_vcpu_t *vcpu) | 
|  | { | 
|  | vmm_lapic_t *apic = vcpu->lapic; | 
|  | int highest_irr; | 
|  |  | 
|  | if (vmm_apic_accept_pic_intr(vcpu) && pic_has_interrupt(vcpu->vmm)) { | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | highest_irr = apic_find_highest_irr(apic); | 
|  | if ((highest_irr == -1) || | 
|  | ((highest_irr & 0xF0) <= vmm_apic_get_reg(apic, APIC_PROCPRI))) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return highest_irr; | 
|  | } | 
|  |  | 
|  | #if 0 | 
|  | int vmm_apic_local_deliver(vmm_vcpu_t *vcpu, int lvt_type) | 
|  | { | 
|  | vmm_lapic_t *apic = vcpu->lapic; | 
|  | uint32_t reg = vmm_apic_get_reg(apic, lvt_type); | 
|  | int vector, mode, trig_mode; | 
|  |  | 
|  | if (!(reg & APIC_LVT_MASKED)) { | 
|  | vector = reg & APIC_VECTOR_MASK; | 
|  | mode = reg & APIC_MODE_MASK; | 
|  | trig_mode = reg & APIC_LVT_LEVEL_TRIGGER; | 
|  | return __apic_accept_irq(vcpu, mode, vector, 1, trig_mode, NULL); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  | #endif |