cpr_wakecode.s revision dad255286ee5ada77255c1f9f132ceee0bc314aa
/*
* 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
*/
/*
*/
#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(__amd64)
#endif /* __amd64 */
#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(__amd64)
/*
* For the Sun Studio 10 assembler we needed 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...)
*
* This code, despite always being built with GNU as, has inherited
* the conceptual damage.
*/
#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.
*/
jnc 1f
1:
#if LED
#endif
#if SERIAL
#endif
#if LED
#endif
#if SERIAL
#endif
/*
* if we are not running on the boot CPU restore stack contents by
* calling i_cpr_restore_stack(curthread, save_stack);
*/
je 2f
2:
/*
* APIC initialization
*/
/*
* skip iff function pointer is NULL
*/
cmpq $0, ap_mlsetup
je 3f
3:
#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 $_CONST(COM1+LCR), %edx
D16 movb $DLAB, %al / divisor latch
outb (%dx)
D16 movl $_CONST(COM1+DLL), %edx / divisor latch lsb
D16 movb $B9600L, %al / divisor latch
outb (%dx)
D16 movl $_CONST(COM1+DLH), %edx / divisor latch hsb
D16 movb $B9600H, %al / divisor latch
outb (%dx)
D16 movl $_CONST(COM1+LCR), %edx / select COM1
D16 movb $_CONST(STOP1|BITS8), %al / 1 stop bit, 8bit word len
outb (%dx)
D16 movl $_CONST(COM1+MCR), %edx / select COM1
D16 movb $_CONST(RTS|DTR), %al / 1 stop bit, 8bit word len
outb (%dx)
/ select COM2
D16 movl $_CONST(COM2+LCR), %edx
D16 movb $DLAB, %al / divisor latch
outb (%dx)
D16 movl $_CONST(COM2+DLL), %edx / divisor latch lsb
D16 movb $B9600L, %al / divisor latch
outb (%dx)
D16 movl $_CONST(COM2+DLH), %edx / divisor latch hsb
D16 movb $B9600H, %al / divisor latch
outb (%dx)
D16 movl $_CONST(COM2+LCR), %edx / select COM1
D16 movb $_CONST(STOP1|BITS8), %al / 1 stop bit, 8bit word len
outb (%dx)
D16 movl $_CONST(COM2+MCR), %edx / select COM1
D16 movb $_CONST(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.
*/
jnc 1f
1:
/*
* set the stack pointer to point into the identity mapped page
* temporarily, so we can make function calls
*/
/*
* if we are not running on the boot CPU restore stack contents by
* calling i_cpr_restore_stack(curthread, save_stack);
*/
je 2f
2:
/*
* APIC initialization, skip iff function pointer is NULL
*/
cmpl $0, ap_mlsetup
je 3f
3:
#endif /* defined(__amd64) */
#endif /* lint */