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>
32 #include <sys/segments.h>
43 #define WC_COM COM2 /* either COM1 or COM2 */
44 #define WC_LED 0x80 /* diagnostic led port ON motherboard */
47 * defined as offsets from the data register
49 #define DLL 0 /* divisor latch (lsb) */
50 #define DLH 1 /* divisor latch (msb) */
51 #define LCR 3 /* line control register */
52 #define MCR 4 /* modem control register */
55 #define DLAB 0x80 /* divisor latch access bit */
56 #define B9600L 0X0c /* lsb bit pattern for 9600 baud */
57 #define B9600H 0X0 /* hsb bit pattern for 9600 baud */
58 #define DTR 0x01 /* Data Terminal Ready */
59 #define RTS 0x02 /* Request To Send */
60 #define STOP1 0x00 /* 1 stop bit */
61 #define BITS8 0x03 /* 8 bits per char */
66 * This file contains the low level routines involved in getting
67 * into and out of ACPI S3, including those needed for restarting
79 ENTRY_NP
(wc_save_context
)
81 movq
(%rsp
), %rdx
/ return address
82 movq
%rdx
, WC_RETADDR
(%rdi
)
86 movq
%rdi
, WC_VIRTADDR
(%rdi
)
87 movq
%rdi
, WC_RDI
(%rdi
)
89 movq
%rdx
, WC_RDX
(%rdi
)
91 / stash everything else we need
98 movq
%rdx
, WC_CR0
(%rdi
)
100 movq
%rdx
, WC_CR3
(%rdi
)
102 movq
%rdx
, WC_CR4
(%rdi
)
104 movq
%rdx
, WC_CR8
(%rdi
)
106 movq
%r8, WC_R8
(%rdi
)
107 movq
%r9, WC_R9
(%rdi
)
108 movq
%r10, WC_R10
(%rdi
)
109 movq
%r11, WC_R11
(%rdi
)
110 movq
%r12, WC_R12
(%rdi
)
111 movq
%r13, WC_R13
(%rdi
)
112 movq
%r14, WC_R14
(%rdi
)
113 movq
%r15, WC_R15
(%rdi
)
114 movq
%rax
, WC_RAX
(%rdi
)
115 movq
%rbp
, WC_RBP
(%rdi
)
116 movq
%rbx
, WC_RBX
(%rdi
)
117 movq
%rcx
, WC_RCX
(%rdi
)
118 movq
%rsi
, WC_RSI
(%rdi
)
119 movq
%rsp
, WC_RSP
(%rdi
)
121 movw
%ss
, WC_SS
(%rdi
)
122 movw
%cs
, WC_CS
(%rdi
)
123 movw
%ds
, WC_DS
(%rdi
)
124 movw
%es
, WC_ES
(%rdi
)
126 movq $
0, %rcx
/ save
%fs register
128 movq
%rcx
, WC_FS
(%rdi
)
130 movl $MSR_AMD_FSBASE
, %ecx
132 movl
%eax
, WC_FSBASE
(%rdi
)
133 movl
%edx
, WC_FSBASE+
4(%rdi
)
135 movq $
0, %rcx
/ save
%gs register
137 movq
%rcx
, WC_GS
(%rdi
)
139 movl $MSR_AMD_GSBASE
, %ecx
/ save gsbase
msr
141 movl
%eax
, WC_GSBASE
(%rdi
)
142 movl
%edx
, WC_GSBASE+
4(%rdi
)
144 movl $MSR_AMD_KGSBASE
, %ecx
/ save kgsbase
msr
146 movl
%eax
, WC_KGSBASE
(%rdi
)
147 movl
%edx
, WC_KGSBASE+
4(%rdi
)
149 movq
%gs
:CPU_ID
, %rax
/ save current cpu id
150 movq
%rax
, WC_CPU_ID
(%rdi
)
155 wbinvd
/ flush the cache
158 movq $
1, %rax
/ at suspend return
1
164 SET_SIZE
(wc_save_context
)
166 #elif defined(__i386)
168 ENTRY_NP
(wc_save_context
)
170 movl
4(%esp
), %eax
/ wc_cpu_t
*
171 movl
%eax
, WC_VIRTADDR
(%eax
)
173 movl
(%esp
), %edx
/ return address
174 movl
%edx
, WC_RETADDR
(%eax
)
176 str WC_TR
(%eax
) / stash everything else we need
182 movl
%edx
, WC_CR0
(%eax
)
184 movl
%edx
, WC_CR3
(%eax
)
186 movl
%edx
, WC_CR4
(%eax
)
188 movl
%ebx
, WC_EBX
(%eax
)
189 movl
%edi
, WC_EDI
(%eax
)
190 movl
%esi
, WC_ESI
(%eax
)
191 movl
%ebp
, WC_EBP
(%eax
)
192 movl
%esp
, WC_ESP
(%eax
)
194 movw
%ss
, WC_SS
(%eax
)
195 movw
%cs
, WC_CS
(%eax
)
196 movw
%ds
, WC_DS
(%eax
)
197 movw
%es
, WC_ES
(%eax
)
198 movw
%fs, WC_FS
(%eax
)
199 movw
%gs
, WC_GS
(%eax
)
204 pushl
%gs
:CPU_ID
/ save current cpu id
207 wbinvd
/ flush the cache
210 movl $
1, %eax
/ at suspend return
1
213 SET_SIZE
(wc_save_context
)
221 * - We are running in real mode.
222 * - Interrupts are disabled.
225 * - We start using our GDT by loading correct values in the
226 * selector registers (cs=KCS_SEL, ds=es=ss=KDS_SEL, fs=KFS_SEL,
228 * - We change over to using our IDT.
229 * - We load the default LDT into the hardware LDT register.
230 * - We load the default TSS into the hardware task register.
231 * - We restore registers
232 * - We return to original caller (a la setjmp)
238 ENTRY_NP
(wc_rm_start
)
241 * For the Sun Studio 10 assembler we needed to do a .code32 and
242 * mentally invert the meaning of the addr16 and data16 prefixes to
243 * get 32-bit access when generating code to be executed in 16-bit
246 * This code, despite always being built with GNU as, has inherited
247 * the conceptual damage.
254 movw
%ax
, %ds
/ establish ds
...
255 movw
%ax
, %ss
/ ... and ss:esp
256 D16 movl $WC_STKSTART
, %esp
257 / using the following value blows up machines
! - DO
NOT USE
258 / D16 movl
0xffc, %esp
262 D16 movl $WC_LED
, %edx
268 D16 movl $WC_COM
, %edx
276 * Enable protected-mode, write protect, and alignment mask
277 * %cr0 has already been initialsed to zero
280 D16 orl $_CONST
(CR0_PE|CR0_WP|CR0_AM
), %eax
284 * Do a jmp immediately after writing to cr0 when enabling protected
285 * mode to clear the real mode prefetch queue (per Intel's docs)
291 D16 movl $WC_LED
, %edx
297 D16 movl $WC_COM
, %edx
303 * 16-bit protected mode is now active, so prepare to turn on long
308 D16 movl $WC_LED
, %edx
314 D16 movl $WC_COM
, %edx
320 * Add any initial cr4 bits
323 A16 D16 orl CR4OFF
, %eax
326 * Enable PAE mode (CR4.PAE)
328 D16 orl $CR4_PAE
, %eax
332 D16 movl $WC_LED
, %edx
338 D16 movl $WC_COM
, %edx
344 * Point cr3 to the 64-bit long mode page tables.
346 * Note that these MUST exist in 32-bit space, as we don't have
347 * a way to load %cr3 with a 64-bit base address for the page tables
348 * until the CPU is actually executing in 64-bit long mode.
350 A16 D16 movl CR3OFF
, %eax
354 * Set long mode enable in EFER (EFER.LME = 1)
356 D16 movl $MSR_AMD_EFER
, %ecx
359 D16 orl $AMD_EFER_LME
, %eax
363 D16 movl $WC_LED
, %edx
369 D16 movl $WC_COM
, %edx
375 * Finally, turn on paging (CR0.PG = 1) to activate long mode.
378 D16 orl $CR0_PG
, %eax
382 * The instruction after enabling paging in CR0 MUST be a branch.
389 D16 movl $WC_LED
, %edx
395 D16 movl $WC_COM
, %edx
401 * Long mode is now active but since we're still running with the
402 * original 16-bit CS we're actually in 16-bit compatability mode.
404 * We have to load an intermediate GDT and IDT here that we know are
405 * in 32-bit space before we can use the kernel's GDT and IDT, which
406 * may be in the 64-bit address space, and since we're in compatability
407 * mode, we only have access to 16 and 32-bit instructions at the
410 A16 D16 lgdt TEMPGDTOFF
/* load temporary GDT */
411 A16 D16 lidt TEMPIDTOFF
/* load temporary IDT */
415 * Do a far transfer to 64-bit mode. Set the CS selector to a 64-bit
416 * long mode selector (CS.L=1) in the temporary 32-bit GDT and jump
417 * to the real mode platter address of wc_long_mode_64 as until the
418 * 64-bit CS is in place we don't have access to 64-bit instructions
419 * and thus can't reference a 64-bit %rip.
423 D16 movl $WC_LED
, %edx
429 D16 movl $WC_COM
, %edx
434 D16 pushl $TEMP_CS64_SEL
435 A16 D16 pushl LM64OFF
441 * Support routine to re-initialize VGA subsystem
447 * Support routine to re-initialize keyboard (which is USB - help!)
453 * Support routine to re-initialize COM ports to something sane
460 * on debug kernels we need to initialize COM1 & COM2 here, so that
461 * we can get debug output before the asy driver has resumed
465 D16 movl $_CONST
(COM1+LCR
), %edx
466 D16 movb $DLAB
, %al
/ divisor latch
469 D16 movl $_CONST
(COM1+DLL
), %edx
/ divisor latch lsb
470 D16 movb $B9600L
, %al
/ divisor latch
473 D16 movl $_CONST
(COM1+DLH
), %edx
/ divisor latch hsb
474 D16 movb $B9600H
, %al
/ divisor latch
477 D16 movl $_CONST
(COM1+LCR
), %edx
/ select COM1
478 D16 movb $_CONST
(STOP1|BITS8
), %al
/ 1 stop bit
, 8bit word len
481 D16 movl $_CONST
(COM1+MCR
), %edx
/ select COM1
482 D16 movb $_CONST
(RTS|DTR
), %al
/ data term ready
& req to send
486 D16 movl $_CONST
(COM2+LCR
), %edx
487 D16 movb $DLAB
, %al
/ divisor latch
490 D16 movl $_CONST
(COM2+DLL
), %edx
/ divisor latch lsb
491 D16 movb $B9600L
, %al
/ divisor latch
494 D16 movl $_CONST
(COM2+DLH
), %edx
/ divisor latch hsb
495 D16 movb $B9600H
, %al
/ divisor latch
498 D16 movl $_CONST
(COM2+LCR
), %edx
/ select COM1
499 D16 movb $_CONST
(STOP1|BITS8
), %al
/ 1 stop bit
, 8bit word len
502 D16 movl $_CONST
(COM2+MCR
), %edx
/ select COM1
503 D16 movb $_CONST
(RTS|DTR
), %al
/ data term ready
& req to send
511 .globl wc_long_mode_64
527 * We are now running in long mode with a 64-bit CS (EFER.LMA=1,
528 * CS.L=1) so we now have access to 64-bit instructions.
530 * First, set the 64-bit GDT base.
533 movl rm_platter_pa
, %eax
535 lgdtq GDTROFF
(%rax
) /* load 64-bit GDT */
538 * Save the CPU number in %r11; get the value here since it's saved in
539 * the real mode platter.
542 / the following is wrong
! need to figure out MP systems
543 / movl CPUNOFF
(%rax
), %r11d
546 * Add rm_platter_pa to %rsp to point it to the same location as seen
552 * Now do an lretq to load CS with the appropriate selector for the
553 * kernel's 64-bit GDT and to start executing 64-bit setup code at the
554 * virtual address where boot originally loaded this code rather than
555 * the copy in the real mode platter's rm_code array as we've been
565 / JAN this should produce
'i' but we get
'g' instead ???
573 pushq $kernel_wc_code
576 .globl kernel_wc_code
585 / JAN this should produce
'j' but we get
'g' instead ???
593 * Complete the balance of the setup we need to before executing
594 * 64-bit kernel code (namely init rsp, TSS, LGDT, FS and GS).
597 movq rm_platter_va
, %rbx
613 * restore the rest of the registers
631 * restore the rest of the registers
640 * Before proceeding, enable usage of the page table NX bit if
641 * that's how the page tables are set up.
643 bt $X86FSET_NX
, x86_featureset
(%rip
)
645 movl $MSR_AMD_EFER
, %ecx
647 orl $AMD_EFER_NXE
, %eax
651 movq WC_CR4
(%rbx
), %rax
/ restore full cr4
(with Global Enable
)
655 movzwq WC_TR
(%rbx
), %rax
/ clear TSS busy bit
656 addq WC_GDT+
2(%rbx
), %rax
657 andl $
0xfffffdff, 4(%rax
)
673 / restore
%fsbase
%gsbase
%kgbase registers using wrmsr instruction
675 movq WC_FS
(%rbx
), %rcx
/ restore
fs register
678 movl $MSR_AMD_FSBASE
, %ecx
679 movl WC_FSBASE
(%rbx
), %eax
680 movl WC_FSBASE+
4(%rbx
), %edx
683 movq WC_GS
(%rbx
), %rcx
/ restore gs register
686 movl $MSR_AMD_GSBASE
, %ecx
/ restore gsbase
msr
687 movl WC_GSBASE
(%rbx
), %eax
688 movl WC_GSBASE+
4(%rbx
), %edx
691 movl $MSR_AMD_KGSBASE
, %ecx
/ restore kgsbase
msr
692 movl WC_KGSBASE
(%rbx
), %eax
693 movl WC_KGSBASE+
4(%rbx
), %edx
696 movq WC_CR0
(%rbx
), %rdx
698 movq WC_CR3
(%rbx
), %rdx
700 movq WC_CR8
(%rbx
), %rdx
716 * if we are not running on the boot CPU restore stack contents by
717 * calling i_cpr_restore_stack(curthread, save_stack);
721 cmpl %eax
, WC_CPU_ID
(%rbx
)
724 movq
%gs
:CPU_THREAD
, %rdi
725 movq WC_SAVED_STACK
(%rbx
), %rsi
726 call i_cpr_restore_stack
729 movq WC_RSP
(%rbx
), %rsp
/ restore stack pointer
732 * APIC initialization
737 * skip iff function pointer is NULL
744 call
*cpr_start_cpu_func
746 / restore
%rbx to the value it ahd before we called the functions above
747 movq rm_platter_va
, %rbx
750 movq WC_R8
(%rbx
), %r8
751 movq WC_R9
(%rbx
), %r9
752 movq WC_R10
(%rbx
), %r10
753 movq WC_R11
(%rbx
), %r11
754 movq WC_R12
(%rbx
), %r12
755 movq WC_R13
(%rbx
), %r13
756 movq WC_R14
(%rbx
), %r14
757 movq WC_R15
(%rbx
), %r15
758 / movq WC_RAX
(%rbx
), %rax
759 movq WC_RBP
(%rbx
), %rbp
760 movq WC_RCX
(%rbx
), %rcx
761 / movq WC_RDX
(%rbx
), %rdx
762 movq WC_RDI
(%rbx
), %rdi
763 movq WC_RSI
(%rbx
), %rsi
766 / assume that
%cs does
not need to
be restored
767 / %ds
, %es
& %ss are ignored in
64bit mode
768 movw WC_SS
(%rbx
), %ss
769 movw WC_DS
(%rbx
), %ds
770 movw WC_ES
(%rbx
), %es
785 movq WC_RBP
(%rbx
), %rbp
786 movq WC_RSP
(%rbx
), %rsp
801 movq WC_RCX
(%rbx
), %rcx
803 pushq WC_EFLAGS
(%rbx
) / restore flags
819 * can not use outb after this point, because doing so would mean using
820 * %dx which would modify %rdx which is restored here
824 movq WC_RDX
(%rax
), %rdx
825 movq WC_RBX
(%rax
), %rbx
829 movq WC_RETADDR
(%rax
), %rax
830 movq
%rax
, (%rsp
) / return to caller of wc_save_context
832 xorl
%eax
, %eax
/ at wakeup return
0
836 SET_SIZE
(wc_rm_start
)
850 #elif defined(__i386)
852 ENTRY_NP
(wc_rm_start
)
854 /entry
: jmp entry
/ stop here for HDT
858 movw
%ax
, %ds
/ establish ds
...
859 movw
%ax
, %ss
/ ... and ss:esp
860 D16 movl $WC_STKSTART
, %esp
863 D16 movl $WC_LED
, %edx
869 D16 movl $WC_COM
, %edx
880 D16 movl $WC_LED
, %edx
886 D16 movl $WC_COM
, %edx
891 D16 A16 movl $WC_CPU
, %ebx
/ base
add of wc_cpu_t
899 D16 movl $WC_COM
, %edx
904 D16 A16 movl
%cs
:WC_DS
(%ebx
), %edx
/ %ds post prot
/paging transit
911 D16 A16 lgdt
%cs
:WC_GDT
(%ebx
) / restore gdt
and idtr
912 D16 A16 lidt
%cs
:WC_IDT
(%ebx
)
919 D16 A16 movl
%cs
:WC_CR4
(%ebx
), %eax
/ restore cr4
920 D16 andl $_BITNOT
(CR4_PGE
), %eax
/ don
't set Global Enable yet
928 D16 A16 movl %cs:WC_CR3(%ebx), %eax / set PDPT
936 D16 A16 movl %cs:WC_CR0(%ebx), %eax / enable prot/paging, etc.
944 D16 A16 movl %cs:WC_VIRTADDR(%ebx), %ebx / virtaddr of wc_cpu_t
956 jmp flush / flush prefetch queue
959 D16 pushl $kernel_wc_code
960 D16 lret / re-appear at kernel_wc_code
964 * Support routine to re-initialize VGA subsystem
970 * Support routine to re-initialize keyboard (which is USB - help!)
976 * Support routine to re-initialize COM ports to something sane for debug output
981 * on debug kernels we need to initialize COM1 & COM2 here, so that
982 * we can get debug output before the asy driver has resumed
986 D16 movl $_CONST(COM1+LCR), %edx
987 D16 movb $DLAB, %al / divisor latch
990 D16 movl $_CONST(COM1+DLL), %edx / divisor latch lsb
991 D16 movb $B9600L, %al / divisor latch
994 D16 movl $_CONST(COM1+DLH), %edx / divisor latch hsb
995 D16 movb $B9600H, %al / divisor latch
998 D16 movl $_CONST(COM1+LCR), %edx / select COM1
999 D16 movb $_CONST(STOP1|BITS8), %al / 1 stop bit, 8bit word len
1002 D16 movl $_CONST(COM1+MCR), %edx / select COM1
1003 D16 movb $_CONST(RTS|DTR), %al / 1 stop bit, 8bit word len
1007 D16 movl $_CONST(COM2+LCR), %edx
1008 D16 movb $DLAB, %al / divisor latch
1011 D16 movl $_CONST(COM2+DLL), %edx / divisor latch lsb
1012 D16 movb $B9600L, %al / divisor latch
1015 D16 movl $_CONST(COM2+DLH), %edx / divisor latch hsb
1016 D16 movb $B9600H, %al / divisor latch
1019 D16 movl $_CONST(COM2+LCR), %edx / select COM1
1020 D16 movb $_CONST(STOP1|BITS8), %al / 1 stop bit, 8bit word len
1023 D16 movl $_CONST(COM2+MCR), %edx / select COM1
1024 D16 movb $_CONST(RTS|DTR), %al / 1 stop bit, 8bit word len
1034 .globl kernel_wc_code
1036 / At this point we are with kernel's cs
and proper eip.
1037 / We will
be executing
not from the copy in real mode platter
,
1038 / but from the original code where boot loaded us.
1039 / By this time GDT
and IDT are loaded as is
cr0, cr3
and cr4.
1050 movw
%dx
, %ds
/ $KDS_SEL
1058 * Before proceeding, enable usage of the page table NX bit if
1059 * that's how the page tables are set up.
1061 bt $X86FSET_NX
, x86_featureset
1063 movl $MSR_AMD_EFER
, %ecx
1065 orl $AMD_EFER_NXE
, %eax
1069 movl WC_CR4
(%ebx
), %eax
/ restore full cr4
(with Global Enable
)
1073 lldt WC_LDT
(%ebx
) / $LDT_SEL
1075 movzwl WC_TR
(%ebx
), %eax
/ clear TSS busy bit
1076 addl WC_GDT+
2(%ebx
), %eax
1077 andl $_BITNOT
(0x200), 4(%eax
)
1078 ltr WC_TR
(%ebx
) / $UTSS_SEL
1080 movw WC_SS
(%ebx
), %ss
/ restore segment registers
1081 movw WC_ES
(%ebx
), %es
1082 movw WC_FS
(%ebx
), %fs
1083 movw WC_GS
(%ebx
), %gs
1086 * set the stack pointer to point into the identity mapped page
1087 * temporarily, so we can make function calls
1089 .globl rm_platter_va
1090 movl rm_platter_va
, %eax
1091 movl $WC_STKSTART
, %esp
1096 * if we are not running on the boot CPU restore stack contents by
1097 * calling i_cpr_restore_stack(curthread, save_stack);
1099 call i_cpr_bootcpuid
1100 cmpl %eax
, WC_CPU_ID
(%ebx
)
1103 pushl WC_SAVED_STACK
(%ebx
)
1104 pushl
%gs
:CPU_THREAD
1105 call i_cpr_restore_stack
1109 movl WC_ESP
(%ebx
), %esp
1112 movl WC_RETADDR
(%ebx
), %eax
/ return to caller of wc_save_context
1116 * APIC initialization, skip iff function pointer is NULL
1123 call
*cpr_start_cpu_func
1125 pushl WC_EFLAGS
(%ebx
) / restore flags
1128 movl WC_EDI
(%ebx
), %edi
/ restore general registers
1129 movl WC_ESI
(%ebx
), %esi
1130 movl WC_EBP
(%ebx
), %ebp
1131 movl WC_EBX
(%ebx
), %ebx
1133 /exit
: jmp exit
/ stop here for HDT
1135 xorl
%eax
, %eax
/ at wakeup return
0
1138 SET_SIZE
(wc_rm_start
)
1141 #endif /* defined(__amd64) */