/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2014 Josef 'Jeff' Sipek * Copyright (c) 2014 by Delphix. All rights reserved. * Copyright 2018 Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* Function prototypes of X2APIC */ static uint64_t local_x2apic_read(uint32_t msr); static void local_x2apic_write(uint32_t msr, uint64_t value); static int get_local_x2apic_pri(void); static void local_x2apic_write_task_reg(uint64_t value); static void local_x2apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1); /* * According to the X2APIC specification: * * xAPIC global enable X2APIC enable Description * (IA32_APIC_BASE[11]) (IA32_APIC_BASE[10]) * ----------------------------------------------------------- * 0 0 APIC is disabled * 0 1 Invalid * 1 0 APIC is enabled in xAPIC mode * 1 1 APIC is enabled in X2APIC mode * ----------------------------------------------------------- */ int x2apic_enable = 1; /* X2APIC : Uses RDMSR/WRMSR instructions to access APIC registers */ static apic_reg_ops_t x2apic_regs_ops = { local_x2apic_read, local_x2apic_write, get_local_x2apic_pri, local_x2apic_write_task_reg, local_x2apic_write_int_cmd, apic_send_EOI, }; /* * X2APIC Implementation. */ static uint64_t local_x2apic_read(uint32_t msr) { uint64_t i; i = (uint64_t)(rdmsr(REG_X2APIC_BASE_MSR + (msr >> 2)) & 0xffffffff); return (i); } static void local_x2apic_write(uint32_t msr, uint64_t value) { uint64_t tmp; if (msr != APIC_EOI_REG) { tmp = rdmsr(REG_X2APIC_BASE_MSR + (msr >> 2)); tmp = (tmp & 0xffffffff00000000) | value; } else { tmp = 0; } wrmsr((REG_X2APIC_BASE_MSR + (msr >> 2)), tmp); } static int get_local_x2apic_pri(void) { return (rdmsr(REG_X2APIC_BASE_MSR + (APIC_TASK_REG >> 2))); } static void local_x2apic_write_task_reg(uint64_t value) { X2APIC_WRITE(APIC_TASK_REG, value); } static void local_x2apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1) { wrmsr((REG_X2APIC_BASE_MSR + (APIC_INT_CMD1 >> 2)), (((uint64_t)cpu_id << 32) | cmd1)); } int apic_detect_x2apic(void) { if (x2apic_enable == 0) return (0); return (is_x86_feature(x86_featureset, X86FSET_X2APIC)); } void apic_enable_x2apic(void) { uint64_t apic_base_msr; if (apic_local_mode() == LOCAL_X2APIC) { /* BIOS apparently has enabled X2APIC */ if (apic_mode != LOCAL_X2APIC) x2apic_update_psm(); return; } /* * This is the first time we are enabling X2APIC on this CPU */ apic_base_msr = rdmsr(REG_APIC_BASE_MSR); apic_base_msr = apic_base_msr | (0x1 << X2APIC_ENABLE_BIT); wrmsr(REG_APIC_BASE_MSR, apic_base_msr); if (apic_mode != LOCAL_X2APIC) x2apic_update_psm(); } /* * Change apic_reg_ops depending upon the apic_mode. */ void apic_change_ops() { if (apic_mode == LOCAL_APIC) apic_reg_ops = &local_apic_regs_ops; else if (apic_mode == LOCAL_X2APIC) apic_reg_ops = &x2apic_regs_ops; } /* * Generates an interprocessor interrupt to another CPU when X2APIC mode is * enabled. */ void x2apic_send_ipi(int cpun, int ipl) { int vector; ulong_t flag; ASSERT(apic_mode == LOCAL_X2APIC); /* * With X2APIC, Intel relaxed the semantics of the * WRMSR instruction such that references to the X2APIC * MSR registers are no longer serializing instructions. * The code that initiates IPIs assumes that some sort * of memory serialization occurs. The old APIC code * did a write to uncachable memory mapped registers. * Any reference to uncached memory is a serializing * operation. To mimic those semantics here, we do an * atomic operation, which translates to a LOCK OR instruction, * which is serializing. */ atomic_or_ulong(&flag, 1); vector = apic_resv_vector[ipl]; flag = intr_clear(); /* * According to X2APIC specification in section '2.3.5.1' of * Interrupt Command Register Semantics, the semantics of * programming Interrupt Command Register to dispatch an interrupt * is simplified. A single MSR write to the 64-bit ICR is required * for dispatching an interrupt. Specifically with the 64-bit MSR * interface to ICR, system software is not required to check the * status of the delivery status bit prior to writing to the ICR * to send an IPI. With the removal of the Delivery Status bit, * system software no longer has a reason to read the ICR. It remains * readable only to aid in debugging. */ #ifdef DEBUG APIC_AV_PENDING_SET(); #endif /* DEBUG */ if ((cpun == psm_get_cpu_id())) { X2APIC_WRITE(X2APIC_SELF_IPI, vector); } else { apic_reg_ops->apic_write_int_cmd( apic_cpus[cpun].aci_local_id, vector); } intr_restore(flag); } void x2apic_send_pir_ipi(processorid_t cpun) { const int vector = apic_pir_vect; ulong_t flag; ASSERT(apic_mode == LOCAL_X2APIC); ASSERT((vector >= APIC_BASE_VECT) && (vector <= APIC_SPUR_INTR)); /* Serialize as described in x2apic_send_ipi() above. */ atomic_or_ulong(&flag, 1); flag = intr_clear(); /* Self-IPI for inducing PIR makes no sense. */ if ((cpun != psm_get_cpu_id())) { #ifdef DEBUG /* Only for debugging. (again, see: x2apic_send_ipi) */ APIC_AV_PENDING_SET(); #endif /* DEBUG */ apic_reg_ops->apic_write_int_cmd(apic_cpus[cpun].aci_local_id, vector); } intr_restore(flag); } /* * Generates IPI to another CPU depending on the local APIC mode. * apic_send_ipi() and x2apic_send_ipi() depends on the configured * mode of the local APIC, but that may not match the actual mode * early in CPU startup. * * Any changes made to this routine must be accompanied by similar * changes to apic_send_ipi(). */ void apic_common_send_ipi(int cpun, int ipl) { int vector; ulong_t flag; int mode = apic_local_mode(); if (mode == LOCAL_X2APIC) { x2apic_send_ipi(cpun, ipl); return; } ASSERT(mode == LOCAL_APIC); vector = apic_resv_vector[ipl]; ASSERT((vector >= APIC_BASE_VECT) && (vector <= APIC_SPUR_INTR)); flag = intr_clear(); while (local_apic_regs_ops.apic_read(APIC_INT_CMD1) & AV_PENDING) apic_ret(); local_apic_regs_ops.apic_write_int_cmd(apic_cpus[cpun].aci_local_id, vector); intr_restore(flag); } void apic_common_send_pir_ipi(processorid_t cpun) { const int mode = apic_local_mode(); if (mode == LOCAL_X2APIC) { x2apic_send_pir_ipi(cpun); return; } apic_send_pir_ipi(cpun); }