Home | History | Annotate | Download | only in os
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  * Welcome to the world of the "real mode platter".
     28  * See also startup.c, mpcore.s and apic.c for related routines.
     29  */
     30 
     31 #include <sys/types.h>
     32 #include <sys/systm.h>
     33 #include <sys/cpuvar.h>
     34 #include <sys/kmem.h>
     35 #include <sys/archsystm.h>
     36 #include <sys/machsystm.h>
     37 #include <sys/controlregs.h>
     38 #include <sys/x86_archext.h>
     39 #include <sys/smp_impldefs.h>
     40 #include <sys/sysmacros.h>
     41 #include <sys/mach_mmu.h>
     42 #include <sys/promif.h>
     43 #include <sys/cpu.h>
     44 #include <vm/hat_i86.h>
     45 
     46 extern void real_mode_start(void);
     47 extern void real_mode_end(void);
     48 extern void *(*cpu_pause_func)(void *);
     49 
     50 void rmp_gdt_init(rm_platter_t *);
     51 
     52 /*
     53  * Fill up the real mode platter to make it easy for real mode code to
     54  * kick it off. This area should really be one passed by boot to kernel
     55  * and guaranteed to be below 1MB and aligned to 16 bytes. Should also
     56  * have identical physical and virtual address in paged mode.
     57  */
     58 static ushort_t *warm_reset_vector = NULL;
     59 
     60 int
     61 mach_cpucontext_init(void)
     62 {
     63 	ushort_t *vec;
     64 
     65 	if (!(vec = (ushort_t *)psm_map_phys(WARM_RESET_VECTOR,
     66 	    sizeof (vec), PROT_READ | PROT_WRITE)))
     67 		return (-1);
     68 	/*
     69 	 * setup secondary cpu bios boot up vector
     70 	 */
     71 	*vec = (ushort_t)((caddr_t)
     72 	    ((struct rm_platter *)rm_platter_va)->rm_code - rm_platter_va
     73 	    + ((ulong_t)rm_platter_va & 0xf));
     74 	vec[1] = (ushort_t)(rm_platter_pa >> 4);
     75 	warm_reset_vector = vec;
     76 
     77 	bcopy((caddr_t)real_mode_start,
     78 	    (caddr_t)((rm_platter_t *)rm_platter_va)->rm_code,
     79 	    (size_t)real_mode_end - (size_t)real_mode_start);
     80 
     81 	return (0);
     82 }
     83 
     84 void
     85 mach_cpucontext_fini(void)
     86 {
     87 	if (warm_reset_vector)
     88 		psm_unmap_phys((caddr_t)warm_reset_vector,
     89 		    sizeof (warm_reset_vector));
     90 	hat_unload(kas.a_hat, (caddr_t)(uintptr_t)rm_platter_pa, MMU_PAGESIZE,
     91 	    HAT_UNLOAD);
     92 }
     93 
     94 #if defined(__amd64)
     95 extern void *long_mode_64(void);
     96 #endif	/* __amd64 */
     97 
     98 void *
     99 mach_cpucontext_alloc(struct cpu *cp)
    100 {
    101 	rm_platter_t *rm = (rm_platter_t *)rm_platter_va;
    102 	struct cpu_tables *ct;
    103 	struct tss *ntss;
    104 
    105 	/*
    106 	 * Allocate space for stack, tss, gdt and idt. We round the size
    107 	 * allotted for cpu_tables up, so that the TSS is on a unique page.
    108 	 * This is more efficient when running in virtual machines.
    109 	 */
    110 	ct = kmem_zalloc(P2ROUNDUP(sizeof (*ct), PAGESIZE), KM_SLEEP);
    111 	if ((uintptr_t)ct & PAGEOFFSET)
    112 		panic("mp_startup_init: cpu%d misaligned tables", cp->cpu_id);
    113 
    114 	ntss = cp->cpu_tss = &ct->ct_tss;
    115 
    116 #if defined(__amd64)
    117 
    118 	/*
    119 	 * #DF (double fault).
    120 	 */
    121 	ntss->tss_ist1 = (uint64_t)&ct->ct_stack[sizeof (ct->ct_stack)];
    122 
    123 #elif defined(__i386)
    124 
    125 	ntss->tss_esp0 = ntss->tss_esp1 = ntss->tss_esp2 = ntss->tss_esp =
    126 	    (uint32_t)&ct->ct_stack[sizeof (ct->ct_stack)];
    127 
    128 	ntss->tss_ss0 = ntss->tss_ss1 = ntss->tss_ss2 = ntss->tss_ss = KDS_SEL;
    129 
    130 	ntss->tss_eip = (uint32_t)cp->cpu_thread->t_pc;
    131 
    132 	ntss->tss_cs = KCS_SEL;
    133 	ntss->tss_ds = ntss->tss_es = KDS_SEL;
    134 	ntss->tss_fs = KFS_SEL;
    135 	ntss->tss_gs = KGS_SEL;
    136 
    137 #endif	/* __i386 */
    138 
    139 	/*
    140 	 * Set I/O bit map offset equal to size of TSS segment limit
    141 	 * for no I/O permission map. This will cause all user I/O
    142 	 * instructions to generate #gp fault.
    143 	 */
    144 	ntss->tss_bitmapbase = sizeof (*ntss);
    145 
    146 	/*
    147 	 * Setup kernel tss.
    148 	 */
    149 	set_syssegd((system_desc_t *)&cp->cpu_gdt[GDT_KTSS], cp->cpu_tss,
    150 	    sizeof (*cp->cpu_tss) - 1, SDT_SYSTSS, SEL_KPL);
    151 
    152 	/*
    153 	 * Now copy all that we've set up onto the real mode platter
    154 	 * for the real mode code to digest as part of starting the cpu.
    155 	 */
    156 
    157 	rm->rm_idt_base = cp->cpu_idt;
    158 	rm->rm_idt_lim = sizeof (*cp->cpu_idt) * NIDT - 1;
    159 	rm->rm_gdt_base = cp->cpu_gdt;
    160 	rm->rm_gdt_lim = sizeof (*cp->cpu_gdt) * NGDT - 1;
    161 
    162 	rm->rm_pdbr = getcr3();
    163 	rm->rm_cpu = cp->cpu_id;
    164 	rm->rm_x86feature = x86_feature;
    165 	rm->rm_cr4 = getcr4();
    166 
    167 	rmp_gdt_init(rm);
    168 
    169 	return (ct);
    170 }
    171 
    172 /*ARGSUSED*/
    173 void
    174 rmp_gdt_init(rm_platter_t *rm)
    175 {
    176 
    177 #if defined(__amd64)
    178 
    179 	if (getcr3() > 0xffffffffUL)
    180 		panic("Cannot initialize CPUs; kernel's 64-bit page tables\n"
    181 		    "located above 4G in physical memory (@ 0x%lx)", getcr3());
    182 
    183 	/*
    184 	 * Setup pseudo-descriptors for temporary GDT and IDT for use ONLY
    185 	 * by code in real_mode_start():
    186 	 *
    187 	 * GDT[0]:  NULL selector
    188 	 * GDT[1]:  64-bit CS: Long = 1, Present = 1, bits 12, 11 = 1
    189 	 *
    190 	 * Clear the IDT as interrupts will be off and a limit of 0 will cause
    191 	 * the CPU to triple fault and reset on an NMI, seemingly as reasonable
    192 	 * a course of action as any other, though it may cause the entire
    193 	 * platform to reset in some cases...
    194 	 */
    195 	rm->rm_temp_gdt[0] = 0;
    196 	rm->rm_temp_gdt[TEMPGDT_KCODE64] = 0x20980000000000ULL;
    197 
    198 	rm->rm_temp_gdt_lim = (ushort_t)(sizeof (rm->rm_temp_gdt) - 1);
    199 	rm->rm_temp_gdt_base = rm_platter_pa +
    200 	    (uint32_t)offsetof(rm_platter_t, rm_temp_gdt);
    201 	rm->rm_temp_idt_lim = 0;
    202 	rm->rm_temp_idt_base = 0;
    203 
    204 	/*
    205 	 * Since the CPU needs to jump to protected mode using an identity
    206 	 * mapped address, we need to calculate it here.
    207 	 */
    208 	rm->rm_longmode64_addr = rm_platter_pa +
    209 	    ((uint32_t)long_mode_64 - (uint32_t)real_mode_start);
    210 #endif	/* __amd64 */
    211 }
    212 
    213 /*ARGSUSED*/
    214 void
    215 mach_cpucontext_free(struct cpu *cp, void *arg, int err)
    216 {
    217 	struct cpu_tables *ct = arg;
    218 
    219 	ASSERT(&ct->ct_tss == cp->cpu_tss);
    220 
    221 	switch (err) {
    222 	case 0:
    223 		break;
    224 	case ETIMEDOUT:
    225 		/*
    226 		 * The processor was poked, but failed to start before
    227 		 * we gave up waiting for it.  In case it starts later,
    228 		 * don't free anything.
    229 		 */
    230 		break;
    231 	default:
    232 		/*
    233 		 * Some other, passive, error occurred.
    234 		 */
    235 		kmem_free(ct, P2ROUNDUP(sizeof (*ct), PAGESIZE));
    236 		cp->cpu_tss = NULL;
    237 		break;
    238 	}
    239 }
    240 
    241 /*
    242  * "Enter monitor."  Called via cross-call from stop_other_cpus().
    243  */
    244 void
    245 mach_cpu_halt(char *msg)
    246 {
    247 	if (msg)
    248 		prom_printf("%s\n", msg);
    249 
    250 	/*CONSTANTCONDITION*/
    251 	while (1)
    252 		;
    253 }
    254 
    255 void
    256 mach_cpu_idle(void)
    257 {
    258 	i86_halt();
    259 }
    260 
    261 void
    262 mach_cpu_pause(volatile char *safe)
    263 {
    264 	/*
    265 	 * This cpu is now safe.
    266 	 */
    267 	*safe = PAUSE_WAIT;
    268 	membar_enter(); /* make sure stores are flushed */
    269 
    270 	/*
    271 	 * Now we wait.  When we are allowed to continue, safe
    272 	 * will be set to PAUSE_IDLE.
    273 	 */
    274 	while (*safe != PAUSE_IDLE)
    275 		SMT_PAUSE();
    276 }
    277 
    278 /*
    279  * Power on CPU.
    280  */
    281 /*ARGSUSED*/
    282 int
    283 mp_cpu_poweron(struct cpu *cp)
    284 {
    285 	ASSERT(MUTEX_HELD(&cpu_lock));
    286 	return (ENOTSUP);		/* not supported */
    287 }
    288 
    289 /*
    290  * Power off CPU.
    291  */
    292 /*ARGSUSED*/
    293 int
    294 mp_cpu_poweroff(struct cpu *cp)
    295 {
    296 	ASSERT(MUTEX_HELD(&cpu_lock));
    297 	return (ENOTSUP);		/* not supported */
    298 }
    299 
    300 /*
    301  * Return vcpu state, since this could be a virtual environment that we
    302  * are unaware of, return "unknown".
    303  */
    304 /* ARGSUSED */
    305 int
    306 vcpu_on_pcpu(processorid_t cpu)
    307 {
    308 	return (VCPU_STATE_UNKNOWN);
    309 }
    310