xref: /illumos-gate/usr/src/uts/i86pc/io/pcplusmp/apic_common.c (revision 11ed32a0b3b424ec966d0330d0efaf049baaf8d2)
17ff178cdSJimmy Vetayases /*
27ff178cdSJimmy Vetayases  * CDDL HEADER START
37ff178cdSJimmy Vetayases  *
47ff178cdSJimmy Vetayases  * The contents of this file are subject to the terms of the
57ff178cdSJimmy Vetayases  * Common Development and Distribution License (the "License").
67ff178cdSJimmy Vetayases  * You may not use this file except in compliance with the License.
77ff178cdSJimmy Vetayases  *
87ff178cdSJimmy Vetayases  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97ff178cdSJimmy Vetayases  * or http://www.opensolaris.org/os/licensing.
107ff178cdSJimmy Vetayases  * See the License for the specific language governing permissions
117ff178cdSJimmy Vetayases  * and limitations under the License.
127ff178cdSJimmy Vetayases  *
137ff178cdSJimmy Vetayases  * When distributing Covered Code, include this CDDL HEADER in each
147ff178cdSJimmy Vetayases  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157ff178cdSJimmy Vetayases  * If applicable, add the following below this CDDL HEADER, with the
167ff178cdSJimmy Vetayases  * fields enclosed by brackets "[]" replaced with your own identifying
177ff178cdSJimmy Vetayases  * information: Portions Copyright [yyyy] [name of copyright owner]
187ff178cdSJimmy Vetayases  *
197ff178cdSJimmy Vetayases  * CDDL HEADER END
207ff178cdSJimmy Vetayases  */
217ff178cdSJimmy Vetayases 
227ff178cdSJimmy Vetayases /*
237ff178cdSJimmy Vetayases  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
247ff178cdSJimmy Vetayases  */
25a288e5a9SJoshua M. Clulow /*
260c26abfeSJohn Levon  * Copyright 2019, Joyent, Inc.
27e8763682SPavel Zakharov  * Copyright (c) 2016, 2017 by Delphix. All rights reserved.
28*11ed32a0SJoshua M. Clulow  * Copyright 2019 Joshua M. Clulow <josh@sysmgr.org>
29a288e5a9SJoshua M. Clulow  */
307ff178cdSJimmy Vetayases 
317ff178cdSJimmy Vetayases /*
327ff178cdSJimmy Vetayases  * PSMI 1.1 extensions are supported only in 2.6 and later versions.
337ff178cdSJimmy Vetayases  * PSMI 1.2 extensions are supported only in 2.7 and later versions.
347ff178cdSJimmy Vetayases  * PSMI 1.3 and 1.4 extensions are supported in Solaris 10.
357ff178cdSJimmy Vetayases  * PSMI 1.5 extensions are supported in Solaris Nevada.
367ff178cdSJimmy Vetayases  * PSMI 1.6 extensions are supported in Solaris Nevada.
377ff178cdSJimmy Vetayases  * PSMI 1.7 extensions are supported in Solaris Nevada.
387ff178cdSJimmy Vetayases  */
397ff178cdSJimmy Vetayases #define	PSMI_1_7
407ff178cdSJimmy Vetayases 
417ff178cdSJimmy Vetayases #include <sys/processor.h>
427ff178cdSJimmy Vetayases #include <sys/time.h>
437ff178cdSJimmy Vetayases #include <sys/psm.h>
447ff178cdSJimmy Vetayases #include <sys/smp_impldefs.h>
457ff178cdSJimmy Vetayases #include <sys/cram.h>
467ff178cdSJimmy Vetayases #include <sys/acpi/acpi.h>
477ff178cdSJimmy Vetayases #include <sys/acpica.h>
487ff178cdSJimmy Vetayases #include <sys/psm_common.h>
497ff178cdSJimmy Vetayases #include <sys/apic.h>
507ff178cdSJimmy Vetayases #include <sys/pit.h>
517ff178cdSJimmy Vetayases #include <sys/ddi.h>
527ff178cdSJimmy Vetayases #include <sys/sunddi.h>
537ff178cdSJimmy Vetayases #include <sys/ddi_impldefs.h>
547ff178cdSJimmy Vetayases #include <sys/pci.h>
557ff178cdSJimmy Vetayases #include <sys/promif.h>
567ff178cdSJimmy Vetayases #include <sys/x86_archext.h>
577ff178cdSJimmy Vetayases #include <sys/cpc_impl.h>
587ff178cdSJimmy Vetayases #include <sys/uadmin.h>
597ff178cdSJimmy Vetayases #include <sys/panic.h>
607ff178cdSJimmy Vetayases #include <sys/debug.h>
617ff178cdSJimmy Vetayases #include <sys/archsystm.h>
627ff178cdSJimmy Vetayases #include <sys/trap.h>
637ff178cdSJimmy Vetayases #include <sys/machsystm.h>
647ff178cdSJimmy Vetayases #include <sys/sysmacros.h>
657ff178cdSJimmy Vetayases #include <sys/cpuvar.h>
667ff178cdSJimmy Vetayases #include <sys/rm_platter.h>
677ff178cdSJimmy Vetayases #include <sys/privregs.h>
687ff178cdSJimmy Vetayases #include <sys/note.h>
697ff178cdSJimmy Vetayases #include <sys/pci_intr_lib.h>
707ff178cdSJimmy Vetayases #include <sys/spl.h>
717ff178cdSJimmy Vetayases #include <sys/clock.h>
727ff178cdSJimmy Vetayases #include <sys/dditypes.h>
737ff178cdSJimmy Vetayases #include <sys/sunddi.h>
747ff178cdSJimmy Vetayases #include <sys/x_call.h>
757ff178cdSJimmy Vetayases #include <sys/reboot.h>
767ff178cdSJimmy Vetayases #include <sys/hpet.h>
777ff178cdSJimmy Vetayases #include <sys/apic_common.h>
7841afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/apic_timer.h>
797ff178cdSJimmy Vetayases 
807ff178cdSJimmy Vetayases static void	apic_record_ioapic_rdt(void *intrmap_private,
817ff178cdSJimmy Vetayases 		    ioapic_rdt_t *irdt);
827ff178cdSJimmy Vetayases static void	apic_record_msi(void *intrmap_private, msi_regs_t *mregs);
837ff178cdSJimmy Vetayases 
847ff178cdSJimmy Vetayases /*
857ff178cdSJimmy Vetayases  * Common routines between pcplusmp & apix (taken from apic.c).
867ff178cdSJimmy Vetayases  */
877ff178cdSJimmy Vetayases 
887ff178cdSJimmy Vetayases int	apic_clkinit(int);
897ff178cdSJimmy Vetayases hrtime_t apic_gethrtime(void);
907ff178cdSJimmy Vetayases void	apic_send_ipi(int, int);
917ff178cdSJimmy Vetayases void	apic_set_idlecpu(processorid_t);
927ff178cdSJimmy Vetayases void	apic_unset_idlecpu(processorid_t);
937ff178cdSJimmy Vetayases void	apic_shutdown(int, int);
947ff178cdSJimmy Vetayases void	apic_preshutdown(int, int);
957ff178cdSJimmy Vetayases processorid_t	apic_get_next_processorid(processorid_t);
967ff178cdSJimmy Vetayases 
977ff178cdSJimmy Vetayases hrtime_t apic_gettime();
987ff178cdSJimmy Vetayases 
997ff178cdSJimmy Vetayases enum apic_ioapic_method_type apix_mul_ioapic_method = APIC_MUL_IOAPIC_PCPLUSMP;
1007ff178cdSJimmy Vetayases 
1017ff178cdSJimmy Vetayases /* Now the ones for Dynamic Interrupt distribution */
1027ff178cdSJimmy Vetayases int	apic_enable_dynamic_migration = 0;
1037ff178cdSJimmy Vetayases 
1047ff178cdSJimmy Vetayases /* maximum loop count when sending Start IPIs. */
1057ff178cdSJimmy Vetayases int apic_sipi_max_loop_count = 0x1000;
1067ff178cdSJimmy Vetayases 
1077ff178cdSJimmy Vetayases /*
1087ff178cdSJimmy Vetayases  * These variables are frequently accessed in apic_intr_enter(),
1097ff178cdSJimmy Vetayases  * apic_intr_exit and apic_setspl, so group them together
1107ff178cdSJimmy Vetayases  */
1117ff178cdSJimmy Vetayases volatile uint32_t *apicadr =  NULL;	/* virtual addr of local APIC	*/
1127ff178cdSJimmy Vetayases int apic_setspl_delay = 1;		/* apic_setspl - delay enable	*/
1137ff178cdSJimmy Vetayases int apic_clkvect;
1147ff178cdSJimmy Vetayases 
1157ff178cdSJimmy Vetayases /* vector at which error interrupts come in */
1167ff178cdSJimmy Vetayases int apic_errvect;
1177ff178cdSJimmy Vetayases int apic_enable_error_intr = 1;
1187ff178cdSJimmy Vetayases int apic_error_display_delay = 100;
1197ff178cdSJimmy Vetayases 
1207ff178cdSJimmy Vetayases /* vector at which performance counter overflow interrupts come in */
1217ff178cdSJimmy Vetayases int apic_cpcovf_vect;
1227ff178cdSJimmy Vetayases int apic_enable_cpcovf_intr = 1;
1237ff178cdSJimmy Vetayases 
1247ff178cdSJimmy Vetayases /* vector at which CMCI interrupts come in */
1257ff178cdSJimmy Vetayases int apic_cmci_vect;
1267ff178cdSJimmy Vetayases extern void cmi_cmci_trap(void);
1277ff178cdSJimmy Vetayases 
1287ff178cdSJimmy Vetayases lock_t apic_mode_switch_lock;
1297ff178cdSJimmy Vetayases 
1301c2d0470SPatrick Mooney int apic_pir_vect;
1311c2d0470SPatrick Mooney 
1327ff178cdSJimmy Vetayases /*
1337ff178cdSJimmy Vetayases  * Patchable global variables.
1347ff178cdSJimmy Vetayases  */
1357ff178cdSJimmy Vetayases int	apic_forceload = 0;
1367ff178cdSJimmy Vetayases 
1377ff178cdSJimmy Vetayases int	apic_coarse_hrtime = 1;		/* 0 - use accurate slow gethrtime() */
1387ff178cdSJimmy Vetayases 
1397ff178cdSJimmy Vetayases int	apic_flat_model = 0;		/* 0 - clustered. 1 - flat */
1407ff178cdSJimmy Vetayases int	apic_panic_on_nmi = 0;
1417ff178cdSJimmy Vetayases int	apic_panic_on_apic_error = 0;
1427ff178cdSJimmy Vetayases 
1437ff178cdSJimmy Vetayases int	apic_verbose = 0;	/* 0x1ff */
1447ff178cdSJimmy Vetayases 
1457ff178cdSJimmy Vetayases #ifdef DEBUG
1467ff178cdSJimmy Vetayases int	apic_debug = 0;
1477ff178cdSJimmy Vetayases int	apic_restrict_vector = 0;
1487ff178cdSJimmy Vetayases 
1497ff178cdSJimmy Vetayases int	apic_debug_msgbuf[APIC_DEBUG_MSGBUFSIZE];
1507ff178cdSJimmy Vetayases int	apic_debug_msgbufindex = 0;
1517ff178cdSJimmy Vetayases 
1527ff178cdSJimmy Vetayases #endif /* DEBUG */
1537ff178cdSJimmy Vetayases 
1547ff178cdSJimmy Vetayases uint_t apic_nticks = 0;
1557ff178cdSJimmy Vetayases uint_t apic_skipped_redistribute = 0;
1567ff178cdSJimmy Vetayases 
1577ff178cdSJimmy Vetayases uint_t last_count_read = 0;
1587ff178cdSJimmy Vetayases lock_t	apic_gethrtime_lock;
1597ff178cdSJimmy Vetayases volatile int	apic_hrtime_stamp = 0;
1607ff178cdSJimmy Vetayases volatile hrtime_t apic_nsec_since_boot = 0;
1617ff178cdSJimmy Vetayases 
1627ff178cdSJimmy Vetayases static	hrtime_t	apic_last_hrtime = 0;
1637ff178cdSJimmy Vetayases int		apic_hrtime_error = 0;
1647ff178cdSJimmy Vetayases int		apic_remote_hrterr = 0;
1657ff178cdSJimmy Vetayases int		apic_num_nmis = 0;
1667ff178cdSJimmy Vetayases int		apic_apic_error = 0;
1677ff178cdSJimmy Vetayases int		apic_num_apic_errors = 0;
1687ff178cdSJimmy Vetayases int		apic_num_cksum_errors = 0;
1697ff178cdSJimmy Vetayases 
1707ff178cdSJimmy Vetayases int	apic_error = 0;
1717ff178cdSJimmy Vetayases 
1727ff178cdSJimmy Vetayases static	int	apic_cmos_ssb_set = 0;
1737ff178cdSJimmy Vetayases 
1747ff178cdSJimmy Vetayases /* use to make sure only one cpu handles the nmi */
1757ff178cdSJimmy Vetayases lock_t	apic_nmi_lock;
1767ff178cdSJimmy Vetayases /* use to make sure only one cpu handles the error interrupt */
1777ff178cdSJimmy Vetayases lock_t	apic_error_lock;
1787ff178cdSJimmy Vetayases 
1797ff178cdSJimmy Vetayases static	struct {
1807ff178cdSJimmy Vetayases 	uchar_t	cntl;
1817ff178cdSJimmy Vetayases 	uchar_t	data;
1827ff178cdSJimmy Vetayases } aspen_bmc[] = {
1837ff178cdSJimmy Vetayases 	{ CC_SMS_WR_START,	0x18 },		/* NetFn/LUN */
1847ff178cdSJimmy Vetayases 	{ CC_SMS_WR_NEXT,	0x24 },		/* Cmd SET_WATCHDOG_TIMER */
1857ff178cdSJimmy Vetayases 	{ CC_SMS_WR_NEXT,	0x84 },		/* DataByte 1: SMS/OS no log */
1867ff178cdSJimmy Vetayases 	{ CC_SMS_WR_NEXT,	0x2 },		/* DataByte 2: Power Down */
1877ff178cdSJimmy Vetayases 	{ CC_SMS_WR_NEXT,	0x0 },		/* DataByte 3: no pre-timeout */
1887ff178cdSJimmy Vetayases 	{ CC_SMS_WR_NEXT,	0x0 },		/* DataByte 4: timer expir. */
1897ff178cdSJimmy Vetayases 	{ CC_SMS_WR_NEXT,	0xa },		/* DataByte 5: init countdown */
1907ff178cdSJimmy Vetayases 	{ CC_SMS_WR_END,	0x0 },		/* DataByte 6: init countdown */
1917ff178cdSJimmy Vetayases 
1927ff178cdSJimmy Vetayases 	{ CC_SMS_WR_START,	0x18 },		/* NetFn/LUN */
1937ff178cdSJimmy Vetayases 	{ CC_SMS_WR_END,	0x22 }		/* Cmd RESET_WATCHDOG_TIMER */
1947ff178cdSJimmy Vetayases };
1957ff178cdSJimmy Vetayases 
1967ff178cdSJimmy Vetayases static	struct {
1977ff178cdSJimmy Vetayases 	int	port;
1987ff178cdSJimmy Vetayases 	uchar_t	data;
1997ff178cdSJimmy Vetayases } sitka_bmc[] = {
2007ff178cdSJimmy Vetayases 	{ SMS_COMMAND_REGISTER,	SMS_WRITE_START },
2017ff178cdSJimmy Vetayases 	{ SMS_DATA_REGISTER,	0x18 },		/* NetFn/LUN */
2027ff178cdSJimmy Vetayases 	{ SMS_DATA_REGISTER,	0x24 },		/* Cmd SET_WATCHDOG_TIMER */
2037ff178cdSJimmy Vetayases 	{ SMS_DATA_REGISTER,	0x84 },		/* DataByte 1: SMS/OS no log */
2047ff178cdSJimmy Vetayases 	{ SMS_DATA_REGISTER,	0x2 },		/* DataByte 2: Power Down */
2057ff178cdSJimmy Vetayases 	{ SMS_DATA_REGISTER,	0x0 },		/* DataByte 3: no pre-timeout */
2067ff178cdSJimmy Vetayases 	{ SMS_DATA_REGISTER,	0x0 },		/* DataByte 4: timer expir. */
2077ff178cdSJimmy Vetayases 	{ SMS_DATA_REGISTER,	0xa },		/* DataByte 5: init countdown */
2087ff178cdSJimmy Vetayases 	{ SMS_COMMAND_REGISTER,	SMS_WRITE_END },
2097ff178cdSJimmy Vetayases 	{ SMS_DATA_REGISTER,	0x0 },		/* DataByte 6: init countdown */
2107ff178cdSJimmy Vetayases 
2117ff178cdSJimmy Vetayases 	{ SMS_COMMAND_REGISTER,	SMS_WRITE_START },
2127ff178cdSJimmy Vetayases 	{ SMS_DATA_REGISTER,	0x18 },		/* NetFn/LUN */
2137ff178cdSJimmy Vetayases 	{ SMS_COMMAND_REGISTER,	SMS_WRITE_END },
2147ff178cdSJimmy Vetayases 	{ SMS_DATA_REGISTER,	0x22 }		/* Cmd RESET_WATCHDOG_TIMER */
2157ff178cdSJimmy Vetayases };
2167ff178cdSJimmy Vetayases 
2177ff178cdSJimmy Vetayases /* Patchable global variables. */
2187ff178cdSJimmy Vetayases int		apic_kmdb_on_nmi = 0;		/* 0 - no, 1 - yes enter kmdb */
2197ff178cdSJimmy Vetayases uint32_t	apic_divide_reg_init = 0;	/* 0 - divide by 2 */
2207ff178cdSJimmy Vetayases 
2217ff178cdSJimmy Vetayases /* default apic ops without interrupt remapping */
2227ff178cdSJimmy Vetayases static apic_intrmap_ops_t apic_nointrmap_ops = {
2237ff178cdSJimmy Vetayases 	(int (*)(int))return_instr,
2247ff178cdSJimmy Vetayases 	(void (*)(int))return_instr,
2257ff178cdSJimmy Vetayases 	(void (*)(void **, dev_info_t *, uint16_t, int, uchar_t))return_instr,
2267ff178cdSJimmy Vetayases 	(void (*)(void *, void *, uint16_t, int))return_instr,
2277ff178cdSJimmy Vetayases 	(void (*)(void **))return_instr,
2287ff178cdSJimmy Vetayases 	apic_record_ioapic_rdt,
2297ff178cdSJimmy Vetayases 	apic_record_msi,
2307ff178cdSJimmy Vetayases };
2317ff178cdSJimmy Vetayases 
2327ff178cdSJimmy Vetayases apic_intrmap_ops_t *apic_vt_ops = &apic_nointrmap_ops;
2337ff178cdSJimmy Vetayases apic_cpus_info_t	*apic_cpus = NULL;
2347ff178cdSJimmy Vetayases cpuset_t	apic_cpumask;
2357ff178cdSJimmy Vetayases uint_t		apic_picinit_called;
2367ff178cdSJimmy Vetayases 
2377ff178cdSJimmy Vetayases /* Flag to indicate that we need to shut down all processors */
2387ff178cdSJimmy Vetayases static uint_t	apic_shutdown_processors;
2397ff178cdSJimmy Vetayases 
2407ff178cdSJimmy Vetayases /*
2417ff178cdSJimmy Vetayases  * Probe the ioapic method for apix module. Called in apic_probe_common()
2427ff178cdSJimmy Vetayases  */
2437ff178cdSJimmy Vetayases int
2447ff178cdSJimmy Vetayases apic_ioapic_method_probe()
2457ff178cdSJimmy Vetayases {
2467ff178cdSJimmy Vetayases 	if (apix_enable == 0)
2477ff178cdSJimmy Vetayases 		return (PSM_SUCCESS);
2487ff178cdSJimmy Vetayases 
2497ff178cdSJimmy Vetayases 	/*
2507ff178cdSJimmy Vetayases 	 * Set IOAPIC EOI handling method. The priority from low to high is:
25158b49504SHans Rosenfeld 	 *	1. IOxAPIC: with EOI register
25258b49504SHans Rosenfeld 	 *	2. IOMMU interrupt mapping
2537ff178cdSJimmy Vetayases 	 *	3. Mask-Before-EOI method for systems without boot
2547ff178cdSJimmy Vetayases 	 *	interrupt routing, such as systems with only one IOAPIC;
2557ff178cdSJimmy Vetayases 	 *	NVIDIA CK8-04/MCP55 systems; systems with bridge solution
2567ff178cdSJimmy Vetayases 	 *	which disables the boot interrupt routing already.
25758b49504SHans Rosenfeld 	 *	4. Directed EOI
2587ff178cdSJimmy Vetayases 	 */
2597ff178cdSJimmy Vetayases 	if (apic_io_ver[0] >= 0x20)
2607ff178cdSJimmy Vetayases 		apix_mul_ioapic_method = APIC_MUL_IOAPIC_IOXAPIC;
2617ff178cdSJimmy Vetayases 	if ((apic_io_max == 1) || (apic_nvidia_io_max == apic_io_max))
2627ff178cdSJimmy Vetayases 		apix_mul_ioapic_method = APIC_MUL_IOAPIC_MASK;
2637ff178cdSJimmy Vetayases 	if (apic_directed_EOI_supported())
2647ff178cdSJimmy Vetayases 		apix_mul_ioapic_method = APIC_MUL_IOAPIC_DEOI;
2657ff178cdSJimmy Vetayases 
2667ff178cdSJimmy Vetayases 	/* fall back to pcplusmp */
2677ff178cdSJimmy Vetayases 	if (apix_mul_ioapic_method == APIC_MUL_IOAPIC_PCPLUSMP) {
2687ff178cdSJimmy Vetayases 		/* make sure apix is after pcplusmp in /etc/mach */
2697ff178cdSJimmy Vetayases 		apix_enable = 0; /* go ahead with pcplusmp install next */
2707ff178cdSJimmy Vetayases 		return (PSM_FAILURE);
2717ff178cdSJimmy Vetayases 	}
2727ff178cdSJimmy Vetayases 
2737ff178cdSJimmy Vetayases 	return (PSM_SUCCESS);
2747ff178cdSJimmy Vetayases }
2757ff178cdSJimmy Vetayases 
2767ff178cdSJimmy Vetayases /*
2777ff178cdSJimmy Vetayases  * handler for APIC Error interrupt. Just print a warning and continue
2787ff178cdSJimmy Vetayases  */
2797ff178cdSJimmy Vetayases int
2807ff178cdSJimmy Vetayases apic_error_intr()
2817ff178cdSJimmy Vetayases {
2827ff178cdSJimmy Vetayases 	uint_t	error0, error1, error;
2837ff178cdSJimmy Vetayases 	uint_t	i;
2847ff178cdSJimmy Vetayases 
2857ff178cdSJimmy Vetayases 	/*
2867ff178cdSJimmy Vetayases 	 * We need to write before read as per 7.4.17 of system prog manual.
2877ff178cdSJimmy Vetayases 	 * We do both and or the results to be safe
2887ff178cdSJimmy Vetayases 	 */
2897ff178cdSJimmy Vetayases 	error0 = apic_reg_ops->apic_read(APIC_ERROR_STATUS);
2907ff178cdSJimmy Vetayases 	apic_reg_ops->apic_write(APIC_ERROR_STATUS, 0);
2917ff178cdSJimmy Vetayases 	error1 = apic_reg_ops->apic_read(APIC_ERROR_STATUS);
2927ff178cdSJimmy Vetayases 	error = error0 | error1;
2937ff178cdSJimmy Vetayases 
2947ff178cdSJimmy Vetayases 	/*
2957ff178cdSJimmy Vetayases 	 * Clear the APIC error status (do this on all cpus that enter here)
2967ff178cdSJimmy Vetayases 	 * (two writes are required due to the semantics of accessing the
2977ff178cdSJimmy Vetayases 	 * error status register.)
2987ff178cdSJimmy Vetayases 	 */
2997ff178cdSJimmy Vetayases 	apic_reg_ops->apic_write(APIC_ERROR_STATUS, 0);
3007ff178cdSJimmy Vetayases 	apic_reg_ops->apic_write(APIC_ERROR_STATUS, 0);
3017ff178cdSJimmy Vetayases 
3027ff178cdSJimmy Vetayases 	/*
3037ff178cdSJimmy Vetayases 	 * Prevent more than 1 CPU from handling error interrupt causing
3047ff178cdSJimmy Vetayases 	 * double printing (interleave of characters from multiple
3057ff178cdSJimmy Vetayases 	 * CPU's when using prom_printf)
3067ff178cdSJimmy Vetayases 	 */
3077ff178cdSJimmy Vetayases 	if (lock_try(&apic_error_lock) == 0)
3087ff178cdSJimmy Vetayases 		return (error ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED);
3097ff178cdSJimmy Vetayases 	if (error) {
3107ff178cdSJimmy Vetayases #if	DEBUG
3117ff178cdSJimmy Vetayases 		if (apic_debug)
3127ff178cdSJimmy Vetayases 			debug_enter("pcplusmp: APIC Error interrupt received");
3137ff178cdSJimmy Vetayases #endif /* DEBUG */
3147ff178cdSJimmy Vetayases 		if (apic_panic_on_apic_error)
3157ff178cdSJimmy Vetayases 			cmn_err(CE_PANIC,
3167ff178cdSJimmy Vetayases 			    "APIC Error interrupt on CPU %d. Status = %x",
3177ff178cdSJimmy Vetayases 			    psm_get_cpu_id(), error);
3187ff178cdSJimmy Vetayases 		else {
3197ff178cdSJimmy Vetayases 			if ((error & ~APIC_CS_ERRORS) == 0) {
3207ff178cdSJimmy Vetayases 				/* cksum error only */
3217ff178cdSJimmy Vetayases 				apic_error |= APIC_ERR_APIC_ERROR;
3227ff178cdSJimmy Vetayases 				apic_apic_error |= error;
3237ff178cdSJimmy Vetayases 				apic_num_apic_errors++;
3247ff178cdSJimmy Vetayases 				apic_num_cksum_errors++;
3257ff178cdSJimmy Vetayases 			} else {
3267ff178cdSJimmy Vetayases 				/*
3277ff178cdSJimmy Vetayases 				 * prom_printf is the best shot we have of
3287ff178cdSJimmy Vetayases 				 * something which is problem free from
3297ff178cdSJimmy Vetayases 				 * high level/NMI type of interrupts
3307ff178cdSJimmy Vetayases 				 */
3317ff178cdSJimmy Vetayases 				prom_printf("APIC Error interrupt on CPU %d. "
3327ff178cdSJimmy Vetayases 				    "Status 0 = %x, Status 1 = %x\n",
3337ff178cdSJimmy Vetayases 				    psm_get_cpu_id(), error0, error1);
3347ff178cdSJimmy Vetayases 				apic_error |= APIC_ERR_APIC_ERROR;
3357ff178cdSJimmy Vetayases 				apic_apic_error |= error;
3367ff178cdSJimmy Vetayases 				apic_num_apic_errors++;
3377ff178cdSJimmy Vetayases 				for (i = 0; i < apic_error_display_delay; i++) {
3387ff178cdSJimmy Vetayases 					tenmicrosec();
3397ff178cdSJimmy Vetayases 				}
3407ff178cdSJimmy Vetayases 				/*
3417ff178cdSJimmy Vetayases 				 * provide more delay next time limited to
3427ff178cdSJimmy Vetayases 				 * roughly 1 clock tick time
3437ff178cdSJimmy Vetayases 				 */
3447ff178cdSJimmy Vetayases 				if (apic_error_display_delay < 500)
3457ff178cdSJimmy Vetayases 					apic_error_display_delay *= 2;
3467ff178cdSJimmy Vetayases 			}
3477ff178cdSJimmy Vetayases 		}
3487ff178cdSJimmy Vetayases 		lock_clear(&apic_error_lock);
3497ff178cdSJimmy Vetayases 		return (DDI_INTR_CLAIMED);
3507ff178cdSJimmy Vetayases 	} else {
3517ff178cdSJimmy Vetayases 		lock_clear(&apic_error_lock);
3527ff178cdSJimmy Vetayases 		return (DDI_INTR_UNCLAIMED);
3537ff178cdSJimmy Vetayases 	}
3547ff178cdSJimmy Vetayases }
3557ff178cdSJimmy Vetayases 
3567ff178cdSJimmy Vetayases /*
3577ff178cdSJimmy Vetayases  * Turn off the mask bit in the performance counter Local Vector Table entry.
3587ff178cdSJimmy Vetayases  */
3597ff178cdSJimmy Vetayases void
3607ff178cdSJimmy Vetayases apic_cpcovf_mask_clear(void)
3617ff178cdSJimmy Vetayases {
3627ff178cdSJimmy Vetayases 	apic_reg_ops->apic_write(APIC_PCINT_VECT,
3637ff178cdSJimmy Vetayases 	    (apic_reg_ops->apic_read(APIC_PCINT_VECT) & ~APIC_LVT_MASK));
3647ff178cdSJimmy Vetayases }
3657ff178cdSJimmy Vetayases 
3667ff178cdSJimmy Vetayases /*ARGSUSED*/
3677ff178cdSJimmy Vetayases static int
3687ff178cdSJimmy Vetayases apic_cmci_enable(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3)
3697ff178cdSJimmy Vetayases {
3707ff178cdSJimmy Vetayases 	apic_reg_ops->apic_write(APIC_CMCI_VECT, apic_cmci_vect);
3717ff178cdSJimmy Vetayases 	return (0);
3727ff178cdSJimmy Vetayases }
3737ff178cdSJimmy Vetayases 
3747ff178cdSJimmy Vetayases /*ARGSUSED*/
3757ff178cdSJimmy Vetayases static int
3767ff178cdSJimmy Vetayases apic_cmci_disable(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3)
3777ff178cdSJimmy Vetayases {
3787ff178cdSJimmy Vetayases 	apic_reg_ops->apic_write(APIC_CMCI_VECT, apic_cmci_vect | AV_MASK);
3797ff178cdSJimmy Vetayases 	return (0);
3807ff178cdSJimmy Vetayases }
3817ff178cdSJimmy Vetayases 
382918e0d92SRobert Mustacchi void
383918e0d92SRobert Mustacchi apic_cmci_setup(processorid_t cpuid, boolean_t enable)
3847ff178cdSJimmy Vetayases {
3857ff178cdSJimmy Vetayases 	cpuset_t	cpu_set;
3867ff178cdSJimmy Vetayases 
3877ff178cdSJimmy Vetayases 	CPUSET_ONLY(cpu_set, cpuid);
3887ff178cdSJimmy Vetayases 
389918e0d92SRobert Mustacchi 	if (enable) {
3905ffc2536SToomas Soome 		xc_call(0, 0, 0, CPUSET2BV(cpu_set),
391918e0d92SRobert Mustacchi 		    (xc_func_t)apic_cmci_enable);
392918e0d92SRobert Mustacchi 	} else {
3935ffc2536SToomas Soome 		xc_call(0, 0, 0, CPUSET2BV(cpu_set),
394918e0d92SRobert Mustacchi 		    (xc_func_t)apic_cmci_disable);
3957ff178cdSJimmy Vetayases 	}
3967ff178cdSJimmy Vetayases }
3977ff178cdSJimmy Vetayases 
3987ff178cdSJimmy Vetayases static void
3997ff178cdSJimmy Vetayases apic_disable_local_apic(void)
4007ff178cdSJimmy Vetayases {
4017ff178cdSJimmy Vetayases 	apic_reg_ops->apic_write_task_reg(APIC_MASK_ALL);
4027ff178cdSJimmy Vetayases 	apic_reg_ops->apic_write(APIC_LOCAL_TIMER, AV_MASK);
4037ff178cdSJimmy Vetayases 
4047ff178cdSJimmy Vetayases 	/* local intr reg 0 */
4057ff178cdSJimmy Vetayases 	apic_reg_ops->apic_write(APIC_INT_VECT0, AV_MASK);
4067ff178cdSJimmy Vetayases 
4077ff178cdSJimmy Vetayases 	/* disable NMI */
4087ff178cdSJimmy Vetayases 	apic_reg_ops->apic_write(APIC_INT_VECT1, AV_MASK);
4097ff178cdSJimmy Vetayases 
4107ff178cdSJimmy Vetayases 	/* and error interrupt */
4117ff178cdSJimmy Vetayases 	apic_reg_ops->apic_write(APIC_ERR_VECT, AV_MASK);
4127ff178cdSJimmy Vetayases 
4137ff178cdSJimmy Vetayases 	/* and perf counter intr */
4147ff178cdSJimmy Vetayases 	apic_reg_ops->apic_write(APIC_PCINT_VECT, AV_MASK);
4157ff178cdSJimmy Vetayases 
4167ff178cdSJimmy Vetayases 	apic_reg_ops->apic_write(APIC_SPUR_INT_REG, APIC_SPUR_INTR);
4177ff178cdSJimmy Vetayases }
4187ff178cdSJimmy Vetayases 
4197ff178cdSJimmy Vetayases static void
4207ff178cdSJimmy Vetayases apic_cpu_send_SIPI(processorid_t cpun, boolean_t start)
4217ff178cdSJimmy Vetayases {
4227ff178cdSJimmy Vetayases 	int		loop_count;
4237ff178cdSJimmy Vetayases 	uint32_t	vector;
4247ff178cdSJimmy Vetayases 	uint_t		apicid;
4257ff178cdSJimmy Vetayases 	ulong_t		iflag;
4267ff178cdSJimmy Vetayases 
4277ff178cdSJimmy Vetayases 	apicid =  apic_cpus[cpun].aci_local_id;
4287ff178cdSJimmy Vetayases 
4297ff178cdSJimmy Vetayases 	/*
4307ff178cdSJimmy Vetayases 	 * Interrupts on current CPU will be disabled during the
4317ff178cdSJimmy Vetayases 	 * steps in order to avoid unwanted side effects from
4327ff178cdSJimmy Vetayases 	 * executing interrupt handlers on a problematic BIOS.
4337ff178cdSJimmy Vetayases 	 */
4347ff178cdSJimmy Vetayases 	iflag = intr_clear();
4357ff178cdSJimmy Vetayases 
4367ff178cdSJimmy Vetayases 	if (start) {
4377ff178cdSJimmy Vetayases 		outb(CMOS_ADDR, SSB);
4387ff178cdSJimmy Vetayases 		outb(CMOS_DATA, BIOS_SHUTDOWN);
4397ff178cdSJimmy Vetayases 	}
4407ff178cdSJimmy Vetayases 
4417ff178cdSJimmy Vetayases 	/*
4427ff178cdSJimmy Vetayases 	 * According to X2APIC specification in section '2.3.5.1' of
4437ff178cdSJimmy Vetayases 	 * Interrupt Command Register Semantics, the semantics of
4447ff178cdSJimmy Vetayases 	 * programming the Interrupt Command Register to dispatch an interrupt
4457ff178cdSJimmy Vetayases 	 * is simplified. A single MSR write to the 64-bit ICR is required
4467ff178cdSJimmy Vetayases 	 * for dispatching an interrupt. Specifically, with the 64-bit MSR
4477ff178cdSJimmy Vetayases 	 * interface to ICR, system software is not required to check the
4487ff178cdSJimmy Vetayases 	 * status of the delivery status bit prior to writing to the ICR
4497ff178cdSJimmy Vetayases 	 * to send an IPI. With the removal of the Delivery Status bit,
4507ff178cdSJimmy Vetayases 	 * system software no longer has a reason to read the ICR. It remains
4517ff178cdSJimmy Vetayases 	 * readable only to aid in debugging.
4527ff178cdSJimmy Vetayases 	 */
4537ff178cdSJimmy Vetayases #ifdef	DEBUG
4547ff178cdSJimmy Vetayases 	APIC_AV_PENDING_SET();
4557ff178cdSJimmy Vetayases #else
4567ff178cdSJimmy Vetayases 	if (apic_mode == LOCAL_APIC) {
4577ff178cdSJimmy Vetayases 		APIC_AV_PENDING_SET();
4587ff178cdSJimmy Vetayases 	}
4597ff178cdSJimmy Vetayases #endif /* DEBUG */
4607ff178cdSJimmy Vetayases 
4617ff178cdSJimmy Vetayases 	/* for integrated - make sure there is one INIT IPI in buffer */
4627ff178cdSJimmy Vetayases 	/* for external - it will wake up the cpu */
4637ff178cdSJimmy Vetayases 	apic_reg_ops->apic_write_int_cmd(apicid, AV_ASSERT | AV_RESET);
4647ff178cdSJimmy Vetayases 
4657ff178cdSJimmy Vetayases 	/* If only 1 CPU is installed, PENDING bit will not go low */
4667ff178cdSJimmy Vetayases 	for (loop_count = apic_sipi_max_loop_count; loop_count; loop_count--) {
4677ff178cdSJimmy Vetayases 		if (apic_mode == LOCAL_APIC &&
4687ff178cdSJimmy Vetayases 		    apic_reg_ops->apic_read(APIC_INT_CMD1) & AV_PENDING)
4697ff178cdSJimmy Vetayases 			apic_ret();
4707ff178cdSJimmy Vetayases 		else
4717ff178cdSJimmy Vetayases 			break;
4727ff178cdSJimmy Vetayases 	}
4737ff178cdSJimmy Vetayases 
4747ff178cdSJimmy Vetayases 	apic_reg_ops->apic_write_int_cmd(apicid, AV_DEASSERT | AV_RESET);
4757ff178cdSJimmy Vetayases 	drv_usecwait(20000);		/* 20 milli sec */
4767ff178cdSJimmy Vetayases 
4777ff178cdSJimmy Vetayases 	if (apic_cpus[cpun].aci_local_ver >= APIC_INTEGRATED_VERS) {
4787ff178cdSJimmy Vetayases 		/* integrated apic */
4797ff178cdSJimmy Vetayases 
4807ff178cdSJimmy Vetayases 		vector = (rm_platter_pa >> MMU_PAGESHIFT) &
4817ff178cdSJimmy Vetayases 		    (APIC_VECTOR_MASK | APIC_IPL_MASK);
4827ff178cdSJimmy Vetayases 
4837ff178cdSJimmy Vetayases 		/* to offset the INIT IPI queue up in the buffer */
4847ff178cdSJimmy Vetayases 		apic_reg_ops->apic_write_int_cmd(apicid, vector | AV_STARTUP);
4857ff178cdSJimmy Vetayases 		drv_usecwait(200);		/* 20 micro sec */
4867ff178cdSJimmy Vetayases 
4877ff178cdSJimmy Vetayases 		/*
4887ff178cdSJimmy Vetayases 		 * send the second SIPI (Startup IPI) as recommended by Intel
4897ff178cdSJimmy Vetayases 		 * software development manual.
4907ff178cdSJimmy Vetayases 		 */
4917ff178cdSJimmy Vetayases 		apic_reg_ops->apic_write_int_cmd(apicid, vector | AV_STARTUP);
4927ff178cdSJimmy Vetayases 		drv_usecwait(200);	/* 20 micro sec */
4937ff178cdSJimmy Vetayases 	}
4947ff178cdSJimmy Vetayases 
4957ff178cdSJimmy Vetayases 	intr_restore(iflag);
4967ff178cdSJimmy Vetayases }
4977ff178cdSJimmy Vetayases 
4987ff178cdSJimmy Vetayases /*ARGSUSED1*/
4997ff178cdSJimmy Vetayases int
5007ff178cdSJimmy Vetayases apic_cpu_start(processorid_t cpun, caddr_t arg)
5017ff178cdSJimmy Vetayases {
5027ff178cdSJimmy Vetayases 	ASSERT(MUTEX_HELD(&cpu_lock));
5037ff178cdSJimmy Vetayases 
5047ff178cdSJimmy Vetayases 	if (!apic_cpu_in_range(cpun)) {
5057ff178cdSJimmy Vetayases 		return (EINVAL);
5067ff178cdSJimmy Vetayases 	}
5077ff178cdSJimmy Vetayases 
5087ff178cdSJimmy Vetayases 	/*
5097ff178cdSJimmy Vetayases 	 * Switch to apic_common_send_ipi for safety during starting other CPUs.
5107ff178cdSJimmy Vetayases 	 */
5117ff178cdSJimmy Vetayases 	if (apic_mode == LOCAL_X2APIC) {
5127ff178cdSJimmy Vetayases 		apic_switch_ipi_callback(B_TRUE);
5137ff178cdSJimmy Vetayases 	}
5147ff178cdSJimmy Vetayases 
5157ff178cdSJimmy Vetayases 	apic_cmos_ssb_set = 1;
5167ff178cdSJimmy Vetayases 	apic_cpu_send_SIPI(cpun, B_TRUE);
5177ff178cdSJimmy Vetayases 
5187ff178cdSJimmy Vetayases 	return (0);
5197ff178cdSJimmy Vetayases }
5207ff178cdSJimmy Vetayases 
5217ff178cdSJimmy Vetayases /*
5227ff178cdSJimmy Vetayases  * Put CPU into halted state with interrupts disabled.
5237ff178cdSJimmy Vetayases  */
5247ff178cdSJimmy Vetayases /*ARGSUSED1*/
5257ff178cdSJimmy Vetayases int
5267ff178cdSJimmy Vetayases apic_cpu_stop(processorid_t cpun, caddr_t arg)
5277ff178cdSJimmy Vetayases {
5287ff178cdSJimmy Vetayases 	int		rc;
52958b49504SHans Rosenfeld 	cpu_t		*cp;
5307ff178cdSJimmy Vetayases 	extern cpuset_t cpu_ready_set;
5317ff178cdSJimmy Vetayases 	extern void cpu_idle_intercept_cpu(cpu_t *cp);
5327ff178cdSJimmy Vetayases 
5337ff178cdSJimmy Vetayases 	ASSERT(MUTEX_HELD(&cpu_lock));
5347ff178cdSJimmy Vetayases 
5357ff178cdSJimmy Vetayases 	if (!apic_cpu_in_range(cpun)) {
5367ff178cdSJimmy Vetayases 		return (EINVAL);
5377ff178cdSJimmy Vetayases 	}
5387ff178cdSJimmy Vetayases 	if (apic_cpus[cpun].aci_local_ver < APIC_INTEGRATED_VERS) {
5397ff178cdSJimmy Vetayases 		return (ENOTSUP);
5407ff178cdSJimmy Vetayases 	}
5417ff178cdSJimmy Vetayases 
5427ff178cdSJimmy Vetayases 	cp = cpu_get(cpun);
5437ff178cdSJimmy Vetayases 	ASSERT(cp != NULL);
5447ff178cdSJimmy Vetayases 	ASSERT((cp->cpu_flags & CPU_OFFLINE) != 0);
5457ff178cdSJimmy Vetayases 	ASSERT((cp->cpu_flags & CPU_QUIESCED) != 0);
5467ff178cdSJimmy Vetayases 	ASSERT((cp->cpu_flags & CPU_ENABLE) == 0);
5477ff178cdSJimmy Vetayases 
5487ff178cdSJimmy Vetayases 	/* Clear CPU_READY flag to disable cross calls. */
5497ff178cdSJimmy Vetayases 	cp->cpu_flags &= ~CPU_READY;
5507ff178cdSJimmy Vetayases 	CPUSET_ATOMIC_DEL(cpu_ready_set, cpun);
5517ff178cdSJimmy Vetayases 	rc = xc_flush_cpu(cp);
5527ff178cdSJimmy Vetayases 	if (rc != 0) {
5537ff178cdSJimmy Vetayases 		CPUSET_ATOMIC_ADD(cpu_ready_set, cpun);
5547ff178cdSJimmy Vetayases 		cp->cpu_flags |= CPU_READY;
5557ff178cdSJimmy Vetayases 		return (rc);
5567ff178cdSJimmy Vetayases 	}
5577ff178cdSJimmy Vetayases 
5587ff178cdSJimmy Vetayases 	/* Intercept target CPU at a safe point before powering it off. */
5597ff178cdSJimmy Vetayases 	cpu_idle_intercept_cpu(cp);
5607ff178cdSJimmy Vetayases 
5617ff178cdSJimmy Vetayases 	apic_cpu_send_SIPI(cpun, B_FALSE);
5627ff178cdSJimmy Vetayases 	cp->cpu_flags &= ~CPU_RUNNING;
5637ff178cdSJimmy Vetayases 
5647ff178cdSJimmy Vetayases 	return (0);
5657ff178cdSJimmy Vetayases }
5667ff178cdSJimmy Vetayases 
5677ff178cdSJimmy Vetayases int
5687ff178cdSJimmy Vetayases apic_cpu_ops(psm_cpu_request_t *reqp)
5697ff178cdSJimmy Vetayases {
5707ff178cdSJimmy Vetayases 	if (reqp == NULL) {
5717ff178cdSJimmy Vetayases 		return (EINVAL);
5727ff178cdSJimmy Vetayases 	}
5737ff178cdSJimmy Vetayases 
5747ff178cdSJimmy Vetayases 	switch (reqp->pcr_cmd) {
5757ff178cdSJimmy Vetayases 	case PSM_CPU_ADD:
5767ff178cdSJimmy Vetayases 		return (apic_cpu_add(reqp));
5777ff178cdSJimmy Vetayases 
5787ff178cdSJimmy Vetayases 	case PSM_CPU_REMOVE:
5797ff178cdSJimmy Vetayases 		return (apic_cpu_remove(reqp));
5807ff178cdSJimmy Vetayases 
5817ff178cdSJimmy Vetayases 	case PSM_CPU_STOP:
5827ff178cdSJimmy Vetayases 		return (apic_cpu_stop(reqp->req.cpu_stop.cpuid,
5837ff178cdSJimmy Vetayases 		    reqp->req.cpu_stop.ctx));
5847ff178cdSJimmy Vetayases 
5857ff178cdSJimmy Vetayases 	default:
5867ff178cdSJimmy Vetayases 		return (ENOTSUP);
5877ff178cdSJimmy Vetayases 	}
5887ff178cdSJimmy Vetayases }
5897ff178cdSJimmy Vetayases 
5907ff178cdSJimmy Vetayases #ifdef	DEBUG
5917ff178cdSJimmy Vetayases int	apic_break_on_cpu = 9;
5927ff178cdSJimmy Vetayases int	apic_stretch_interrupts = 0;
5937ff178cdSJimmy Vetayases int	apic_stretch_ISR = 1 << 3;	/* IPL of 3 matches nothing now */
5947ff178cdSJimmy Vetayases #endif /* DEBUG */
5957ff178cdSJimmy Vetayases 
5967ff178cdSJimmy Vetayases /*
5977ff178cdSJimmy Vetayases  * generates an interprocessor interrupt to another CPU. Any changes made to
5987ff178cdSJimmy Vetayases  * this routine must be accompanied by similar changes to
5997ff178cdSJimmy Vetayases  * apic_common_send_ipi().
6007ff178cdSJimmy Vetayases  */
6017ff178cdSJimmy Vetayases void
6027ff178cdSJimmy Vetayases apic_send_ipi(int cpun, int ipl)
6037ff178cdSJimmy Vetayases {
6047ff178cdSJimmy Vetayases 	int vector;
6057ff178cdSJimmy Vetayases 	ulong_t flag;
6067ff178cdSJimmy Vetayases 
6077ff178cdSJimmy Vetayases 	vector = apic_resv_vector[ipl];
6087ff178cdSJimmy Vetayases 
6097ff178cdSJimmy Vetayases 	ASSERT((vector >= APIC_BASE_VECT) && (vector <= APIC_SPUR_INTR));
6107ff178cdSJimmy Vetayases 
6117ff178cdSJimmy Vetayases 	flag = intr_clear();
6127ff178cdSJimmy Vetayases 
6137ff178cdSJimmy Vetayases 	APIC_AV_PENDING_SET();
6147ff178cdSJimmy Vetayases 
6157ff178cdSJimmy Vetayases 	apic_reg_ops->apic_write_int_cmd(apic_cpus[cpun].aci_local_id,
6167ff178cdSJimmy Vetayases 	    vector);
6177ff178cdSJimmy Vetayases 
6187ff178cdSJimmy Vetayases 	intr_restore(flag);
6197ff178cdSJimmy Vetayases }
6207ff178cdSJimmy Vetayases 
6211c2d0470SPatrick Mooney void
6221c2d0470SPatrick Mooney apic_send_pir_ipi(processorid_t cpun)
6231c2d0470SPatrick Mooney {
6241c2d0470SPatrick Mooney 	const int vector = apic_pir_vect;
6251c2d0470SPatrick Mooney 	ulong_t flag;
6261c2d0470SPatrick Mooney 
6271c2d0470SPatrick Mooney 	ASSERT((vector >= APIC_BASE_VECT) && (vector <= APIC_SPUR_INTR));
6281c2d0470SPatrick Mooney 
6291c2d0470SPatrick Mooney 	flag = intr_clear();
6301c2d0470SPatrick Mooney 
6311c2d0470SPatrick Mooney 	/* Self-IPI for inducing PIR makes no sense. */
6321c2d0470SPatrick Mooney 	if ((cpun != psm_get_cpu_id())) {
6331c2d0470SPatrick Mooney 		APIC_AV_PENDING_SET();
6341c2d0470SPatrick Mooney 		apic_reg_ops->apic_write_int_cmd(apic_cpus[cpun].aci_local_id,
6351c2d0470SPatrick Mooney 		    vector);
6361c2d0470SPatrick Mooney 	}
6371c2d0470SPatrick Mooney 
6381c2d0470SPatrick Mooney 	intr_restore(flag);
6391c2d0470SPatrick Mooney }
6401c2d0470SPatrick Mooney 
6411c2d0470SPatrick Mooney int
6421c2d0470SPatrick Mooney apic_get_pir_ipivect(void)
6431c2d0470SPatrick Mooney {
6441c2d0470SPatrick Mooney 	return (apic_pir_vect);
6451c2d0470SPatrick Mooney }
6467ff178cdSJimmy Vetayases 
6477ff178cdSJimmy Vetayases /*ARGSUSED*/
6487ff178cdSJimmy Vetayases void
6497ff178cdSJimmy Vetayases apic_set_idlecpu(processorid_t cpun)
6507ff178cdSJimmy Vetayases {
6517ff178cdSJimmy Vetayases }
6527ff178cdSJimmy Vetayases 
6537ff178cdSJimmy Vetayases /*ARGSUSED*/
6547ff178cdSJimmy Vetayases void
6557ff178cdSJimmy Vetayases apic_unset_idlecpu(processorid_t cpun)
6567ff178cdSJimmy Vetayases {
6577ff178cdSJimmy Vetayases }
6587ff178cdSJimmy Vetayases 
6597ff178cdSJimmy Vetayases 
6607ff178cdSJimmy Vetayases void
6617ff178cdSJimmy Vetayases apic_ret()
6627ff178cdSJimmy Vetayases {
6637ff178cdSJimmy Vetayases }
6647ff178cdSJimmy Vetayases 
6657ff178cdSJimmy Vetayases /*
6667ff178cdSJimmy Vetayases  * If apic_coarse_time == 1, then apic_gettime() is used instead of
6677ff178cdSJimmy Vetayases  * apic_gethrtime().  This is used for performance instead of accuracy.
6687ff178cdSJimmy Vetayases  */
6697ff178cdSJimmy Vetayases 
6707ff178cdSJimmy Vetayases hrtime_t
6717ff178cdSJimmy Vetayases apic_gettime()
6727ff178cdSJimmy Vetayases {
6737ff178cdSJimmy Vetayases 	int old_hrtime_stamp;
6747ff178cdSJimmy Vetayases 	hrtime_t temp;
6757ff178cdSJimmy Vetayases 
6767ff178cdSJimmy Vetayases 	/*
6777ff178cdSJimmy Vetayases 	 * In one-shot mode, we do not keep time, so if anyone
6787ff178cdSJimmy Vetayases 	 * calls psm_gettime() directly, we vector over to
6797ff178cdSJimmy Vetayases 	 * gethrtime().
6807ff178cdSJimmy Vetayases 	 * one-shot mode MUST NOT be enabled if this psm is the source of
6817ff178cdSJimmy Vetayases 	 * hrtime.
6827ff178cdSJimmy Vetayases 	 */
6837ff178cdSJimmy Vetayases 
6847ff178cdSJimmy Vetayases 	if (apic_oneshot)
6857ff178cdSJimmy Vetayases 		return (gethrtime());
6867ff178cdSJimmy Vetayases 
6877ff178cdSJimmy Vetayases 
6887ff178cdSJimmy Vetayases gettime_again:
6897ff178cdSJimmy Vetayases 	while ((old_hrtime_stamp = apic_hrtime_stamp) & 1)
6907ff178cdSJimmy Vetayases 		apic_ret();
6917ff178cdSJimmy Vetayases 
6927ff178cdSJimmy Vetayases 	temp = apic_nsec_since_boot;
6937ff178cdSJimmy Vetayases 
6947ff178cdSJimmy Vetayases 	if (apic_hrtime_stamp != old_hrtime_stamp) {	/* got an interrupt */
6957ff178cdSJimmy Vetayases 		goto gettime_again;
6967ff178cdSJimmy Vetayases 	}
6977ff178cdSJimmy Vetayases 	return (temp);
6987ff178cdSJimmy Vetayases }
6997ff178cdSJimmy Vetayases 
7007ff178cdSJimmy Vetayases /*
7017ff178cdSJimmy Vetayases  * Here we return the number of nanoseconds since booting.  Note every
7027ff178cdSJimmy Vetayases  * clock interrupt increments apic_nsec_since_boot by the appropriate
7037ff178cdSJimmy Vetayases  * amount.
7047ff178cdSJimmy Vetayases  */
7057ff178cdSJimmy Vetayases hrtime_t
7067ff178cdSJimmy Vetayases apic_gethrtime(void)
7077ff178cdSJimmy Vetayases {
7087ff178cdSJimmy Vetayases 	int curr_timeval, countval, elapsed_ticks;
7097ff178cdSJimmy Vetayases 	int old_hrtime_stamp, status;
7107ff178cdSJimmy Vetayases 	hrtime_t temp;
7117ff178cdSJimmy Vetayases 	uint32_t cpun;
7127ff178cdSJimmy Vetayases 	ulong_t oflags;
7137ff178cdSJimmy Vetayases 
7147ff178cdSJimmy Vetayases 	/*
7157ff178cdSJimmy Vetayases 	 * In one-shot mode, we do not keep time, so if anyone
7167ff178cdSJimmy Vetayases 	 * calls psm_gethrtime() directly, we vector over to
7177ff178cdSJimmy Vetayases 	 * gethrtime().
7187ff178cdSJimmy Vetayases 	 * one-shot mode MUST NOT be enabled if this psm is the source of
7197ff178cdSJimmy Vetayases 	 * hrtime.
7207ff178cdSJimmy Vetayases 	 */
7217ff178cdSJimmy Vetayases 
7227ff178cdSJimmy Vetayases 	if (apic_oneshot)
7237ff178cdSJimmy Vetayases 		return (gethrtime());
7247ff178cdSJimmy Vetayases 
7257ff178cdSJimmy Vetayases 	oflags = intr_clear();	/* prevent migration */
7267ff178cdSJimmy Vetayases 
7277ff178cdSJimmy Vetayases 	cpun = apic_reg_ops->apic_read(APIC_LID_REG);
7287ff178cdSJimmy Vetayases 	if (apic_mode == LOCAL_APIC)
7297ff178cdSJimmy Vetayases 		cpun >>= APIC_ID_BIT_OFFSET;
7307ff178cdSJimmy Vetayases 
7317ff178cdSJimmy Vetayases 	lock_set(&apic_gethrtime_lock);
7327ff178cdSJimmy Vetayases 
7337ff178cdSJimmy Vetayases gethrtime_again:
7347ff178cdSJimmy Vetayases 	while ((old_hrtime_stamp = apic_hrtime_stamp) & 1)
7357ff178cdSJimmy Vetayases 		apic_ret();
7367ff178cdSJimmy Vetayases 
7377ff178cdSJimmy Vetayases 	/*
7387ff178cdSJimmy Vetayases 	 * Check to see which CPU we are on.  Note the time is kept on
7397ff178cdSJimmy Vetayases 	 * the local APIC of CPU 0.  If on CPU 0, simply read the current
7407ff178cdSJimmy Vetayases 	 * counter.  If on another CPU, issue a remote read command to CPU 0.
7417ff178cdSJimmy Vetayases 	 */
7427ff178cdSJimmy Vetayases 	if (cpun == apic_cpus[0].aci_local_id) {
7437ff178cdSJimmy Vetayases 		countval = apic_reg_ops->apic_read(APIC_CURR_COUNT);
7447ff178cdSJimmy Vetayases 	} else {
7457ff178cdSJimmy Vetayases #ifdef	DEBUG
7467ff178cdSJimmy Vetayases 		APIC_AV_PENDING_SET();
7477ff178cdSJimmy Vetayases #else
7487ff178cdSJimmy Vetayases 		if (apic_mode == LOCAL_APIC)
7497ff178cdSJimmy Vetayases 			APIC_AV_PENDING_SET();
7507ff178cdSJimmy Vetayases #endif /* DEBUG */
7517ff178cdSJimmy Vetayases 
7527ff178cdSJimmy Vetayases 		apic_reg_ops->apic_write_int_cmd(
7537ff178cdSJimmy Vetayases 		    apic_cpus[0].aci_local_id, APIC_CURR_ADD | AV_REMOTE);
7547ff178cdSJimmy Vetayases 
7557ff178cdSJimmy Vetayases 		while ((status = apic_reg_ops->apic_read(APIC_INT_CMD1))
7567ff178cdSJimmy Vetayases 		    & AV_READ_PENDING) {
7577ff178cdSJimmy Vetayases 			apic_ret();
7587ff178cdSJimmy Vetayases 		}
7597ff178cdSJimmy Vetayases 
7607ff178cdSJimmy Vetayases 		if (status & AV_REMOTE_STATUS)	/* 1 = valid */
7617ff178cdSJimmy Vetayases 			countval = apic_reg_ops->apic_read(APIC_REMOTE_READ);
7627ff178cdSJimmy Vetayases 		else {	/* 0 = invalid */
7637ff178cdSJimmy Vetayases 			apic_remote_hrterr++;
7647ff178cdSJimmy Vetayases 			/*
7657ff178cdSJimmy Vetayases 			 * return last hrtime right now, will need more
7667ff178cdSJimmy Vetayases 			 * testing if change to retry
7677ff178cdSJimmy Vetayases 			 */
7687ff178cdSJimmy Vetayases 			temp = apic_last_hrtime;
7697ff178cdSJimmy Vetayases 
7707ff178cdSJimmy Vetayases 			lock_clear(&apic_gethrtime_lock);
7717ff178cdSJimmy Vetayases 
7727ff178cdSJimmy Vetayases 			intr_restore(oflags);
7737ff178cdSJimmy Vetayases 
7747ff178cdSJimmy Vetayases 			return (temp);
7757ff178cdSJimmy Vetayases 		}
7767ff178cdSJimmy Vetayases 	}
7777ff178cdSJimmy Vetayases 	if (countval > last_count_read)
7787ff178cdSJimmy Vetayases 		countval = 0;
7797ff178cdSJimmy Vetayases 	else
7807ff178cdSJimmy Vetayases 		last_count_read = countval;
7817ff178cdSJimmy Vetayases 
7827ff178cdSJimmy Vetayases 	elapsed_ticks = apic_hertz_count - countval;
7837ff178cdSJimmy Vetayases 
7847ff178cdSJimmy Vetayases 	curr_timeval = APIC_TICKS_TO_NSECS(elapsed_ticks);
7857ff178cdSJimmy Vetayases 	temp = apic_nsec_since_boot + curr_timeval;
7867ff178cdSJimmy Vetayases 
7877ff178cdSJimmy Vetayases 	if (apic_hrtime_stamp != old_hrtime_stamp) {	/* got an interrupt */
7887ff178cdSJimmy Vetayases 		/* we might have clobbered last_count_read. Restore it */
7897ff178cdSJimmy Vetayases 		last_count_read = apic_hertz_count;
7907ff178cdSJimmy Vetayases 		goto gethrtime_again;
7917ff178cdSJimmy Vetayases 	}
7927ff178cdSJimmy Vetayases 
7937ff178cdSJimmy Vetayases 	if (temp < apic_last_hrtime) {
7947ff178cdSJimmy Vetayases 		/* return last hrtime if error occurs */
7957ff178cdSJimmy Vetayases 		apic_hrtime_error++;
7967ff178cdSJimmy Vetayases 		temp = apic_last_hrtime;
7977ff178cdSJimmy Vetayases 	}
7987ff178cdSJimmy Vetayases 	else
7997ff178cdSJimmy Vetayases 		apic_last_hrtime = temp;
8007ff178cdSJimmy Vetayases 
8017ff178cdSJimmy Vetayases 	lock_clear(&apic_gethrtime_lock);
8027ff178cdSJimmy Vetayases 	intr_restore(oflags);
8037ff178cdSJimmy Vetayases 
8047ff178cdSJimmy Vetayases 	return (temp);
8057ff178cdSJimmy Vetayases }
8067ff178cdSJimmy Vetayases 
8077ff178cdSJimmy Vetayases /* apic NMI handler */
8087ff178cdSJimmy Vetayases /*ARGSUSED*/
8097ff178cdSJimmy Vetayases void
8107ff178cdSJimmy Vetayases apic_nmi_intr(caddr_t arg, struct regs *rp)
8117ff178cdSJimmy Vetayases {
8120c26abfeSJohn Levon 	nmi_action_t action = nmi_action;
8130c26abfeSJohn Levon 
8147ff178cdSJimmy Vetayases 	if (apic_shutdown_processors) {
8157ff178cdSJimmy Vetayases 		apic_disable_local_apic();
8167ff178cdSJimmy Vetayases 		return;
8177ff178cdSJimmy Vetayases 	}
8187ff178cdSJimmy Vetayases 
8197ff178cdSJimmy Vetayases 	apic_error |= APIC_ERR_NMI;
8207ff178cdSJimmy Vetayases 
8217ff178cdSJimmy Vetayases 	if (!lock_try(&apic_nmi_lock))
8227ff178cdSJimmy Vetayases 		return;
8237ff178cdSJimmy Vetayases 	apic_num_nmis++;
8247ff178cdSJimmy Vetayases 
8250c26abfeSJohn Levon 	/*
8260c26abfeSJohn Levon 	 * "nmi_action" always over-rides the older way of doing this, unless we
8270c26abfeSJohn Levon 	 * can't actually drop into kmdb when requested.
8280c26abfeSJohn Levon 	 */
8290c26abfeSJohn Levon 	if (action == NMI_ACTION_KMDB && !psm_debugger())
8300c26abfeSJohn Levon 		action = NMI_ACTION_UNSET;
8310c26abfeSJohn Levon 
8320c26abfeSJohn Levon 	if (action == NMI_ACTION_UNSET) {
8330c26abfeSJohn Levon 		if (apic_kmdb_on_nmi && psm_debugger())
8340c26abfeSJohn Levon 			action = NMI_ACTION_KMDB;
8350c26abfeSJohn Levon 		else if (apic_panic_on_nmi)
8360c26abfeSJohn Levon 			action = NMI_ACTION_PANIC;
8370c26abfeSJohn Levon 		else
8380c26abfeSJohn Levon 			action = NMI_ACTION_IGNORE;
8390c26abfeSJohn Levon 	}
8400c26abfeSJohn Levon 
8410c26abfeSJohn Levon 	switch (action) {
8420c26abfeSJohn Levon 	case NMI_ACTION_IGNORE:
8437ff178cdSJimmy Vetayases 		/*
8447ff178cdSJimmy Vetayases 		 * prom_printf is the best shot we have of something which is
8457ff178cdSJimmy Vetayases 		 * problem free from high level/NMI type of interrupts
8467ff178cdSJimmy Vetayases 		 */
8477ff178cdSJimmy Vetayases 		prom_printf("NMI received\n");
8480c26abfeSJohn Levon 		break;
8490c26abfeSJohn Levon 
8500c26abfeSJohn Levon 	case NMI_ACTION_PANIC:
8510c26abfeSJohn Levon 		/* Keep panic from entering kmdb. */
8520c26abfeSJohn Levon 		nopanicdebug = 1;
8530c26abfeSJohn Levon 		panic("NMI received\n");
8540c26abfeSJohn Levon 		break;
8550c26abfeSJohn Levon 
8560c26abfeSJohn Levon 	case NMI_ACTION_KMDB:
8570c26abfeSJohn Levon 	default:
8580c26abfeSJohn Levon 		debug_enter("NMI received: entering kmdb\n");
8590c26abfeSJohn Levon 		break;
8607ff178cdSJimmy Vetayases 	}
8617ff178cdSJimmy Vetayases 
8627ff178cdSJimmy Vetayases 	lock_clear(&apic_nmi_lock);
8637ff178cdSJimmy Vetayases }
8647ff178cdSJimmy Vetayases 
8657ff178cdSJimmy Vetayases processorid_t
8667ff178cdSJimmy Vetayases apic_get_next_processorid(processorid_t cpu_id)
8677ff178cdSJimmy Vetayases {
8687ff178cdSJimmy Vetayases 
8697ff178cdSJimmy Vetayases 	int i;
8707ff178cdSJimmy Vetayases 
8717ff178cdSJimmy Vetayases 	if (cpu_id == -1)
8727ff178cdSJimmy Vetayases 		return ((processorid_t)0);
8737ff178cdSJimmy Vetayases 
8747ff178cdSJimmy Vetayases 	for (i = cpu_id + 1; i < NCPU; i++) {
8757ff178cdSJimmy Vetayases 		if (apic_cpu_in_range(i))
8767ff178cdSJimmy Vetayases 			return (i);
8777ff178cdSJimmy Vetayases 	}
8787ff178cdSJimmy Vetayases 
8797ff178cdSJimmy Vetayases 	return ((processorid_t)-1);
8807ff178cdSJimmy Vetayases }
8817ff178cdSJimmy Vetayases 
8827ff178cdSJimmy Vetayases int
8837ff178cdSJimmy Vetayases apic_cpu_add(psm_cpu_request_t *reqp)
8847ff178cdSJimmy Vetayases {
8857ff178cdSJimmy Vetayases 	int i, rv = 0;
8867ff178cdSJimmy Vetayases 	ulong_t iflag;
8877ff178cdSJimmy Vetayases 	boolean_t first = B_TRUE;
88830acb30dSHans Rosenfeld 	uchar_t localver = 0;
8897ff178cdSJimmy Vetayases 	uint32_t localid, procid;
8907ff178cdSJimmy Vetayases 	processorid_t cpuid = (processorid_t)-1;
8917ff178cdSJimmy Vetayases 	mach_cpu_add_arg_t *ap;
8927ff178cdSJimmy Vetayases 
8937ff178cdSJimmy Vetayases 	ASSERT(reqp != NULL);
8947ff178cdSJimmy Vetayases 	reqp->req.cpu_add.cpuid = (processorid_t)-1;
8957ff178cdSJimmy Vetayases 
8967ff178cdSJimmy Vetayases 	/* Check whether CPU hotplug is supported. */
8977ff178cdSJimmy Vetayases 	if (!plat_dr_support_cpu() || apic_max_nproc == -1) {
8987ff178cdSJimmy Vetayases 		return (ENOTSUP);
8997ff178cdSJimmy Vetayases 	}
9007ff178cdSJimmy Vetayases 
9017ff178cdSJimmy Vetayases 	ap = (mach_cpu_add_arg_t *)reqp->req.cpu_add.argp;
9027ff178cdSJimmy Vetayases 	switch (ap->type) {
9037ff178cdSJimmy Vetayases 	case MACH_CPU_ARG_LOCAL_APIC:
9047ff178cdSJimmy Vetayases 		localid = ap->arg.apic.apic_id;
9057ff178cdSJimmy Vetayases 		procid = ap->arg.apic.proc_id;
9067ff178cdSJimmy Vetayases 		if (localid >= 255 || procid > 255) {
9077ff178cdSJimmy Vetayases 			cmn_err(CE_WARN,
9087ff178cdSJimmy Vetayases 			    "!apic: apicid(%u) or procid(%u) is invalid.",
9097ff178cdSJimmy Vetayases 			    localid, procid);
9107ff178cdSJimmy Vetayases 			return (EINVAL);
9117ff178cdSJimmy Vetayases 		}
9127ff178cdSJimmy Vetayases 		break;
9137ff178cdSJimmy Vetayases 
9147ff178cdSJimmy Vetayases 	case MACH_CPU_ARG_LOCAL_X2APIC:
9157ff178cdSJimmy Vetayases 		localid = ap->arg.apic.apic_id;
9167ff178cdSJimmy Vetayases 		procid = ap->arg.apic.proc_id;
9177ff178cdSJimmy Vetayases 		if (localid >= UINT32_MAX) {
9187ff178cdSJimmy Vetayases 			cmn_err(CE_WARN,
9197ff178cdSJimmy Vetayases 			    "!apic: x2apicid(%u) is invalid.", localid);
9207ff178cdSJimmy Vetayases 			return (EINVAL);
9217ff178cdSJimmy Vetayases 		} else if (localid >= 255 && apic_mode == LOCAL_APIC) {
9227ff178cdSJimmy Vetayases 			cmn_err(CE_WARN, "!apic: system is in APIC mode, "
9237ff178cdSJimmy Vetayases 			    "can't support x2APIC processor.");
9247ff178cdSJimmy Vetayases 			return (ENOTSUP);
9257ff178cdSJimmy Vetayases 		}
9267ff178cdSJimmy Vetayases 		break;
9277ff178cdSJimmy Vetayases 
9287ff178cdSJimmy Vetayases 	default:
9297ff178cdSJimmy Vetayases 		cmn_err(CE_WARN,
9307ff178cdSJimmy Vetayases 		    "!apic: unknown argument type %d to apic_cpu_add().",
9317ff178cdSJimmy Vetayases 		    ap->type);
9327ff178cdSJimmy Vetayases 		return (EINVAL);
9337ff178cdSJimmy Vetayases 	}
9347ff178cdSJimmy Vetayases 
9357ff178cdSJimmy Vetayases 	/* Use apic_ioapic_lock to sync with apic_get_next_bind_cpu. */
9367ff178cdSJimmy Vetayases 	iflag = intr_clear();
9377ff178cdSJimmy Vetayases 	lock_set(&apic_ioapic_lock);
9387ff178cdSJimmy Vetayases 
9397ff178cdSJimmy Vetayases 	/* Check whether local APIC id already exists. */
9407ff178cdSJimmy Vetayases 	for (i = 0; i < apic_nproc; i++) {
9417ff178cdSJimmy Vetayases 		if (!CPU_IN_SET(apic_cpumask, i))
9427ff178cdSJimmy Vetayases 			continue;
9437ff178cdSJimmy Vetayases 		if (apic_cpus[i].aci_local_id == localid) {
9447ff178cdSJimmy Vetayases 			lock_clear(&apic_ioapic_lock);
9457ff178cdSJimmy Vetayases 			intr_restore(iflag);
9467ff178cdSJimmy Vetayases 			cmn_err(CE_WARN,
9477ff178cdSJimmy Vetayases 			    "!apic: local apic id %u already exists.",
9487ff178cdSJimmy Vetayases 			    localid);
9497ff178cdSJimmy Vetayases 			return (EEXIST);
9507ff178cdSJimmy Vetayases 		} else if (apic_cpus[i].aci_processor_id == procid) {
9517ff178cdSJimmy Vetayases 			lock_clear(&apic_ioapic_lock);
9527ff178cdSJimmy Vetayases 			intr_restore(iflag);
9537ff178cdSJimmy Vetayases 			cmn_err(CE_WARN,
9547ff178cdSJimmy Vetayases 			    "!apic: processor id %u already exists.",
9557ff178cdSJimmy Vetayases 			    (int)procid);
9567ff178cdSJimmy Vetayases 			return (EEXIST);
9577ff178cdSJimmy Vetayases 		}
9587ff178cdSJimmy Vetayases 
9597ff178cdSJimmy Vetayases 		/*
9607ff178cdSJimmy Vetayases 		 * There's no local APIC version number available in MADT table,
9617ff178cdSJimmy Vetayases 		 * so assume that all CPUs are homogeneous and use local APIC
9627ff178cdSJimmy Vetayases 		 * version number of the first existing CPU.
9637ff178cdSJimmy Vetayases 		 */
9647ff178cdSJimmy Vetayases 		if (first) {
9657ff178cdSJimmy Vetayases 			first = B_FALSE;
9667ff178cdSJimmy Vetayases 			localver = apic_cpus[i].aci_local_ver;
9677ff178cdSJimmy Vetayases 		}
9687ff178cdSJimmy Vetayases 	}
9697ff178cdSJimmy Vetayases 	ASSERT(first == B_FALSE);
9707ff178cdSJimmy Vetayases 
9717ff178cdSJimmy Vetayases 	/*
9727ff178cdSJimmy Vetayases 	 * Try to assign the same cpuid if APIC id exists in the dirty cache.
9737ff178cdSJimmy Vetayases 	 */
9747ff178cdSJimmy Vetayases 	for (i = 0; i < apic_max_nproc; i++) {
9757ff178cdSJimmy Vetayases 		if (CPU_IN_SET(apic_cpumask, i)) {
9767ff178cdSJimmy Vetayases 			ASSERT((apic_cpus[i].aci_status & APIC_CPU_FREE) == 0);
9777ff178cdSJimmy Vetayases 			continue;
9787ff178cdSJimmy Vetayases 		}
9797ff178cdSJimmy Vetayases 		ASSERT(apic_cpus[i].aci_status & APIC_CPU_FREE);
9807ff178cdSJimmy Vetayases 		if ((apic_cpus[i].aci_status & APIC_CPU_DIRTY) &&
9817ff178cdSJimmy Vetayases 		    apic_cpus[i].aci_local_id == localid &&
9827ff178cdSJimmy Vetayases 		    apic_cpus[i].aci_processor_id == procid) {
9837ff178cdSJimmy Vetayases 			cpuid = i;
9847ff178cdSJimmy Vetayases 			break;
9857ff178cdSJimmy Vetayases 		}
9867ff178cdSJimmy Vetayases 	}
9877ff178cdSJimmy Vetayases 
9887ff178cdSJimmy Vetayases 	/* Avoid the dirty cache and allocate fresh slot if possible. */
9897ff178cdSJimmy Vetayases 	if (cpuid == (processorid_t)-1) {
9907ff178cdSJimmy Vetayases 		for (i = 0; i < apic_max_nproc; i++) {
9917ff178cdSJimmy Vetayases 			if ((apic_cpus[i].aci_status & APIC_CPU_FREE) &&
9927ff178cdSJimmy Vetayases 			    (apic_cpus[i].aci_status & APIC_CPU_DIRTY) == 0) {
9937ff178cdSJimmy Vetayases 				cpuid = i;
9947ff178cdSJimmy Vetayases 				break;
9957ff178cdSJimmy Vetayases 			}
9967ff178cdSJimmy Vetayases 		}
9977ff178cdSJimmy Vetayases 	}
9987ff178cdSJimmy Vetayases 
9997ff178cdSJimmy Vetayases 	/* Try to find any free slot as last resort. */
10007ff178cdSJimmy Vetayases 	if (cpuid == (processorid_t)-1) {
10017ff178cdSJimmy Vetayases 		for (i = 0; i < apic_max_nproc; i++) {
10027ff178cdSJimmy Vetayases 			if (apic_cpus[i].aci_status & APIC_CPU_FREE) {
10037ff178cdSJimmy Vetayases 				cpuid = i;
10047ff178cdSJimmy Vetayases 				break;
10057ff178cdSJimmy Vetayases 			}
10067ff178cdSJimmy Vetayases 		}
10077ff178cdSJimmy Vetayases 	}
10087ff178cdSJimmy Vetayases 
10097ff178cdSJimmy Vetayases 	if (cpuid == (processorid_t)-1) {
10107ff178cdSJimmy Vetayases 		lock_clear(&apic_ioapic_lock);
10117ff178cdSJimmy Vetayases 		intr_restore(iflag);
10127ff178cdSJimmy Vetayases 		cmn_err(CE_NOTE,
10137ff178cdSJimmy Vetayases 		    "!apic: failed to allocate cpu id for processor %u.",
10147ff178cdSJimmy Vetayases 		    procid);
10157ff178cdSJimmy Vetayases 		rv = EAGAIN;
10167ff178cdSJimmy Vetayases 	} else if (ACPI_FAILURE(acpica_map_cpu(cpuid, procid))) {
10177ff178cdSJimmy Vetayases 		lock_clear(&apic_ioapic_lock);
10187ff178cdSJimmy Vetayases 		intr_restore(iflag);
10197ff178cdSJimmy Vetayases 		cmn_err(CE_NOTE,
10207ff178cdSJimmy Vetayases 		    "!apic: failed to build mapping for processor %u.",
10217ff178cdSJimmy Vetayases 		    procid);
10227ff178cdSJimmy Vetayases 		rv = EBUSY;
10237ff178cdSJimmy Vetayases 	} else {
10247ff178cdSJimmy Vetayases 		ASSERT(cpuid >= 0 && cpuid < NCPU);
10257ff178cdSJimmy Vetayases 		ASSERT(cpuid < apic_max_nproc && cpuid < max_ncpus);
10267ff178cdSJimmy Vetayases 		bzero(&apic_cpus[cpuid], sizeof (apic_cpus[0]));
10277ff178cdSJimmy Vetayases 		apic_cpus[cpuid].aci_processor_id = procid;
10287ff178cdSJimmy Vetayases 		apic_cpus[cpuid].aci_local_id = localid;
10297ff178cdSJimmy Vetayases 		apic_cpus[cpuid].aci_local_ver = localver;
10307ff178cdSJimmy Vetayases 		CPUSET_ATOMIC_ADD(apic_cpumask, cpuid);
10317ff178cdSJimmy Vetayases 		if (cpuid >= apic_nproc) {
10327ff178cdSJimmy Vetayases 			apic_nproc = cpuid + 1;
10337ff178cdSJimmy Vetayases 		}
10347ff178cdSJimmy Vetayases 		lock_clear(&apic_ioapic_lock);
10357ff178cdSJimmy Vetayases 		intr_restore(iflag);
10367ff178cdSJimmy Vetayases 		reqp->req.cpu_add.cpuid = cpuid;
10377ff178cdSJimmy Vetayases 	}
10387ff178cdSJimmy Vetayases 
10397ff178cdSJimmy Vetayases 	return (rv);
10407ff178cdSJimmy Vetayases }
10417ff178cdSJimmy Vetayases 
10427ff178cdSJimmy Vetayases int
10437ff178cdSJimmy Vetayases apic_cpu_remove(psm_cpu_request_t *reqp)
10447ff178cdSJimmy Vetayases {
10457ff178cdSJimmy Vetayases 	int i;
10467ff178cdSJimmy Vetayases 	ulong_t iflag;
10477ff178cdSJimmy Vetayases 	processorid_t cpuid;
10487ff178cdSJimmy Vetayases 
10497ff178cdSJimmy Vetayases 	/* Check whether CPU hotplug is supported. */
10507ff178cdSJimmy Vetayases 	if (!plat_dr_support_cpu() || apic_max_nproc == -1) {
10517ff178cdSJimmy Vetayases 		return (ENOTSUP);
10527ff178cdSJimmy Vetayases 	}
10537ff178cdSJimmy Vetayases 
10547ff178cdSJimmy Vetayases 	cpuid = reqp->req.cpu_remove.cpuid;
10557ff178cdSJimmy Vetayases 
10567ff178cdSJimmy Vetayases 	/* Use apic_ioapic_lock to sync with apic_get_next_bind_cpu. */
10577ff178cdSJimmy Vetayases 	iflag = intr_clear();
10587ff178cdSJimmy Vetayases 	lock_set(&apic_ioapic_lock);
10597ff178cdSJimmy Vetayases 
10607ff178cdSJimmy Vetayases 	if (!apic_cpu_in_range(cpuid)) {
10617ff178cdSJimmy Vetayases 		lock_clear(&apic_ioapic_lock);
10627ff178cdSJimmy Vetayases 		intr_restore(iflag);
10637ff178cdSJimmy Vetayases 		cmn_err(CE_WARN,
10647ff178cdSJimmy Vetayases 		    "!apic: cpuid %d doesn't exist in apic_cpus array.",
10657ff178cdSJimmy Vetayases 		    cpuid);
10667ff178cdSJimmy Vetayases 		return (ENODEV);
10677ff178cdSJimmy Vetayases 	}
10687ff178cdSJimmy Vetayases 	ASSERT((apic_cpus[cpuid].aci_status & APIC_CPU_FREE) == 0);
10697ff178cdSJimmy Vetayases 
10707ff178cdSJimmy Vetayases 	if (ACPI_FAILURE(acpica_unmap_cpu(cpuid))) {
10717ff178cdSJimmy Vetayases 		lock_clear(&apic_ioapic_lock);
10727ff178cdSJimmy Vetayases 		intr_restore(iflag);
10737ff178cdSJimmy Vetayases 		return (ENOENT);
10747ff178cdSJimmy Vetayases 	}
10757ff178cdSJimmy Vetayases 
10767ff178cdSJimmy Vetayases 	if (cpuid == apic_nproc - 1) {
10777ff178cdSJimmy Vetayases 		/*
10787ff178cdSJimmy Vetayases 		 * We are removing the highest numbered cpuid so we need to
10797ff178cdSJimmy Vetayases 		 * find the next highest cpuid as the new value for apic_nproc.
10807ff178cdSJimmy Vetayases 		 */
10817ff178cdSJimmy Vetayases 		for (i = apic_nproc; i > 0; i--) {
10827ff178cdSJimmy Vetayases 			if (CPU_IN_SET(apic_cpumask, i - 1)) {
10837ff178cdSJimmy Vetayases 				apic_nproc = i;
10847ff178cdSJimmy Vetayases 				break;
10857ff178cdSJimmy Vetayases 			}
10867ff178cdSJimmy Vetayases 		}
10877ff178cdSJimmy Vetayases 		/* at least one CPU left */
10887ff178cdSJimmy Vetayases 		ASSERT(i > 0);
10897ff178cdSJimmy Vetayases 	}
10907ff178cdSJimmy Vetayases 	CPUSET_ATOMIC_DEL(apic_cpumask, cpuid);
10917ff178cdSJimmy Vetayases 	/* mark slot as free and keep it in the dirty cache */
10927ff178cdSJimmy Vetayases 	apic_cpus[cpuid].aci_status = APIC_CPU_FREE | APIC_CPU_DIRTY;
10937ff178cdSJimmy Vetayases 
10947ff178cdSJimmy Vetayases 	lock_clear(&apic_ioapic_lock);
10957ff178cdSJimmy Vetayases 	intr_restore(iflag);
10967ff178cdSJimmy Vetayases 
10977ff178cdSJimmy Vetayases 	return (0);
10987ff178cdSJimmy Vetayases }
10997ff178cdSJimmy Vetayases 
11007ff178cdSJimmy Vetayases /*
1101e8763682SPavel Zakharov  * Return the number of ticks the APIC decrements in SF nanoseconds.
1102e8763682SPavel Zakharov  * The fixed-frequency PIT (aka 8254) is used for the measurement.
11037ff178cdSJimmy Vetayases  */
1104e8763682SPavel Zakharov static uint64_t
1105e8763682SPavel Zakharov apic_calibrate_impl()
11067ff178cdSJimmy Vetayases {
11077ff178cdSJimmy Vetayases 	uint8_t		pit_tick_lo;
1108e8763682SPavel Zakharov 	uint16_t	pit_tick, target_pit_tick, pit_ticks_adj;
1109e8763682SPavel Zakharov 	uint32_t	pit_ticks;
1110e8763682SPavel Zakharov 	uint32_t	start_apic_tick, end_apic_tick, apic_ticks;
11117ff178cdSJimmy Vetayases 	ulong_t		iflag;
11127ff178cdSJimmy Vetayases 
1113e8763682SPavel Zakharov 	apic_reg_ops->apic_write(APIC_DIVIDE_REG, apic_divide_reg_init);
1114e8763682SPavel Zakharov 	apic_reg_ops->apic_write(APIC_INIT_COUNT, APIC_MAXVAL);
11157ff178cdSJimmy Vetayases 
11167ff178cdSJimmy Vetayases 	iflag = intr_clear();
11177ff178cdSJimmy Vetayases 
1118*11ed32a0SJoshua M. Clulow 	/*
1119*11ed32a0SJoshua M. Clulow 	 * Put the PIT in mode 0, "Interrupt On Terminal Count":
1120*11ed32a0SJoshua M. Clulow 	 */
1121*11ed32a0SJoshua M. Clulow 	outb(PITCTL_PORT, PIT_C0 | PIT_LOADMODE | PIT_ENDSIGMODE);
1122*11ed32a0SJoshua M. Clulow 
1123*11ed32a0SJoshua M. Clulow 	/*
1124*11ed32a0SJoshua M. Clulow 	 * The PIT counts down and then the counter value wraps around.  Load
1125*11ed32a0SJoshua M. Clulow 	 * the maximum counter value:
1126*11ed32a0SJoshua M. Clulow 	 */
1127*11ed32a0SJoshua M. Clulow 	outb(PITCTR0_PORT, 0xFF);
1128*11ed32a0SJoshua M. Clulow 	outb(PITCTR0_PORT, 0xFF);
1129*11ed32a0SJoshua M. Clulow 
11307ff178cdSJimmy Vetayases 	do {
11317ff178cdSJimmy Vetayases 		pit_tick_lo = inb(PITCTR0_PORT);
11327ff178cdSJimmy Vetayases 		pit_tick = (inb(PITCTR0_PORT) << 8) | pit_tick_lo;
11337ff178cdSJimmy Vetayases 	} while (pit_tick < APIC_TIME_MIN ||
11347ff178cdSJimmy Vetayases 	    pit_tick_lo <= APIC_LB_MIN || pit_tick_lo >= APIC_LB_MAX);
11357ff178cdSJimmy Vetayases 
11367ff178cdSJimmy Vetayases 	/*
1137e8763682SPavel Zakharov 	 * Wait for the PIT to decrement by 5 ticks to ensure
11387ff178cdSJimmy Vetayases 	 * we didn't start in the middle of a tick.
11397ff178cdSJimmy Vetayases 	 * Compare with 0x10 for the wrap around case.
11407ff178cdSJimmy Vetayases 	 */
11417ff178cdSJimmy Vetayases 	target_pit_tick = pit_tick - 5;
11427ff178cdSJimmy Vetayases 	do {
11437ff178cdSJimmy Vetayases 		pit_tick_lo = inb(PITCTR0_PORT);
11447ff178cdSJimmy Vetayases 		pit_tick = (inb(PITCTR0_PORT) << 8) | pit_tick_lo;
11457ff178cdSJimmy Vetayases 	} while (pit_tick > target_pit_tick || pit_tick_lo < 0x10);
11467ff178cdSJimmy Vetayases 
1147e8763682SPavel Zakharov 	start_apic_tick = apic_reg_ops->apic_read(APIC_CURR_COUNT);
11487ff178cdSJimmy Vetayases 
11497ff178cdSJimmy Vetayases 	/*
1150e8763682SPavel Zakharov 	 * Wait for the PIT to decrement by APIC_TIME_COUNT ticks
11517ff178cdSJimmy Vetayases 	 */
11527ff178cdSJimmy Vetayases 	target_pit_tick = pit_tick - APIC_TIME_COUNT;
11537ff178cdSJimmy Vetayases 	do {
11547ff178cdSJimmy Vetayases 		pit_tick_lo = inb(PITCTR0_PORT);
11557ff178cdSJimmy Vetayases 		pit_tick = (inb(PITCTR0_PORT) << 8) | pit_tick_lo;
11567ff178cdSJimmy Vetayases 	} while (pit_tick > target_pit_tick || pit_tick_lo < 0x10);
11577ff178cdSJimmy Vetayases 
1158e8763682SPavel Zakharov 	end_apic_tick = apic_reg_ops->apic_read(APIC_CURR_COUNT);
11597ff178cdSJimmy Vetayases 
11607ff178cdSJimmy Vetayases 	intr_restore(iflag);
11617ff178cdSJimmy Vetayases 
1162e8763682SPavel Zakharov 	apic_ticks = start_apic_tick - end_apic_tick;
1163e8763682SPavel Zakharov 
1164e8763682SPavel Zakharov 	/* The PIT might have decremented by more ticks than planned */
1165e8763682SPavel Zakharov 	pit_ticks_adj = target_pit_tick - pit_tick;
1166e8763682SPavel Zakharov 	/* total number of PIT ticks corresponding to apic_ticks */
1167e8763682SPavel Zakharov 	pit_ticks = APIC_TIME_COUNT + pit_ticks_adj;
1168e8763682SPavel Zakharov 
1169e8763682SPavel Zakharov 	/*
1170e8763682SPavel Zakharov 	 * Determine the number of nanoseconds per APIC clock tick
1171e8763682SPavel Zakharov 	 * and then determine how many APIC ticks to interrupt at the
1172e8763682SPavel Zakharov 	 * desired frequency
1173e8763682SPavel Zakharov 	 * apic_ticks / (pitticks / PIT_HZ) = apic_ticks_per_s
1174e8763682SPavel Zakharov 	 * (apic_ticks * PIT_HZ) / pitticks = apic_ticks_per_s
1175e8763682SPavel Zakharov 	 * apic_ticks_per_ns = (apic_ticks * PIT_HZ) / (pitticks * 10^9)
1176e8763682SPavel Zakharov 	 * apic_ticks_per_SFns =
1177e8763682SPavel Zakharov 	 * (SF * apic_ticks * PIT_HZ) / (pitticks * 10^9)
1178e8763682SPavel Zakharov 	 */
1179e8763682SPavel Zakharov 	return ((SF * apic_ticks * PIT_HZ) / ((uint64_t)pit_ticks * NANOSEC));
1180e8763682SPavel Zakharov }
1181e8763682SPavel Zakharov 
1182e8763682SPavel Zakharov /*
1183e8763682SPavel Zakharov  * It was found empirically that 5 measurements seem sufficient to give a good
1184e8763682SPavel Zakharov  * accuracy. Most spurious measurements are higher than the target value thus
1185e8763682SPavel Zakharov  * we eliminate up to 2/5 spurious measurements.
1186e8763682SPavel Zakharov  */
1187e8763682SPavel Zakharov #define	APIC_CALIBRATE_MEASUREMENTS		5
1188e8763682SPavel Zakharov 
1189e8763682SPavel Zakharov #define	APIC_CALIBRATE_PERCENT_OFF_WARNING	10
1190e8763682SPavel Zakharov 
1191e8763682SPavel Zakharov /*
1192e8763682SPavel Zakharov  * Return the number of ticks the APIC decrements in SF nanoseconds.
1193e8763682SPavel Zakharov  * Several measurements are taken to filter out outliers.
1194e8763682SPavel Zakharov  */
1195e8763682SPavel Zakharov uint64_t
1196e8763682SPavel Zakharov apic_calibrate()
1197e8763682SPavel Zakharov {
1198e8763682SPavel Zakharov 	uint64_t	measurements[APIC_CALIBRATE_MEASUREMENTS];
1199e8763682SPavel Zakharov 	int		median_idx;
1200e8763682SPavel Zakharov 	uint64_t	median;
1201e8763682SPavel Zakharov 
1202e8763682SPavel Zakharov 	/*
1203e8763682SPavel Zakharov 	 * When running under a virtual machine, the emulated PIT and APIC
1204e8763682SPavel Zakharov 	 * counters do not always return the right values and can roll over.
1205e8763682SPavel Zakharov 	 * Those spurious measurements are relatively rare but could
1206e8763682SPavel Zakharov 	 * significantly affect the calibration.
1207e8763682SPavel Zakharov 	 * Therefore we take several measurements and then keep the median.
1208e8763682SPavel Zakharov 	 * The median is preferred to the average here as we only want to
1209e8763682SPavel Zakharov 	 * discard outliers.
1210e8763682SPavel Zakharov 	 */
1211e8763682SPavel Zakharov 	for (int i = 0; i < APIC_CALIBRATE_MEASUREMENTS; i++)
1212e8763682SPavel Zakharov 		measurements[i] = apic_calibrate_impl();
1213e8763682SPavel Zakharov 
1214e8763682SPavel Zakharov 	/*
1215e8763682SPavel Zakharov 	 * sort results and retrieve median.
1216e8763682SPavel Zakharov 	 */
1217e8763682SPavel Zakharov 	for (int i = 0; i < APIC_CALIBRATE_MEASUREMENTS; i++) {
1218e8763682SPavel Zakharov 		for (int j = i + 1; j < APIC_CALIBRATE_MEASUREMENTS; j++) {
1219e8763682SPavel Zakharov 			if (measurements[j] < measurements[i]) {
1220e8763682SPavel Zakharov 				uint64_t tmp = measurements[i];
1221e8763682SPavel Zakharov 				measurements[i] = measurements[j];
1222e8763682SPavel Zakharov 				measurements[j] = tmp;
1223e8763682SPavel Zakharov 			}
1224e8763682SPavel Zakharov 		}
1225e8763682SPavel Zakharov 	}
1226e8763682SPavel Zakharov 	median_idx = APIC_CALIBRATE_MEASUREMENTS / 2;
1227e8763682SPavel Zakharov 	median = measurements[median_idx];
1228e8763682SPavel Zakharov 
1229e8763682SPavel Zakharov #if (APIC_CALIBRATE_MEASUREMENTS >= 3)
1230e8763682SPavel Zakharov 	/*
1231e8763682SPavel Zakharov 	 * Check that measurements are consistent. Post a warning
1232e8763682SPavel Zakharov 	 * if the three middle values are not close to each other.
1233e8763682SPavel Zakharov 	 */
1234e8763682SPavel Zakharov 	uint64_t delta_warn = median *
1235e8763682SPavel Zakharov 	    APIC_CALIBRATE_PERCENT_OFF_WARNING / 100;
1236e8763682SPavel Zakharov 	if ((median - measurements[median_idx - 1]) > delta_warn ||
1237e8763682SPavel Zakharov 	    (measurements[median_idx + 1] - median) > delta_warn) {
1238e8763682SPavel Zakharov 		cmn_err(CE_WARN, "apic_calibrate measurements lack "
1239e8763682SPavel Zakharov 		    "precision: %llu, %llu, %llu.",
1240e8763682SPavel Zakharov 		    (u_longlong_t)measurements[median_idx - 1],
1241e8763682SPavel Zakharov 		    (u_longlong_t)median,
1242e8763682SPavel Zakharov 		    (u_longlong_t)measurements[median_idx + 1]);
1243e8763682SPavel Zakharov 	}
1244e8763682SPavel Zakharov #endif
1245e8763682SPavel Zakharov 
1246e8763682SPavel Zakharov 	return (median);
12477ff178cdSJimmy Vetayases }
12487ff178cdSJimmy Vetayases 
12497ff178cdSJimmy Vetayases /*
12507ff178cdSJimmy Vetayases  * Initialise the APIC timer on the local APIC of CPU 0 to the desired
12517ff178cdSJimmy Vetayases  * frequency.  Note at this stage in the boot sequence, the boot processor
12527ff178cdSJimmy Vetayases  * is the only active processor.
12537ff178cdSJimmy Vetayases  * hertz value of 0 indicates a one-shot mode request.  In this case
12547ff178cdSJimmy Vetayases  * the function returns the resolution (in nanoseconds) for the hardware
12557ff178cdSJimmy Vetayases  * timer interrupt.  If one-shot mode capability is not available,
12567ff178cdSJimmy Vetayases  * the return value will be 0. apic_enable_oneshot is a global switch
12577ff178cdSJimmy Vetayases  * for disabling the functionality.
12587ff178cdSJimmy Vetayases  * A non-zero positive value for hertz indicates a periodic mode request.
12597ff178cdSJimmy Vetayases  * In this case the hardware will be programmed to generate clock interrupts
12607ff178cdSJimmy Vetayases  * at hertz frequency and returns the resolution of interrupts in
12617ff178cdSJimmy Vetayases  * nanosecond.
12627ff178cdSJimmy Vetayases  */
12637ff178cdSJimmy Vetayases 
12647ff178cdSJimmy Vetayases int
12657ff178cdSJimmy Vetayases apic_clkinit(int hertz)
12667ff178cdSJimmy Vetayases {
12677ff178cdSJimmy Vetayases 	int		ret;
12687ff178cdSJimmy Vetayases 
12697ff178cdSJimmy Vetayases 	apic_int_busy_mark = (apic_int_busy_mark *
12707ff178cdSJimmy Vetayases 	    apic_sample_factor_redistribution) / 100;
12717ff178cdSJimmy Vetayases 	apic_int_free_mark = (apic_int_free_mark *
12727ff178cdSJimmy Vetayases 	    apic_sample_factor_redistribution) / 100;
12737ff178cdSJimmy Vetayases 	apic_diff_for_redistribution = (apic_diff_for_redistribution *
12747ff178cdSJimmy Vetayases 	    apic_sample_factor_redistribution) / 100;
12757ff178cdSJimmy Vetayases 
127641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	ret = apic_timer_init(hertz);
12777ff178cdSJimmy Vetayases 	return (ret);
12787ff178cdSJimmy Vetayases 
12797ff178cdSJimmy Vetayases }
12807ff178cdSJimmy Vetayases 
12817ff178cdSJimmy Vetayases /*
12827ff178cdSJimmy Vetayases  * apic_preshutdown:
12837ff178cdSJimmy Vetayases  * Called early in shutdown whilst we can still access filesystems to do
12847ff178cdSJimmy Vetayases  * things like loading modules which will be required to complete shutdown
12857ff178cdSJimmy Vetayases  * after filesystems are all unmounted.
12867ff178cdSJimmy Vetayases  */
12877ff178cdSJimmy Vetayases void
12887ff178cdSJimmy Vetayases apic_preshutdown(int cmd, int fcn)
12897ff178cdSJimmy Vetayases {
12907ff178cdSJimmy Vetayases 	APIC_VERBOSE_POWEROFF(("apic_preshutdown(%d,%d); m=%d a=%d\n",
12917ff178cdSJimmy Vetayases 	    cmd, fcn, apic_poweroff_method, apic_enable_acpi));
12927ff178cdSJimmy Vetayases }
12937ff178cdSJimmy Vetayases 
12947ff178cdSJimmy Vetayases void
12957ff178cdSJimmy Vetayases apic_shutdown(int cmd, int fcn)
12967ff178cdSJimmy Vetayases {
12977ff178cdSJimmy Vetayases 	int restarts, attempts;
12987ff178cdSJimmy Vetayases 	int i;
12997ff178cdSJimmy Vetayases 	uchar_t	byte;
13007ff178cdSJimmy Vetayases 	ulong_t iflag;
13017ff178cdSJimmy Vetayases 
13027ff178cdSJimmy Vetayases 	hpet_acpi_fini();
13037ff178cdSJimmy Vetayases 
13047ff178cdSJimmy Vetayases 	/* Send NMI to all CPUs except self to do per processor shutdown */
13057ff178cdSJimmy Vetayases 	iflag = intr_clear();
13067ff178cdSJimmy Vetayases #ifdef	DEBUG
13077ff178cdSJimmy Vetayases 	APIC_AV_PENDING_SET();
13087ff178cdSJimmy Vetayases #else
13097ff178cdSJimmy Vetayases 	if (apic_mode == LOCAL_APIC)
13107ff178cdSJimmy Vetayases 		APIC_AV_PENDING_SET();
13117ff178cdSJimmy Vetayases #endif /* DEBUG */
13127ff178cdSJimmy Vetayases 	apic_shutdown_processors = 1;
13137ff178cdSJimmy Vetayases 	apic_reg_ops->apic_write(APIC_INT_CMD1,
13147ff178cdSJimmy Vetayases 	    AV_NMI | AV_LEVEL | AV_SH_ALL_EXCSELF);
13157ff178cdSJimmy Vetayases 
13167ff178cdSJimmy Vetayases 	/* restore cmos shutdown byte before reboot */
13177ff178cdSJimmy Vetayases 	if (apic_cmos_ssb_set) {
13187ff178cdSJimmy Vetayases 		outb(CMOS_ADDR, SSB);
13197ff178cdSJimmy Vetayases 		outb(CMOS_DATA, 0);
13207ff178cdSJimmy Vetayases 	}
13217ff178cdSJimmy Vetayases 
13227ff178cdSJimmy Vetayases 	ioapic_disable_redirection();
13237ff178cdSJimmy Vetayases 
13247ff178cdSJimmy Vetayases 	/*	disable apic mode if imcr present	*/
13257ff178cdSJimmy Vetayases 	if (apic_imcrp) {
13267ff178cdSJimmy Vetayases 		outb(APIC_IMCR_P1, (uchar_t)APIC_IMCR_SELECT);
13277ff178cdSJimmy Vetayases 		outb(APIC_IMCR_P2, (uchar_t)APIC_IMCR_PIC);
13287ff178cdSJimmy Vetayases 	}
13297ff178cdSJimmy Vetayases 
13307ff178cdSJimmy Vetayases 	apic_disable_local_apic();
13317ff178cdSJimmy Vetayases 
13327ff178cdSJimmy Vetayases 	intr_restore(iflag);
13337ff178cdSJimmy Vetayases 
13347ff178cdSJimmy Vetayases 	/* remainder of function is for shutdown cases only */
13357ff178cdSJimmy Vetayases 	if (cmd != A_SHUTDOWN)
13367ff178cdSJimmy Vetayases 		return;
13377ff178cdSJimmy Vetayases 
13387ff178cdSJimmy Vetayases 	/*
13397ff178cdSJimmy Vetayases 	 * Switch system back into Legacy-Mode if using ACPI and
13407ff178cdSJimmy Vetayases 	 * not powering-off.  Some BIOSes need to remain in ACPI-mode
13417ff178cdSJimmy Vetayases 	 * for power-off to succeed (Dell Dimension 4600)
13427ff178cdSJimmy Vetayases 	 * Do not disable ACPI while doing fastreboot
13437ff178cdSJimmy Vetayases 	 */
13447ff178cdSJimmy Vetayases 	if (apic_enable_acpi && fcn != AD_POWEROFF && fcn != AD_FASTREBOOT)
13457ff178cdSJimmy Vetayases 		(void) AcpiDisable();
13467ff178cdSJimmy Vetayases 
13477ff178cdSJimmy Vetayases 	if (fcn == AD_FASTREBOOT) {
13487ff178cdSJimmy Vetayases 		apic_reg_ops->apic_write(APIC_INT_CMD1,
13497ff178cdSJimmy Vetayases 		    AV_ASSERT | AV_RESET | AV_SH_ALL_EXCSELF);
13507ff178cdSJimmy Vetayases 	}
13517ff178cdSJimmy Vetayases 
13527ff178cdSJimmy Vetayases 	/* remainder of function is for shutdown+poweroff case only */
13537ff178cdSJimmy Vetayases 	if (fcn != AD_POWEROFF)
13547ff178cdSJimmy Vetayases 		return;
13557ff178cdSJimmy Vetayases 
13567ff178cdSJimmy Vetayases 	switch (apic_poweroff_method) {
13577ff178cdSJimmy Vetayases 		case APIC_POWEROFF_VIA_RTC:
13587ff178cdSJimmy Vetayases 
13597ff178cdSJimmy Vetayases 			/* select the extended NVRAM bank in the RTC */
13607ff178cdSJimmy Vetayases 			outb(CMOS_ADDR, RTC_REGA);
13617ff178cdSJimmy Vetayases 			byte = inb(CMOS_DATA);
13627ff178cdSJimmy Vetayases 			outb(CMOS_DATA, (byte | EXT_BANK));
13637ff178cdSJimmy Vetayases 
13647ff178cdSJimmy Vetayases 			outb(CMOS_ADDR, PFR_REG);
13657ff178cdSJimmy Vetayases 
13667ff178cdSJimmy Vetayases 			/* for Predator must toggle the PAB bit */
13677ff178cdSJimmy Vetayases 			byte = inb(CMOS_DATA);
13687ff178cdSJimmy Vetayases 
13697ff178cdSJimmy Vetayases 			/*
13707ff178cdSJimmy Vetayases 			 * clear power active bar, wakeup alarm and
13717ff178cdSJimmy Vetayases 			 * kickstart
13727ff178cdSJimmy Vetayases 			 */
13737ff178cdSJimmy Vetayases 			byte &= ~(PAB_CBIT | WF_FLAG | KS_FLAG);
13747ff178cdSJimmy Vetayases 			outb(CMOS_DATA, byte);
13757ff178cdSJimmy Vetayases 
13767ff178cdSJimmy Vetayases 			/* delay before next write */
13777ff178cdSJimmy Vetayases 			drv_usecwait(1000);
13787ff178cdSJimmy Vetayases 
13797ff178cdSJimmy Vetayases 			/* for S40 the following would suffice */
13807ff178cdSJimmy Vetayases 			byte = inb(CMOS_DATA);
13817ff178cdSJimmy Vetayases 
13827ff178cdSJimmy Vetayases 			/* power active bar control bit */
13837ff178cdSJimmy Vetayases 			byte |= PAB_CBIT;
13847ff178cdSJimmy Vetayases 			outb(CMOS_DATA, byte);
13857ff178cdSJimmy Vetayases 
13867ff178cdSJimmy Vetayases 			break;
13877ff178cdSJimmy Vetayases 
13887ff178cdSJimmy Vetayases 		case APIC_POWEROFF_VIA_ASPEN_BMC:
13897ff178cdSJimmy Vetayases 			restarts = 0;
13907ff178cdSJimmy Vetayases restart_aspen_bmc:
13917ff178cdSJimmy Vetayases 			if (++restarts == 3)
13927ff178cdSJimmy Vetayases 				break;
13937ff178cdSJimmy Vetayases 			attempts = 0;
13947ff178cdSJimmy Vetayases 			do {
13957ff178cdSJimmy Vetayases 				byte = inb(MISMIC_FLAG_REGISTER);
13967ff178cdSJimmy Vetayases 				byte &= MISMIC_BUSY_MASK;
13977ff178cdSJimmy Vetayases 				if (byte != 0) {
13987ff178cdSJimmy Vetayases 					drv_usecwait(1000);
13997ff178cdSJimmy Vetayases 					if (attempts >= 3)
14007ff178cdSJimmy Vetayases 						goto restart_aspen_bmc;
14017ff178cdSJimmy Vetayases 					++attempts;
14027ff178cdSJimmy Vetayases 				}
14037ff178cdSJimmy Vetayases 			} while (byte != 0);
14047ff178cdSJimmy Vetayases 			outb(MISMIC_CNTL_REGISTER, CC_SMS_GET_STATUS);
14057ff178cdSJimmy Vetayases 			byte = inb(MISMIC_FLAG_REGISTER);
14067ff178cdSJimmy Vetayases 			byte |= 0x1;
14077ff178cdSJimmy Vetayases 			outb(MISMIC_FLAG_REGISTER, byte);
14087ff178cdSJimmy Vetayases 			i = 0;
14097ff178cdSJimmy Vetayases 			for (; i < (sizeof (aspen_bmc)/sizeof (aspen_bmc[0]));
14107ff178cdSJimmy Vetayases 			    i++) {
14117ff178cdSJimmy Vetayases 				attempts = 0;
14127ff178cdSJimmy Vetayases 				do {
14137ff178cdSJimmy Vetayases 					byte = inb(MISMIC_FLAG_REGISTER);
14147ff178cdSJimmy Vetayases 					byte &= MISMIC_BUSY_MASK;
14157ff178cdSJimmy Vetayases 					if (byte != 0) {
14167ff178cdSJimmy Vetayases 						drv_usecwait(1000);
14177ff178cdSJimmy Vetayases 						if (attempts >= 3)
14187ff178cdSJimmy Vetayases 							goto restart_aspen_bmc;
14197ff178cdSJimmy Vetayases 						++attempts;
14207ff178cdSJimmy Vetayases 					}
14217ff178cdSJimmy Vetayases 				} while (byte != 0);
14227ff178cdSJimmy Vetayases 				outb(MISMIC_CNTL_REGISTER, aspen_bmc[i].cntl);
14237ff178cdSJimmy Vetayases 				outb(MISMIC_DATA_REGISTER, aspen_bmc[i].data);
14247ff178cdSJimmy Vetayases 				byte = inb(MISMIC_FLAG_REGISTER);
14257ff178cdSJimmy Vetayases 				byte |= 0x1;
14267ff178cdSJimmy Vetayases 				outb(MISMIC_FLAG_REGISTER, byte);
14277ff178cdSJimmy Vetayases 			}
14287ff178cdSJimmy Vetayases 			break;
14297ff178cdSJimmy Vetayases 
14307ff178cdSJimmy Vetayases 		case APIC_POWEROFF_VIA_SITKA_BMC:
14317ff178cdSJimmy Vetayases 			restarts = 0;
14327ff178cdSJimmy Vetayases restart_sitka_bmc:
14337ff178cdSJimmy Vetayases 			if (++restarts == 3)
14347ff178cdSJimmy Vetayases 				break;
14357ff178cdSJimmy Vetayases 			attempts = 0;
14367ff178cdSJimmy Vetayases 			do {
14377ff178cdSJimmy Vetayases 				byte = inb(SMS_STATUS_REGISTER);
14387ff178cdSJimmy Vetayases 				byte &= SMS_STATE_MASK;
14397ff178cdSJimmy Vetayases 				if ((byte == SMS_READ_STATE) ||
14407ff178cdSJimmy Vetayases 				    (byte == SMS_WRITE_STATE)) {
14417ff178cdSJimmy Vetayases 					drv_usecwait(1000);
14427ff178cdSJimmy Vetayases 					if (attempts >= 3)
14437ff178cdSJimmy Vetayases 						goto restart_sitka_bmc;
14447ff178cdSJimmy Vetayases 					++attempts;
14457ff178cdSJimmy Vetayases 				}
14467ff178cdSJimmy Vetayases 			} while ((byte == SMS_READ_STATE) ||
14477ff178cdSJimmy Vetayases 			    (byte == SMS_WRITE_STATE));
14487ff178cdSJimmy Vetayases 			outb(SMS_COMMAND_REGISTER, SMS_GET_STATUS);
14497ff178cdSJimmy Vetayases 			i = 0;
14507ff178cdSJimmy Vetayases 			for (; i < (sizeof (sitka_bmc)/sizeof (sitka_bmc[0]));
14517ff178cdSJimmy Vetayases 			    i++) {
14527ff178cdSJimmy Vetayases 				attempts = 0;
14537ff178cdSJimmy Vetayases 				do {
14547ff178cdSJimmy Vetayases 					byte = inb(SMS_STATUS_REGISTER);
14557ff178cdSJimmy Vetayases 					byte &= SMS_IBF_MASK;
14567ff178cdSJimmy Vetayases 					if (byte != 0) {
14577ff178cdSJimmy Vetayases 						drv_usecwait(1000);
14587ff178cdSJimmy Vetayases 						if (attempts >= 3)
14597ff178cdSJimmy Vetayases 							goto restart_sitka_bmc;
14607ff178cdSJimmy Vetayases 						++attempts;
14617ff178cdSJimmy Vetayases 					}
14627ff178cdSJimmy Vetayases 				} while (byte != 0);
14637ff178cdSJimmy Vetayases 				outb(sitka_bmc[i].port, sitka_bmc[i].data);
14647ff178cdSJimmy Vetayases 			}
14657ff178cdSJimmy Vetayases 			break;
14667ff178cdSJimmy Vetayases 
14677ff178cdSJimmy Vetayases 		case APIC_POWEROFF_NONE:
14687ff178cdSJimmy Vetayases 
14697ff178cdSJimmy Vetayases 			/* If no APIC direct method, we will try using ACPI */
14707ff178cdSJimmy Vetayases 			if (apic_enable_acpi) {
14717ff178cdSJimmy Vetayases 				if (acpi_poweroff() == 1)
14727ff178cdSJimmy Vetayases 					return;
14737ff178cdSJimmy Vetayases 			} else
14747ff178cdSJimmy Vetayases 				return;
14757ff178cdSJimmy Vetayases 
14767ff178cdSJimmy Vetayases 			break;
14777ff178cdSJimmy Vetayases 	}
14787ff178cdSJimmy Vetayases 	/*
14797ff178cdSJimmy Vetayases 	 * Wait a limited time here for power to go off.
14807ff178cdSJimmy Vetayases 	 * If the power does not go off, then there was a
14817ff178cdSJimmy Vetayases 	 * problem and we should continue to the halt which
14827ff178cdSJimmy Vetayases 	 * prints a message for the user to press a key to
14837ff178cdSJimmy Vetayases 	 * reboot.
14847ff178cdSJimmy Vetayases 	 */
14857ff178cdSJimmy Vetayases 	drv_usecwait(7000000); /* wait seven seconds */
14867ff178cdSJimmy Vetayases 
14877ff178cdSJimmy Vetayases }
14887ff178cdSJimmy Vetayases 
1489a288e5a9SJoshua M. Clulow cyclic_id_t apic_cyclic_id;
14907ff178cdSJimmy Vetayases 
14917ff178cdSJimmy Vetayases /*
14927ff178cdSJimmy Vetayases  * The following functions are in the platform specific file so that they
14937ff178cdSJimmy Vetayases  * can be different functions depending on whether we are running on
14947ff178cdSJimmy Vetayases  * bare metal or a hypervisor.
14957ff178cdSJimmy Vetayases  */
14967ff178cdSJimmy Vetayases 
14977ff178cdSJimmy Vetayases /*
14987ff178cdSJimmy Vetayases  * map an apic for memory-mapped access
14997ff178cdSJimmy Vetayases  */
15007ff178cdSJimmy Vetayases uint32_t *
15017ff178cdSJimmy Vetayases mapin_apic(uint32_t addr, size_t len, int flags)
15027ff178cdSJimmy Vetayases {
15037ff178cdSJimmy Vetayases 	return ((void *)psm_map_phys(addr, len, flags));
15047ff178cdSJimmy Vetayases }
15057ff178cdSJimmy Vetayases 
15067ff178cdSJimmy Vetayases uint32_t *
15077ff178cdSJimmy Vetayases mapin_ioapic(uint32_t addr, size_t len, int flags)
15087ff178cdSJimmy Vetayases {
15097ff178cdSJimmy Vetayases 	return (mapin_apic(addr, len, flags));
15107ff178cdSJimmy Vetayases }
15117ff178cdSJimmy Vetayases 
15127ff178cdSJimmy Vetayases /*
15137ff178cdSJimmy Vetayases  * unmap an apic
15147ff178cdSJimmy Vetayases  */
15157ff178cdSJimmy Vetayases void
15167ff178cdSJimmy Vetayases mapout_apic(caddr_t addr, size_t len)
15177ff178cdSJimmy Vetayases {
15187ff178cdSJimmy Vetayases 	psm_unmap_phys(addr, len);
15197ff178cdSJimmy Vetayases }
15207ff178cdSJimmy Vetayases 
15217ff178cdSJimmy Vetayases void
15227ff178cdSJimmy Vetayases mapout_ioapic(caddr_t addr, size_t len)
15237ff178cdSJimmy Vetayases {
15247ff178cdSJimmy Vetayases 	mapout_apic(addr, len);
15257ff178cdSJimmy Vetayases }
15267ff178cdSJimmy Vetayases 
15277ff178cdSJimmy Vetayases uint32_t
15287ff178cdSJimmy Vetayases ioapic_read(int ioapic_ix, uint32_t reg)
15297ff178cdSJimmy Vetayases {
15307ff178cdSJimmy Vetayases 	volatile uint32_t *ioapic;
15317ff178cdSJimmy Vetayases 
15327ff178cdSJimmy Vetayases 	ioapic = apicioadr[ioapic_ix];
15337ff178cdSJimmy Vetayases 	ioapic[APIC_IO_REG] = reg;
15347ff178cdSJimmy Vetayases 	return (ioapic[APIC_IO_DATA]);
15357ff178cdSJimmy Vetayases }
15367ff178cdSJimmy Vetayases 
15377ff178cdSJimmy Vetayases void
15387ff178cdSJimmy Vetayases ioapic_write(int ioapic_ix, uint32_t reg, uint32_t value)
15397ff178cdSJimmy Vetayases {
15407ff178cdSJimmy Vetayases 	volatile uint32_t *ioapic;
15417ff178cdSJimmy Vetayases 
15427ff178cdSJimmy Vetayases 	ioapic = apicioadr[ioapic_ix];
15437ff178cdSJimmy Vetayases 	ioapic[APIC_IO_REG] = reg;
15447ff178cdSJimmy Vetayases 	ioapic[APIC_IO_DATA] = value;
15457ff178cdSJimmy Vetayases }
15467ff178cdSJimmy Vetayases 
15477ff178cdSJimmy Vetayases void
15487ff178cdSJimmy Vetayases ioapic_write_eoi(int ioapic_ix, uint32_t value)
15497ff178cdSJimmy Vetayases {
15507ff178cdSJimmy Vetayases 	volatile uint32_t *ioapic;
15517ff178cdSJimmy Vetayases 
15527ff178cdSJimmy Vetayases 	ioapic = apicioadr[ioapic_ix];
15537ff178cdSJimmy Vetayases 	ioapic[APIC_IO_EOI] = value;
15547ff178cdSJimmy Vetayases }
15557ff178cdSJimmy Vetayases 
15567ff178cdSJimmy Vetayases /*
15577ff178cdSJimmy Vetayases  * Round-robin algorithm to find the next CPU with interrupts enabled.
15587ff178cdSJimmy Vetayases  * It can't share the same static variable apic_next_bind_cpu with
15597ff178cdSJimmy Vetayases  * apic_get_next_bind_cpu(), since that will cause all interrupts to be
15607ff178cdSJimmy Vetayases  * bound to CPU1 at boot time.  During boot, only CPU0 is online with
15617ff178cdSJimmy Vetayases  * interrupts enabled when apic_get_next_bind_cpu() and apic_find_cpu()
15627ff178cdSJimmy Vetayases  * are called.  However, the pcplusmp driver assumes that there will be
15637ff178cdSJimmy Vetayases  * boot_ncpus CPUs configured eventually so it tries to distribute all
15647ff178cdSJimmy Vetayases  * interrupts among CPU0 - CPU[boot_ncpus - 1].  Thus to prevent all
15657ff178cdSJimmy Vetayases  * interrupts being targetted at CPU1, we need to use a dedicated static
15667ff178cdSJimmy Vetayases  * variable for find_next_cpu() instead of sharing apic_next_bind_cpu.
15677ff178cdSJimmy Vetayases  */
15687ff178cdSJimmy Vetayases 
15697ff178cdSJimmy Vetayases processorid_t
15707ff178cdSJimmy Vetayases apic_find_cpu(int flag)
15717ff178cdSJimmy Vetayases {
15727ff178cdSJimmy Vetayases 	int i;
15737ff178cdSJimmy Vetayases 	static processorid_t acid = 0;
15747ff178cdSJimmy Vetayases 
15757ff178cdSJimmy Vetayases 	/* Find the first CPU with the passed-in flag set */
15767ff178cdSJimmy Vetayases 	for (i = 0; i < apic_nproc; i++) {
15777ff178cdSJimmy Vetayases 		if (++acid >= apic_nproc) {
15787ff178cdSJimmy Vetayases 			acid = 0;
15797ff178cdSJimmy Vetayases 		}
15807ff178cdSJimmy Vetayases 		if (apic_cpu_in_range(acid) &&
15817ff178cdSJimmy Vetayases 		    (apic_cpus[acid].aci_status & flag)) {
15827ff178cdSJimmy Vetayases 			break;
15837ff178cdSJimmy Vetayases 		}
15847ff178cdSJimmy Vetayases 	}
15857ff178cdSJimmy Vetayases 
15867ff178cdSJimmy Vetayases 	ASSERT((apic_cpus[acid].aci_status & flag) != 0);
15877ff178cdSJimmy Vetayases 	return (acid);
15887ff178cdSJimmy Vetayases }
15897ff178cdSJimmy Vetayases 
15907ff178cdSJimmy Vetayases void
15917ff178cdSJimmy Vetayases apic_intrmap_init(int apic_mode)
15927ff178cdSJimmy Vetayases {
15937ff178cdSJimmy Vetayases 	int suppress_brdcst_eoi = 0;
15947ff178cdSJimmy Vetayases 
15951a9079eeSJosef 'Jeff' Sipek 	/*
15961a9079eeSJosef 'Jeff' Sipek 	 * Intel Software Developer's Manual 3A, 10.12.7:
15971a9079eeSJosef 'Jeff' Sipek 	 *
15981a9079eeSJosef 'Jeff' Sipek 	 * Routing of device interrupts to local APIC units operating in
15991a9079eeSJosef 'Jeff' Sipek 	 * x2APIC mode requires use of the interrupt-remapping architecture
16001a9079eeSJosef 'Jeff' Sipek 	 * specified in the Intel Virtualization Technology for Directed
16011a9079eeSJosef 'Jeff' Sipek 	 * I/O, Revision 1.3.  Because of this, BIOS must enumerate support
16021a9079eeSJosef 'Jeff' Sipek 	 * for and software must enable this interrupt remapping with
16031a9079eeSJosef 'Jeff' Sipek 	 * Extended Interrupt Mode Enabled before it enabling x2APIC mode in
16041a9079eeSJosef 'Jeff' Sipek 	 * the local APIC units.
16051a9079eeSJosef 'Jeff' Sipek 	 *
16061a9079eeSJosef 'Jeff' Sipek 	 *
16071a9079eeSJosef 'Jeff' Sipek 	 * In other words, to use the APIC in x2APIC mode, we need interrupt
16081a9079eeSJosef 'Jeff' Sipek 	 * remapping.  Since we don't start up the IOMMU by default, we
16091a9079eeSJosef 'Jeff' Sipek 	 * won't be able to do any interrupt remapping and therefore have to
16101a9079eeSJosef 'Jeff' Sipek 	 * use the APIC in traditional 'local APIC' mode with memory mapped
16111a9079eeSJosef 'Jeff' Sipek 	 * I/O.
16121a9079eeSJosef 'Jeff' Sipek 	 */
16131a9079eeSJosef 'Jeff' Sipek 
16147ff178cdSJimmy Vetayases 	if (psm_vt_ops != NULL) {
16157ff178cdSJimmy Vetayases 		if (((apic_intrmap_ops_t *)psm_vt_ops)->
16167ff178cdSJimmy Vetayases 		    apic_intrmap_init(apic_mode) == DDI_SUCCESS) {
16177ff178cdSJimmy Vetayases 
16187ff178cdSJimmy Vetayases 			apic_vt_ops = psm_vt_ops;
16197ff178cdSJimmy Vetayases 
16207ff178cdSJimmy Vetayases 			/*
16217ff178cdSJimmy Vetayases 			 * We leverage the interrupt remapping engine to
16227ff178cdSJimmy Vetayases 			 * suppress broadcast EOI; thus we must send the
16237ff178cdSJimmy Vetayases 			 * directed EOI with the directed-EOI handler.
16247ff178cdSJimmy Vetayases 			 */
16257ff178cdSJimmy Vetayases 			if (apic_directed_EOI_supported() == 0) {
16267ff178cdSJimmy Vetayases 				suppress_brdcst_eoi = 1;
16277ff178cdSJimmy Vetayases 			}
16287ff178cdSJimmy Vetayases 
16297ff178cdSJimmy Vetayases 			apic_vt_ops->apic_intrmap_enable(suppress_brdcst_eoi);
16307ff178cdSJimmy Vetayases 
16317ff178cdSJimmy Vetayases 			if (apic_detect_x2apic()) {
16327ff178cdSJimmy Vetayases 				apic_enable_x2apic();
16337ff178cdSJimmy Vetayases 			}
16347ff178cdSJimmy Vetayases 
16357ff178cdSJimmy Vetayases 			if (apic_directed_EOI_supported() == 0) {
16367ff178cdSJimmy Vetayases 				apic_set_directed_EOI_handler();
16377ff178cdSJimmy Vetayases 			}
16387ff178cdSJimmy Vetayases 		}
16397ff178cdSJimmy Vetayases 	}
16407ff178cdSJimmy Vetayases }
16417ff178cdSJimmy Vetayases 
16427ff178cdSJimmy Vetayases /*ARGSUSED*/
16437ff178cdSJimmy Vetayases static void
16447ff178cdSJimmy Vetayases apic_record_ioapic_rdt(void *intrmap_private, ioapic_rdt_t *irdt)
16457ff178cdSJimmy Vetayases {
16467ff178cdSJimmy Vetayases 	irdt->ir_hi <<= APIC_ID_BIT_OFFSET;
16477ff178cdSJimmy Vetayases }
16487ff178cdSJimmy Vetayases 
16497ff178cdSJimmy Vetayases /*ARGSUSED*/
16507ff178cdSJimmy Vetayases static void
16517ff178cdSJimmy Vetayases apic_record_msi(void *intrmap_private, msi_regs_t *mregs)
16527ff178cdSJimmy Vetayases {
16537ff178cdSJimmy Vetayases 	mregs->mr_addr = MSI_ADDR_HDR |
16547ff178cdSJimmy Vetayases 	    (MSI_ADDR_RH_FIXED << MSI_ADDR_RH_SHIFT) |
16557ff178cdSJimmy Vetayases 	    (MSI_ADDR_DM_PHYSICAL << MSI_ADDR_DM_SHIFT) |
16567ff178cdSJimmy Vetayases 	    (mregs->mr_addr << MSI_ADDR_DEST_SHIFT);
16577ff178cdSJimmy Vetayases 	mregs->mr_data = (MSI_DATA_TM_EDGE << MSI_DATA_TM_SHIFT) |
16587ff178cdSJimmy Vetayases 	    mregs->mr_data;
16597ff178cdSJimmy Vetayases }
16607ff178cdSJimmy Vetayases 
16617ff178cdSJimmy Vetayases /*
16627ff178cdSJimmy Vetayases  * Functions from apic_introp.c
16637ff178cdSJimmy Vetayases  *
16647ff178cdSJimmy Vetayases  * Those functions are used by apic_intr_ops().
16657ff178cdSJimmy Vetayases  */
16667ff178cdSJimmy Vetayases 
16677ff178cdSJimmy Vetayases /*
16687ff178cdSJimmy Vetayases  * MSI support flag:
16697ff178cdSJimmy Vetayases  * reflects whether MSI is supported at APIC level
16707ff178cdSJimmy Vetayases  * it can also be patched through /etc/system
16717ff178cdSJimmy Vetayases  *
16727ff178cdSJimmy Vetayases  *  0 = default value - don't know and need to call apic_check_msi_support()
16737ff178cdSJimmy Vetayases  *      to find out then set it accordingly
16747ff178cdSJimmy Vetayases  *  1 = supported
16757ff178cdSJimmy Vetayases  * -1 = not supported
16767ff178cdSJimmy Vetayases  */
16777ff178cdSJimmy Vetayases int	apic_support_msi = 0;
16787ff178cdSJimmy Vetayases 
16797ff178cdSJimmy Vetayases /* Multiple vector support for MSI-X */
16807ff178cdSJimmy Vetayases int	apic_msix_enable = 1;
16817ff178cdSJimmy Vetayases 
16827ff178cdSJimmy Vetayases /* Multiple vector support for MSI */
16837ff178cdSJimmy Vetayases int	apic_multi_msi_enable = 1;
16847ff178cdSJimmy Vetayases 
16857ff178cdSJimmy Vetayases /*
1686970db7b7SDan Kimmel  * Check whether the system supports MSI.
16877ff178cdSJimmy Vetayases  *
1688970db7b7SDan Kimmel  * MSI is required for PCI-E and for PCI versions later than 2.2, so if we find
1689970db7b7SDan Kimmel  * a PCI-E bus or we find a PCI bus whose version we know is >= 2.2, then we
1690970db7b7SDan Kimmel  * return PSM_SUCCESS to indicate this system supports MSI.
1691970db7b7SDan Kimmel  *
1692970db7b7SDan Kimmel  * (Currently the only way we check whether a given PCI bus supports >= 2.2 is
1693970db7b7SDan Kimmel  * by detecting if we are running inside the KVM hypervisor, which guarantees
1694970db7b7SDan Kimmel  * this version number.)
16957ff178cdSJimmy Vetayases  */
16967ff178cdSJimmy Vetayases int
16977ff178cdSJimmy Vetayases apic_check_msi_support()
16987ff178cdSJimmy Vetayases {
16997ff178cdSJimmy Vetayases 	dev_info_t *cdip;
17007ff178cdSJimmy Vetayases 	char dev_type[16];
17017ff178cdSJimmy Vetayases 	int dev_len;
1702fbd54cb5SHans Rosenfeld 	int hwenv = get_hwenv();
17037ff178cdSJimmy Vetayases 
17047ff178cdSJimmy Vetayases 	DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support:\n"));
17057ff178cdSJimmy Vetayases 
17067ff178cdSJimmy Vetayases 	/*
17077ff178cdSJimmy Vetayases 	 * check whether the first level children of root_node have
1708970db7b7SDan Kimmel 	 * PCI-E or PCI capability.
17097ff178cdSJimmy Vetayases 	 */
17107ff178cdSJimmy Vetayases 	for (cdip = ddi_get_child(ddi_root_node()); cdip != NULL;
17117ff178cdSJimmy Vetayases 	    cdip = ddi_get_next_sibling(cdip)) {
17127ff178cdSJimmy Vetayases 
17137ff178cdSJimmy Vetayases 		DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: cdip: 0x%p,"
17147ff178cdSJimmy Vetayases 		    " driver: %s, binding: %s, nodename: %s\n", (void *)cdip,
17157ff178cdSJimmy Vetayases 		    ddi_driver_name(cdip), ddi_binding_name(cdip),
17167ff178cdSJimmy Vetayases 		    ddi_node_name(cdip)));
17177ff178cdSJimmy Vetayases 		dev_len = sizeof (dev_type);
17187ff178cdSJimmy Vetayases 		if (ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
17197ff178cdSJimmy Vetayases 		    "device_type", (caddr_t)dev_type, &dev_len)
17207ff178cdSJimmy Vetayases 		    != DDI_PROP_SUCCESS)
17217ff178cdSJimmy Vetayases 			continue;
17227ff178cdSJimmy Vetayases 		if (strcmp(dev_type, "pciex") == 0)
17237ff178cdSJimmy Vetayases 			return (PSM_SUCCESS);
1724fbd54cb5SHans Rosenfeld 		if (strcmp(dev_type, "pci") == 0 &&
1725fbd54cb5SHans Rosenfeld 		    (hwenv == HW_KVM || hwenv == HW_BHYVE))
1726970db7b7SDan Kimmel 			return (PSM_SUCCESS);
17277ff178cdSJimmy Vetayases 	}
17287ff178cdSJimmy Vetayases 
17297ff178cdSJimmy Vetayases 	/* MSI is not supported on this system */
17307ff178cdSJimmy Vetayases 	DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: no 'pciex' "
17317ff178cdSJimmy Vetayases 	    "device_type found\n"));
17327ff178cdSJimmy Vetayases 	return (PSM_FAILURE);
17337ff178cdSJimmy Vetayases }
17347ff178cdSJimmy Vetayases 
17357ff178cdSJimmy Vetayases /*
17367ff178cdSJimmy Vetayases  * apic_pci_msi_unconfigure:
17377ff178cdSJimmy Vetayases  *
17387ff178cdSJimmy Vetayases  * This and next two interfaces are copied from pci_intr_lib.c
17397ff178cdSJimmy Vetayases  * Do ensure that these two files stay in sync.
17407ff178cdSJimmy Vetayases  * These needed to be copied over here to avoid a deadlock situation on
17417ff178cdSJimmy Vetayases  * certain mp systems that use MSI interrupts.
17427ff178cdSJimmy Vetayases  *
17437ff178cdSJimmy Vetayases  * IMPORTANT regards next three interfaces:
17447ff178cdSJimmy Vetayases  * i) are called only for MSI/X interrupts.
17457ff178cdSJimmy Vetayases  * ii) called with interrupts disabled, and must not block
17467ff178cdSJimmy Vetayases  */
17477ff178cdSJimmy Vetayases void
17487ff178cdSJimmy Vetayases apic_pci_msi_unconfigure(dev_info_t *rdip, int type, int inum)
17497ff178cdSJimmy Vetayases {
17507ff178cdSJimmy Vetayases 	ushort_t		msi_ctrl;
17517ff178cdSJimmy Vetayases 	int			cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip);
17527ff178cdSJimmy Vetayases 	ddi_acc_handle_t	handle = i_ddi_get_pci_config_handle(rdip);
17537ff178cdSJimmy Vetayases 
17547ff178cdSJimmy Vetayases 	ASSERT((handle != NULL) && (cap_ptr != 0));
17557ff178cdSJimmy Vetayases 
17567ff178cdSJimmy Vetayases 	if (type == DDI_INTR_TYPE_MSI) {
17577ff178cdSJimmy Vetayases 		msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL);
17587ff178cdSJimmy Vetayases 		msi_ctrl &= (~PCI_MSI_MME_MASK);
17597ff178cdSJimmy Vetayases 		pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl);
17607ff178cdSJimmy Vetayases 		pci_config_put32(handle, cap_ptr + PCI_MSI_ADDR_OFFSET, 0);
17617ff178cdSJimmy Vetayases 
17627ff178cdSJimmy Vetayases 		if (msi_ctrl &  PCI_MSI_64BIT_MASK) {
17637ff178cdSJimmy Vetayases 			pci_config_put16(handle,
17647ff178cdSJimmy Vetayases 			    cap_ptr + PCI_MSI_64BIT_DATA, 0);
17657ff178cdSJimmy Vetayases 			pci_config_put32(handle,
17667ff178cdSJimmy Vetayases 			    cap_ptr + PCI_MSI_ADDR_OFFSET + 4, 0);
17677ff178cdSJimmy Vetayases 		} else {
17687ff178cdSJimmy Vetayases 			pci_config_put16(handle,
17697ff178cdSJimmy Vetayases 			    cap_ptr + PCI_MSI_32BIT_DATA, 0);
17707ff178cdSJimmy Vetayases 		}
17717ff178cdSJimmy Vetayases 
17727ff178cdSJimmy Vetayases 	} else if (type == DDI_INTR_TYPE_MSIX) {
17737ff178cdSJimmy Vetayases 		uintptr_t	off;
17747ff178cdSJimmy Vetayases 		uint32_t	mask;
17757ff178cdSJimmy Vetayases 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
17767ff178cdSJimmy Vetayases 
17777ff178cdSJimmy Vetayases 		ASSERT(msix_p != NULL);
17787ff178cdSJimmy Vetayases 
17797ff178cdSJimmy Vetayases 		/* Offset into "inum"th entry in the MSI-X table & mask it */
17807ff178cdSJimmy Vetayases 		off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
17817ff178cdSJimmy Vetayases 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
17827ff178cdSJimmy Vetayases 
17837ff178cdSJimmy Vetayases 		mask = ddi_get32(msix_p->msix_tbl_hdl, (uint32_t *)off);
17847ff178cdSJimmy Vetayases 
17857ff178cdSJimmy Vetayases 		ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, (mask | 1));
17867ff178cdSJimmy Vetayases 
17877ff178cdSJimmy Vetayases 		/* Offset into the "inum"th entry in the MSI-X table */
17887ff178cdSJimmy Vetayases 		off = (uintptr_t)msix_p->msix_tbl_addr +
17897ff178cdSJimmy Vetayases 		    (inum * PCI_MSIX_VECTOR_SIZE);
17907ff178cdSJimmy Vetayases 
17917ff178cdSJimmy Vetayases 		/* Reset the "data" and "addr" bits */
17927ff178cdSJimmy Vetayases 		ddi_put32(msix_p->msix_tbl_hdl,
17937ff178cdSJimmy Vetayases 		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0);
17947ff178cdSJimmy Vetayases 		ddi_put64(msix_p->msix_tbl_hdl, (uint64_t *)off, 0);
17957ff178cdSJimmy Vetayases 	}
17967ff178cdSJimmy Vetayases }
17977ff178cdSJimmy Vetayases 
17987ff178cdSJimmy Vetayases /*
17997ff178cdSJimmy Vetayases  * apic_pci_msi_disable_mode:
18007ff178cdSJimmy Vetayases  */
18017ff178cdSJimmy Vetayases void
18027ff178cdSJimmy Vetayases apic_pci_msi_disable_mode(dev_info_t *rdip, int type)
18037ff178cdSJimmy Vetayases {
18047ff178cdSJimmy Vetayases 	ushort_t		msi_ctrl;
18057ff178cdSJimmy Vetayases 	int			cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip);
18067ff178cdSJimmy Vetayases 	ddi_acc_handle_t	handle = i_ddi_get_pci_config_handle(rdip);
18077ff178cdSJimmy Vetayases 
18087ff178cdSJimmy Vetayases 	ASSERT((handle != NULL) && (cap_ptr != 0));
18097ff178cdSJimmy Vetayases 
18107ff178cdSJimmy Vetayases 	if (type == DDI_INTR_TYPE_MSI) {
18117ff178cdSJimmy Vetayases 		msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL);
18127ff178cdSJimmy Vetayases 		if (!(msi_ctrl & PCI_MSI_ENABLE_BIT))
18137ff178cdSJimmy Vetayases 			return;
18147ff178cdSJimmy Vetayases 
18157ff178cdSJimmy Vetayases 		msi_ctrl &= ~PCI_MSI_ENABLE_BIT;	/* MSI disable */
18167ff178cdSJimmy Vetayases 		pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl);
18177ff178cdSJimmy Vetayases 
18187ff178cdSJimmy Vetayases 	} else if (type == DDI_INTR_TYPE_MSIX) {
18197ff178cdSJimmy Vetayases 		msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSIX_CTRL);
18207ff178cdSJimmy Vetayases 		if (msi_ctrl & PCI_MSIX_ENABLE_BIT) {
18217ff178cdSJimmy Vetayases 			msi_ctrl &= ~PCI_MSIX_ENABLE_BIT;
18227ff178cdSJimmy Vetayases 			pci_config_put16(handle, cap_ptr + PCI_MSIX_CTRL,
18237ff178cdSJimmy Vetayases 			    msi_ctrl);
18247ff178cdSJimmy Vetayases 		}
18257ff178cdSJimmy Vetayases 	}
18267ff178cdSJimmy Vetayases }
18277ff178cdSJimmy Vetayases 
18287ff178cdSJimmy Vetayases uint32_t
18297ff178cdSJimmy Vetayases apic_get_localapicid(uint32_t cpuid)
18307ff178cdSJimmy Vetayases {
18317ff178cdSJimmy Vetayases 	ASSERT(cpuid < apic_nproc && apic_cpus != NULL);
18327ff178cdSJimmy Vetayases 
18337ff178cdSJimmy Vetayases 	return (apic_cpus[cpuid].aci_local_id);
18347ff178cdSJimmy Vetayases }
18357ff178cdSJimmy Vetayases 
18367ff178cdSJimmy Vetayases uchar_t
18377ff178cdSJimmy Vetayases apic_get_ioapicid(uchar_t ioapicindex)
18387ff178cdSJimmy Vetayases {
18397ff178cdSJimmy Vetayases 	ASSERT(ioapicindex < MAX_IO_APIC);
18407ff178cdSJimmy Vetayases 
18417ff178cdSJimmy Vetayases 	return (apic_io_id[ioapicindex]);
18427ff178cdSJimmy Vetayases }
1843