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