diff options
author | Hans Rosenfeld <hans.rosenfeld@joyent.com> | 2017-04-26 14:02:21 +0200 |
---|---|---|
committer | Hans Rosenfeld <hans.rosenfeld@joyent.com> | 2017-05-08 08:54:38 +0000 |
commit | 56f03ffa93d78b09f5d08328b97ee73234660f22 (patch) | |
tree | 7ddbdf5ece5aa89d7345cce24f2401e799dad9be | |
parent | 6a968985a46f0a8d77b8c029edb43dfd4a44ae66 (diff) | |
download | illumos-joyent-56f03ffa93d78b09f5d08328b97ee73234660f22.tar.gz |
OS-6085 pcplusmp shouldn't support x2APIC mode
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Approved by: Jerry Jelinek <jerry.jelinek@joyent.com>
-rw-r--r-- | usr/src/uts/i86pc/Makefile.files | 4 | ||||
-rw-r--r-- | usr/src/uts/i86pc/apix/Makefile | 6 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/apix/apix.c | 42 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/apix/apix_regops.c | 252 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/pcplusmp/apic.c | 132 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/pcplusmp/apic_common.c | 43 | ||||
-rw-r--r-- | usr/src/uts/i86pc/io/pcplusmp/apic_regops.c | 201 | ||||
-rw-r--r-- | usr/src/uts/i86pc/sys/apic.h | 6 |
8 files changed, 341 insertions, 345 deletions
diff --git a/usr/src/uts/i86pc/Makefile.files b/usr/src/uts/i86pc/Makefile.files index f3f10e1b6c..26d7f3f039 100644 --- a/usr/src/uts/i86pc/Makefile.files +++ b/usr/src/uts/i86pc/Makefile.files @@ -23,7 +23,7 @@ # Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved. # # Copyright (c) 2010, Intel Corporation. -# Copyright 2016 Joyent, Inc. +# Copyright 2017 Joyent, Inc. # # This Makefile defines file modules in the directory uts/i86pc # and its children. These are the source files which are i86pc @@ -200,7 +200,7 @@ PCPLUSMP_OBJS += apic.o apic_regops.o psm_common.o apic_introp.o \ hpet_acpi.o apic_common.o apic_timer.o APIX_OBJS += apix.o apic_regops.o psm_common.o apix_intr.o apix_utils.o \ apix_irm.o mp_platform_common.o hpet_acpi.o apic_common.o \ - apic_timer.o + apic_timer.o apix_regops.o ACPI_DRV_OBJS += acpi_drv.o acpi_video.o diff --git a/usr/src/uts/i86pc/apix/Makefile b/usr/src/uts/i86pc/apix/Makefile index 18100b7ced..c562707a10 100644 --- a/usr/src/uts/i86pc/apix/Makefile +++ b/usr/src/uts/i86pc/apix/Makefile @@ -22,12 +22,12 @@ # uts/i86pc/apix/Makefile # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright 2016, Joyent, Inc. +# Copyright 2017, Joyent, Inc. # -# This makefile drives the production of the pcplusmp "mach" +# This makefile drives the production of the apix "mach" # kernel module. # -# pcplusmp implementation architecture dependent +# apix implementation architecture dependent # # diff --git a/usr/src/uts/i86pc/io/apix/apix.c b/usr/src/uts/i86pc/io/apix/apix.c index 9bacbd7c5e..85890a1db6 100644 --- a/usr/src/uts/i86pc/io/apix/apix.c +++ b/usr/src/uts/i86pc/io/apix/apix.c @@ -221,6 +221,9 @@ int apix_nipis = 16; /* Maximum number of IPIs */ */ int apix_cpu_nvectors = APIX_NVECTOR; +/* number of CPUs in power-on transition state */ +static int apic_poweron_cnt = 0; + /* gcpu.h */ extern void apic_do_interrupt(struct regs *rp, trap_trace_rec_t *ttp); @@ -2560,6 +2563,45 @@ apix_intx_xlate_vector(dev_info_t *dip, int inum, struct intrspec *ispec) return (vecp); } +/* + * Switch between safe and x2APIC IPI sending method. + * The CPU may power on in xapic mode or x2apic mode. If the CPU needs to send + * an IPI to other CPUs before entering x2APIC mode, it still needs to use the + * xAPIC method. Before sending a StartIPI to the target CPU, psm_send_ipi will + * be changed to apic_common_send_ipi, which detects current local APIC mode and + * use the right method to send an IPI. If some CPUs fail to start up, + * apic_poweron_cnt won't return to zero, so apic_common_send_ipi will always be + * used. psm_send_ipi can't be simply changed back to x2apic_send_ipi if some + * CPUs failed to start up because those failed CPUs may recover itself later at + * unpredictable time. + */ +void +apic_switch_ipi_callback(boolean_t enter) +{ + ulong_t iflag; + struct psm_ops *pops = psmops; + + iflag = intr_clear(); + lock_set(&apic_mode_switch_lock); + if (enter) { + ASSERT(apic_poweron_cnt >= 0); + if (apic_poweron_cnt == 0) { + pops->psm_send_ipi = apic_common_send_ipi; + send_dirintf = pops->psm_send_ipi; + } + apic_poweron_cnt++; + } else { + ASSERT(apic_poweron_cnt > 0); + apic_poweron_cnt--; + if (apic_poweron_cnt == 0) { + pops->psm_send_ipi = x2apic_send_ipi; + send_dirintf = pops->psm_send_ipi; + } + } + lock_clear(&apic_mode_switch_lock); + intr_restore(iflag); +} + /* stub function */ int apix_loaded(void) diff --git a/usr/src/uts/i86pc/io/apix/apix_regops.c b/usr/src/uts/i86pc/io/apix/apix_regops.c new file mode 100644 index 0000000000..1432b66a81 --- /dev/null +++ b/usr/src/uts/i86pc/io/apix/apix_regops.c @@ -0,0 +1,252 @@ +/* + * 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 2017 Joyent, Inc. + */ + +#include <sys/cpuvar.h> +#include <sys/psm.h> +#include <sys/archsystm.h> +#include <sys/apic.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); +} + +/* + * 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); +} diff --git a/usr/src/uts/i86pc/io/pcplusmp/apic.c b/usr/src/uts/i86pc/io/pcplusmp/apic.c index cd06f76967..8523112173 100644 --- a/usr/src/uts/i86pc/io/pcplusmp/apic.c +++ b/usr/src/uts/i86pc/io/pcplusmp/apic.c @@ -100,7 +100,6 @@ static void apic_picinit(void); static int apic_post_cpu_start(void); static int apic_intr_enter(int ipl, int *vect); static void apic_setspl(int ipl); -static void x2apic_setspl(int ipl); static int apic_addspl(int ipl, int vector, int min_ipl, int max_ipl); static int apic_delspl(int ipl, int vector, int min_ipl, int max_ipl); static int apic_disable_intr(processorid_t cpun); @@ -312,22 +311,19 @@ apic_init_intr(void) apic_reg_ops->apic_write_task_reg(APIC_MASK_ALL); - if (apic_mode == LOCAL_APIC) { - /* - * We are running APIC in MMIO mode. - */ - if (apic_flat_model) { - apic_reg_ops->apic_write(APIC_FORMAT_REG, - APIC_FLAT_MODEL); - } else { - apic_reg_ops->apic_write(APIC_FORMAT_REG, - APIC_CLUSTER_MODEL); - } + ASSERT(apic_mode == LOCAL_APIC); - apic_reg_ops->apic_write(APIC_DEST_REG, - AV_HIGH_ORDER >> cpun); + /* + * We are running APIC in MMIO mode. + */ + if (apic_flat_model) { + apic_reg_ops->apic_write(APIC_FORMAT_REG, APIC_FLAT_MODEL); + } else { + apic_reg_ops->apic_write(APIC_FORMAT_REG, APIC_CLUSTER_MODEL); } + apic_reg_ops->apic_write(APIC_DEST_REG, AV_HIGH_ORDER >> cpun); + if (apic_directed_EOI_supported()) { /* * Setting the 12th bit in the Spurious Interrupt Vector @@ -633,24 +629,6 @@ apic_intr_enter(int ipl, int *vectorp) return (nipl); } -/* - * This macro is a common code used by MMIO local apic and X2APIC - * local apic. - */ -#define APIC_INTR_EXIT() \ -{ \ - cpu_infop = &apic_cpus[psm_get_cpu_id()]; \ - if (apic_level_intr[irq]) \ - apic_reg_ops->apic_send_eoi(irq); \ - cpu_infop->aci_curipl = (uchar_t)prev_ipl; \ - /* ISR above current pri could not be in progress */ \ - cpu_infop->aci_ISR_in_progress &= (2 << prev_ipl) - 1; \ -} - -/* - * Any changes made to this function must also change X2APIC - * version of intr_exit. - */ void apic_intr_exit(int prev_ipl, int irq) { @@ -658,35 +636,22 @@ apic_intr_exit(int prev_ipl, int irq) apic_reg_ops->apic_write_task_reg(apic_ipltopri[prev_ipl]); - APIC_INTR_EXIT(); -} - -/* - * Same as apic_intr_exit() except it uses MSR rather than MMIO - * to access local apic registers. - */ -void -x2apic_intr_exit(int prev_ipl, int irq) -{ - apic_cpus_info_t *cpu_infop; - - X2APIC_WRITE(APIC_TASK_REG, apic_ipltopri[prev_ipl]); - APIC_INTR_EXIT(); + cpu_infop = &apic_cpus[psm_get_cpu_id()]; + if (apic_level_intr[irq]) + apic_reg_ops->apic_send_eoi(irq); + cpu_infop->aci_curipl = (uchar_t)prev_ipl; + /* ISR above current pri could not be in progress */ + cpu_infop->aci_ISR_in_progress &= (2 << prev_ipl) - 1; } intr_exit_fn_t psm_intr_exit_fn(void) { - if (apic_mode == LOCAL_X2APIC) - return (x2apic_intr_exit); - return (apic_intr_exit); } /* * Mask all interrupts below or equal to the given IPL. - * Any changes made to this function must also change X2APIC - * version of setspl. */ static void apic_setspl(int ipl) @@ -704,19 +669,6 @@ apic_setspl(int ipl) (void) apic_reg_ops->apic_get_pri(); } -/* - * X2APIC version of setspl. - * Mask all interrupts below or equal to the given IPL - */ -static void -x2apic_setspl(int ipl) -{ - X2APIC_WRITE(APIC_TASK_REG, apic_ipltopri[ipl]); - - /* interrupts at ipl above this cannot be in progress */ - apic_cpus[psm_get_cpu_id()].aci_ISR_in_progress &= (2 << ipl) - 1; -} - /*ARGSUSED*/ static int apic_addspl(int irqno, int ipl, int min_ipl, int max_ipl) @@ -739,26 +691,6 @@ apic_post_cpu_start(void) /* We know this CPU + BSP started successfully. */ cpus_started++; - /* - * On BSP we would have enabled X2APIC, if supported by processor, - * in acpi_probe(), but on AP we do it here. - * - * We enable X2APIC mode only if BSP is running in X2APIC & the - * local APIC mode of the current CPU is MMIO (xAPIC). - */ - if (apic_mode == LOCAL_X2APIC && apic_detect_x2apic() && - apic_local_mode() == LOCAL_APIC) { - apic_enable_x2apic(); - } - - /* - * Switch back to x2apic IPI sending method for performance when target - * CPU has entered x2apic mode. - */ - if (apic_mode == LOCAL_X2APIC) { - apic_switch_ipi_callback(B_FALSE); - } - splx(ipltospl(LOCK_LEVEL)); apic_init_intr(); @@ -768,12 +700,7 @@ apic_post_cpu_start(void) */ setcr0(getcr0() & ~(CR0_CD | CR0_NW)); -#ifdef DEBUG APIC_AV_PENDING_SET(); -#else - if (apic_mode == LOCAL_APIC) - APIC_AV_PENDING_SET(); -#endif /* DEBUG */ /* * We may be booting, or resuming from suspend; aci_status will @@ -1330,18 +1257,25 @@ apic_get_apic_type(void) } void -x2apic_update_psm(void) +apic_switch_ipi_callback(boolean_t enter) { - struct psm_ops *pops = &apic_ops; - - ASSERT(pops != NULL); + ASSERT(enter == B_TRUE); +} - pops->psm_intr_exit = x2apic_intr_exit; - pops->psm_setspl = x2apic_setspl; +int +apic_detect_x2apic(void) +{ + return (0); +} - pops->psm_send_ipi = x2apic_send_ipi; - send_dirintf = pops->psm_send_ipi; +void +apic_enable_x2apic(void) +{ + cmn_err(CE_PANIC, "apic_enable_x2apic() called in pcplusmp"); +} - apic_mode = LOCAL_X2APIC; - apic_change_ops(); +void +x2apic_update_psm(void) +{ + cmn_err(CE_PANIC, "x2apic_update_psm() called in pcplusmp"); } diff --git a/usr/src/uts/i86pc/io/pcplusmp/apic_common.c b/usr/src/uts/i86pc/io/pcplusmp/apic_common.c index 649e5ce950..7498150d49 100644 --- a/usr/src/uts/i86pc/io/pcplusmp/apic_common.c +++ b/usr/src/uts/i86pc/io/pcplusmp/apic_common.c @@ -23,7 +23,7 @@ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ /* - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2017, Joyent, Inc. All rights reserved. * Copyright (c) 2016 by Delphix. All rights reserved. */ @@ -128,8 +128,6 @@ extern void cmi_cmci_trap(void); kmutex_t cmci_cpu_setup_lock; /* protects cmci_cpu_setup_registered */ int cmci_cpu_setup_registered; -/* number of CPUs in power-on transition state */ -static int apic_poweron_cnt = 0; lock_t apic_mode_switch_lock; /* @@ -1456,45 +1454,6 @@ apic_find_cpu(int flag) return (acid); } -/* - * Switch between safe and x2APIC IPI sending method. - * CPU may power on in xapic mode or x2apic mode. If CPU needs to send IPI to - * other CPUs before entering x2APIC mode, it still needs to xAPIC method. - * Before sending StartIPI to target CPU, psm_send_ipi will be changed to - * apic_common_send_ipi, which detects current local APIC mode and use right - * method to send IPI. If some CPUs fail to start up, apic_poweron_cnt - * won't return to zero, so apic_common_send_ipi will always be used. - * psm_send_ipi can't be simply changed back to x2apic_send_ipi if some CPUs - * failed to start up because those failed CPUs may recover itself later at - * unpredictable time. - */ -void -apic_switch_ipi_callback(boolean_t enter) -{ - ulong_t iflag; - struct psm_ops *pops = psmops; - - iflag = intr_clear(); - lock_set(&apic_mode_switch_lock); - if (enter) { - ASSERT(apic_poweron_cnt >= 0); - if (apic_poweron_cnt == 0) { - pops->psm_send_ipi = apic_common_send_ipi; - send_dirintf = pops->psm_send_ipi; - } - apic_poweron_cnt++; - } else { - ASSERT(apic_poweron_cnt > 0); - apic_poweron_cnt--; - if (apic_poweron_cnt == 0) { - pops->psm_send_ipi = x2apic_send_ipi; - send_dirintf = pops->psm_send_ipi; - } - } - lock_clear(&apic_mode_switch_lock); - intr_restore(iflag); -} - void apic_intrmap_init(int apic_mode) { diff --git a/usr/src/uts/i86pc/io/pcplusmp/apic_regops.c b/usr/src/uts/i86pc/io/pcplusmp/apic_regops.c index 60ce7a3771..e0b647975d 100644 --- a/usr/src/uts/i86pc/io/pcplusmp/apic_regops.c +++ b/usr/src/uts/i86pc/io/pcplusmp/apic_regops.c @@ -25,6 +25,7 @@ /* * Copyright 2014 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> * Copyright (c) 2014 by Delphix. All rights reserved. + * Copyright 2017 Joyent, Inc. */ #include <sys/cpuvar.h> @@ -40,17 +41,12 @@ #include <sys/privregs.h> #include <sys/psm_common.h> -/* Function prototypes of local apic and X2APIC */ +/* Function prototypes of local apic */ static uint64_t local_apic_read(uint32_t reg); static void local_apic_write(uint32_t reg, uint64_t value); static int get_local_apic_pri(void); static void local_apic_write_task_reg(uint64_t value); static void local_apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1); -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: @@ -64,14 +60,13 @@ static void local_x2apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1); * 1 1 APIC is enabled in X2APIC mode * ----------------------------------------------------------- */ -int x2apic_enable = 1; apic_mode_t apic_mode = LOCAL_APIC; /* Default mode is Local APIC */ /* See apic_directed_EOI_supported(). Currently 3-state variable. */ volatile int apic_directed_eoi_state = 2; /* Uses MMIO (Memory Mapped IO) */ -static apic_reg_ops_t local_apic_regs_ops = { +apic_reg_ops_t local_apic_regs_ops = { local_apic_read, local_apic_write, get_local_apic_pri, @@ -80,16 +75,6 @@ static apic_reg_ops_t local_apic_regs_ops = { apic_send_EOI, }; -/* 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, -}; - int apic_have_32bit_cr8 = 0; /* The default ops is local APIC (Memory Mapped IO) */ @@ -101,8 +86,6 @@ apic_reg_ops_t *apic_reg_ops = &local_apic_regs_ops; void apic_send_EOI(); void apic_send_directed_EOI(uint32_t irq); -#define X2APIC_ENABLE_BIT 10 - /* * Local APIC Implementation */ @@ -150,51 +133,6 @@ local_apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1) apicadr[APIC_INT_CMD1] = cmd1; } -/* - * 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)); -} /*ARGSUSED*/ void @@ -236,38 +174,6 @@ apic_send_directed_EOI(uint32_t irq) } } -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(); -} - /* * Determine which mode the current CPU is in. See the table above. * (IA32_APIC_BASE[11]) (IA32_APIC_BASE[10]) @@ -339,104 +245,3 @@ apic_directed_EOI_supported() return (0); } - -/* - * 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); -} - -/* - * 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); -} diff --git a/usr/src/uts/i86pc/sys/apic.h b/usr/src/uts/i86pc/sys/apic.h index 11ae48340a..828ef5b20d 100644 --- a/usr/src/uts/i86pc/sys/apic.h +++ b/usr/src/uts/i86pc/sys/apic.h @@ -20,8 +20,8 @@ */ /* * Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2017 Joyent, Inc. */ - /* * Copyright (c) 2010, Intel Corporation. * All rights reserved. @@ -117,6 +117,9 @@ typedef enum apic_mode { #define APIC_SVR_SUPPRESS_BROADCAST_EOI 0x1000 #define APIC_DIRECTED_EOI_BIT 0x1000000 +/* x2APIC enable bit in REG_APIC_BASE_MSR */ +#define X2APIC_ENABLE_BIT 10 + /* IRR register */ #define APIC_IRR_REG 0x80 @@ -867,6 +870,7 @@ extern int apic_sci_vect; extern int apic_hpet_vect; extern uchar_t apic_ipls[]; extern apic_reg_ops_t *apic_reg_ops; +extern apic_reg_ops_t local_apic_regs_ops; extern apic_mode_t apic_mode; extern void x2apic_update_psm(); extern void apic_change_ops(); |