root/usr/src/uts/i86pc/io/apix/apix_regops.c
/*
 * 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 <jeffpc@josefsipek.net>
 * Copyright (c) 2014 by Delphix. All rights reserved.
 * Copyright 2018 Joyent, Inc.
 */

#include <sys/cpuvar.h>
#include <sys/psm.h>
#include <sys/archsystm.h>
#include <sys/apic.h>
#include <sys/apic_common.h>
#include <sys/sunddi.h>
#include <sys/ddi_impldefs.h>
#include <sys/mach_intr.h>
#include <sys/sysmacros.h>
#include <sys/trap.h>
#include <sys/x86_archext.h>
#include <sys/privregs.h>
#include <sys/psm_common.h>

/* 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);
}