cpr_wakecode.s revision 1b1c71b2a16b821c15117fe73e4c435706a6272b
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/asm_linkage.h>
#include <sys/asm_misc.h>
#include <sys/privregs.h>
#include <sys/x86_archext.h>
#include <sys/cpr_wakecode.h>
#if !defined(__lint)
#include <sys/segments.h>
#include "assym.h"
#endif
#ifdef DEBUG
#define LED 1
#define SERIAL 1
#endif /* DEBUG */
#ifdef DEBUG
#define COM1 0x3f8
#define COM2 0x2f8
/*
* defined as offsets from the data register
*/
#define DLL 0 /* divisor latch (lsb) */
#endif /* DEBUG */
/*
* This file contains the low level routines involved in getting
* into and out of ACPI S3, including those needed for restarting
* the non-boot cpus.
*
* Our assumptions:
*
* Our actions:
*
*/
/*ARGSUSED*/
int
{ return 0; }
#else /* lint */
#if defined(__GNU_AS__)
#else /* !defined(__GNU_AS__) */
#if defined(__amd64)
/*
* C calling convention with no local variables, just use 1st arg ie %rdi
* and the registers. Therefore push caller's fp, set out fp to be sp and
* push %r12, %r13 %r14. At function end unwind this by: popping %r14, %r13
* %r14, restore the sp from fp and pop caller's fp.
*/
/*
* call save_stack(cpup)
* NB %rdi is the first arguemnt to both wc_save_context() and save_stack()
* so it must not be modified during either of these calls.
* The pushq will decrement the value of %rsp
* we need to push the %rbp because it is the frame pointer and we need
* to use the C calling convention
*/
#endif /* __amd64 */
#endif /* __GNU_AS__ */
#endif /* lint */
/*
* Our assumptions:
* - We are running in real mode.
* - Interrupts are disabled.
*
* Our actions:
* - We start using our GDT by loading correct values in the
* selector registers (cs=KCS_SEL, ds=es=ss=KDS_SEL, fs=KFS_SEL,
* gs=KGS_SEL).
* - We change over to using our IDT.
* - We load the default LDT into the hardware LDT register.
* - We load the default TSS into the hardware task register.
* - We restore registers
* - We return to original caller (a la setjmp)
*/
void
wc_rm_start(void)
{}
void
wc_rm_end(void)
{}
#else /* lint */
#if defined(__GNU_AS__)
#else /* __GNU_AS__ */
#if defined(__amd64)
/*
* For vulcan as we need to do a .code32 and mentally invert the
* meaning of the addr16 and data16 prefixes to get 32-bit access when
* generating code to be executed in 16-bit mode (sigh...)
*/
#if LED
#endif
#if SERIAL
#endif
/*
* Enable protected-mode, write protect, and alignment mask
* %cr0 has already been initialsed to zero
*/
/*
* Do a jmp immediately after writing to cr0 when enabling protected
* mode to clear the real mode prefetch queue (per Intel's docs)
*/
#if LED
#endif
#if SERIAL
#endif
/*
* 16-bit protected mode is now active, so prepare to turn on long
* mode
*/
#if LED
#endif
#if SERIAL
#endif
/*
* Add any initial cr4 bits
*/
/*
* Enable PAE mode (CR4.PAE)
*/
#if LED
#endif
#if SERIAL
#endif
/*
* Point cr3 to the 64-bit long mode page tables.
*
* Note that these MUST exist in 32-bit space, as we don't have
* a way to load %cr3 with a 64-bit base address for the page tables
* until the CPU is actually executing in 64-bit long mode.
*/
/*
* Set long mode enable in EFER (EFER.LME = 1)
*/
#if LED
#endif
#if SERIAL
#endif
/*
* Finally, turn on paging (CR0.PG = 1) to activate long mode.
*/
/*
* The instruction after enabling paging in CR0 MUST be a branch.
*/
#if LED
#endif
#if SERIAL
#endif
/*
* Long mode is now active but since we're still running with the
* original 16-bit CS we're actually in 16-bit compatability mode.
*
* We have to load an intermediate GDT and IDT here that we know are
* in 32-bit space before we can use the kernel's GDT and IDT, which
* may be in the 64-bit address space, and since we're in compatability
* mode, we only have access to 16 and 32-bit instructions at the
* moment.
*/
/*
* Do a far transfer to 64-bit mode. Set the CS selector to a 64-bit
* long mode selector (CS.L=1) in the temporary 32-bit GDT and jump
* to the real mode platter address of wc_long_mode_64 as until the
* 64-bit CS is in place we don't have access to 64-bit instructions
* and thus can't reference a 64-bit %rip.
*/
#if LED
#endif
#if SERIAL
#endif
/*
* Support routine to re-initialize VGA subsystem
*/
/*
* Support routine to re-initialize keyboard (which is USB - help!)
*/
/*
* Support routine to re-initialize COM ports to something sane
*/
#if DEBUG
/*
* on debug kernels we need to initialize COM1 & COM2 here, so that
* we can get debug output before the asy driver has resumed
*/
#endif /* DEBUG */
#if LED
#endif
#if SERIAL
#endif
/*
* We are now running in long mode with a 64-bit CS (EFER.LMA=1,
* CS.L=1) so we now have access to 64-bit instructions.
*
* First, set the 64-bit GDT base.
*/
/*
* Save the CPU number in %r11; get the value here since it's saved in
* the real mode platter.
*/
/ JAN
/*
* Add rm_platter_pa to %rsp to point it to the same location as seen
* from 64-bit mode.
*/
/*
* Now do an lretq to load CS with the appropriate selector for the
* kernel's 64-bit GDT and to start executing 64-bit setup code at the
* virtual address where boot originally loaded this code rather than
* the copy in the real mode platter's rm_code array as we've been
* doing so far.
*/
#if LED
#endif
#if SERIAL
#endif
#if LED
#endif
#if SERIAL
#endif
/*
* Complete the balance of the setup we need to before executing
* 64-bit kernel code (namely init rsp, TSS, LGDT, FS and GS).
*/
#if LED
#endif
#if SERIAL
#endif
/*
* restore the rest of the registers
*/
#if LED
#endif
#if SERIAL
#endif
/*
* restore the rest of the registers
*/
/*
* Before proceeding, enable usage of the page table NX bit if
* that's how the page tables are set up.
*/
jz 1f
1:
#if LED
#endif
#if SERIAL
#endif
#if LED
#endif
#if SERIAL
#endif
/*
* APIC initialization (we dummy up a stack so we can make this call)
*/
pushq $0 /* null frame pointer terminates stack trace */
/*
* skip iff function pointer is NULL
*/
cmpq $0, ap_mlsetup
je 2f
2:
#if LED
#endif
#if SERIAL
#endif
#if LED
#endif
#if SERIAL
#endif
#if LED
#endif
#if SERIAL
#endif
/*
* can not use outb after this point, because doing so would mean using
* %dx which would modify %rdx which is restored here
*/
A1:
#if LED
#endif
#if SERIAL
#endif
#if LED
#endif
#if SERIAL
#endif
#if LED
#endif
#if SERIAL
#endif
#if LED
#endif
#if LED
#endif
movl %eax, %cr4
#if LED
D16 movb $0xd6, %al
outb $WC_LED
#endif
D16 A16 movl %cs:WC_CR3(%ebx), %eax / set PDPT
movl %eax, %cr3
#if LED
D16 movb $0xd7, %al
outb $WC_LED
#endif
movl %eax, %cr0
#if LED
D16 movb $0xd8, %al
outb $WC_LED
#endif
D16 A16 movl %cs:WC_VIRTADDR(%ebx), %ebx / virtaddr of wc_cpu_t
#if LED
D16 movb $0xd9, %al
outb $WC_LED
#endif
#if LED
D16 movb $0xda, %al
outb $WC_LED
#endif
jmp flush / flush prefetch queue
flush:
D16 pushl $KCS_SEL
D16 pushl $kernel_wc_code
D16 lret / re-appear at kernel_wc_code
/*
* Support routine to re-initialize VGA subsystem
*/
vgainit:
D16 ret
/*
* Support routine to re-initialize keyboard (which is USB - help!)
*/
kbdinit:
D16 ret
/*
* Support routine to re-initialize COM ports to something sane for debug output
*/
cominit:
#if DEBUG
/*
* on debug kernels we need to initialize COM1 & COM2 here, so that
* we can get debug output before the asy driver has resumed
*/
/ select COM1
D16 movl $[COM1+LCR], %edx
D16 movb $DLAB, %al / divisor latch
outb (%dx)
D16 movl $[COM1+DLL], %edx / divisor latch lsb
D16 movb $B9600L, %al / divisor latch
outb (%dx)
D16 movl $[COM1+DLH], %edx / divisor latch hsb
D16 movb $B9600H, %al / divisor latch
outb (%dx)
D16 movl $[COM1+LCR], %edx / select COM1
D16 movb $[STOP1|BITS8], %al / 1 stop bit, 8bit word len
outb (%dx)
D16 movl $[COM1+MCR], %edx / select COM1
D16 movb $[RTS|DTR], %al / 1 stop bit, 8bit word len
outb (%dx)
/ select COM2
D16 movl $[COM2+LCR], %edx
D16 movb $DLAB, %al / divisor latch
outb (%dx)
D16 movl $[COM2+DLL], %edx / divisor latch lsb
D16 movb $B9600L, %al / divisor latch
outb (%dx)
D16 movl $[COM2+DLH], %edx / divisor latch hsb
D16 movb $B9600H, %al / divisor latch
outb (%dx)
D16 movl $[COM2+LCR], %edx / select COM1
D16 movb $[STOP1|BITS8], %al / 1 stop bit, 8bit word len
outb (%dx)
D16 movl $[COM2+MCR], %edx / select COM1
D16 movb $[RTS|DTR], %al / 1 stop bit, 8bit word len
outb (%dx)
#endif /* DEBUG */
D16 ret
.globl wc_rm_end
wc_rm_end:
nop
.globl kernel_wc_code
kernel_wc_code:
#if LED
#endif
#if LED
#endif
/*
* Before proceeding, enable usage of the page table NX bit if
* that's how the page tables are set up.
*/
jz 1f
1:
movl WC_RETADDR(%ebx), %eax / return to caller of wc_save_context
movl %eax, (%esp)
movw WC_ES(%ebx), %es / restore segment registers
movw WC_FS(%ebx), %fs
movw WC_GS(%ebx), %gs
/*
* APIC initialization, skip iff function pointer is NULL
*/
cmpl $0, ap_mlsetup
je 2f
call *ap_mlsetup
2:
call *cpr_start_cpu_func
pushl WC_EFLAGS(%ebx) / restore flags
popfl
movl WC_EDI(%ebx), %edi / restore general registers
movl WC_ESI(%ebx), %esi
movl WC_EBP(%ebx), %ebp
movl WC_EBX(%ebx), %ebx
/exit: jmp exit / stop here for HDT
xorl %eax, %eax / at wakeup return 0
ret
SET_SIZE(wc_rm_start)
#endif /* defined(__amd64) */
#endif /* !defined(__GNU_AS__) */
#endif /* lint */