xref: /titanic_41/usr/src/uts/i86pc/ml/cpr_wakecode.s (revision 95efa359b507db290a2484b8f615822b1e06096f)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <sys/asm_linkage.h>
27#include <sys/asm_misc.h>
28#include <sys/regset.h>
29#include <sys/privregs.h>
30#include <sys/x86_archext.h>
31#include <sys/cpr_wakecode.h>
32
33#if !defined(__lint)
34#include <sys/segments.h>
35#include "assym.h"
36#endif
37
38#ifdef  DEBUG
39#define LED     1
40#define SERIAL  1
41#endif	/*	DEBUG	*/
42
43#ifdef	DEBUG
44#define	COM1	0x3f8
45#define	COM2	0x2f8
46#define	WC_COM	COM2	/* either COM1 or COM2			*/
47#define	WC_LED	0x80    /* diagnostic led port ON motherboard	*/
48
49/*
50 * defined as offsets from the data register
51 */
52#define	DLL	0	/* divisor latch (lsb) */
53#define	DLH	1	/* divisor latch (msb) */
54#define	LCR	3	/* line control register		*/
55#define	MCR	4	/* modem control register		*/
56
57
58#define	DLAB	0x80    /* divisor latch access bit		*/
59#define	B9600L	0X0c	/* lsb bit pattern for 9600 baud	*/
60#define	B9600H	0X0	/* hsb bit pattern for 9600 baud	*/
61#define	DTR	0x01    /* Data Terminal Ready			*/
62#define	RTS	0x02    /* Request To Send			*/
63#define	STOP1	0x00	/* 1 stop bit				*/
64#define	BITS8	0x03    /* 8 bits per char			*/
65
66#endif	/*	DEBUG	*/
67
68/*
69 *	This file contains the low level routines involved in getting
70 *	into and out of ACPI S3, including those needed for restarting
71 *	the non-boot cpus.
72 *
73 *	Our assumptions:
74 *
75 *	Our actions:
76 *
77 */
78
79#if defined(lint) || defined(__lint)
80
81/*ARGSUSED*/
82int
83wc_save_context(wc_cpu_t *pcpu)
84{ return 0; }
85
86#else	/* lint */
87
88#if defined(__GNU_AS__)
89
90	NOTHING AT ALL YET!
91
92#else	/* !defined(__GNU_AS__) */
93
94#if defined(__amd64)
95
96	ENTRY_NP(wc_save_context)
97
98	movq	(%rsp), %rdx		/ return address
99	movq	%rdx, WC_RETADDR(%rdi)
100	pushq	%rbp
101	movq	%rsp,%rbp
102
103	movq    %rdi, WC_VIRTADDR(%rdi)
104	movq    %rdi, WC_RDI(%rdi)
105
106	movq    %rdx, WC_RDX(%rdi)
107
108/ stash everything else we need
109	sgdt	WC_GDT(%rdi)
110	sidt	WC_IDT(%rdi)
111	sldt	WC_LDT(%rdi)
112	str	WC_TR(%rdi)
113
114	movq	%cr0, %rdx
115	movq	%rdx, WC_CR0(%rdi)
116	movq	%cr3, %rdx
117	movq	%rdx, WC_CR3(%rdi)
118	movq	%cr4, %rdx
119	movq	%rdx, WC_CR4(%rdi)
120	movq	%cr8, %rdx
121	movq	%rdx, WC_CR8(%rdi)
122
123	movq    %r8, WC_R8(%rdi)
124	movq    %r9, WC_R9(%rdi)
125	movq    %r10, WC_R10(%rdi)
126	movq    %r11, WC_R11(%rdi)
127	movq    %r12, WC_R12(%rdi)
128	movq    %r13, WC_R13(%rdi)
129	movq    %r14, WC_R14(%rdi)
130	movq    %r15, WC_R15(%rdi)
131	movq    %rax, WC_RAX(%rdi)
132	movq    %rbp, WC_RBP(%rdi)
133	movq    %rbx, WC_RBX(%rdi)
134	movq    %rcx, WC_RCX(%rdi)
135	movq    %rsi, WC_RSI(%rdi)
136	movq    %rsp, WC_RSP(%rdi)
137
138	movw	%ss, WC_SS(%rdi)
139	movw	%cs, WC_CS(%rdi)
140	movw	%ds, WC_DS(%rdi)
141	movw	%es, WC_ES(%rdi)
142
143	movq	$0, %rcx		/ save %fs register
144	movw    %fs, %cx
145	movq    %rcx, WC_FS(%rdi)
146
147	movl    $MSR_AMD_FSBASE, %ecx
148	rdmsr
149	movl    %eax, WC_FSBASE(%rdi)
150	movl    %edx, WC_FSBASE+4(%rdi)
151
152	movq	$0, %rcx		/ save %gs register
153	movw    %gs, %cx
154	movq    %rcx, WC_GS(%rdi)
155
156	movl    $MSR_AMD_GSBASE, %ecx	/ save gsbase msr
157	rdmsr
158	movl    %eax, WC_GSBASE(%rdi)
159	movl    %edx, WC_GSBASE+4(%rdi)
160
161	movl    $MSR_AMD_KGSBASE, %ecx	/ save kgsbase msr
162	rdmsr
163	movl    %eax, WC_KGSBASE(%rdi)
164	movl    %edx, WC_KGSBASE+4(%rdi)
165
166	pushfq
167	popq	WC_EFLAGS(%rdi)
168
169	wbinvd				/ flush the cache
170
171	movq	$1, %rax		/ at suspend return 1
172
173	leave
174
175	ret
176
177	SET_SIZE(wc_save_context)
178
179#elif defined(__i386)
180
181	ENTRY_NP(wc_save_context)
182
183	movl	4(%esp), %eax		/ wc_cpu_t *
184	movl	%eax, WC_VIRTADDR(%eax)
185
186	movl	(%esp), %edx		/ return address
187	movl	%edx, WC_RETADDR(%eax)
188
189	str	WC_TR(%eax)		/ stash everything else we need
190	sgdt	WC_GDT(%eax)
191	sldt	WC_LDT(%eax)
192	sidt	WC_IDT(%eax)
193
194	movl	%cr0, %edx
195	movl	%edx, WC_CR0(%eax)
196	movl	%cr3, %edx
197	movl	%edx, WC_CR3(%eax)
198	movl	%cr4, %edx
199	movl	%edx, WC_CR4(%eax)
200
201	movl	%ebx, WC_EBX(%eax)
202	movl	%edi, WC_EDI(%eax)
203	movl	%esi, WC_ESI(%eax)
204	movl	%ebp, WC_EBP(%eax)
205	movl	%esp, WC_ESP(%eax)
206
207	movw	%ss, WC_SS(%eax)
208	movw	%cs, WC_CS(%eax)
209	movw	%ds, WC_DS(%eax)
210	movw	%es, WC_ES(%eax)
211	movw	%fs, WC_FS(%eax)
212	movw	%gs, WC_GS(%eax)
213
214	pushfl
215	popl	WC_EFLAGS(%eax)
216
217	wbinvd				/ flush the cache
218
219	movl	$1, %eax		/ at suspend return 1
220	ret
221
222	SET_SIZE(wc_save_context)
223
224#endif	/* __amd64 */
225
226#endif	/* __GNU_AS__ */
227
228#endif /* lint */
229
230
231/*
232 *	Our assumptions:
233 *		- We are running in real mode.
234 *		- Interrupts are disabled.
235 *
236 *	Our actions:
237 *		- We start using our GDT by loading correct values in the
238 *		  selector registers (cs=KCS_SEL, ds=es=ss=KDS_SEL, fs=KFS_SEL,
239 *		  gs=KGS_SEL).
240 *		- We change over to using our IDT.
241 *		- We load the default LDT into the hardware LDT register.
242 *		- We load the default TSS into the hardware task register.
243 *		- We restore registers
244 *		- We return to original caller (a la setjmp)
245 */
246
247#if defined(lint) || defined(__lint)
248
249void
250wc_rm_start(void)
251{}
252
253void
254wc_rm_end(void)
255{}
256
257#else	/* lint */
258
259#if defined(__GNU_AS__)
260
261	NOTHING AT ALL YET!
262
263#else	/* __GNU_AS__ */
264
265#if defined(__amd64)
266
267	ENTRY_NP(wc_rm_start)
268
269	/*
270	 * For vulcan as we need to do a .code32 and mentally invert the
271	 * meaning of the addr16 and data16 prefixes to get 32-bit access when
272	 * generating code to be executed in 16-bit mode (sigh...)
273	 */
274
275	.code32
276
277	cli
278	movw		%cs, %ax
279	movw		%ax, %ds		/ establish ds ...
280	movw		%ax, %ss		/ ... and ss:esp
281	D16 movl	$WC_STKSTART, %esp
282/ using the following value blows up machines! - DO NOT USE
283/	D16 movl	0xffc, %esp
284
285
286#if     LED
287	D16 movl        $WC_LED, %edx
288	D16 movb        $0xd1, %al
289	outb    (%dx)
290#endif
291
292#if     SERIAL
293	D16 movl        $WC_COM, %edx
294	D16 movb        $0x61, %al
295	outb    (%dx)
296#endif
297
298	D16 call	cominit
299
300	/*
301	 * Enable protected-mode, write protect, and alignment mask
302	 * %cr0 has already been initialsed to zero
303	 */
304	movl		%cr0, %eax
305	D16 orl		$[CR0_PE|CR0_WP|CR0_AM], %eax
306	movl		%eax, %cr0
307
308	/*
309	 * Do a jmp immediately after writing to cr0 when enabling protected
310	 * mode to clear the real mode prefetch queue (per Intel's docs)
311	 */
312	jmp		pestart
313pestart:
314
315#if     LED
316	D16 movl        $WC_LED, %edx
317	D16 movb        $0xd2, %al
318	outb    (%dx)
319#endif
320
321#if     SERIAL
322	D16 movl        $WC_COM, %edx
323	D16 movb        $0x62, %al
324	outb    (%dx)
325#endif
326
327	/*
328	 * 16-bit protected mode is now active, so prepare to turn on long
329	 * mode
330	 */
331
332#if     LED
333	D16 movl        $WC_LED, %edx
334	D16 movb        $0xd3, %al
335	outb    (%dx)
336#endif
337
338#if     SERIAL
339	D16 movl        $WC_COM, %edx
340	D16 movb        $0x63, %al
341	outb    (%dx)
342#endif
343
344	/*
345 	 * Add any initial cr4 bits
346	 */
347	movl		%cr4, %eax
348	A16 D16 orl	CR4OFF, %eax
349
350	/*
351	 * Enable PAE mode (CR4.PAE)
352	 */
353	D16 orl		$CR4_PAE, %eax
354	movl		%eax, %cr4
355
356#if     LED
357	D16 movl        $WC_LED, %edx
358	D16 movb        $0xd4, %al
359	outb    (%dx)
360#endif
361
362#if     SERIAL
363	D16 movl        $WC_COM, %edx
364	D16 movb        $0x64, %al
365	outb    (%dx)
366#endif
367
368	/*
369	 * Point cr3 to the 64-bit long mode page tables.
370	 *
371	 * Note that these MUST exist in 32-bit space, as we don't have
372	 * a way to load %cr3 with a 64-bit base address for the page tables
373	 * until the CPU is actually executing in 64-bit long mode.
374	 */
375	A16 D16 movl	CR3OFF, %eax
376	movl		%eax, %cr3
377
378	/*
379	 * Set long mode enable in EFER (EFER.LME = 1)
380	 */
381	D16 movl	$MSR_AMD_EFER, %ecx
382	rdmsr
383
384	D16 orl		$AMD_EFER_LME, %eax
385	wrmsr
386
387#if     LED
388	D16 movl        $WC_LED, %edx
389	D16 movb        $0xd5, %al
390	outb    (%dx)
391#endif
392
393#if     SERIAL
394	D16 movl        $WC_COM, %edx
395	D16 movb        $0x65, %al
396	outb    (%dx)
397#endif
398
399	/*
400	 * Finally, turn on paging (CR0.PG = 1) to activate long mode.
401	 */
402	movl		%cr0, %eax
403	D16 orl		$CR0_PG, %eax
404	movl		%eax, %cr0
405
406	/*
407	 * The instruction after enabling paging in CR0 MUST be a branch.
408	 */
409	jmp		long_mode_active
410
411long_mode_active:
412
413#if     LED
414	D16 movl        $WC_LED, %edx
415	D16 movb        $0xd6, %al
416	outb    (%dx)
417#endif
418
419#if     SERIAL
420	D16 movl        $WC_COM, %edx
421	D16 movb        $0x66, %al
422	outb    (%dx)
423#endif
424
425	/*
426	 * Long mode is now active but since we're still running with the
427	 * original 16-bit CS we're actually in 16-bit compatability mode.
428	 *
429	 * We have to load an intermediate GDT and IDT here that we know are
430	 * in 32-bit space before we can use the kernel's GDT and IDT, which
431	 * may be in the 64-bit address space, and since we're in compatability
432	 * mode, we only have access to 16 and 32-bit instructions at the
433	 * moment.
434	 */
435	A16 D16 lgdt	TEMPGDTOFF	/* load temporary GDT */
436	A16 D16 lidt	TEMPIDTOFF	/* load temporary IDT */
437
438
439	/*
440 	 * Do a far transfer to 64-bit mode.  Set the CS selector to a 64-bit
441	 * long mode selector (CS.L=1) in the temporary 32-bit GDT and jump
442	 * to the real mode platter address of wc_long_mode_64 as until the
443	 * 64-bit CS is in place we don't have access to 64-bit instructions
444	 * and thus can't reference a 64-bit %rip.
445	 */
446
447#if     LED
448	D16 movl        $WC_LED, %edx
449	D16 movb        $0xd7, %al
450	outb    (%dx)
451#endif
452
453#if     SERIAL
454	D16 movl        $WC_COM, %edx
455	D16 movb        $0x67, %al
456	outb    (%dx)
457#endif
458
459	D16 	pushl 	$TEMP_CS64_SEL
460	A16 D16 pushl	LM64OFF
461
462	D16 lret
463
464
465/*
466 * Support routine to re-initialize VGA subsystem
467 */
468vgainit:
469	D16 ret
470
471/*
472 * Support routine to re-initialize keyboard (which is USB - help!)
473 */
474kbdinit:
475	D16 ret
476
477/*
478 * Support routine to re-initialize COM ports to something sane
479 */
480cominit:
481	/ init COM1 & COM2
482
483#if     DEBUG
484/*
485 * on debug kernels we need to initialize COM1 & COM2 here, so that
486 * we can get debug output before the asy driver has resumed
487 */
488
489/ select COM1
490	D16 movl	$[COM1+LCR], %edx
491	D16 movb	$DLAB, %al		/ divisor latch
492	outb	(%dx)
493
494	D16 movl	$[COM1+DLL], %edx	/ divisor latch lsb
495	D16 movb	$B9600L, %al		/ divisor latch
496	outb	(%dx)
497
498	D16 movl	$[COM1+DLH], %edx	/ divisor latch hsb
499	D16 movb	$B9600H, %al		/ divisor latch
500	outb	(%dx)
501
502	D16 movl	$[COM1+LCR], %edx	/ select COM1
503	D16 movb	$[STOP1|BITS8], %al	/ 1 stop bit, 8bit word len
504	outb	(%dx)
505
506	D16 movl	$[COM1+MCR], %edx	/ select COM1
507	D16 movb	$[RTS|DTR], %al		/ data term ready & req to send
508	outb	(%dx)
509
510/ select COM2
511	D16 movl	$[COM2+LCR], %edx
512	D16 movb	$DLAB, %al		/ divisor latch
513	outb	(%dx)
514
515	D16 movl	$[COM2+DLL], %edx	/ divisor latch lsb
516	D16 movb	$B9600L, %al		/ divisor latch
517	outb	(%dx)
518
519	D16 movl	$[COM2+DLH], %edx	/ divisor latch hsb
520	D16 movb	$B9600H, %al		/ divisor latch
521	outb	(%dx)
522
523	D16 movl	$[COM2+LCR], %edx	/ select COM1
524	D16 movb	$[STOP1|BITS8], %al	/ 1 stop bit, 8bit word len
525	outb	(%dx)
526
527	D16 movl	$[COM2+MCR], %edx	/ select COM1
528	D16 movb	$[RTS|DTR], %al		/ data term ready & req to send
529	outb	(%dx)
530#endif	/*	DEBUG	*/
531
532	D16 ret
533
534	.code64
535
536	.globl wc_long_mode_64
537wc_long_mode_64:
538
539#if     LED
540	movw        $WC_LED, %dx
541	movb        $0xd8, %al
542	outb    (%dx)
543#endif
544
545#if     SERIAL
546	movw        $WC_COM, %dx
547	movb        $0x68, %al
548	outb    (%dx)
549#endif
550
551	/*
552	 * We are now running in long mode with a 64-bit CS (EFER.LMA=1,
553	 * CS.L=1) so we now have access to 64-bit instructions.
554	 *
555	 * First, set the 64-bit GDT base.
556	 */
557	.globl	rm_platter_pa
558	movl	rm_platter_pa, %eax
559
560	lgdtq	GDTROFF(%rax)		/* load 64-bit GDT */
561
562	/*
563	 * Save the CPU number in %r11; get the value here since it's saved in
564	 * the real mode platter.
565	 */
566/ JAN
567/ the following is wrong! need to figure out MP systems
568/	movl	CPUNOFF(%rax), %r11d
569
570	/*
571	 * Add rm_platter_pa to %rsp to point it to the same location as seen
572	 * from 64-bit mode.
573	 */
574	addq	%rax, %rsp
575
576	/*
577	 * Now do an lretq to load CS with the appropriate selector for the
578	 * kernel's 64-bit GDT and to start executing 64-bit setup code at the
579	 * virtual address where boot originally loaded this code rather than
580	 * the copy in the real mode platter's rm_code array as we've been
581	 * doing so far.
582	 */
583
584#if     LED
585	movw        $WC_LED, %dx
586	movb        $0xd9, %al
587	outb    (%dx)
588#endif
589
590/ JAN this should produce 'i' but we get 'g' instead ???
591#if     SERIAL
592	movw        $WC_COM, %dx
593	movb        $0x69, %al
594	outb    (%dx)
595#endif
596
597	pushq	$KCS_SEL
598	pushq	$kernel_wc_code
599	lretq
600
601	.globl kernel_wc_code
602kernel_wc_code:
603
604#if     LED
605	movw        $WC_LED, %dx
606	movb        $0xda, %al
607	outb    (%dx)
608#endif
609
610/ JAN this should produce 'j' but we get 'g' instead ???
611#if     SERIAL
612	movw        $WC_COM, %dx
613	movb        $0x6a, %al
614	outb    (%dx)
615#endif
616
617	/*
618	 * Complete the balance of the setup we need to before executing
619	 * 64-bit kernel code (namely init rsp, TSS, LGDT, FS and GS).
620	 */
621	.globl  rm_platter_va
622	movq    rm_platter_va, %rbx
623	addq	$WC_CPU, %rbx
624
625#if     LED
626	movw        $WC_LED, %dx
627	movb        $0xdb, %al
628	outb    (%dx)
629#endif
630
631#if     SERIAL
632	movw        $WC_COM, %dx
633	movw        $0x6b, %ax
634	outb    (%dx)
635#endif
636
637	/*
638	 * restore the rest of the registers
639	 */
640
641	lidtq	WC_IDT(%rbx)
642
643#if     LED
644	movw        $WC_LED, %dx
645	movb        $0xdc, %al
646	outb    (%dx)
647#endif
648
649#if     SERIAL
650	movw        $WC_COM, %dx
651	movw        $0x6c, %ax
652	outb    (%dx)
653#endif
654
655	/*
656	 * restore the rest of the registers
657	 */
658
659	movw    $KDS_SEL, %ax
660	movw    %ax, %ds
661	movw    %ax, %es
662	movw    %ax, %ss
663
664	/*
665	 * Before proceeding, enable usage of the page table NX bit if
666	 * that's how the page tables are set up.
667	 */
668	movl    x86_feature, %ecx
669	andl   	 $X86_NX, %ecx
670	jz      1f
671	movl    $MSR_AMD_EFER, %ecx
672	rdmsr
673	orl     $AMD_EFER_NXE, %eax
674	wrmsr
6751:
676
677	movq	WC_CR4(%rbx), %rax	/ restore full cr4 (with Global Enable)
678	movq	%rax, %cr4
679
680	lldt	WC_LDT(%rbx)
681	movzwq	WC_TR(%rbx), %rax	/ clear TSS busy bit
682	addq	WC_GDT+2(%rbx), %rax
683	andl	$0xfffffdff, 4(%rax)
684	movq	4(%rax), %rcx
685	ltr	WC_TR(%rbx)
686
687#if     LED
688	movw        $WC_LED, %dx
689	movb        $0xdd, %al
690	outb    (%dx)
691#endif
692
693#if     SERIAL
694	movw        $WC_COM, %dx
695	movw        $0x6d, %ax
696	outb    (%dx)
697#endif
698
699/ restore %fsbase %gsbase %kgbase registers using wrmsr instruction
700
701	movq    WC_FS(%rbx), %rcx	/ restore fs register
702	movw    %cx, %fs
703
704	movl    $MSR_AMD_FSBASE, %ecx
705	movl    WC_FSBASE(%rbx), %eax
706	movl    WC_FSBASE+4(%rbx), %edx
707	wrmsr
708
709	movq    WC_GS(%rbx), %rcx	/ restore gs register
710	movw    %cx, %gs
711
712	movl    $MSR_AMD_GSBASE, %ecx	/ restore gsbase msr
713	movl    WC_GSBASE(%rbx), %eax
714	movl    WC_GSBASE+4(%rbx), %edx
715	wrmsr
716
717	movl    $MSR_AMD_KGSBASE, %ecx	/ restore kgsbase msr
718	movl    WC_KGSBASE(%rbx), %eax
719	movl    WC_KGSBASE+4(%rbx), %edx
720	wrmsr
721
722	movq	WC_CR0(%rbx), %rdx
723	movq	%rdx, %cr0
724	movq	WC_CR3(%rbx), %rdx
725	movq	%rdx, %cr3
726	movq	WC_CR8(%rbx), %rdx
727	movq	%rdx, %cr8
728
729#if     LED
730	movw        $WC_LED, %dx
731	movb        $0xde, %al
732	outb    (%dx)
733#endif
734
735#if     SERIAL
736	movw        $WC_COM, %dx
737	movb        $0x6e, %al
738	outb    (%dx)
739#endif
740
741/ dummy up a stck so we can make C function calls
742	movq    WC_RSP(%rbx), %rsp
743
744	/*
745	 * APIC initialization
746	 */
747	movq    %rsp, %rbp      /* stack aligned on 16-byte boundary */
748
749	/*
750	 * skip iff function pointer is NULL
751	 */
752	cmpq	$0, ap_mlsetup
753	je	2f
754	call	*ap_mlsetup
7552:
756
757	call    *cpr_start_cpu_func
758
759/ restore %rbx to the value it ahd before we called the functions above
760	movq    rm_platter_va, %rbx
761	addq	$WC_CPU, %rbx
762
763	movq    WC_R8(%rbx), %r8
764	movq    WC_R9(%rbx), %r9
765	movq    WC_R10(%rbx), %r10
766	movq    WC_R11(%rbx), %r11
767	movq    WC_R12(%rbx), %r12
768	movq    WC_R13(%rbx), %r13
769	movq    WC_R14(%rbx), %r14
770	movq    WC_R15(%rbx), %r15
771/	movq    WC_RAX(%rbx), %rax
772	movq    WC_RBP(%rbx), %rbp
773	movq    WC_RCX(%rbx), %rcx
774/	movq    WC_RDX(%rbx), %rdx
775	movq    WC_RDI(%rbx), %rdi
776	movq    WC_RSI(%rbx), %rsi
777
778
779/ assume that %cs does not need to be restored
780/ %ds, %es & %ss are ignored in 64bit mode
781	movw	WC_SS(%rbx), %ss
782	movw	WC_DS(%rbx), %ds
783	movw	WC_ES(%rbx), %es
784
785#if     LED
786	movw        $WC_LED, %dx
787	movb        $0xdf, %al
788	outb    (%dx)
789#endif
790
791#if     SERIAL
792	movw        $WC_COM, %dx
793	movb        $0x6f, %al
794	outb    (%dx)
795#endif
796
797
798	movq    WC_RBP(%rbx), %rbp
799	movq    WC_RSP(%rbx), %rsp
800
801#if     LED
802	movw        $WC_LED, %dx
803	movb        $0xe0, %al
804	outb    (%dx)
805#endif
806
807#if     SERIAL
808	movw        $WC_COM, %dx
809	movb        $0x70, %al
810	outb    (%dx)
811#endif
812
813
814	movq    WC_RCX(%rbx), %rcx
815
816	pushq	WC_EFLAGS(%rbx)			/ restore flags
817	popfq
818
819#if     LED
820	movw        $WC_LED, %dx
821	movb        $0xe1, %al
822	outb    (%dx)
823#endif
824
825#if     SERIAL
826	movw        $WC_COM, %dx
827	movb        $0x71, %al
828	outb    (%dx)
829#endif
830
831/*
832 * can not use outb after this point, because doing so would mean using
833 * %dx which would modify %rdx which is restored here
834 */
835
836	movq	%rbx, %rax
837	movq    WC_RDX(%rax), %rdx
838	movq    WC_RBX(%rax), %rbx
839
840	leave
841
842	movq	WC_RETADDR(%rax), %rax
843	movq	%rax, (%rsp)		/ return to caller of wc_save_context
844
845	xorl	%eax, %eax			/ at wakeup return 0
846	ret
847
848
849	SET_SIZE(wc_rm_start)
850
851	ENTRY_NP(asmspin)
852
853	movl	%edi, %ecx
854A1:
855	loop	A1
856
857	SET_SIZE(asmspin)
858
859	.globl wc_rm_end
860wc_rm_end:
861	nop
862
863#elif defined(__i386)
864
865	ENTRY_NP(wc_rm_start)
866
867/entry:	jmp		entry			/ stop here for HDT
868
869	cli
870	movw		%cs, %ax
871	movw		%ax, %ds		/ establish ds ...
872	movw		%ax, %ss		/ ... and ss:esp
873	D16 movl	$WC_STKSTART, %esp
874
875#if     LED
876	D16 movl        $WC_LED, %edx
877	D16 movb        $0xd1, %al
878	outb    (%dx)
879#endif
880
881#if     SERIAL
882	D16 movl        $WC_COM, %edx
883	D16 movb        $0x61, %al
884	outb    (%dx)
885#endif
886
887
888	D16 call	vgainit
889	D16 call	kbdinit
890	D16 call	cominit
891
892#if     LED
893	D16 movl        $WC_LED, %edx
894	D16 movb        $0xd2, %al
895	outb    (%dx)
896#endif
897
898#if     SERIAL
899	D16 movl        $WC_COM, %edx
900	D16 movb        $0x62, %al
901	outb    (%dx)
902#endif
903
904	D16 A16 movl	$WC_CPU, %ebx		/ base add of wc_cpu_t
905
906#if     LED
907	D16 movb        $0xd3, %al
908	outb    $WC_LED
909#endif
910
911#if     SERIAL
912	D16 movl        $WC_COM, %edx
913	D16 movb        $0x63, %al
914	outb    (%dx)
915#endif
916
917	D16 A16 movl	%cs:WC_DS(%ebx), %edx	/ %ds post prot/paging transit
918
919#if     LED
920	D16 movb        $0xd4, %al
921	outb    $WC_LED
922#endif
923
924	D16 A16 lgdt	%cs:WC_GDT(%ebx)	/ restore gdt and idtr
925	D16 A16 lidt	%cs:WC_IDT(%ebx)
926
927#if     LED
928	D16 movb        $0xd5, %al
929	outb    $WC_LED
930#endif
931
932	D16 A16 movl	%cs:WC_CR4(%ebx), %eax	/ restore cr4
933	D16 andl	$-1!CR4_PGE, %eax	/ don't set Global Enable yet
934	movl		%eax, %cr4
935
936#if     LED
937	D16 movb        $0xd6, %al
938	outb    $WC_LED
939#endif
940
941	D16 A16 movl	%cs:WC_CR3(%ebx), %eax	/ set PDPT
942	movl		%eax, %cr3
943
944#if     LED
945	D16 movb        $0xd7, %al
946	outb    $WC_LED
947#endif
948
949	D16 A16 movl	%cs:WC_CR0(%ebx), %eax	/ enable prot/paging, etc.
950	movl		%eax, %cr0
951
952#if     LED
953	D16 movb        $0xd8, %al
954	outb    $WC_LED
955#endif
956
957	D16 A16 movl	%cs:WC_VIRTADDR(%ebx), %ebx	/ virtaddr of wc_cpu_t
958
959#if     LED
960	D16 movb        $0xd9, %al
961	outb    $WC_LED
962#endif
963
964#if     LED
965	D16 movb        $0xda, %al
966	outb    $WC_LED
967#endif
968
969	jmp		flush			/ flush prefetch queue
970flush:
971	D16 pushl	$KCS_SEL
972	D16 pushl	$kernel_wc_code
973	D16 lret				/ re-appear at kernel_wc_code
974
975
976/*
977 * Support routine to re-initialize VGA subsystem
978 */
979vgainit:
980	D16 ret
981
982/*
983 * Support routine to re-initialize keyboard (which is USB - help!)
984 */
985kbdinit:
986	D16 ret
987
988/*
989 * Support routine to re-initialize COM ports to something sane for debug output
990 */
991cominit:
992#if     DEBUG
993/*
994 * on debug kernels we need to initialize COM1 & COM2 here, so that
995 * we can get debug output before the asy driver has resumed
996 */
997
998/ select COM1
999	D16 movl	$[COM1+LCR], %edx
1000	D16 movb	$DLAB, %al		/ divisor latch
1001	outb	(%dx)
1002
1003	D16 movl	$[COM1+DLL], %edx	/ divisor latch lsb
1004	D16 movb	$B9600L, %al		/ divisor latch
1005	outb	(%dx)
1006
1007	D16 movl	$[COM1+DLH], %edx	/ divisor latch hsb
1008	D16 movb	$B9600H, %al		/ divisor latch
1009	outb	(%dx)
1010
1011	D16 movl	$[COM1+LCR], %edx	/ select COM1
1012	D16 movb	$[STOP1|BITS8], %al	/ 1 stop bit, 8bit word len
1013	outb	(%dx)
1014
1015	D16 movl	$[COM1+MCR], %edx	/ select COM1
1016	D16 movb	$[RTS|DTR], %al		/ 1 stop bit, 8bit word len
1017	outb	(%dx)
1018
1019/ select COM2
1020	D16 movl	$[COM2+LCR], %edx
1021	D16 movb	$DLAB, %al		/ divisor latch
1022	outb	(%dx)
1023
1024	D16 movl	$[COM2+DLL], %edx	/ divisor latch lsb
1025	D16 movb	$B9600L, %al		/ divisor latch
1026	outb	(%dx)
1027
1028	D16 movl	$[COM2+DLH], %edx	/ divisor latch hsb
1029	D16 movb	$B9600H, %al		/ divisor latch
1030	outb	(%dx)
1031
1032	D16 movl	$[COM2+LCR], %edx	/ select COM1
1033	D16 movb	$[STOP1|BITS8], %al	/ 1 stop bit, 8bit word len
1034	outb	(%dx)
1035
1036	D16 movl	$[COM2+MCR], %edx	/ select COM1
1037	D16 movb	$[RTS|DTR], %al		/ 1 stop bit, 8bit word len
1038	outb	(%dx)
1039#endif	/*	DEBUG	*/
1040
1041	D16 ret
1042
1043	.globl wc_rm_end
1044wc_rm_end:
1045	nop
1046
1047	.globl	kernel_wc_code
1048kernel_wc_code:
1049	/ At this point we are with kernel's cs and proper eip.
1050	/ We will be executing not from the copy in real mode platter,
1051	/ but from the original code where boot loaded us.
1052	/ By this time GDT and IDT are loaded as is cr0, cr3 and cr4.
1053	/ %ebx is wc_cpu
1054	/ %dx is our ds
1055
1056#if     LED
1057	D16 movb        $0xdb, %al
1058	outb	$WC_LED
1059#endif
1060
1061/ got here OK
1062
1063	movw	%dx, %ds		/ $KDS_SEL
1064
1065#if     LED
1066	movb	$0xdc, %al
1067	outb	$WC_LED
1068#endif
1069
1070	/*
1071	 * Before proceeding, enable usage of the page table NX bit if
1072	 * that's how the page tables are set up.
1073	 */
1074	movl    x86_feature, %ecx
1075	andl   	 $X86_NX, %ecx
1076	jz      1f
1077	movl    $MSR_AMD_EFER, %ecx
1078	rdmsr
1079	orl     $AMD_EFER_NXE, %eax
1080	wrmsr
10811:
1082
1083	movl	WC_CR4(%ebx), %eax	/ restore full cr4 (with Global Enable)
1084	movl	%eax, %cr4
1085
1086
1087	lldt	WC_LDT(%ebx)		/ $LDT_SEL
1088
1089	movzwl	WC_TR(%ebx), %eax	/ clear TSS busy bit
1090	addl	WC_GDT+2(%ebx), %eax
1091	andl	$-1!0x200, 4(%eax)
1092	ltr	WC_TR(%ebx)		/ $UTSS_SEL
1093
1094	movw	WC_SS(%ebx), %ss	/ lssl	WC_ESP(%ebx), %esp
1095	movl	WC_ESP(%ebx), %esp	/ ^ don't use, asm busted!
1096
1097	movl	WC_RETADDR(%ebx), %eax	/ return to caller of wc_save_context
1098	movl	%eax, (%esp)
1099
1100	movw	WC_ES(%ebx), %es	/ restore segment registers
1101	movw	WC_FS(%ebx), %fs
1102	movw	WC_GS(%ebx), %gs
1103
1104	/*
1105	 * APIC initialization, skip iff function pointer is NULL
1106	 */
1107	cmpl	$0, ap_mlsetup
1108	je	2f
1109	call	*ap_mlsetup
11102:
1111
1112	call    *cpr_start_cpu_func
1113
1114	pushl	WC_EFLAGS(%ebx)		/ restore flags
1115	popfl
1116
1117	movl	WC_EDI(%ebx), %edi	/ restore general registers
1118	movl	WC_ESI(%ebx), %esi
1119	movl	WC_EBP(%ebx), %ebp
1120	movl	WC_EBX(%ebx), %ebx
1121
1122/exit:	jmp	exit			/ stop here for HDT
1123
1124	xorl	%eax, %eax		/ at wakeup return 0
1125	ret
1126
1127	SET_SIZE(wc_rm_start)
1128
1129
1130#endif	/* defined(__amd64) */
1131
1132#endif	/* !defined(__GNU_AS__) */
1133
1134#endif /* lint */
1135
1136