X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/b0d623f7f2ae71ed96e60569f61f9a9a27016e80..db6096698656d32db7df630594bd9617ee54f828:/osfmk/i386/lapic.c?ds=sidebyside diff --git a/osfmk/i386/lapic.c b/osfmk/i386/lapic.c index 0206d0986..b365d6070 100644 --- a/osfmk/i386/lapic.c +++ b/osfmk/i386/lapic.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2010 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -32,86 +32,42 @@ #include #include -#include -#include -#include -#include -#include - -#include -#include - #include #include #include #include #include #include -#include #include #include #include -#if CONFIG_MCA -#include -#endif - -#if CONFIG_COUNTERS -#include -#endif - -#if MACH_KDB -#include -#endif +#include #include -#if MP_DEBUG -#define PAUSE delay(1000000) -#define DBG(x...) kprintf(x) -#else -#define DBG(x...) -#define PAUSE -#endif /* MP_DEBUG */ - -/* Initialize lapic_id so cpu_number() works on non SMP systems */ -unsigned long lapic_id_initdata = 0; -unsigned long lapic_id = (unsigned long)&lapic_id_initdata; -vm_offset_t lapic_start; - -static i386_intr_func_t lapic_intr_func[LAPIC_FUNC_TABLE_SIZE]; - -/* TRUE if local APIC was enabled by the OS not by the BIOS */ -static boolean_t lapic_os_enabled = FALSE; - -static boolean_t lapic_errors_masked = FALSE; -static uint64_t lapic_last_master_error = 0; -static uint64_t lapic_error_time_threshold = 0; -static unsigned lapic_master_error_count = 0; -static unsigned lapic_error_count_threshold = 5; -static boolean_t lapic_dont_panic = FALSE; - -extern int debug_boot_arg; - /* Base vector for local APIC interrupt sources */ int lapic_interrupt_base = LAPIC_DEFAULT_INTERRUPT_BASE; -int lapic_to_cpu[MAX_CPUS]; +#define MAX_LAPICIDS (LAPIC_ID_MAX+1) +int lapic_to_cpu[MAX_LAPICIDS]; int cpu_to_lapic[MAX_CPUS]; -static void +void lapic_cpu_map_init(void) { int i; - for (i = 0; i < MAX_CPUS; i++) { - lapic_to_cpu[i] = -1; + for (i = 0; i < MAX_CPUS; i++) cpu_to_lapic[i] = -1; - } + for (i = 0; i < MAX_LAPICIDS; i++) + lapic_to_cpu[i] = -1; } void lapic_cpu_map(int apic_id, int cpu) { + assert(apic_id < MAX_LAPICIDS); + assert(cpu < MAX_CPUS); cpu_to_lapic[cpu] = apic_id; lapic_to_cpu[apic_id] = cpu; } @@ -137,608 +93,10 @@ ml_get_apicid(uint32_t cpu) uint32_t ml_get_cpuid(uint32_t lapic_index) { - if(lapic_index >= (uint32_t)MAX_CPUS) + if(lapic_index >= (uint32_t)MAX_LAPICIDS) return 0xFFFFFFFF; /* Return -1 if cpu too big */ /* Return the cpu ID (or -1 if not configured) */ return (uint32_t)lapic_to_cpu[lapic_index]; } - - -#ifdef MP_DEBUG -static void -lapic_cpu_map_dump(void) -{ - int i; - - for (i = 0; i < MAX_CPUS; i++) { - if (cpu_to_lapic[i] == -1) - continue; - kprintf("cpu_to_lapic[%d]: %d\n", - i, cpu_to_lapic[i]); - } - for (i = 0; i < MAX_CPUS; i++) { - if (lapic_to_cpu[i] == -1) - continue; - kprintf("lapic_to_cpu[%d]: %d\n", - i, lapic_to_cpu[i]); - } -} -#endif /* MP_DEBUG */ - -void -lapic_init(void) -{ - int result; - vm_map_entry_t entry; - uint32_t lo; - uint32_t hi; - boolean_t is_boot_processor; - boolean_t is_lapic_enabled; - vm_offset_t lapic_base; - - /* Examine the local APIC state */ - rdmsr(MSR_IA32_APIC_BASE, lo, hi); - is_boot_processor = (lo & MSR_IA32_APIC_BASE_BSP) != 0; - is_lapic_enabled = (lo & MSR_IA32_APIC_BASE_ENABLE) != 0; - lapic_base = (lo & MSR_IA32_APIC_BASE_BASE); - kprintf("MSR_IA32_APIC_BASE %p %s %s\n", (void *) lapic_base, - is_lapic_enabled ? "enabled" : "disabled", - is_boot_processor ? "BSP" : "AP"); - if (!is_boot_processor || !is_lapic_enabled) - panic("Unexpected local APIC state\n"); - - /* Establish a map to the local apic */ - lapic_start = (vm_offset_t)vm_map_min(kernel_map); - result = vm_map_find_space(kernel_map, - (vm_map_address_t *) &lapic_start, - round_page(LAPIC_SIZE), 0, - VM_MAKE_TAG(VM_MEMORY_IOKIT), &entry); - if (result != KERN_SUCCESS) { - panic("smp_init: vm_map_find_entry FAILED (err=%d)", result); - } - vm_map_unlock(kernel_map); -/* Map in the local APIC non-cacheable, as recommended by Intel - * in section 8.4.1 of the "System Programming Guide". - */ - pmap_enter(pmap_kernel(), - lapic_start, - (ppnum_t) i386_btop(lapic_base), - VM_PROT_READ|VM_PROT_WRITE, - VM_WIMG_IO, - TRUE); - lapic_id = (unsigned long)(lapic_start + LAPIC_ID); - - if ((LAPIC_READ(VERSION)&LAPIC_VERSION_MASK) < 0x14) { - panic("Local APIC version 0x%x, 0x14 or more expected\n", - (LAPIC_READ(VERSION)&LAPIC_VERSION_MASK)); - } - - /* Set up the lapic_id <-> cpu_number map and add this boot processor */ - lapic_cpu_map_init(); - lapic_cpu_map((LAPIC_READ(ID)>>LAPIC_ID_SHIFT)&LAPIC_ID_MASK, 0); - kprintf("Boot cpu local APIC id 0x%x\n", cpu_to_lapic[0]); -} - - -static int -lapic_esr_read(void) -{ - /* write-read register */ - LAPIC_WRITE(ERROR_STATUS, 0); - return LAPIC_READ(ERROR_STATUS); -} - -static void -lapic_esr_clear(void) -{ - LAPIC_WRITE(ERROR_STATUS, 0); - LAPIC_WRITE(ERROR_STATUS, 0); -} - -static const char *DM_str[8] = { - "Fixed", - "Lowest Priority", - "Invalid", - "Invalid", - "NMI", - "Reset", - "Invalid", - "ExtINT"}; - -void -lapic_dump(void) -{ - int i; - -#define BOOL(a) ((a)?' ':'!') -#define VEC(lvt) \ - LAPIC_READ(lvt)&LAPIC_LVT_VECTOR_MASK -#define DS(lvt) \ - (LAPIC_READ(lvt)&LAPIC_LVT_DS_PENDING)?" SendPending" : "Idle" -#define DM(lvt) \ - DM_str[(LAPIC_READ(lvt)>>LAPIC_LVT_DM_SHIFT)&LAPIC_LVT_DM_MASK] -#define MASK(lvt) \ - BOOL(LAPIC_READ(lvt)&LAPIC_LVT_MASKED) -#define TM(lvt) \ - (LAPIC_READ(lvt)&LAPIC_LVT_TM_LEVEL)? "Level" : "Edge" -#define IP(lvt) \ - (LAPIC_READ(lvt)&LAPIC_LVT_IP_PLRITY_LOW)? "Low " : "High" - - kprintf("LAPIC %d at %p version 0x%x\n", - (LAPIC_READ(ID)>>LAPIC_ID_SHIFT)&LAPIC_ID_MASK, - (void *) lapic_start, - LAPIC_READ(VERSION)&LAPIC_VERSION_MASK); - kprintf("Priorities: Task 0x%x Arbitration 0x%x Processor 0x%x\n", - LAPIC_READ(TPR)&LAPIC_TPR_MASK, - LAPIC_READ(APR)&LAPIC_APR_MASK, - LAPIC_READ(PPR)&LAPIC_PPR_MASK); - kprintf("Destination Format 0x%x Logical Destination 0x%x\n", - LAPIC_READ(DFR)>>LAPIC_DFR_SHIFT, - LAPIC_READ(LDR)>>LAPIC_LDR_SHIFT); - kprintf("%cEnabled %cFocusChecking SV 0x%x\n", - BOOL(LAPIC_READ(SVR)&LAPIC_SVR_ENABLE), - BOOL(!(LAPIC_READ(SVR)&LAPIC_SVR_FOCUS_OFF)), - LAPIC_READ(SVR) & LAPIC_SVR_MASK); -#if CONFIG_MCA - if (mca_is_cmci_present()) - kprintf("LVT_CMCI: Vector 0x%02x [%s] %s %cmasked\n", - VEC(LVT_CMCI), - DM(LVT_CMCI), - DS(LVT_CMCI), - MASK(LVT_CMCI)); -#endif - kprintf("LVT_TIMER: Vector 0x%02x %s %cmasked %s\n", - VEC(LVT_TIMER), - DS(LVT_TIMER), - MASK(LVT_TIMER), - (LAPIC_READ(LVT_TIMER)&LAPIC_LVT_PERIODIC)?"Periodic":"OneShot"); - kprintf(" Initial Count: 0x%08x \n", LAPIC_READ(TIMER_INITIAL_COUNT)); - kprintf(" Current Count: 0x%08x \n", LAPIC_READ(TIMER_CURRENT_COUNT)); - kprintf(" Divide Config: 0x%08x \n", LAPIC_READ(TIMER_DIVIDE_CONFIG)); - kprintf("LVT_PERFCNT: Vector 0x%02x [%s] %s %cmasked\n", - VEC(LVT_PERFCNT), - DM(LVT_PERFCNT), - DS(LVT_PERFCNT), - MASK(LVT_PERFCNT)); - kprintf("LVT_THERMAL: Vector 0x%02x [%s] %s %cmasked\n", - VEC(LVT_THERMAL), - DM(LVT_THERMAL), - DS(LVT_THERMAL), - MASK(LVT_THERMAL)); - kprintf("LVT_LINT0: Vector 0x%02x [%s][%s][%s] %s %cmasked\n", - VEC(LVT_LINT0), - DM(LVT_LINT0), - TM(LVT_LINT0), - IP(LVT_LINT0), - DS(LVT_LINT0), - MASK(LVT_LINT0)); - kprintf("LVT_LINT1: Vector 0x%02x [%s][%s][%s] %s %cmasked\n", - VEC(LVT_LINT1), - DM(LVT_LINT1), - TM(LVT_LINT1), - IP(LVT_LINT1), - DS(LVT_LINT1), - MASK(LVT_LINT1)); - kprintf("LVT_ERROR: Vector 0x%02x %s %cmasked\n", - VEC(LVT_ERROR), - DS(LVT_ERROR), - MASK(LVT_ERROR)); - kprintf("ESR: %08x \n", lapic_esr_read()); - kprintf(" "); - for(i=0xf; i>=0; i--) - kprintf("%x%x%x%x",i,i,i,i); - kprintf("\n"); - kprintf("TMR: 0x"); - for(i=7; i>=0; i--) - kprintf("%08x",LAPIC_READ_OFFSET(TMR_BASE, i*0x10)); - kprintf("\n"); - kprintf("IRR: 0x"); - for(i=7; i>=0; i--) - kprintf("%08x",LAPIC_READ_OFFSET(IRR_BASE, i*0x10)); - kprintf("\n"); - kprintf("ISR: 0x"); - for(i=7; i >= 0; i--) - kprintf("%08x",LAPIC_READ_OFFSET(ISR_BASE, i*0x10)); - kprintf("\n"); -} - -#if MACH_KDB -/* - * Displays apic junk - * - * da - */ -void -db_apic(__unused db_expr_t addr, - __unused int have_addr, - __unused db_expr_t count, - __unused char *modif) -{ - - lapic_dump(); - - return; -} - -#endif - -boolean_t -lapic_probe(void) -{ - uint32_t lo; - uint32_t hi; - - if (cpuid_features() & CPUID_FEATURE_APIC) - return TRUE; - - if (cpuid_family() == 6 || cpuid_family() == 15) { - /* - * Mobile Pentiums: - * There may be a local APIC which wasn't enabled by BIOS. - * So we try to enable it explicitly. - */ - rdmsr(MSR_IA32_APIC_BASE, lo, hi); - lo &= ~MSR_IA32_APIC_BASE_BASE; - lo |= MSR_IA32_APIC_BASE_ENABLE | LAPIC_START; - lo |= MSR_IA32_APIC_BASE_ENABLE; - wrmsr(MSR_IA32_APIC_BASE, lo, hi); - - /* - * Re-initialize cpu features info and re-check. - */ - cpuid_set_info(); - if (cpuid_features() & CPUID_FEATURE_APIC) { - printf("Local APIC discovered and enabled\n"); - lapic_os_enabled = TRUE; - lapic_interrupt_base = LAPIC_REDUCED_INTERRUPT_BASE; - return TRUE; - } - } - - return FALSE; -} - -void -lapic_shutdown(void) -{ - uint32_t lo; - uint32_t hi; - uint32_t value; - - /* Shutdown if local APIC was enabled by OS */ - if (lapic_os_enabled == FALSE) - return; - - mp_disable_preemption(); - - /* ExtINT: masked */ - if (get_cpu_number() == master_cpu) { - value = LAPIC_READ(LVT_LINT0); - value |= LAPIC_LVT_MASKED; - LAPIC_WRITE(LVT_LINT0, value); - } - - /* Error: masked */ - LAPIC_WRITE(LVT_ERROR, LAPIC_READ(LVT_ERROR) | LAPIC_LVT_MASKED); - - /* Timer: masked */ - LAPIC_WRITE(LVT_TIMER, LAPIC_READ(LVT_TIMER) | LAPIC_LVT_MASKED); - - /* Perfmon: masked */ - LAPIC_WRITE(LVT_PERFCNT, LAPIC_READ(LVT_PERFCNT) | LAPIC_LVT_MASKED); - - /* APIC software disabled */ - LAPIC_WRITE(SVR, LAPIC_READ(SVR) & ~LAPIC_SVR_ENABLE); - - /* Bypass the APIC completely and update cpu features */ - rdmsr(MSR_IA32_APIC_BASE, lo, hi); - lo &= ~MSR_IA32_APIC_BASE_ENABLE; - wrmsr(MSR_IA32_APIC_BASE, lo, hi); - cpuid_set_info(); - - mp_enable_preemption(); -} - -void -lapic_configure(void) -{ - int value; - - if (lapic_error_time_threshold == 0 && cpu_number() == 0) { - nanoseconds_to_absolutetime(NSEC_PER_SEC >> 2, &lapic_error_time_threshold); - if (!PE_parse_boot_argn("lapic_dont_panic", &lapic_dont_panic, sizeof(lapic_dont_panic))) { - lapic_dont_panic = FALSE; - } - } - - /* Set flat delivery model, logical processor id */ - LAPIC_WRITE(DFR, LAPIC_DFR_FLAT); - LAPIC_WRITE(LDR, (get_cpu_number()) << LAPIC_LDR_SHIFT); - - /* Accept all */ - LAPIC_WRITE(TPR, 0); - - LAPIC_WRITE(SVR, LAPIC_VECTOR(SPURIOUS) | LAPIC_SVR_ENABLE); - - /* ExtINT */ - if (get_cpu_number() == master_cpu) { - value = LAPIC_READ(LVT_LINT0); - value &= ~LAPIC_LVT_MASKED; - value |= LAPIC_LVT_DM_EXTINT; - LAPIC_WRITE(LVT_LINT0, value); - } - - /* Timer: unmasked, one-shot */ - LAPIC_WRITE(LVT_TIMER, LAPIC_VECTOR(TIMER)); - - /* Perfmon: unmasked */ - LAPIC_WRITE(LVT_PERFCNT, LAPIC_VECTOR(PERFCNT)); - - /* Thermal: unmasked */ - LAPIC_WRITE(LVT_THERMAL, LAPIC_VECTOR(THERMAL)); - -#if CONFIG_MCA - /* CMCI, if available */ - if (mca_is_cmci_present()) - LAPIC_WRITE(LVT_CMCI, LAPIC_VECTOR(CMCI)); -#endif - - if (((cpu_number() == master_cpu) && lapic_errors_masked == FALSE) || - (cpu_number() != master_cpu)) { - lapic_esr_clear(); - LAPIC_WRITE(LVT_ERROR, LAPIC_VECTOR(ERROR)); - } -} - -void -lapic_set_timer( - boolean_t interrupt_unmasked, - lapic_timer_mode_t mode, - lapic_timer_divide_t divisor, - lapic_timer_count_t initial_count) -{ - boolean_t state; - uint32_t timer_vector; - - state = ml_set_interrupts_enabled(FALSE); - timer_vector = LAPIC_READ(LVT_TIMER); - timer_vector &= ~(LAPIC_LVT_MASKED|LAPIC_LVT_PERIODIC);; - timer_vector |= interrupt_unmasked ? 0 : LAPIC_LVT_MASKED; - timer_vector |= (mode == periodic) ? LAPIC_LVT_PERIODIC : 0; - LAPIC_WRITE(LVT_TIMER, timer_vector); - LAPIC_WRITE(TIMER_DIVIDE_CONFIG, divisor); - LAPIC_WRITE(TIMER_INITIAL_COUNT, initial_count); - ml_set_interrupts_enabled(state); -} - -void -lapic_get_timer( - lapic_timer_mode_t *mode, - lapic_timer_divide_t *divisor, - lapic_timer_count_t *initial_count, - lapic_timer_count_t *current_count) -{ - boolean_t state; - - state = ml_set_interrupts_enabled(FALSE); - if (mode) - *mode = (LAPIC_READ(LVT_TIMER) & LAPIC_LVT_PERIODIC) ? - periodic : one_shot; - if (divisor) - *divisor = LAPIC_READ(TIMER_DIVIDE_CONFIG) & LAPIC_TIMER_DIVIDE_MASK; - if (initial_count) - *initial_count = LAPIC_READ(TIMER_INITIAL_COUNT); - if (current_count) - *current_count = LAPIC_READ(TIMER_CURRENT_COUNT); - ml_set_interrupts_enabled(state); -} - -static inline void -_lapic_end_of_interrupt(void) -{ - LAPIC_WRITE(EOI, 0); -} - -void -lapic_end_of_interrupt(void) -{ - _lapic_end_of_interrupt(); -} - -void lapic_unmask_perfcnt_interrupt(void) { - LAPIC_WRITE(LVT_PERFCNT, LAPIC_VECTOR(PERFCNT)); -} - -void -lapic_set_intr_func(int vector, i386_intr_func_t func) -{ - if (vector > lapic_interrupt_base) - vector -= lapic_interrupt_base; - - switch (vector) { - case LAPIC_NMI_INTERRUPT: - case LAPIC_INTERPROCESSOR_INTERRUPT: - case LAPIC_TIMER_INTERRUPT: - case LAPIC_THERMAL_INTERRUPT: - case LAPIC_PERFCNT_INTERRUPT: - case LAPIC_CMCI_INTERRUPT: - case LAPIC_PM_INTERRUPT: - lapic_intr_func[vector] = func; - break; - default: - panic("lapic_set_intr_func(%d,%p) invalid vector\n", - vector, func); - } -} - -int -lapic_interrupt(int interrupt_num, x86_saved_state_t *state) -{ - int retval = 0; - int esr = -1; - - interrupt_num -= lapic_interrupt_base; - if (interrupt_num < 0) { - if (interrupt_num == (LAPIC_NMI_INTERRUPT - lapic_interrupt_base) && - lapic_intr_func[LAPIC_NMI_INTERRUPT] != NULL) { - retval = (*lapic_intr_func[LAPIC_NMI_INTERRUPT])(state); - _lapic_end_of_interrupt(); - return retval; - } - else - return 0; - } - - switch(interrupt_num) { - case LAPIC_TIMER_INTERRUPT: - case LAPIC_THERMAL_INTERRUPT: - case LAPIC_INTERPROCESSOR_INTERRUPT: - case LAPIC_PM_INTERRUPT: - if (lapic_intr_func[interrupt_num] != NULL) - (void) (*lapic_intr_func[interrupt_num])(state); - _lapic_end_of_interrupt(); - retval = 1; - break; - case LAPIC_PERFCNT_INTERRUPT: - /* If a function has been registered, invoke it. Otherwise, - * pass up to IOKit. - */ - if (lapic_intr_func[interrupt_num] != NULL) { - (void) (*lapic_intr_func[interrupt_num])(state); - /* Unmask the interrupt since we don't expect legacy users - * to be responsible for it. - */ - lapic_unmask_perfcnt_interrupt(); - _lapic_end_of_interrupt(); - retval = 1; - } - break; - case LAPIC_CMCI_INTERRUPT: - if (lapic_intr_func[interrupt_num] != NULL) - (void) (*lapic_intr_func[interrupt_num])(state); - /* return 0 for plaform expert to handle */ - break; - case LAPIC_ERROR_INTERRUPT: - /* We treat error interrupts on APs as fatal. - * The current interrupt steering scheme directs most - * external interrupts to the BSP (HPET interrupts being - * a notable exception); hence, such an error - * on an AP may signify LVT corruption (with "may" being - * the operative word). On the BSP, we adopt a more - * lenient approach, in the interests of enhancing - * debuggability and reducing fragility. - * If "lapic_error_count_threshold" error interrupts - * occur within "lapic_error_time_threshold" absolute - * time units, we mask the error vector and log. The - * error interrupts themselves are likely - * side effects of issues which are beyond the purview of - * the local APIC interrupt handler, however. The Error - * Status Register value (the illegal destination - * vector code is one observed in practice) indicates - * the immediate cause of the error. - */ - esr = lapic_esr_read(); - lapic_dump(); - - if ((debug_boot_arg && (lapic_dont_panic == FALSE)) || - cpu_number() != master_cpu) { - panic("Local APIC error, ESR: %d\n", esr); - } - - if (cpu_number() == master_cpu) { - uint64_t abstime = mach_absolute_time(); - if ((abstime - lapic_last_master_error) < lapic_error_time_threshold) { - if (lapic_master_error_count++ > lapic_error_count_threshold) { - lapic_errors_masked = TRUE; - LAPIC_WRITE(LVT_ERROR, LAPIC_READ(LVT_ERROR) | LAPIC_LVT_MASKED); - printf("Local APIC: errors masked\n"); - } - } - else { - lapic_last_master_error = abstime; - lapic_master_error_count = 0; - } - printf("Local APIC error on master CPU, ESR: %d, error count this run: %d\n", esr, lapic_master_error_count); - } - - _lapic_end_of_interrupt(); - retval = 1; - break; - case LAPIC_SPURIOUS_INTERRUPT: - kprintf("SPIV\n"); - /* No EOI required here */ - retval = 1; - break; - case LAPIC_PMC_SW_INTERRUPT: - { -#if CONFIG_COUNTERS - thread_t old, new; - ml_get_csw_threads(&old, &new); - - if (pmc_context_switch(old, new) == TRUE) { - retval = 1; - /* No EOI required for SWI */ - } -#endif /* CONFIG_COUNTERS */ - } - break; - } - - return retval; -} - -void -lapic_smm_restore(void) -{ - boolean_t state; - - if (lapic_os_enabled == FALSE) - return; - - state = ml_set_interrupts_enabled(FALSE); - - if (LAPIC_ISR_IS_SET(LAPIC_REDUCED_INTERRUPT_BASE, TIMER)) { - /* - * Bogus SMI handler enables interrupts but does not know about - * local APIC interrupt sources. When APIC timer counts down to - * zero while in SMM, local APIC will end up waiting for an EOI - * but no interrupt was delivered to the OS. - */ - _lapic_end_of_interrupt(); - - /* - * timer is one-shot, trigger another quick countdown to trigger - * another timer interrupt. - */ - if (LAPIC_READ(TIMER_CURRENT_COUNT) == 0) { - LAPIC_WRITE(TIMER_INITIAL_COUNT, 1); - } - - kprintf("lapic_smm_restore\n"); - } - - ml_set_interrupts_enabled(state); -} - -void -lapic_send_ipi(int cpu, int vector) -{ - boolean_t state; - - if (vector < lapic_interrupt_base) - vector += lapic_interrupt_base; - - state = ml_set_interrupts_enabled(FALSE); - - /* Wait for pending outgoing send to complete */ - while (LAPIC_READ(ICR) & LAPIC_ICR_DS_PENDING) { - cpu_pause(); - } - - LAPIC_WRITE(ICRD, cpu_to_lapic[cpu] << LAPIC_ICRD_DEST_SHIFT); - LAPIC_WRITE(ICR, vector | LAPIC_ICR_DM_FIXED); - - (void) ml_set_interrupts_enabled(state); -}