xref: /onnv-gate/usr/src/uts/i86pc/os/mp_pc.c (revision 9637:60f2a2d63713)
13446Smrj /*
23446Smrj  * CDDL HEADER START
33446Smrj  *
43446Smrj  * The contents of this file are subject to the terms of the
53446Smrj  * Common Development and Distribution License (the "License").
63446Smrj  * You may not use this file except in compliance with the License.
73446Smrj  *
83446Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93446Smrj  * or http://www.opensolaris.org/os/licensing.
103446Smrj  * See the License for the specific language governing permissions
113446Smrj  * and limitations under the License.
123446Smrj  *
133446Smrj  * When distributing Covered Code, include this CDDL HEADER in each
143446Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153446Smrj  * If applicable, add the following below this CDDL HEADER, with the
163446Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
173446Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
183446Smrj  *
193446Smrj  * CDDL HEADER END
203446Smrj  */
213446Smrj /*
22*9637SRandy.Fishel@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
233446Smrj  * Use is subject to license terms.
243446Smrj  */
253446Smrj 
263446Smrj /*
273446Smrj  * Welcome to the world of the "real mode platter".
283446Smrj  * See also startup.c, mpcore.s and apic.c for related routines.
293446Smrj  */
303446Smrj 
313446Smrj #include <sys/types.h>
323446Smrj #include <sys/systm.h>
333446Smrj #include <sys/cpuvar.h>
343446Smrj #include <sys/kmem.h>
353446Smrj #include <sys/archsystm.h>
363446Smrj #include <sys/machsystm.h>
373446Smrj #include <sys/controlregs.h>
383446Smrj #include <sys/x86_archext.h>
393446Smrj #include <sys/smp_impldefs.h>
403446Smrj #include <sys/sysmacros.h>
413446Smrj #include <sys/mach_mmu.h>
423446Smrj #include <sys/promif.h>
433446Smrj #include <sys/cpu.h>
444191Sjosephb #include <vm/hat_i86.h>
453446Smrj 
463446Smrj extern void real_mode_start(void);
473446Smrj extern void real_mode_end(void);
485295Srandyf extern void *(*cpu_pause_func)(void *);
495295Srandyf 
505295Srandyf void rmp_gdt_init(rm_platter_t *);
513446Smrj 
523446Smrj /*
533446Smrj  * Fill up the real mode platter to make it easy for real mode code to
543446Smrj  * kick it off. This area should really be one passed by boot to kernel
553446Smrj  * and guaranteed to be below 1MB and aligned to 16 bytes. Should also
563446Smrj  * have identical physical and virtual address in paged mode.
573446Smrj  */
583446Smrj static ushort_t *warm_reset_vector = NULL;
593446Smrj 
603446Smrj int
613446Smrj mach_cpucontext_init(void)
623446Smrj {
633446Smrj 	ushort_t *vec;
643446Smrj 
653446Smrj 	if (!(vec = (ushort_t *)psm_map_phys(WARM_RESET_VECTOR,
663446Smrj 	    sizeof (vec), PROT_READ | PROT_WRITE)))
673446Smrj 		return (-1);
683446Smrj 	/*
693446Smrj 	 * setup secondary cpu bios boot up vector
703446Smrj 	 */
713446Smrj 	*vec = (ushort_t)((caddr_t)
725295Srandyf 	    ((struct rm_platter *)rm_platter_va)->rm_code - rm_platter_va
735295Srandyf 	    + ((ulong_t)rm_platter_va & 0xf));
743446Smrj 	vec[1] = (ushort_t)(rm_platter_pa >> 4);
753446Smrj 	warm_reset_vector = vec;
763446Smrj 
773446Smrj 	bcopy((caddr_t)real_mode_start,
783446Smrj 	    (caddr_t)((rm_platter_t *)rm_platter_va)->rm_code,
793446Smrj 	    (size_t)real_mode_end - (size_t)real_mode_start);
803446Smrj 
813446Smrj 	return (0);
823446Smrj }
833446Smrj 
843446Smrj void
853446Smrj mach_cpucontext_fini(void)
863446Smrj {
873446Smrj 	if (warm_reset_vector)
883446Smrj 		psm_unmap_phys((caddr_t)warm_reset_vector,
893446Smrj 		    sizeof (warm_reset_vector));
903446Smrj 	hat_unload(kas.a_hat, (caddr_t)(uintptr_t)rm_platter_pa, MMU_PAGESIZE,
913446Smrj 	    HAT_UNLOAD);
923446Smrj }
933446Smrj 
943446Smrj #if defined(__amd64)
953446Smrj extern void *long_mode_64(void);
963446Smrj #endif	/* __amd64 */
973446Smrj 
983446Smrj void *
993446Smrj mach_cpucontext_alloc(struct cpu *cp)
1003446Smrj {
1013446Smrj 	rm_platter_t *rm = (rm_platter_t *)rm_platter_va;
1023446Smrj 	struct cpu_tables *ct;
1033446Smrj 	struct tss *ntss;
1043446Smrj 
1053446Smrj 	/*
1065460Sjosephb 	 * Allocate space for stack, tss, gdt and idt. We round the size
107*9637SRandy.Fishel@Sun.COM 	 * allotted for cpu_tables up, so that the TSS is on a unique page.
1085460Sjosephb 	 * This is more efficient when running in virtual machines.
1093446Smrj 	 */
1105460Sjosephb 	ct = kmem_zalloc(P2ROUNDUP(sizeof (*ct), PAGESIZE), KM_SLEEP);
1115460Sjosephb 	if ((uintptr_t)ct & PAGEOFFSET)
1123446Smrj 		panic("mp_startup_init: cpu%d misaligned tables", cp->cpu_id);
1133446Smrj 
1143446Smrj 	ntss = cp->cpu_tss = &ct->ct_tss;
1153446Smrj 
1163446Smrj #if defined(__amd64)
1173446Smrj 
1183446Smrj 	/*
1193446Smrj 	 * #DF (double fault).
1203446Smrj 	 */
1213446Smrj 	ntss->tss_ist1 = (uint64_t)&ct->ct_stack[sizeof (ct->ct_stack)];
1223446Smrj 
1233446Smrj #elif defined(__i386)
1243446Smrj 
1253446Smrj 	ntss->tss_esp0 = ntss->tss_esp1 = ntss->tss_esp2 = ntss->tss_esp =
1263446Smrj 	    (uint32_t)&ct->ct_stack[sizeof (ct->ct_stack)];
1273446Smrj 
1283446Smrj 	ntss->tss_ss0 = ntss->tss_ss1 = ntss->tss_ss2 = ntss->tss_ss = KDS_SEL;
1293446Smrj 
1303446Smrj 	ntss->tss_eip = (uint32_t)cp->cpu_thread->t_pc;
1313446Smrj 
1323446Smrj 	ntss->tss_cs = KCS_SEL;
1333446Smrj 	ntss->tss_ds = ntss->tss_es = KDS_SEL;
1343446Smrj 	ntss->tss_fs = KFS_SEL;
1353446Smrj 	ntss->tss_gs = KGS_SEL;
1363446Smrj 
1373446Smrj #endif	/* __i386 */
1383446Smrj 
1393446Smrj 	/*
1403446Smrj 	 * Set I/O bit map offset equal to size of TSS segment limit
1413446Smrj 	 * for no I/O permission map. This will cause all user I/O
1423446Smrj 	 * instructions to generate #gp fault.
1433446Smrj 	 */
1443446Smrj 	ntss->tss_bitmapbase = sizeof (*ntss);
1453446Smrj 
1463446Smrj 	/*
1473446Smrj 	 * Setup kernel tss.
1483446Smrj 	 */
1493446Smrj 	set_syssegd((system_desc_t *)&cp->cpu_gdt[GDT_KTSS], cp->cpu_tss,
1505460Sjosephb 	    sizeof (*cp->cpu_tss) - 1, SDT_SYSTSS, SEL_KPL);
1513446Smrj 
1523446Smrj 	/*
1533446Smrj 	 * Now copy all that we've set up onto the real mode platter
1543446Smrj 	 * for the real mode code to digest as part of starting the cpu.
1553446Smrj 	 */
1563446Smrj 
1573446Smrj 	rm->rm_idt_base = cp->cpu_idt;
1585460Sjosephb 	rm->rm_idt_lim = sizeof (*cp->cpu_idt) * NIDT - 1;
1593446Smrj 	rm->rm_gdt_base = cp->cpu_gdt;
1605460Sjosephb 	rm->rm_gdt_lim = sizeof (*cp->cpu_gdt) * NGDT - 1;
1613446Smrj 
1623446Smrj 	rm->rm_pdbr = getcr3();
1633446Smrj 	rm->rm_cpu = cp->cpu_id;
1643446Smrj 	rm->rm_x86feature = x86_feature;
1653446Smrj 	rm->rm_cr4 = getcr4();
1663446Smrj 
1675295Srandyf 	rmp_gdt_init(rm);
1685295Srandyf 
1695295Srandyf 	return (ct);
1705295Srandyf }
1715295Srandyf 
1725295Srandyf /*ARGSUSED*/
1735295Srandyf void
1745295Srandyf rmp_gdt_init(rm_platter_t *rm)
1755295Srandyf {
1765295Srandyf 
1773446Smrj #if defined(__amd64)
1783446Smrj 
1793446Smrj 	if (getcr3() > 0xffffffffUL)
1803446Smrj 		panic("Cannot initialize CPUs; kernel's 64-bit page tables\n"
1813446Smrj 		    "located above 4G in physical memory (@ 0x%lx)", getcr3());
1823446Smrj 
1833446Smrj 	/*
1843446Smrj 	 * Setup pseudo-descriptors for temporary GDT and IDT for use ONLY
1853446Smrj 	 * by code in real_mode_start():
1863446Smrj 	 *
1873446Smrj 	 * GDT[0]:  NULL selector
1883446Smrj 	 * GDT[1]:  64-bit CS: Long = 1, Present = 1, bits 12, 11 = 1
1893446Smrj 	 *
1903446Smrj 	 * Clear the IDT as interrupts will be off and a limit of 0 will cause
1913446Smrj 	 * the CPU to triple fault and reset on an NMI, seemingly as reasonable
1923446Smrj 	 * a course of action as any other, though it may cause the entire
1933446Smrj 	 * platform to reset in some cases...
1943446Smrj 	 */
1953446Smrj 	rm->rm_temp_gdt[0] = 0;
1963446Smrj 	rm->rm_temp_gdt[TEMPGDT_KCODE64] = 0x20980000000000ULL;
1973446Smrj 
1983446Smrj 	rm->rm_temp_gdt_lim = (ushort_t)(sizeof (rm->rm_temp_gdt) - 1);
1993446Smrj 	rm->rm_temp_gdt_base = rm_platter_pa +
2003446Smrj 	    (uint32_t)offsetof(rm_platter_t, rm_temp_gdt);
2013446Smrj 	rm->rm_temp_idt_lim = 0;
2023446Smrj 	rm->rm_temp_idt_base = 0;
2033446Smrj 
2043446Smrj 	/*
2053446Smrj 	 * Since the CPU needs to jump to protected mode using an identity
2063446Smrj 	 * mapped address, we need to calculate it here.
2073446Smrj 	 */
2083446Smrj 	rm->rm_longmode64_addr = rm_platter_pa +
2093446Smrj 	    ((uint32_t)long_mode_64 - (uint32_t)real_mode_start);
2103446Smrj #endif	/* __amd64 */
2113446Smrj }
2123446Smrj 
2133446Smrj /*ARGSUSED*/
2143446Smrj void
2153446Smrj mach_cpucontext_free(struct cpu *cp, void *arg, int err)
2163446Smrj {
2173446Smrj 	struct cpu_tables *ct = arg;
2183446Smrj 
2193446Smrj 	ASSERT(&ct->ct_tss == cp->cpu_tss);
2203446Smrj 
2213446Smrj 	switch (err) {
2223446Smrj 	case 0:
2233446Smrj 		break;
2243446Smrj 	case ETIMEDOUT:
2253446Smrj 		/*
2263446Smrj 		 * The processor was poked, but failed to start before
2273446Smrj 		 * we gave up waiting for it.  In case it starts later,
2283446Smrj 		 * don't free anything.
2293446Smrj 		 */
2303446Smrj 		break;
2313446Smrj 	default:
2323446Smrj 		/*
2333446Smrj 		 * Some other, passive, error occurred.
2343446Smrj 		 */
2355460Sjosephb 		kmem_free(ct, P2ROUNDUP(sizeof (*ct), PAGESIZE));
2363446Smrj 		cp->cpu_tss = NULL;
2373446Smrj 		break;
2383446Smrj 	}
2393446Smrj }
2403446Smrj 
2413446Smrj /*
2423446Smrj  * "Enter monitor."  Called via cross-call from stop_other_cpus().
2433446Smrj  */
2443446Smrj void
2453446Smrj mach_cpu_halt(char *msg)
2463446Smrj {
2473446Smrj 	if (msg)
2483446Smrj 		prom_printf("%s\n", msg);
2493446Smrj 
2503446Smrj 	/*CONSTANTCONDITION*/
2513446Smrj 	while (1)
2523446Smrj 		;
2533446Smrj }
2543446Smrj 
2553446Smrj void
2563446Smrj mach_cpu_idle(void)
2573446Smrj {
2583446Smrj 	i86_halt();
2593446Smrj }
2603446Smrj 
2613446Smrj void
2623446Smrj mach_cpu_pause(volatile char *safe)
2633446Smrj {
2643446Smrj 	/*
2653446Smrj 	 * This cpu is now safe.
2663446Smrj 	 */
2673446Smrj 	*safe = PAUSE_WAIT;
2683446Smrj 	membar_enter(); /* make sure stores are flushed */
2693446Smrj 
2703446Smrj 	/*
2713446Smrj 	 * Now we wait.  When we are allowed to continue, safe
2723446Smrj 	 * will be set to PAUSE_IDLE.
2733446Smrj 	 */
2743446Smrj 	while (*safe != PAUSE_IDLE)
2753446Smrj 		SMT_PAUSE();
2763446Smrj }
2773446Smrj 
2783446Smrj /*
2793446Smrj  * Power on CPU.
2803446Smrj  */
2813446Smrj /*ARGSUSED*/
2823446Smrj int
2833446Smrj mp_cpu_poweron(struct cpu *cp)
2843446Smrj {
2853446Smrj 	ASSERT(MUTEX_HELD(&cpu_lock));
2863446Smrj 	return (ENOTSUP);		/* not supported */
2873446Smrj }
2883446Smrj 
2893446Smrj /*
2903446Smrj  * Power off CPU.
2913446Smrj  */
2923446Smrj /*ARGSUSED*/
2933446Smrj int
2943446Smrj mp_cpu_poweroff(struct cpu *cp)
2953446Smrj {
2963446Smrj 	ASSERT(MUTEX_HELD(&cpu_lock));
2973446Smrj 	return (ENOTSUP);		/* not supported */
2983446Smrj }
2995529Ssmaybe 
3005529Ssmaybe /*
3015529Ssmaybe  * Return vcpu state, since this could be a virtual environment that we
3025529Ssmaybe  * are unaware of, return "unknown".
3035529Ssmaybe  */
3045529Ssmaybe /* ARGSUSED */
3055529Ssmaybe int
3065529Ssmaybe vcpu_on_pcpu(processorid_t cpu)
3075529Ssmaybe {
3085529Ssmaybe 	return (VCPU_STATE_UNKNOWN);
3095529Ssmaybe }
310