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.
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.
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]
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
25 #include <sys/asm_linkage.h>
26 #include <sys/asm_misc.h>
27 #include <sys/regset.h>
28 #include <sys/privregs.h>
29 #include <sys/x86_archext.h>
30 #include <sys/cpr_wakecode.h>
33 #include <sys/segments.h>
45 #define WC_COM COM2 /* either COM1 or COM2 */
46 #define WC_LED 0x80 /* diagnostic led port ON motherboard */
49 * defined as offsets from the data register
51 #define DLL 0 /* divisor latch (lsb) */
52 #define DLH 1 /* divisor latch (msb) */
53 #define LCR 3 /* line control register */
54 #define MCR 4 /* modem control register */
57 #define DLAB 0x80 /* divisor latch access bit */
58 #define B9600L 0X0c /* lsb bit pattern for 9600 baud */
59 #define B9600H 0X0 /* hsb bit pattern for 9600 baud */
60 #define DTR 0x01 /* Data Terminal Ready */
61 #define RTS 0x02 /* Request To Send */
62 #define STOP1 0x00 /* 1 stop bit */
63 #define BITS8 0x03 /* 8 bits per char */
68 * This file contains the low level routines involved in getting
69 * into and out of ACPI S3, including those needed for restarting
78 #if defined(lint) || defined(__lint)
82 wc_save_context
(wc_cpu_t
*pcpu
)
89 ENTRY_NP
(wc_save_context
)
91 movq
(%rsp
), %rdx
/ return address
92 movq
%rdx
, WC_RETADDR
(%rdi
)
96 movq
%rdi
, WC_VIRTADDR
(%rdi
)
97 movq
%rdi
, WC_RDI
(%rdi
)
99 movq
%rdx
, WC_RDX
(%rdi
)
101 / stash everything else we need
108 movq
%rdx
, WC_CR0
(%rdi
)
110 movq
%rdx
, WC_CR3
(%rdi
)
112 movq
%rdx
, WC_CR4
(%rdi
)
114 movq
%rdx
, WC_CR8
(%rdi
)
116 movq
%r8, WC_R8
(%rdi
)
117 movq
%r9, WC_R9
(%rdi
)
118 movq
%r10, WC_R10
(%rdi
)
119 movq
%r11, WC_R11
(%rdi
)
120 movq
%r12, WC_R12
(%rdi
)
121 movq
%r13, WC_R13
(%rdi
)
122 movq
%r14, WC_R14
(%rdi
)
123 movq
%r15, WC_R15
(%rdi
)
124 movq
%rax
, WC_RAX
(%rdi
)
125 movq
%rbp
, WC_RBP
(%rdi
)
126 movq
%rbx
, WC_RBX
(%rdi
)
127 movq
%rcx
, WC_RCX
(%rdi
)
128 movq
%rsi
, WC_RSI
(%rdi
)
129 movq
%rsp
, WC_RSP
(%rdi
)
131 movw
%ss
, WC_SS
(%rdi
)
132 movw
%cs
, WC_CS
(%rdi
)
133 movw
%ds
, WC_DS
(%rdi
)
134 movw
%es
, WC_ES
(%rdi
)
136 movq $
0, %rcx
/ save
%fs register
138 movq
%rcx
, WC_FS
(%rdi
)
140 movl $MSR_AMD_FSBASE
, %ecx
142 movl
%eax
, WC_FSBASE
(%rdi
)
143 movl
%edx
, WC_FSBASE+
4(%rdi
)
145 movq $
0, %rcx
/ save
%gs register
147 movq
%rcx
, WC_GS
(%rdi
)
149 movl $MSR_AMD_GSBASE
, %ecx
/ save gsbase
msr
151 movl
%eax
, WC_GSBASE
(%rdi
)
152 movl
%edx
, WC_GSBASE+
4(%rdi
)
154 movl $MSR_AMD_KGSBASE
, %ecx
/ save kgsbase
msr
156 movl
%eax
, WC_KGSBASE
(%rdi
)
157 movl
%edx
, WC_KGSBASE+
4(%rdi
)
159 movq
%gs
:CPU_ID
, %rax
/ save current cpu id
160 movq
%rax
, WC_CPU_ID
(%rdi
)
165 wbinvd
/ flush the cache
168 movq $
1, %rax
/ at suspend return
1
174 SET_SIZE
(wc_save_context
)
176 #elif defined(__i386)
178 ENTRY_NP
(wc_save_context
)
180 movl
4(%esp
), %eax
/ wc_cpu_t
*
181 movl
%eax
, WC_VIRTADDR
(%eax
)
183 movl
(%esp
), %edx
/ return address
184 movl
%edx
, WC_RETADDR
(%eax
)
186 str WC_TR
(%eax
) / stash everything else we need
192 movl
%edx
, WC_CR0
(%eax
)
194 movl
%edx
, WC_CR3
(%eax
)
196 movl
%edx
, WC_CR4
(%eax
)
198 movl
%ebx
, WC_EBX
(%eax
)
199 movl
%edi
, WC_EDI
(%eax
)
200 movl
%esi
, WC_ESI
(%eax
)
201 movl
%ebp
, WC_EBP
(%eax
)
202 movl
%esp
, WC_ESP
(%eax
)
204 movw
%ss
, WC_SS
(%eax
)
205 movw
%cs
, WC_CS
(%eax
)
206 movw
%ds
, WC_DS
(%eax
)
207 movw
%es
, WC_ES
(%eax
)
208 movw
%fs, WC_FS
(%eax
)
209 movw
%gs
, WC_GS
(%eax
)
214 pushl
%gs
:CPU_ID
/ save current cpu id
217 wbinvd
/ flush the cache
220 movl $
1, %eax
/ at suspend return
1
223 SET_SIZE
(wc_save_context
)
232 * - We are running in real mode.
233 * - Interrupts are disabled.
236 * - We start using our GDT by loading correct values in the
237 * selector registers (cs=KCS_SEL, ds=es=ss=KDS_SEL, fs=KFS_SEL,
239 * - We change over to using our IDT.
240 * - We load the default LDT into the hardware LDT register.
241 * - We load the default TSS into the hardware task register.
242 * - We restore registers
243 * - We return to original caller (a la setjmp)
246 #if defined(lint) || defined(__lint)
260 ENTRY_NP
(wc_rm_start
)
263 * For the Sun Studio 10 assembler we needed to do a .code32 and
264 * mentally invert the meaning of the addr16 and data16 prefixes to
265 * get 32-bit access when generating code to be executed in 16-bit
268 * This code, despite always being built with GNU as, has inherited
269 * the conceptual damage.
276 movw
%ax
, %ds
/ establish ds
...
277 movw
%ax
, %ss
/ ... and ss:esp
278 D16 movl $WC_STKSTART
, %esp
279 / using the following value blows up machines
! - DO
NOT USE
280 / D16 movl
0xffc, %esp
284 D16 movl $WC_LED
, %edx
290 D16 movl $WC_COM
, %edx
298 * Enable protected-mode, write protect, and alignment mask
299 * %cr0 has already been initialsed to zero
302 D16 orl $_CONST
(CR0_PE|CR0_WP|CR0_AM
), %eax
306 * Do a jmp immediately after writing to cr0 when enabling protected
307 * mode to clear the real mode prefetch queue (per Intel's docs)
313 D16 movl $WC_LED
, %edx
319 D16 movl $WC_COM
, %edx
325 * 16-bit protected mode is now active, so prepare to turn on long
330 D16 movl $WC_LED
, %edx
336 D16 movl $WC_COM
, %edx
342 * Add any initial cr4 bits
345 A16 D16 orl CR4OFF
, %eax
348 * Enable PAE mode (CR4.PAE)
350 D16 orl $CR4_PAE
, %eax
354 D16 movl $WC_LED
, %edx
360 D16 movl $WC_COM
, %edx
366 * Point cr3 to the 64-bit long mode page tables.
368 * Note that these MUST exist in 32-bit space, as we don't have
369 * a way to load %cr3 with a 64-bit base address for the page tables
370 * until the CPU is actually executing in 64-bit long mode.
372 A16 D16 movl CR3OFF
, %eax
376 * Set long mode enable in EFER (EFER.LME = 1)
378 D16 movl $MSR_AMD_EFER
, %ecx
381 D16 orl $AMD_EFER_LME
, %eax
385 D16 movl $WC_LED
, %edx
391 D16 movl $WC_COM
, %edx
397 * Finally, turn on paging (CR0.PG = 1) to activate long mode.
400 D16 orl $CR0_PG
, %eax
404 * The instruction after enabling paging in CR0 MUST be a branch.
411 D16 movl $WC_LED
, %edx
417 D16 movl $WC_COM
, %edx
423 * Long mode is now active but since we're still running with the
424 * original 16-bit CS we're actually in 16-bit compatability mode.
426 * We have to load an intermediate GDT and IDT here that we know are
427 * in 32-bit space before we can use the kernel's GDT and IDT, which
428 * may be in the 64-bit address space, and since we're in compatability
429 * mode, we only have access to 16 and 32-bit instructions at the
432 A16 D16 lgdt TEMPGDTOFF
/* load temporary GDT */
433 A16 D16 lidt TEMPIDTOFF
/* load temporary IDT */
437 * Do a far transfer to 64-bit mode. Set the CS selector to a 64-bit
438 * long mode selector (CS.L=1) in the temporary 32-bit GDT and jump
439 * to the real mode platter address of wc_long_mode_64 as until the
440 * 64-bit CS is in place we don't have access to 64-bit instructions
441 * and thus can't reference a 64-bit %rip.
445 D16 movl $WC_LED
, %edx
451 D16 movl $WC_COM
, %edx
456 D16 pushl $TEMP_CS64_SEL
457 A16 D16 pushl LM64OFF
463 * Support routine to re-initialize VGA subsystem
469 * Support routine to re-initialize keyboard (which is USB - help!)
475 * Support routine to re-initialize COM ports to something sane
482 * on debug kernels we need to initialize COM1 & COM2 here, so that
483 * we can get debug output before the asy driver has resumed
487 D16 movl $_CONST
(COM1+LCR
), %edx
488 D16 movb $DLAB
, %al
/ divisor latch
491 D16 movl $_CONST
(COM1+DLL
), %edx
/ divisor latch lsb
492 D16 movb $B9600L
, %al
/ divisor latch
495 D16 movl $_CONST
(COM1+DLH
), %edx
/ divisor latch hsb
496 D16 movb $B9600H
, %al
/ divisor latch
499 D16 movl $_CONST
(COM1+LCR
), %edx
/ select COM1
500 D16 movb $_CONST
(STOP1|BITS8
), %al
/ 1 stop bit
, 8bit word len
503 D16 movl $_CONST
(COM1+MCR
), %edx
/ select COM1
504 D16 movb $_CONST
(RTS|DTR
), %al
/ data term ready
& req to send
508 D16 movl $_CONST
(COM2+LCR
), %edx
509 D16 movb $DLAB
, %al
/ divisor latch
512 D16 movl $_CONST
(COM2+DLL
), %edx
/ divisor latch lsb
513 D16 movb $B9600L
, %al
/ divisor latch
516 D16 movl $_CONST
(COM2+DLH
), %edx
/ divisor latch hsb
517 D16 movb $B9600H
, %al
/ divisor latch
520 D16 movl $_CONST
(COM2+LCR
), %edx
/ select COM1
521 D16 movb $_CONST
(STOP1|BITS8
), %al
/ 1 stop bit
, 8bit word len
524 D16 movl $_CONST
(COM2+MCR
), %edx
/ select COM1
525 D16 movb $_CONST
(RTS|DTR
), %al
/ data term ready
& req to send
533 .globl wc_long_mode_64
549 * We are now running in long mode with a 64-bit CS (EFER.LMA=1,
550 * CS.L=1) so we now have access to 64-bit instructions.
552 * First, set the 64-bit GDT base.
555 movl rm_platter_pa
, %eax
557 lgdtq GDTROFF
(%rax
) /* load 64-bit GDT */
560 * Save the CPU number in %r11; get the value here since it's saved in
561 * the real mode platter.
564 / the following is wrong
! need to figure out MP systems
565 / movl CPUNOFF
(%rax
), %r11d
568 * Add rm_platter_pa to %rsp to point it to the same location as seen
574 * Now do an lretq to load CS with the appropriate selector for the
575 * kernel's 64-bit GDT and to start executing 64-bit setup code at the
576 * virtual address where boot originally loaded this code rather than
577 * the copy in the real mode platter's rm_code array as we've been
587 / JAN this should produce
'i' but we get
'g' instead ???
595 pushq $kernel_wc_code
598 .globl kernel_wc_code
607 / JAN this should produce
'j' but we get
'g' instead ???
615 * Complete the balance of the setup we need to before executing
616 * 64-bit kernel code (namely init rsp, TSS, LGDT, FS and GS).
619 movq rm_platter_va
, %rbx
635 * restore the rest of the registers
653 * restore the rest of the registers
662 * Before proceeding, enable usage of the page table NX bit if
663 * that's how the page tables are set up.
665 bt $X86FSET_NX
, x86_featureset
(%rip
)
667 movl $MSR_AMD_EFER
, %ecx
669 orl $AMD_EFER_NXE
, %eax
673 movq WC_CR4
(%rbx
), %rax
/ restore full cr4
(with Global Enable
)
677 movzwq WC_TR
(%rbx
), %rax
/ clear TSS busy bit
678 addq WC_GDT+
2(%rbx
), %rax
679 andl $
0xfffffdff, 4(%rax
)
695 / restore
%fsbase
%gsbase
%kgbase registers using wrmsr instruction
697 movq WC_FS
(%rbx
), %rcx
/ restore
fs register
700 movl $MSR_AMD_FSBASE
, %ecx
701 movl WC_FSBASE
(%rbx
), %eax
702 movl WC_FSBASE+
4(%rbx
), %edx
705 movq WC_GS
(%rbx
), %rcx
/ restore gs register
708 movl $MSR_AMD_GSBASE
, %ecx
/ restore gsbase
msr
709 movl WC_GSBASE
(%rbx
), %eax
710 movl WC_GSBASE+
4(%rbx
), %edx
713 movl $MSR_AMD_KGSBASE
, %ecx
/ restore kgsbase
msr
714 movl WC_KGSBASE
(%rbx
), %eax
715 movl WC_KGSBASE+
4(%rbx
), %edx
718 movq WC_CR0
(%rbx
), %rdx
720 movq WC_CR3
(%rbx
), %rdx
722 movq WC_CR8
(%rbx
), %rdx
738 * if we are not running on the boot CPU restore stack contents by
739 * calling i_cpr_restore_stack(curthread, save_stack);
743 cmpl %eax
, WC_CPU_ID
(%rbx
)
746 movq
%gs
:CPU_THREAD
, %rdi
747 movq WC_SAVED_STACK
(%rbx
), %rsi
748 call i_cpr_restore_stack
751 movq WC_RSP
(%rbx
), %rsp
/ restore stack pointer
754 * APIC initialization
759 * skip iff function pointer is NULL
766 call
*cpr_start_cpu_func
768 / restore
%rbx to the value it ahd before we called the functions above
769 movq rm_platter_va
, %rbx
772 movq WC_R8
(%rbx
), %r8
773 movq WC_R9
(%rbx
), %r9
774 movq WC_R10
(%rbx
), %r10
775 movq WC_R11
(%rbx
), %r11
776 movq WC_R12
(%rbx
), %r12
777 movq WC_R13
(%rbx
), %r13
778 movq WC_R14
(%rbx
), %r14
779 movq WC_R15
(%rbx
), %r15
780 / movq WC_RAX
(%rbx
), %rax
781 movq WC_RBP
(%rbx
), %rbp
782 movq WC_RCX
(%rbx
), %rcx
783 / movq WC_RDX
(%rbx
), %rdx
784 movq WC_RDI
(%rbx
), %rdi
785 movq WC_RSI
(%rbx
), %rsi
788 / assume that
%cs does
not need to
be restored
789 / %ds
, %es
& %ss are ignored in
64bit mode
790 movw WC_SS
(%rbx
), %ss
791 movw WC_DS
(%rbx
), %ds
792 movw WC_ES
(%rbx
), %es
807 movq WC_RBP
(%rbx
), %rbp
808 movq WC_RSP
(%rbx
), %rsp
823 movq WC_RCX
(%rbx
), %rcx
825 pushq WC_EFLAGS
(%rbx
) / restore flags
841 * can not use outb after this point, because doing so would mean using
842 * %dx which would modify %rdx which is restored here
846 movq WC_RDX
(%rax
), %rdx
847 movq WC_RBX
(%rax
), %rbx
851 movq WC_RETADDR
(%rax
), %rax
852 movq
%rax
, (%rsp
) / return to caller of wc_save_context
854 xorl
%eax
, %eax
/ at wakeup return
0
858 SET_SIZE
(wc_rm_start
)
872 #elif defined(__i386)
874 ENTRY_NP
(wc_rm_start
)
876 /entry
: jmp entry
/ stop here for HDT
880 movw
%ax
, %ds
/ establish ds
...
881 movw
%ax
, %ss
/ ... and ss:esp
882 D16 movl $WC_STKSTART
, %esp
885 D16 movl $WC_LED
, %edx
891 D16 movl $WC_COM
, %edx
902 D16 movl $WC_LED
, %edx
908 D16 movl $WC_COM
, %edx
913 D16 A16 movl $WC_CPU
, %ebx
/ base
add of wc_cpu_t
921 D16 movl $WC_COM
, %edx
926 D16 A16 movl
%cs
:WC_DS
(%ebx
), %edx
/ %ds post prot
/paging transit
933 D16 A16 lgdt
%cs
:WC_GDT
(%ebx
) / restore gdt
and idtr
934 D16 A16 lidt
%cs
:WC_IDT
(%ebx
)
941 D16 A16 movl
%cs
:WC_CR4
(%ebx
), %eax
/ restore cr4
942 D16 andl $_BITNOT
(CR4_PGE
), %eax
/ don
't set Global Enable yet
950 D16 A16 movl %cs:WC_CR3(%ebx), %eax / set PDPT
958 D16 A16 movl %cs:WC_CR0(%ebx), %eax / enable prot/paging, etc.
966 D16 A16 movl %cs:WC_VIRTADDR(%ebx), %ebx / virtaddr of wc_cpu_t
978 jmp flush / flush prefetch queue
981 D16 pushl $kernel_wc_code
982 D16 lret / re-appear at kernel_wc_code
986 * Support routine to re-initialize VGA subsystem
992 * Support routine to re-initialize keyboard (which is USB - help!)
998 * Support routine to re-initialize COM ports to something sane for debug output
1003 * on debug kernels we need to initialize COM1 & COM2 here, so that
1004 * we can get debug output before the asy driver has resumed
1008 D16 movl $_CONST(COM1+LCR), %edx
1009 D16 movb $DLAB, %al / divisor latch
1012 D16 movl $_CONST(COM1+DLL), %edx / divisor latch lsb
1013 D16 movb $B9600L, %al / divisor latch
1016 D16 movl $_CONST(COM1+DLH), %edx / divisor latch hsb
1017 D16 movb $B9600H, %al / divisor latch
1020 D16 movl $_CONST(COM1+LCR), %edx / select COM1
1021 D16 movb $_CONST(STOP1|BITS8), %al / 1 stop bit, 8bit word len
1024 D16 movl $_CONST(COM1+MCR), %edx / select COM1
1025 D16 movb $_CONST(RTS|DTR), %al / 1 stop bit, 8bit word len
1029 D16 movl $_CONST(COM2+LCR), %edx
1030 D16 movb $DLAB, %al / divisor latch
1033 D16 movl $_CONST(COM2+DLL), %edx / divisor latch lsb
1034 D16 movb $B9600L, %al / divisor latch
1037 D16 movl $_CONST(COM2+DLH), %edx / divisor latch hsb
1038 D16 movb $B9600H, %al / divisor latch
1041 D16 movl $_CONST(COM2+LCR), %edx / select COM1
1042 D16 movb $_CONST(STOP1|BITS8), %al / 1 stop bit, 8bit word len
1045 D16 movl $_CONST(COM2+MCR), %edx / select COM1
1046 D16 movb $_CONST(RTS|DTR), %al / 1 stop bit, 8bit word len
1056 .globl kernel_wc_code
1058 / At this point we are with kernel's cs
and proper eip.
1059 / We will
be executing
not from the copy in real mode platter
,
1060 / but from the original code where boot loaded us.
1061 / By this time GDT
and IDT are loaded as is
cr0, cr3
and cr4.
1072 movw
%dx
, %ds
/ $KDS_SEL
1080 * Before proceeding, enable usage of the page table NX bit if
1081 * that's how the page tables are set up.
1083 bt $X86FSET_NX
, x86_featureset
1085 movl $MSR_AMD_EFER
, %ecx
1087 orl $AMD_EFER_NXE
, %eax
1091 movl WC_CR4
(%ebx
), %eax
/ restore full cr4
(with Global Enable
)
1095 lldt WC_LDT
(%ebx
) / $LDT_SEL
1097 movzwl WC_TR
(%ebx
), %eax
/ clear TSS busy bit
1098 addl WC_GDT+
2(%ebx
), %eax
1099 andl $_BITNOT
(0x200), 4(%eax
)
1100 ltr WC_TR
(%ebx
) / $UTSS_SEL
1102 movw WC_SS
(%ebx
), %ss
/ restore segment registers
1103 movw WC_ES
(%ebx
), %es
1104 movw WC_FS
(%ebx
), %fs
1105 movw WC_GS
(%ebx
), %gs
1108 * set the stack pointer to point into the identity mapped page
1109 * temporarily, so we can make function calls
1111 .globl rm_platter_va
1112 movl rm_platter_va
, %eax
1113 movl $WC_STKSTART
, %esp
1118 * if we are not running on the boot CPU restore stack contents by
1119 * calling i_cpr_restore_stack(curthread, save_stack);
1121 call i_cpr_bootcpuid
1122 cmpl %eax
, WC_CPU_ID
(%ebx
)
1125 pushl WC_SAVED_STACK
(%ebx
)
1126 pushl
%gs
:CPU_THREAD
1127 call i_cpr_restore_stack
1131 movl WC_ESP
(%ebx
), %esp
1134 movl WC_RETADDR
(%ebx
), %eax
/ return to caller of wc_save_context
1138 * APIC initialization, skip iff function pointer is NULL
1145 call
*cpr_start_cpu_func
1147 pushl WC_EFLAGS
(%ebx
) / restore flags
1150 movl WC_EDI
(%ebx
), %edi
/ restore general registers
1151 movl WC_ESI
(%ebx
), %esi
1152 movl WC_EBP
(%ebx
), %ebp
1153 movl WC_EBX
(%ebx
), %ebx
1155 /exit
: jmp exit
/ stop here for HDT
1157 xorl
%eax
, %eax
/ at wakeup return
0
1160 SET_SIZE
(wc_rm_start
)
1163 #endif /* defined(__amd64) */