xref: /illumos-gate/usr/src/grub/grub-0.97/stage2/asm.S (revision 57c40785)
1/*
2 *  GRUB  --  GRand Unified Bootloader
3 *  Copyright (C) 1999,2000,2001,2002,2004 Free Software Foundation, Inc.
4 *
5 *  This program is free software; you can redistribute it and/or modify
6 *  it under the terms of the GNU General Public License as published by
7 *  the Free Software Foundation; either version 2 of the License, or
8 *  (at your option) any later version.
9 *
10 *  This program is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 *  GNU General Public License for more details.
14 *
15 *  You should have received a copy of the GNU General Public License
16 *  along with this program; if not, write to the Free Software
17 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20
21/*
22 * Note: These functions defined in this file may be called from C.
23 *       Be careful of that you must not modify some registers. Quote
24 *       from gcc-2.95.2/gcc/config/i386/i386.h:
25
26   1 for registers not available across function calls.
27   These must include the FIXED_REGISTERS and also any
28   registers that can be used without being saved.
29   The latter must include the registers where values are returned
30   and the register where structure-value addresses are passed.
31   Aside from that, you can include as many other registers as you like.
32
33  ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg
34{  1, 1, 1, 0, 0, 0, 0, 1, 1,  1,  1,  1,  1,  1,  1,  1,  1 }
35 */
36
37#define ASM_FILE
38
39#include "shared.h"
40
41#ifdef STAGE1_5
42# define	ABS(x)	((x) - EXT_C(main) + 0x2200)
43#else
44# define	ABS(x)	((x) - EXT_C(main) + 0x8200)
45#endif
46
47	.file	"asm.S"
48
49	.text
50
51	/* Tell GAS to generate 16-bit instructions so that this code works
52	   in real mode. */
53	.code16
54
55#ifndef STAGE1_5
56	/*
57	 * In stage2, do not link start.S with the rest of the source
58	 * files directly, so define the start symbols here just to
59	 * force ld quiet. These are not referred anyway.
60	 */
61	.globl	start, _start
62start:
63_start:
64#endif /* ! STAGE1_5 */
65
66ENTRY(main)
67	/*
68	 *  Guarantee that "main" is loaded at 0x0:0x8200 in stage2 and
69	 *  at 0x0:0x2200 in stage1.5.
70	 */
71	ljmp $0, $ABS(codestart)
72
73	/*
74	 *  Compatibility version number
75	 *
76	 *  These MUST be at byte offset 6 and 7 of the executable
77	 *  DO NOT MOVE !!!
78	 */
79	. = EXT_C(main) + 0x6
80	.byte	COMPAT_VERSION_MAJOR, COMPAT_VERSION_MINOR
81
82	/*
83	 *  This is a special data area 8 bytes from the beginning.
84	 */
85
86	. = EXT_C(main) + 0x8
87
88VARIABLE(install_partition)
89	.long	0xFFFFFF
90/* This variable is here only because of a historical reason.  */
91VARIABLE(saved_entryno)
92	.long	0
93VARIABLE(stage2_id)
94	.byte	STAGE2_ID
95VARIABLE(force_lba)
96	.byte	0
97VARIABLE(version_string)
98	.string VERSION
99VARIABLE(config_file)
100#ifndef STAGE1_5
101	.string "/boot/grub/menu.lst"
102#else   /* STAGE1_5 */
103	.long	0xffffffff
104	.string "/boot/grub/stage2"
105#endif  /* STAGE1_5 */
106
107	/*
108	 *  Leave some breathing room for the config file name.
109	 */
110
111	. = EXT_C(main) + 0x70
112
113/* the real mode code continues... */
114codestart:
115	cli		/* we're not safe here! */
116
117	/* set up %ds, %ss, and %es */
118	xorw	%ax, %ax
119	movw	%ax, %ds
120	movw	%ax, %ss
121	movw	%ax, %es
122
123#ifndef SUPPORT_DISKLESS
124	/*
125	 * Save the sector number of the second sector (i.e. this sector)
126	 * in INSTALL_SECOND_SECTOR. See also "stage2/start.S".
127	 */
128	ADDR32	movl	%ebp, EXT_C(install_second_sector)
129#endif
130
131	/* set up the real mode/BIOS stack */
132	movl	$STACKOFF, %ebp
133	movl	%ebp, %esp
134
135	sti		/* we're safe again */
136
137#ifndef SUPPORT_DISKLESS
138	/* save boot drive reference */
139	ADDR32	movb	%dl, EXT_C(boot_drive)
140
141	/* reset disk system (%ah = 0) */
142	int	$0x13
143#endif
144
145	/* transition to protected mode */
146	DATA32	call EXT_C(real_to_prot)
147
148	/* The ".code32" directive takes GAS out of 16-bit mode. */
149	.code32
150
151	/* clean out the bss */
152
153	/* set %edi to the bss starting address */
154#if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL)
155	movl	$__bss_start, %edi
156#elif defined(HAVE_USCORE_EDATA_SYMBOL)
157	movl	$_edata, %edi
158#elif defined(HAVE_EDATA_SYMBOL)
159	movl	$edata, %edi
160#endif
161
162	/* set %ecx to the bss end */
163#if defined(HAVE_END_SYMBOL)
164	movl	$end, %ecx
165#elif defined(HAVE_USCORE_END_SYMBOL)
166	movl	$_end, %ecx
167#endif
168
169	/* compute the bss length */
170	subl	%edi, %ecx
171
172	/* zero %al */
173	xorb	%al, %al
174
175	/* set the direction */
176	cld
177
178	/* clean out */
179	rep
180	stosb
181
182	/*
183	 *  Call the start of main body of C code, which does some
184	 *  of it's own initialization before transferring to "cmain".
185	 */
186	call EXT_C(init_bios_info)
187
188
189/*
190 *  This call is special...  it never returns...  in fact it should simply
191 *  hang at this point!
192 */
193
194ENTRY(stop)
195	call	EXT_C(prot_to_real)
196
197	/*
198	 * This next part is sort of evil.  It takes advantage of the
199	 * byte ordering on the x86 to work in either 16-bit or 32-bit
200	 * mode, so think about it before changing it.
201	 */
202
203ENTRY(hard_stop)
204	hlt
205	jmp EXT_C(hard_stop)
206
207#ifndef STAGE1_5
208
209/**************************************************************************
210UNDI_CALL - wrapper around real-mode UNDI API calls
211**************************************************************************/
212ENTRY(__undi_call)
213       pushl   %ebp
214       movl    %esp,%ebp
215       pushl   %esi
216       pushl   %edi
217       pushl   %ebx
218
219       movw    8(%ebp),%cx     /* Seg:off addr of undi_call_info_t struct */
220       movw    12(%ebp),%dx    /* Pass to 16-bit code in %cx:%dx */
221
222       call EXT_C(prot_to_real)
223       .code16
224
225       movw    %cx,%es         /* Seg:off addr of undi_call_info_t struct */
226       movw    %dx,%bx         /* into %es:%bx */
227
228       movw    %es:8(%bx),%ax  /* Transfer contents of undi_call_info_t */
229       pushw   %ax             /* structure to the real-mode stack */
230       movw    %es:6(%bx),%ax
231       pushw   %ax
232       movw    %es:4(%bx),%ax
233       pushw   %ax
234
235       lcall   *%es:0(%bx)     /* Do the UNDI call */
236       cld                     /* Don't know whether or not we need this */
237                               /* but pxelinux includes it for some reason, */
238                               /* so we put it in just in case. */
239
240       popw    %cx             /* Tidy up the stack */
241       popw    %cx
242       popw    %cx
243       movw    %ax,%cx         /* Return %ax via %cx */
244
245       DATA32 call EXT_C(real_to_prot)
246       .code32
247
248       xorl    %eax,%eax       /* %ax is returned via %cx */
249       movw    %cx,%ax
250
251       popl    %ebx
252       popl    %edi
253       popl	%esi
254       popl	%ebp
255       ret
256
257/**************************************************************************
258UNDI_IRQ_HANDLER - UNDI IRQ handler: calls PXENV_UNDI_ISR and send EOI
259NOTE: For some reason, this handler needs to be aligned. Else, the
260	undi driver won't get the trigger count on some platforms.
261**************************************************************************/
262	.align 4
263ENTRY(_undi_irq_handler)
264	.code16
265	pushw	%ax
266	pushw	%bx
267	pushw	%cx
268	call	1f		/* Position-independent access to */
2691:	popw	%bx		/* various locations.		  */
270	pushw	%bx		/* save for after UNDI call */
271
272	/* set funcflag to PXENV_UNDI_ISR_IN_START */
273	movw	$1,%cs:(pxenv_undi_isr-1b+2)(%bx)
274
275	/* push pxenv_undi_isr struct on stack */
276	movl	$(ABS(pxenv_undi_isr)),%eax
277	movw	%ax,%cx
278	shrl	$4,%eax		/* get segment */
279	pushw	%ax
280	andw	$0xf,%cx	/* get offset */
281	pushw	%cx
282	movw    $0x14,%ax	/* opcode PXENV_UNDI_ISR */
283	pushw   %ax
284
285	lcall   *%cs:(pxenv_entrypointsp-1b)(%bx)	/* Do the UNDI call */
286	cld                     /* Don't know whether or not we need this */
287				/* but pxelinux includes it for some reason, */
288				/* so we put it in just in case. */
289	popw    %cx             /* Tidy up the stack */
290	popw    %cx
291	popw    %cx
292
293	popw	%bx		/* restore old position reg */
294
295	cmpw	$0,%ax		/* did the UNDI call succeed? */
296	jne	3f
297	movw	%cs:(pxenv_undi_isr-1b+2)(%bx),%ax
298	cmpw	$0,%ax		/* is this our interrupt? */
299	jne	3f
300
301	/* send EOI -- non specific for now */
302	movw	$0x20,%ax		/* ICR_EOI_NON_SPECIFIC */
303	movb	%cs:(pxenv_undi_irq-1b),%cl
304	cmpb	$8,%cl
305	jg	2f
306	outb	$0xa0			/* PIC2_ICR */
3072:	outb	$0x20			/* PIC1_ICR */
308
309	/* increment trigger count */
310	incw	%cs:(EXT_C(_undi_irq_trigger_count)-1b)(%bx)
311
312	/* restore other registers */
3133:	popw	%cx
314	popw	%bx
315	popw	%ax
316	iret
317ENTRY(_undi_irq_trigger_count)
318undi_irq_trigger_count:
319	.word	0
320ENTRY(_undi_irq_chain_to)
321	.long	0
322ENTRY(_undi_irq_chain)
323	.byte	0
324ENTRY(_pxenv_undi_irq)
325pxenv_undi_irq:
326	.byte	0
327ENTRY(_pxenv_undi_entrypointsp)
328pxenv_entrypointsp:
329	.word	0	/* offset */
330	.word	0	/* segment */
331pxenv_undi_isr:
332	.word	0	/* status */
333	.word	0	/* funcflag */
334	.long	0	/* struct padding not used by ISR */
335	.long	0
336	.long	0
337
338	.code32
339
340/*
341 * stop_floppy()
342 *
343 * Stops the floppy drive from spinning, so that other software is
344 * jumped to with a known state.
345 */
346ENTRY(stop_floppy)
347	pusha
348	call	EXT_C(prot_to_real)
349	.code16
350	xorb	%dl, %dl
351	int	$0x13
352	DATA32  call EXT_C(real_to_prot)
353	.code32
354	popa
355	ret
356
357/*
358 * grub_reboot()
359 *
360 * Reboot the system. At the moment, rely on BIOS.
361 */
362ENTRY(grub_reboot)
363	call	EXT_C(prot_to_real)
364	.code16
365	/* cold boot */
366	movw	$0x0472, %di
367	movw	%ax, (%di)
368	ljmp	$0xFFFF, $0x0000
369	.code32
370
371/*
372 * grub_halt(int no_apm)
373 *
374 * Halt the system, using APM if possible. If NO_APM is true, don't use
375 * APM even if it is available.
376 */
377ENTRY(grub_halt)
378	/* get the argument */
379	movl	4(%esp), %eax
380
381	/* see if zero */
382	testl	%eax, %eax
383	jnz	EXT_C(stop)
384
385	call	EXT_C(prot_to_real)
386	.code16
387
388	/* detect APM */
389	movw	$0x5300, %ax
390	xorw	%bx, %bx
391	int	$0x15
392	jc	EXT_C(hard_stop)
393	/* don't check %bx for buggy BIOSes... */
394
395	/* disconnect APM first */
396	movw	$0x5304, %ax
397	xorw	%bx, %bx
398	int	$0x15
399
400	/* connect APM */
401	movw	$0x5301, %ax
402	xorw	%bx, %bx
403	int	$0x15
404	jc	EXT_C(hard_stop)
405
406	/* set APM protocol level - 1.1 or bust. (this covers APM 1.2 also) */
407	movw	$0x530E, %ax
408	xorw	%bx, %bx
409	movw	$0x0101, %cx
410	int	$0x15
411	jc	EXT_C(hard_stop)
412
413	/* set the power state to off */
414	movw	$0x5307, %ax
415	movw	$1, %bx
416	movw	$3, %cx
417	int	$0x15
418
419	/* shouldn't reach here */
420	jmp	EXT_C(hard_stop)
421	.code32
422
423/*
424 * track_int13(int drive)
425 *
426 * Track the int13 handler to probe I/O address space.
427 */
428ENTRY(track_int13)
429	pushl	%ebp
430	movl	%esp, %ebp
431
432	pushl	%ebx
433	pushl	%edi
434
435	/* copy the original int13 handler segment:offset */
436	movl	$0x4c, %edi
437	movl	(%edi), %eax
438	movl	%eax, track_int13_addr
439
440	/* replace the int1 handler */
441	movl	$0x4, %edi
442	pushl	(%edi)
443	movl	$ABS(int1_handler), %eax
444	movl	%eax, (%edi)
445
446	/* read the MBR to call int13 successfully */
447	movb	8(%ebp), %dl
448
449	call	EXT_C(prot_to_real)
450	.code16
451
452	movw	$SCRATCHSEG, %ax
453	movw	%ax, %es
454	xorw	%bx, %bx
455	movw	$1, %cx
456	xorb	%dh, %dh
457
458	/* save FLAGS on the stack to emulate int13 */
459	pushfw
460
461	/* set the TF flag */
462	/* FIXME: this can be simplified not to use AX */
463	pushfw
464	popw	%ax
465	orw	$0x100, %ax
466	pushw	%ax
467	popfw
468
469	movw	$0x0201, %ax
470
471	.byte	0x9a		/* lcall */
472track_int13_addr:
473	.word	0		/* offset */
474	.word	0		/* segment */
475
476	/* TF is cleared here automatically */
477
478	DATA32	call	EXT_C(real_to_prot)
479	.code32
480
481	/* restore the int1 handler */
482	movl	$0x4, %edi
483	popl	(%edi)
484
485	popl	%edi
486	popl	%ebx
487	popl	%ebp
488
489	ret
490
491
492/*
493 * Check if the next instruction is I/O, and if this is true, add the
494 * port into the io map.
495 *
496 * Note: Probably this will make the execution of int13 very slow.
497 *
498 * Note2: In this implementation, all we can know is I/O-mapped I/O. It
499 * is impossible to detect memory-mapped I/O.
500 */
501int1_handler:
502	.code16
503
504	pushw	%bp
505	movw	%sp, %bp
506	pushw	%ds
507	pushw	%ax
508	pushw	%si
509	pushw	%dx
510
511	/* IP */
512	movw	2(%bp), %si
513	/* CS */
514	movw	4(%bp), %ax
515	movw	%ax, %ds
516
517	/* examine the next instruction */
5181:	lodsb	(%si), %al
519	/* skip this code if it is a prefix */
520	cmpb	$0x2E, %al
521	je	1b
522	cmpb	$0x36, %al
523	je	1b
524	cmpb	$0x3E, %al
525	je	1b
526	cmpb	$0x26, %al
527	je	1b
528	cmpb	$0x64, %al
529	jl	2f
530	cmpb	$0x67, %al
531	jle	1b
5322:	cmpb	$0xF0, %al
533	jl	3f
534	cmpb	$0xF3, %al
535	jle	1b
536
5373:	/* check if this code is out* or in* */
538
539	/* ins? or outs? */
540	cmpb	$0x6C, %al
541	jl	4f
542	cmpb	$0x6F, %al
543	jle	5f
544
5454:	/* in? or out? (register operand version) */
546	cmpb	$0xEC, %al
547	jl	6f
548	cmpb	$0xEF, %al
549	jle	5f
550
5516:	/* in? or out? (immediate operand version) */
552	cmpb	$0xE4, %al
553	jl	8f
554	cmpb	$0xE7, %al
555	jg	8f
556
5577:	/* immediate has a port */
558	lodsb	(%si), %al
559	movzbw	%al, %dx
560
5615:	/* %dx has a port */
562
563	/* set %ds to zero */
564	xorw	%ax, %ax
565	movw	%ax, %ds
566
567	/* set %si to the io map */
568	movw	$ABS(EXT_C(io_map)), %si
569
570
5719:	/* check if the io map already has the port */
572	lodsw	(%si), %ax
573	/* check if this is the end */
574	testw	%ax, %ax
575	jz	1f
576	/* check if this matches the port */
577	cmpw	%ax, %dx
578	jne	9b
579	/* if so, leave from this handler */
580	jmp	8f
581
5821:	/* check for the buffer overrun */
583	cmpw	$(ABS(EXT_C(io_map)) + (IO_MAP_SIZE + 1) * 2), %si
584	je	8f
585	/* add the port into the io map */
586	movw	%dx, -2(%si)
587
5888:	/* restore registers */
589	popw	%dx
590	popw	%si
591	popw	%ax
592	popw	%ds
593	popw	%bp
594
595	iret
596
597	.code32
598
599ENTRY(io_map)
600	.space	(IO_MAP_SIZE + 1) * 2
601
602
603/*
604 * set_int15_handler(void)
605 *
606 * Set up int15_handler.
607 */
608ENTRY(set_int15_handler)
609	pushl	%edi
610
611	/* save the original int15 handler */
612	movl	$0x54, %edi
613	movw	(%edi), %ax
614	movw	%ax, ABS(int15_offset)
615	movw	2(%edi), %ax
616	movw	%ax, ABS(int15_segment)
617
618	/* save the new int15 handler */
619	movw	$ABS(int15_handler), %ax
620	movw	%ax, (%edi)
621	xorw	%ax, %ax
622	movw	%ax, 2(%edi)
623
624	popl	%edi
625	ret
626
627
628/*
629 * unset_int15_handler(void)
630 *
631 * Restore the original int15 handler
632 */
633ENTRY(unset_int15_handler)
634	pushl	%edi
635
636	/* check if int15_handler is set */
637	movl	$0x54, %edi
638	movw	$ABS(int15_handler), %ax
639	cmpw	%ax, (%edi)
640	jne	1f
641	xorw	%ax, %ax
642	cmpw	%ax, 2(%edi)
643	jne	1f
644
645	/* restore the original */
646	movw	ABS(int15_offset), %ax
647	movw	%ax, (%edi)
648	movw	ABS(int15_segment), %ax
649	movw	%ax, 2(%edi)
650
6511:
652	popl	%edi
653	ret
654
655
656/*
657 * Translate a key code to another.
658 *
659 * Note: This implementation cannot handle more than one length
660 * scancodes (such as Right Ctrl).
661 */
662	.code16
663int15_handler:
664	/* if non-carrier, ignore it */
665	jnc	1f
666	/* check if AH=4F */
667	cmpb	$0x4F, %ah
668	jne	1f
669
670	/* E0 and E1 are special */
671	cmpb	$0xE1, %al
672	je	4f
673	cmpb	$0xE0, %al
674	/* this flag is actually the machine code (je or jmp) */
675int15_skip_flag:
676	je	4f
677
678	pushw	%bp
679	movw	%sp, %bp
680
681	pushw	%bx
682	pushw	%dx
683	pushw	%ds
684	pushw	%si
685
686	/* save bits 0-6 of %al in %dl */
687	movw	%ax, %dx
688	andb	$0x7f, %dl
689	/* save the highest bit in %bl */
690	movb	%al, %bl
691	xorb	%dl, %bl
692	/* set %ds to 0 */
693	xorw	%ax, %ax
694	movw	%ax, %ds
695	/* set %si to the key map */
696	movw	$ABS(EXT_C(bios_key_map)), %si
697
698	/* find the key code from the key map */
6992:
700	lodsw
701	/* check if this is the end */
702	testw	%ax, %ax
703	jz	3f
704	/* check if this matches the key code */
705	cmpb	%al, %dl
706	jne	2b
707	/* if so, perform the mapping */
708	movb	%ah, %dl
7093:
710	/* restore %ax */
711	movw	%dx, %ax
712	orb	%bl, %al
713	/* make sure that CF is set */
714	orw	$1, 6(%bp)
715	/* restore other registers */
716	popw	%si
717	popw	%ds
718	popw	%dx
719	popw	%bx
720	popw	%bp
721	iret
722
7234:
724	/* tricky: jmp (0x74) <-> je (0xeb) */
725	xorb	$(0x74 ^ 0xeb), ABS(int15_skip_flag)
7261:
727	/* just cascade to the original */
728	/* ljmp */
729	.byte	0xea
730int15_offset:	.word	0
731int15_segment:	.word	0
732
733	.code32
734
735	.align	4
736ENTRY(bios_key_map)
737	.space	(KEY_MAP_SIZE + 1) * 2
738
739
740/*
741 * set_int13_handler(map)
742 *
743 * Copy MAP to the drive map and set up int13_handler.
744 */
745ENTRY(set_int13_handler)
746	pushl	%ebp
747	movl	%esp, %ebp
748
749	pushl	%edi
750	pushl	%esi
751
752	/* copy MAP to the drive map */
753	movl	$(DRIVE_MAP_SIZE * 2), %ecx
754	movl	$ABS(drive_map), %edi
755	movl	8(%ebp), %esi
756	cld
757	rep
758	movsb
759
760	/* save the original int13 handler */
761	movl	$0x4c, %edi
762	movw	(%edi), %ax
763	movw	%ax, ABS(int13_offset)
764	movw	2(%edi), %ax
765	movw	%ax, ABS(int13_segment)
766
767	/* decrease the lower memory size and set it to the BIOS memory */
768	movl	$0x413, %edi
769	decw	(%edi)
770	xorl	%eax, %eax
771	movw	(%edi), %ax
772
773	/* compute the segment */
774	shll	$6, %eax
775
776	/* save the new int13 handler */
777	movl	$0x4c, %edi
778	movw	%ax, 2(%edi)
779	xorw	%cx, %cx
780	movw	%cx, (%edi)
781
782	/* copy int13_handler to the reserved area */
783	shll	$4, %eax
784	movl	%eax, %edi
785	movl	$ABS(int13_handler), %esi
786	movl	$(int13_handler_end - int13_handler), %ecx
787	rep
788	movsb
789
790	popl	%esi
791	popl	%edi
792	popl	%ebp
793	ret
794
795
796/*
797 * Map a drive to another drive.
798 */
799
800	.code16
801
802int13_handler:
803	pushw	%ax
804	pushw	%bp
805	movw	%sp, %bp
806
807	pushw	%si
808
809	/* set %si to the drive map */
810	movw	$(drive_map - int13_handler), %si
811	/* find the drive number from the drive map */
812	cld
8131:
814	lodsw	%cs:(%si), %ax
815	/* check if this is the end */
816	testw	%ax, %ax
817	jz	2f
818	/* check if this matches the drive number */
819	cmpb	%al, %dl
820	jne	1b
821	/* if so, perform the mapping */
822	movb	%ah, %dl
8232:
824	/* restore %si */
825	popw	%si
826	/* save %ax in the stack */
827	pushw	%ax
828	/* simulate the interrupt call */
829	pushw	8(%bp)
830	/* set %ax and %bp to the original values */
831	movw	2(%bp), %ax
832	movw	(%bp), %bp
833	/* lcall */
834	.byte	0x9a
835int13_offset:	.word	0
836int13_segment:	.word	0
837	/* save flags */
838	pushf
839	/* restore %bp */
840	movw	%sp, %bp
841	/* save %ax */
842	pushw	%ax
843	/* set the flags in the stack to the value returned by int13 */
844	movw	(%bp), %ax
845	movw	%ax, 0xc(%bp)
846	/* check if should map the drive number */
847	movw	6(%bp), %ax
848	cmpw	$0x8, %ax
849	jne	3f
850	cmpw	$0x15, %ax
851	jne	3f
852	/* check if the mapping was performed */
853	movw	2(%bp), %ax
854	testw	%ax, %ax
855	jz	3f
856	/* perform the mapping */
857	movb	%al, %dl
8583:
859	popw	%ax
860	movw	4(%bp), %bp
861	addw	$8, %sp
862	iret
863
864	.align	4
865drive_map:	.space	(DRIVE_MAP_SIZE + 1) * 2
866int13_handler_end:
867
868	.code32
869
870
871/*
872 * chain_stage1(segment, offset, part_table_addr)
873 *
874 *  This starts another stage1 loader, at segment:offset.
875 */
876
877ENTRY(chain_stage1)
878	/* no need to save anything, just use %esp */
879
880	/* store %ESI, presuming %ES is 0 */
881	movl	0xc(%esp), %esi
882
883	/* store new offset */
884	movl	0x8(%esp), %eax
885	movl	%eax, offset
886
887	/* store new segment */
888	movw	0x4(%esp), %ax
889	movw	%ax, segment
890
891	/* set up to pass boot drive */
892	movb	EXT_C(boot_drive), %dl
893
894	call	EXT_C(prot_to_real)
895	.code16
896
897#ifdef ABSOLUTE_WITHOUT_ASTERISK
898	DATA32	ADDR32	ljmp	(offset)
899#else
900	DATA32	ADDR32	ljmp	*(offset)
901#endif
902	.code32
903#endif /* STAGE1_5 */
904
905
906#ifdef STAGE1_5
907/*
908 * chain_stage2(segment, offset, second_sector)
909 *
910 *  This starts another stage2 loader, at segment:offset.  It presumes
911 *  that the other one starts with this same "asm.S" file, and passes
912 *  parameters by writing the embedded install variables.
913 */
914
915ENTRY(chain_stage2)
916	/* no need to save anything, just use %esp */
917
918	/* store new offset */
919	movl	0x8(%esp), %eax
920	movl	%eax, offset
921	movl	%eax, %ebx
922
923	/* store new segment */
924	movw	0x4(%esp), %ax
925	movw	%ax, segment
926	shll	$4, %eax
927
928	/* generate linear address */
929	addl	%eax, %ebx
930
931	/* set up to pass the partition where stage2 is located in */
932	movl	EXT_C(current_partition), %eax
933	movl	%eax, (EXT_C(install_partition)-EXT_C(main))(%ebx)
934
935	/* set up to pass the drive where stage2 is located in */
936	movb	EXT_C(current_drive), %dl
937
938	/* set up to pass the second sector of stage2 */
939	movl	0xc(%esp), %ecx
940
941	call	EXT_C(prot_to_real)
942	.code16
943
944	movl	%ecx, %ebp
945
946#ifdef ABSOLUTE_WITHOUT_ASTERISK
947	DATA32	ADDR32	ljmp	(offset)
948#else
949	DATA32	ADDR32	ljmp	*(offset)
950#endif
951
952	.code32
953#endif /* STAGE1_5 */
954
955/*
956 *  These next two routines, "real_to_prot" and "prot_to_real" are structured
957 *  in a very specific way.  Be very careful when changing them.
958 *
959 *  NOTE:  Use of either one messes up %eax and %ebp.
960 */
961
962ENTRY(real_to_prot)
963	.code16
964	cli
965
966	/* load the GDT register */
967	DATA32	ADDR32	lgdt	gdtdesc
968
969	/* turn on protected mode */
970	movl	%cr0, %eax
971	orl	$CR0_PE_ON, %eax
972	movl	%eax, %cr0
973
974	/* jump to relocation, flush prefetch queue, and reload %cs */
975	DATA32	ljmp	$PROT_MODE_CSEG, $protcseg
976
977	/*
978	 *  The ".code32" directive only works in GAS, the GNU assembler!
979	 *  This gets out of "16-bit" mode.
980	 */
981	.code32
982
983protcseg:
984	/* reload other segment registers */
985	movw	$PROT_MODE_DSEG, %ax
986	movw	%ax, %ds
987	movw	%ax, %es
988	movw	%ax, %fs
989	movw	%ax, %gs
990	movw	%ax, %ss
991
992	/* put the return address in a known safe location */
993	movl	(%esp), %eax
994	movl	%eax, STACKOFF
995
996	/* get protected mode stack */
997	movl	protstack, %eax
998	movl	%eax, %esp
999	movl	%eax, %ebp
1000
1001	/* get return address onto the right stack */
1002	movl	STACKOFF, %eax
1003	movl	%eax, (%esp)
1004
1005	/* zero %eax */
1006	xorl	%eax, %eax
1007
1008	/* return on the old (or initialized) stack! */
1009	ret
1010
1011
1012ENTRY(prot_to_real)
1013	/* just in case, set GDT */
1014	lgdt	gdtdesc
1015
1016	/* save the protected mode stack */
1017	movl	%esp, %eax
1018	movl	%eax, protstack
1019
1020	/* get the return address */
1021	movl	(%esp), %eax
1022	movl	%eax, STACKOFF
1023
1024	/* set up new stack */
1025	movl	$STACKOFF, %eax
1026	movl	%eax, %esp
1027	movl	%eax, %ebp
1028
1029	/* set up segment limits */
1030	movw	$PSEUDO_RM_DSEG, %ax
1031	movw	%ax, %ds
1032	movw	%ax, %es
1033	movw	%ax, %fs
1034	movw	%ax, %gs
1035	movw	%ax, %ss
1036
1037	/* this might be an extra step */
1038	ljmp	$PSEUDO_RM_CSEG, $tmpcseg	/* jump to a 16 bit segment */
1039
1040tmpcseg:
1041	.code16
1042
1043	/* clear the PE bit of CR0 */
1044	movl	%cr0, %eax
1045	andl 	$CR0_PE_OFF, %eax
1046	movl	%eax, %cr0
1047
1048	/* flush prefetch queue, reload %cs */
1049	DATA32	ljmp	$0, $realcseg
1050
1051realcseg:
1052	/* we are in real mode now
1053	 * set up the real mode segment registers : DS, SS, ES
1054	 */
1055	/* zero %eax */
1056	xorl	%eax, %eax
1057
1058	movw	%ax, %ds
1059	movw	%ax, %es
1060	movw	%ax, %fs
1061	movw	%ax, %gs
1062	movw	%ax, %ss
1063
1064	/* restore interrupts */
1065	sti
1066
1067	/* return on new stack! */
1068	DATA32	ret
1069
1070	.code32
1071
1072
1073/*
1074 *   int biosdisk_int13_extensions (int ax, int drive, void *dap)
1075 *
1076 *   Call IBM/MS INT13 Extensions (int 13 %ax=AX) for DRIVE. DAP
1077 *   is passed for disk address packet. If an error occurs, return
1078 *   non-zero, otherwise zero.
1079 */
1080
1081ENTRY(biosdisk_int13_extensions)
1082	pushl	%ebp
1083	movl	%esp, %ebp
1084
1085	pushl	%esi
1086	pushl	%ebx
1087
1088	/* compute the address of disk_address_packet */
1089	movl	0x10(%ebp), %eax
1090	movw	%ax, %si
1091	xorw	%ax, %ax
1092	shrl	$4, %eax
1093	movw	%ax, %cx	/* save the segment to cx */
1094
1095	/* drive */
1096	movb	0xc(%ebp), %dl
1097	/* ax */
1098	movw	0x8(%ebp), %bx
1099	/* enter real mode */
1100	call	EXT_C(prot_to_real)
1101
1102	.code16
1103	movw	%bx, %ax
1104	movw	%cx, %ds
1105	int	$0x13		/* do the operation */
1106	movb	%ah, %dl	/* save return value */
1107	/* clear the data segment */
1108	xorw	%ax, %ax
1109	movw	%ax, %ds
1110	/* back to protected mode */
1111	DATA32	call	EXT_C(real_to_prot)
1112	.code32
1113
1114	movb	%dl, %al	/* return value in %eax */
1115
1116	popl	%ebx
1117	popl	%esi
1118	popl	%ebp
1119
1120	ret
1121
1122/*
1123 *   int biosdisk_standard (int ah, int drive, int coff, int hoff, int soff,
1124 *                          int nsec, int segment)
1125 *
1126 *   Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
1127 *   NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
1128 *   return non-zero, otherwise zero.
1129 */
1130
1131ENTRY(biosdisk_standard)
1132	pushl	%ebp
1133	movl	%esp, %ebp
1134
1135	pushl	%ebx
1136	pushl	%edi
1137	pushl	%esi
1138
1139	/* set up CHS information */
1140	movl	0x10(%ebp), %eax
1141	movb	%al, %ch
1142	movb	0x18(%ebp), %al
1143	shlb	$2, %al
1144	shrw	$2, %ax
1145	movb	%al, %cl
1146	movb	0x14(%ebp), %dh
1147	/* drive */
1148	movb	0xc(%ebp), %dl
1149	/* segment */
1150	movw	0x20(%ebp), %bx
1151	/* save nsec and ah to %di */
1152	movb	0x8(%ebp), %ah
1153	movb	0x1c(%ebp), %al
1154	movw	%ax, %di
1155	/* enter real mode */
1156	call	EXT_C(prot_to_real)
1157
1158	.code16
1159	movw	%bx, %es
1160	xorw	%bx, %bx
1161	movw	$3, %si		/* attempt at least three times */
1162
11631:
1164	movw	%di, %ax
1165	int	$0x13		/* do the operation */
1166	jnc	2f		/* check if successful */
1167
1168	movb	%ah, %bl	/* save return value */
1169	/* if fail, reset the disk system */
1170	xorw	%ax, %ax
1171	int	$0x13
1172
1173	decw	%si
1174	cmpw	$0, %si
1175	je	2f
1176	xorb	%bl, %bl
1177	jmp	1b		/* retry */
11782:
1179	/* back to protected mode */
1180	DATA32	call	EXT_C(real_to_prot)
1181	.code32
1182
1183	movb	%bl, %al	/* return value in %eax */
1184
1185	popl	%esi
1186	popl	%edi
1187	popl	%ebx
1188	popl	%ebp
1189
1190	ret
1191
1192
1193/*
1194 *   int check_int13_extensions (int drive)
1195 *
1196 *   Check if LBA is supported for DRIVE. If it is supported, then return
1197 *   the major version of extensions, otherwise zero.
1198 */
1199
1200ENTRY(check_int13_extensions)
1201	pushl	%ebp
1202	movl	%esp, %ebp
1203
1204	pushl	%ebx
1205
1206	/* drive */
1207	movb	0x8(%ebp), %dl
1208	/* enter real mode */
1209	call	EXT_C(prot_to_real)
1210
1211	.code16
1212	movb	$0x41, %ah
1213	movw	$0x55aa, %bx
1214	int	$0x13		/* do the operation */
1215
1216	/* check the result */
1217	jc	1f
1218	cmpw	$0xaa55, %bx
1219	jne	1f
1220
1221	movb	%ah, %bl	/* save the major version into %bl */
1222
1223	/* check if AH=0x42 is supported if FORCE_LBA is zero */
1224	movb	EXT_C(force_lba), %al
1225	testb	%al, %al
1226	jnz	2f
1227	andw	$1, %cx
1228	jnz	2f
1229
12301:
1231	xorb	%bl, %bl
12322:
1233	/* back to protected mode */
1234	DATA32	call	EXT_C(real_to_prot)
1235	.code32
1236
1237	movb	%bl, %al	/* return value in %eax */
1238
1239	popl	%ebx
1240	popl	%ebp
1241
1242	ret
1243
1244
1245/*
1246 *   int get_diskinfo_standard (int drive, unsigned long *cylinders,
1247 *                              unsigned long *heads, unsigned long *sectors)
1248 *
1249 *   Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
1250 *   error occurs, then return non-zero, otherwise zero.
1251 */
1252
1253ENTRY(get_diskinfo_standard)
1254	pushl	%ebp
1255	movl	%esp, %ebp
1256
1257	pushl	%ebx
1258	pushl	%edi
1259
1260	/* drive */
1261	movb	0x8(%ebp), %dl
1262	/* enter real mode */
1263	call	EXT_C(prot_to_real)
1264
1265	.code16
1266	movb	$0x8, %ah
1267	int	$0x13		/* do the operation */
1268	/* check if successful */
1269	testb	%ah, %ah
1270	jnz	1f
1271	/* bogus BIOSes may not return an error number */
1272	testb	$0x3f, %cl	/* 0 sectors means no disk */
1273	jnz	1f		/* if non-zero, then succeed */
1274	/* XXX 0x60 is one of the unused error numbers */
1275	movb	$0x60, %ah
12761:
1277	movb	%ah, %bl	/* save return value in %bl */
1278	/* back to protected mode */
1279	DATA32	call	EXT_C(real_to_prot)
1280	.code32
1281
1282	/* restore %ebp */
1283	leal	0x8(%esp), %ebp
1284
1285	/* heads */
1286	movb	%dh, %al
1287	incl	%eax		/* the number of heads is counted from zero */
1288	movl	0x10(%ebp), %edi
1289	movl	%eax, (%edi)
1290
1291	/* sectors */
1292	xorl	%eax, %eax
1293	movb	%cl, %al
1294	andb	$0x3f, %al
1295	movl	0x14(%ebp), %edi
1296	movl	%eax, (%edi)
1297
1298	/* cylinders */
1299	shrb	$6, %cl
1300	movb	%cl, %ah
1301	movb	%ch, %al
1302	incl	%eax		/* the number of cylinders is
1303				   counted from zero */
1304	movl	0xc(%ebp), %edi
1305	movl	%eax, (%edi)
1306
1307	xorl	%eax, %eax
1308	movb	%bl, %al	/* return value in %eax */
1309
1310	popl	%edi
1311	popl	%ebx
1312	popl	%ebp
1313
1314	ret
1315
1316
1317#if 0
1318/*
1319 *   int get_diskinfo_floppy (int drive, unsigned long *cylinders,
1320 *                            unsigned long *heads, unsigned long *sectors)
1321 *
1322 *   Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
1323 *   error occurs, then return non-zero, otherwise zero.
1324 */
1325
1326ENTRY(get_diskinfo_floppy)
1327	pushl	%ebp
1328	movl	%esp, %ebp
1329
1330	pushl	%ebx
1331	pushl	%esi
1332
1333	/* drive */
1334	movb	0x8(%ebp), %dl
1335	/* enter real mode */
1336	call	EXT_C(prot_to_real)
1337
1338	.code16
1339	/* init probe value */
1340	movl	$probe_values-1, %esi
13411:
1342	xorw	%ax, %ax
1343	int	$0x13		/* reset floppy controller */
1344
1345	incw	%si
1346	movb	(%si), %cl
1347	cmpb	$0, %cl		/* probe failed if zero */
1348	je	2f
1349
1350	/* perform read */
1351	movw	$SCRATCHSEG, %ax
1352	movw	%ax, %es
1353	xorw	%bx, %bx
1354	movw	$0x0201, %ax
1355	movb	$0, %ch
1356	movb	$0, %dh
1357	int	$0x13
1358
1359	/* FIXME: Read from floppy may fail even if the geometry is correct.
1360	   So should retry at least three times.  */
1361	jc	1b		/* next value */
1362
1363	/* succeed */
1364	jmp	2f
1365
1366probe_values:
1367	.byte	36, 18, 15, 9, 0
1368
13692:
1370	/* back to protected mode */
1371	DATA32	call	EXT_C(real_to_prot)
1372	.code32
1373
1374	/* restore %ebp */
1375	leal	0x8(%esp), %ebp
1376
1377	/* cylinders */
1378	movl	0xc(%ebp), %eax
1379	movl	$80, %ebx
1380	movl	%ebx, (%eax)
1381	/* heads */
1382	movl	0x10(%ebp), %eax
1383	movl	$2, %ebx
1384	movl	%ebx, (%eax)
1385	/* sectors */
1386	movl	0x14(%ebp), %eax
1387	movzbl	%cl, %ebx
1388	movl	%ebx, (%eax)
1389
1390	/* return value in %eax */
1391	xorl	%eax, %eax
1392	cmpb	$0, %cl
1393	jne	3f
1394	incl	%eax		/* %eax = 1 (non-zero) */
13953:
1396	popl	%esi
1397	popl	%ebx
1398	popl	%ebp
1399
1400	ret
1401#endif
1402
1403
1404/* Source files are splitted, as they have different copyrights.  */
1405#ifndef STAGE1_5
1406# include "setjmp.S"
1407# include "apm.S"
1408#endif /* ! STAGE1_5 */
1409
1410
1411
1412#ifndef STAGE1_5
1413/* get_code_end() :  return the address of the end of the code
1414 * This is here so that it can be replaced by asmstub.c.
1415 */
1416ENTRY(get_code_end)
1417	/* will be the end of the bss */
1418# if defined(HAVE_END_SYMBOL)
1419	movl	$end, %eax
1420# elif defined(HAVE_USCORE_END_SYMBOL)
1421	movl	$_end, %eax
1422# endif
1423	shrl	$2, %eax		/* Round up to the next word. */
1424	incl	%eax
1425	shll	$2, %eax
1426	ret
1427#endif /* ! STAGE1_5 */
1428
1429/*
1430 *
1431 * get_memsize(i) :  return the memory size in KB. i == 0 for conventional
1432 *		memory, i == 1 for extended memory
1433 *	BIOS call "INT 12H" to get conventional memory size
1434 *	BIOS call "INT 15H, AH=88H" to get extended memory size
1435 *		Both have the return value in AX.
1436 *
1437 */
1438
1439ENTRY(get_memsize)
1440	push	%ebp
1441	push	%ebx
1442
1443	mov	0xc(%esp), %ebx
1444
1445	call	EXT_C(prot_to_real)	/* enter real mode */
1446	.code16
1447
1448	cmpb	$0x1, %bl
1449	DATA32	je	xext
1450
1451	int	$0x12
1452	DATA32	jmp	xdone
1453
1454xext:
1455	movb	$0x88, %ah
1456	int	$0x15
1457
1458xdone:
1459	movw	%ax, %bx
1460
1461	DATA32	call	EXT_C(real_to_prot)
1462	.code32
1463
1464	movw	%bx, %ax
1465	pop	%ebx
1466	pop	%ebp
1467	ret
1468
1469
1470#ifndef STAGE1_5
1471
1472/*
1473 *
1474 * get_eisamemsize() :  return packed EISA memory map, lower 16 bits is
1475 *		memory between 1M and 16M in 1K parts, upper 16 bits is
1476 *		memory above 16M in 64K parts.  If error, return -1.
1477 *	BIOS call "INT 15H, AH=E801H" to get EISA memory map,
1478 *		AX = memory between 1M and 16M in 1K parts.
1479 *		BX = memory above 16M in 64K parts.
1480 *
1481 */
1482
1483ENTRY(get_eisamemsize)
1484	push	%ebp
1485	push	%ebx
1486
1487	call	EXT_C(prot_to_real)	/* enter real mode */
1488	.code16
1489
1490	movw	$0xe801, %ax
1491	int	$0x15
1492
1493	shll	$16, %ebx
1494	movw	%ax, %bx
1495
1496	DATA32	call	EXT_C(real_to_prot)
1497	.code32
1498
1499	movl	$0xFFFFFFFF, %eax
1500	cmpb	$0x86, %bh
1501	je	xnoteisa
1502
1503	movl	%ebx, %eax
1504
1505xnoteisa:
1506	pop	%ebx
1507	pop	%ebp
1508	ret
1509
1510/*
1511 *
1512 * get_mmap_entry(addr, cont) :  address and old continuation value (zero to
1513 *		start), for the Query System Address Map BIOS call.
1514 *
1515 *  Sets the first 4-byte int value of "addr" to the size returned by
1516 *  the call.  If the call fails, sets it to zero.
1517 *
1518 *	Returns:  new (non-zero) continuation value, 0 if done.
1519 *
1520 * NOTE: Currently hard-coded for a maximum buffer length of 1024.
1521 */
1522
1523ENTRY(get_mmap_entry)
1524	push	%ebp
1525	push	%ebx
1526	push	%edi
1527	push	%esi
1528
1529	/* place address (+4) in ES:DI */
1530	movl	0x14(%esp), %eax
1531	addl	$4, %eax
1532	movl	%eax, %edi
1533	andl	$0xf, %edi
1534	shrl	$4, %eax
1535	movl	%eax, %esi
1536
1537	/* set continuation value */
1538	movl	0x18(%esp), %ebx
1539
1540	/* set default maximum buffer size */
1541	movl	$0x14, %ecx
1542
1543	/* set EDX to 'SMAP' */
1544	movl	$0x534d4150, %edx
1545
1546	call	EXT_C(prot_to_real)	/* enter real mode */
1547	.code16
1548
1549	movw	%si, %es
1550	movl	$0xe820, %eax
1551	int	$0x15
1552
1553	DATA32	jc	xnosmap
1554
1555	cmpl	$0x534d4150, %eax
1556	DATA32	jne	xnosmap
1557
1558	cmpl	$0x14, %ecx
1559	DATA32	jl	xnosmap
1560
1561	cmpl	$0x400, %ecx
1562	DATA32	jg	xnosmap
1563
1564	DATA32	jmp	xsmap
1565
1566xnosmap:
1567	movl	$0, %ecx
1568
1569xsmap:
1570	DATA32	call	EXT_C(real_to_prot)
1571	.code32
1572
1573	/* write length of buffer (zero if error) into "addr" */
1574	movl	0x14(%esp), %eax
1575	movl	%ecx, (%eax)
1576
1577	/* set return value to continuation */
1578	movl	%ebx, %eax
1579
1580	pop	%esi
1581	pop	%edi
1582	pop	%ebx
1583	pop	%ebp
1584	ret
1585
1586/*
1587 * get_rom_config_table()
1588 *
1589 * Get the linear address of a ROM configuration table. Return zero,
1590 * if fails.
1591 */
1592
1593ENTRY(get_rom_config_table)
1594	pushl	%ebp
1595	pushl	%ebx
1596
1597	/* zero %ebx for simplicity */
1598	xorl	%ebx, %ebx
1599
1600	call	EXT_C(prot_to_real)
1601	.code16
1602
1603	movw	$0xc0, %ax
1604	int	$0x15
1605
1606	jc	no_rom_table
1607	testb	%ah, %ah
1608	jnz	no_rom_table
1609
1610	movw	%es, %dx
1611	jmp	found_rom_table
1612
1613no_rom_table:
1614	xorw	%dx, %dx
1615	xorw	%bx, %bx
1616
1617found_rom_table:
1618	DATA32	call	EXT_C(real_to_prot)
1619	.code32
1620
1621	/* compute the linear address */
1622	movw	%dx, %ax
1623	shll	$4, %eax
1624	addl	%ebx, %eax
1625
1626	popl	%ebx
1627	popl	%ebp
1628	ret
1629
1630
1631/*
1632 * int get_vbe_controller_info (struct vbe_controller *controller_ptr)
1633 *
1634 * Get VBE controller information.
1635 */
1636
1637ENTRY(get_vbe_controller_info)
1638	pushl	%ebp
1639	movl	%esp, %ebp
1640
1641	pushl	%edi
1642	pushl	%ebx
1643
1644	/* Convert the linear address to segment:offset */
1645	movl	8(%ebp), %eax
1646	movl	%eax, %edi
1647	andl	$0x0000000f, %edi
1648	shrl	$4, %eax
1649	movl	%eax, %ebx
1650
1651	call	EXT_C(prot_to_real)
1652	.code16
1653
1654	movw	%bx, %es
1655	movw	$0x4F00, %ax
1656	int	$0x10
1657
1658	movw	%ax, %bx
1659	DATA32	call	EXT_C(real_to_prot)
1660	.code32
1661
1662	movzwl	%bx, %eax
1663
1664	popl	%ebx
1665	popl	%edi
1666	popl	%ebp
1667	ret
1668
1669
1670/*
1671 * int get_vbe_mode_info (int mode_number, struct vbe_mode *mode_ptr)
1672 *
1673 * Get VBE mode information.
1674 */
1675
1676ENTRY(get_vbe_mode_info)
1677	pushl	%ebp
1678	movl	%esp, %ebp
1679
1680	pushl	%edi
1681	pushl	%ebx
1682
1683	/* Convert the linear address to segment:offset */
1684	movl	0xc(%ebp), %eax
1685	movl	%eax, %edi
1686	andl	$0x0000000f, %edi
1687	shrl	$4, %eax
1688	movl	%eax, %ebx
1689
1690	/* Save the mode number in %cx */
1691	movl	0x8(%ebp), %ecx
1692
1693	call	EXT_C(prot_to_real)
1694	.code16
1695
1696	movw	%bx, %es
1697	movw	$0x4F01, %ax
1698	int	$0x10
1699
1700	movw	%ax, %bx
1701	DATA32	call	EXT_C(real_to_prot)
1702	.code32
1703
1704	movzwl	%bx, %eax
1705
1706	popl	%ebx
1707	popl	%edi
1708	popl	%ebp
1709	ret
1710
1711
1712/*
1713 * int set_vbe_mode (int mode_number)
1714 *
1715 * Set VBE mode. Don't support user-specified CRTC information.
1716 */
1717
1718ENTRY(set_vbe_mode)
1719	pushl	%ebp
1720	movl	%esp, %ebp
1721
1722	pushl	%ebx
1723
1724	/* Save the mode number in %bx */
1725	movl	0x8(%ebp), %ebx
1726	/* Clear bit D11 */
1727	andl	$0xF7FF, %ebx
1728
1729	call	EXT_C(prot_to_real)
1730	.code16
1731
1732	movw	$0x4F02, %ax
1733	int	$0x10
1734
1735	movw	%ax, %bx
1736	DATA32	call	EXT_C(real_to_prot)
1737	.code32
1738
1739	movzwl	%bx, %eax
1740
1741	popl	%ebx
1742	popl	%ebp
1743	ret
1744
1745
1746/*
1747 * gateA20(int linear)
1748 *
1749 * Gate address-line 20 for high memory.
1750 *
1751 * This routine is probably overconservative in what it does, but so what?
1752 *
1753 * It also eats any keystrokes in the keyboard buffer.  :-(
1754 */
1755
1756ENTRY(gateA20)
1757	/* first, try a BIOS call */
1758	pushl	%ebp
1759	movl	8(%esp), %edx
1760
1761	call	EXT_C(prot_to_real)
1762
1763	.code16
1764	movw	$0x2400, %ax
1765	testw	%dx, %dx
1766	jz	1f
1767	incw	%ax
17681:	stc
1769	int	$0x15
1770	jnc	2f
1771
1772	/* set non-zero if failed */
1773	movb	$1, %ah
1774
1775	/* save the status */
17762:	movb	%ah, %dl
1777
1778	DATA32	call	EXT_C(real_to_prot)
1779	.code32
1780
1781	popl	%ebp
1782	testb	%dl, %dl
1783	jnz	3f
1784	ret
1785
17863:	/*
1787	 * try to switch gateA20 using PORT92, the "Fast A20 and Init"
1788	 * register
1789	 */
1790	mov	$0x92, %dx
1791	inb	%dx, %al
1792	/* skip the port92 code if it's unimplemented (read returns 0xff) */
1793	cmpb	$0xff, %al
1794	jz	6f
1795
1796	/* set or clear bit1, the ALT_A20_GATE bit */
1797	movb	4(%esp), %ah
1798	testb	%ah, %ah
1799	jz	4f
1800	orb	$2, %al
1801	jmp	5f
18024:	and	$0xfd, %al
1803
1804	/* clear the INIT_NOW bit; don't accidently reset the machine */
18055:	and	$0xfe, %al
1806	outb	%al, %dx
1807
18086:	/* use keyboard controller */
1809	pushl	%eax
1810
1811	call    gloop1
1812
1813	movb	$KC_CMD_WOUT, %al
1814	outb	$K_CMD
1815
1816gloopint1:
1817	inb	$K_STATUS
1818	cmpb    $0xff, %al
1819	jz      gloopint1_done
1820	andb	$K_IBUF_FUL, %al
1821	jnz	gloopint1
1822
1823gloopint1_done:
1824	movb	$KB_OUTPUT_MASK, %al
1825	cmpb	$0, 0x8(%esp)
1826	jz	gdoit
1827
1828	orb	$KB_A20_ENABLE, %al
1829gdoit:
1830	outb	$K_RDWR
1831
1832	call	gloop1
1833
1834	/* output a dummy command (USB keyboard hack) */
1835	movb	$0xff, %al
1836	outb	$K_CMD
1837	call	gloop1
1838
1839	popl	%eax
1840	ret
1841
1842gloop1:
1843	inb	$K_STATUS
1844	cmpb	$0xff, %al
1845	jz	gloop2ret
1846	andb	$K_IBUF_FUL, %al
1847	jnz	gloop1
1848
1849gloop2:
1850	inb	$K_STATUS
1851	andb	$K_OBUF_FUL, %al
1852	jz	gloop2ret
1853	inb	$K_RDWR
1854	jmp	gloop2
1855
1856gloop2ret:
1857	ret
1858
1859
1860ENTRY(patch_code)	/* labels start with "pc_" */
1861	.code16
1862
1863	mov	%cs, %ax
1864	mov	%ax, %ds
1865	mov	%ax, %es
1866	mov	%ax, %fs
1867	mov	%ax, %gs
1868	ADDR32	movl	$0, 0
1869pc_stop:
1870	hlt
1871	DATA32	jmp	pc_stop
1872ENTRY(patch_code_end)
1873
1874	.code32
1875
1876
1877/*
1878 * linux_boot()
1879 *
1880 * Does some funky things (including on the stack!), then jumps to the
1881 * entry point of the Linux setup code.
1882 */
1883
1884VARIABLE(linux_text_len)
1885	.long	0
1886
1887VARIABLE(linux_data_tmp_addr)
1888	.long	0
1889
1890VARIABLE(linux_data_real_addr)
1891	.long	0
1892
1893ENTRY(linux_boot)
1894	/* don't worry about saving anything, we're committed at this point */
1895	cld	/* forward copying */
1896
1897	/* copy kernel */
1898	movl	EXT_C(linux_text_len), %ecx
1899	addl	$3, %ecx
1900	shrl	$2, %ecx
1901	movl	$LINUX_BZIMAGE_ADDR, %esi
1902	movl	$LINUX_ZIMAGE_ADDR, %edi
1903
1904	rep
1905	movsl
1906
1907ENTRY(big_linux_boot)
1908	movl	EXT_C(linux_data_real_addr), %ebx
1909
1910	/* copy the real mode part */
1911	movl	EXT_C(linux_data_tmp_addr), %esi
1912	movl	%ebx, %edi
1913	movl	$LINUX_SETUP_MOVE_SIZE, %ecx
1914	cld
1915	rep
1916	movsb
1917
1918	/* change %ebx to the segment address */
1919	shrl	$4, %ebx
1920	movl	%ebx, %eax
1921	addl	$0x20, %eax
1922	movl	%eax, linux_setup_seg
1923
1924	/* XXX new stack pointer in safe area for calling functions */
1925	movl	$0x4000, %esp
1926	call	EXT_C(stop_floppy)
1927
1928	/* final setup for linux boot */
1929
1930	call	EXT_C(prot_to_real)
1931	.code16
1932
1933	/* final setup for linux boot */
1934	cli
1935	movw	%bx, %ss
1936	movw	$LINUX_SETUP_STACK, %sp
1937
1938	movw	%bx, %ds
1939	movw	%bx, %es
1940	movw	%bx, %fs
1941	movw	%bx, %gs
1942
1943	/* jump to start */
1944	/* ljmp */
1945	.byte	0xea
1946	.word	0
1947linux_setup_seg:
1948	.word	0
1949	.code32
1950
1951
1952/*
1953 * multi_boot(int start, int mb_info)
1954 *
1955 *  This starts a kernel in the manner expected of the multiboot standard.
1956 */
1957
1958ENTRY(multi_boot)
1959	/* no need to save anything */
1960	call	EXT_C(stop_floppy)
1961
1962	movl	$0x2BADB002, %eax
1963	movl	0x8(%esp), %ebx
1964
1965	/* boot kernel here (absolute address call) */
1966	call	*0x4(%esp)
1967
1968	/* error */
1969	call	EXT_C(stop)
1970
1971#endif /* ! STAGE1_5 */
1972
1973/*
1974 * void console_putchar (int c)
1975 *
1976 * Put the character C on the console. Because GRUB wants to write a
1977 * character with an attribute, this implementation is a bit tricky.
1978 * If C is a control character (CR, LF, BEL, BS), use INT 10, AH = 0Eh
1979 * (TELETYPE OUTPUT). Otherwise, save the original position, put a space,
1980 * save the current position, restore the original position, write the
1981 * character and the attribute, and restore the current position.
1982 *
1983 * The reason why this is so complicated is that there is no easy way to
1984 * get the height of the screen, and the TELETYPE OUPUT BIOS call doesn't
1985 * support setting a background attribute.
1986 */
1987ENTRY(console_putchar)
1988	movl	0x4(%esp), %edx
1989	pusha
1990#ifdef STAGE1_5
1991	movb	$0x07, %bl
1992#else
1993	movl	EXT_C(console_current_color), %ebx
1994#endif
1995
1996	call	EXT_C(prot_to_real)
1997	.code16
1998	movb	%dl, %al
1999	xorb	%bh, %bh
2000
2001#ifndef STAGE1_5
2002	/* use teletype output if control character */
2003	cmpb	$0x7, %al
2004	je	1f
2005	cmpb	$0x8, %al
2006	je	1f
2007	cmpb	$0xa, %al
2008	je	1f
2009	cmpb	$0xd, %al
2010	je	1f
2011
2012	/* save the character and the attribute on the stack */
2013	pushw	%ax
2014	pushw	%bx
2015
2016	/* get the current position */
2017	movb	$0x3, %ah
2018	int	$0x10
2019
2020	/* check the column with the width */
2021	cmpb	$79, %dl
2022	jl	2f
2023
2024	/* print CR and LF, if next write will exceed the width */
2025	movw	$0x0e0d, %ax
2026	int	$0x10
2027	movb	$0x0a, %al
2028	int	$0x10
2029
2030	/* get the current position */
2031	movb	$0x3, %ah
2032	int	$0x10
2033
20342:
2035	/* restore the character and the attribute */
2036	popw	%bx
2037	popw	%ax
2038
2039	/* write the character with the attribute */
2040	movb	$0x9, %ah
2041	movw	$1, %cx
2042	int	$0x10
2043
2044	/* move the cursor forward */
2045	incb	%dl
2046	movb	$0x2, %ah
2047	int	$0x10
2048
2049	jmp	3f
2050#endif /* ! STAGE1_5 */
2051
20521:	movb	$0xe, %ah
2053	int	$0x10
2054
20553:	DATA32	call	EXT_C(real_to_prot)
2056	.code32
2057
2058	popa
2059	ret
2060
2061
2062#ifndef STAGE1_5
2063
2064/* this table is used in translate_keycode below */
2065translation_table:
2066	.word	KEY_LEFT, 2
2067	.word	KEY_RIGHT, 6
2068	.word	KEY_UP, 16
2069	.word	KEY_DOWN, 14
2070	.word	KEY_HOME, 1
2071	.word	KEY_END, 5
2072	.word	KEY_DC, 4
2073	.word	KEY_BACKSPACE, 8
2074	.word	KEY_PPAGE, 7
2075	.word	KEY_NPAGE, 3
2076	.word	0
2077
2078/*
2079 * translate_keycode translates the key code %dx to an ascii code.
2080 */
2081	.code16
2082
2083translate_keycode:
2084	pushw	%bx
2085	pushw	%si
2086
2087	movw	$ABS(translation_table), %si
2088
20891:	lodsw
2090	/* check if this is the end */
2091	testw	%ax, %ax
2092	jz	2f
2093	/* load the ascii code into %ax */
2094	movw	%ax, %bx
2095	lodsw
2096	/* check if this matches the key code */
2097	cmpw	%bx, %dx
2098	jne	1b
2099	/* translate %dx, if successful */
2100	movw	%ax, %dx
2101
21022:	popw	%si
2103	popw	%bx
2104	ret
2105
2106	.code32
2107
2108
2109/*
2110 * remap_ascii_char remaps the ascii code %dl to another if the code is
2111 * contained in ASCII_KEY_MAP.
2112 */
2113	.code16
2114
2115remap_ascii_char:
2116	pushw	%si
2117
2118	movw	$ABS(EXT_C(ascii_key_map)), %si
21191:
2120	lodsw
2121	/* check if this is the end */
2122	testw	%ax, %ax
2123	jz	2f
2124	/* check if this matches the ascii code */
2125	cmpb	%al, %dl
2126	jne	1b
2127	/* if so, perform the mapping */
2128	movb	%ah, %dl
21292:
2130	/* restore %si */
2131	popw	%si
2132
2133	ret
2134
2135	.code32
2136
2137	.align	4
2138ENTRY(ascii_key_map)
2139	.space	(KEY_MAP_SIZE + 1) * 2
2140
2141
2142/*
2143 * int console_getkey (void)
2144 * BIOS call "INT 16H Function 00H" to read character from keyboard
2145 *	Call with	%ah = 0x0
2146 *	Return:		%ah = keyboard scan code
2147 *			%al = ASCII character
2148 */
2149
2150ENTRY(console_getkey)
2151	push	%ebp
2152
2153wait_for_key:
2154	call	EXT_C(console_checkkey)
2155	incl	%eax
2156	jz	wait_for_key
2157
2158	call	EXT_C(prot_to_real)
2159	.code16
2160
2161	int	$0x16
2162
2163	movw	%ax, %dx		/* real_to_prot uses %eax */
2164	call	translate_keycode
2165	call	remap_ascii_char
2166
2167	DATA32	call	EXT_C(real_to_prot)
2168	.code32
2169
2170	movw	%dx, %ax
2171
2172	pop	%ebp
2173	ret
2174
2175
2176/*
2177 * int console_checkkey (void)
2178 *	if there is a character pending, return it; otherwise return -1
2179 * BIOS call "INT 16H Function 01H" to check whether a character is pending
2180 *	Call with	%ah = 0x1
2181 *	Return:
2182 *		If key waiting to be input:
2183 *			%ah = keyboard scan code
2184 *			%al = ASCII character
2185 *			Zero flag = clear
2186 *		else
2187 *			Zero flag = set
2188 */
2189ENTRY(console_checkkey)
2190	push	%ebp
2191	xorl	%edx, %edx
2192
2193	call	EXT_C(prot_to_real)	/* enter real mode */
2194	.code16
2195
2196	movb	$0x1, %ah
2197	int	$0x16
2198
2199	DATA32	jz	notpending
2200
2201	movw	%ax, %dx
2202	call	translate_keycode
2203	call	remap_ascii_char
2204	DATA32	jmp	pending
2205
2206notpending:
2207	movl	$0xFFFFFFFF, %edx
2208
2209pending:
2210	DATA32	call	EXT_C(real_to_prot)
2211	.code32
2212
2213	mov	%edx, %eax
2214
2215	pop	%ebp
2216	ret
2217
2218
2219/*
2220 * int console_getxy (void)
2221 * BIOS call "INT 10H Function 03h" to get cursor position
2222 *	Call with	%ah = 0x03
2223 *			%bh = page
2224 *      Returns         %ch = starting scan line
2225 *                      %cl = ending scan line
2226 *                      %dh = row (0 is top)
2227 *                      %dl = column (0 is left)
2228 */
2229
2230
2231ENTRY(console_getxy)
2232	push	%ebp
2233	push	%ebx                    /* save EBX */
2234
2235	call	EXT_C(prot_to_real)
2236	.code16
2237
2238        xorb	%bh, %bh                /* set page to 0 */
2239	movb	$0x3, %ah
2240	int	$0x10			/* get cursor position */
2241
2242	DATA32	call	EXT_C(real_to_prot)
2243	.code32
2244
2245	movb	%dl, %ah
2246	movb	%dh, %al
2247
2248	pop	%ebx
2249	pop	%ebp
2250	ret
2251
2252
2253/*
2254 * void console_gotoxy(int x, int y)
2255 * BIOS call "INT 10H Function 02h" to set cursor position
2256 *	Call with	%ah = 0x02
2257 *			%bh = page
2258 *                      %dh = row (0 is top)
2259 *                      %dl = column (0 is left)
2260 */
2261
2262
2263ENTRY(console_gotoxy)
2264	push	%ebp
2265	push	%ebx                    /* save EBX */
2266
2267	movb	0xc(%esp), %dl           /* %dl = x */
2268	movb	0x10(%esp), %dh          /* %dh = y */
2269
2270	call	EXT_C(prot_to_real)
2271	.code16
2272
2273        xorb	%bh, %bh                /* set page to 0 */
2274	movb	$0x2, %ah
2275	int	$0x10			/* set cursor position */
2276
2277	DATA32	call	EXT_C(real_to_prot)
2278	.code32
2279
2280	pop	%ebx
2281	pop	%ebp
2282	ret
2283
2284
2285/*
2286 * void console_cls (void)
2287 * BIOS call "INT 10H Function 09h" to write character and attribute
2288 *	Call with	%ah = 0x09
2289 *                      %al = (character)
2290 *                      %bh = (page number)
2291 *                      %bl = (attribute)
2292 *                      %cx = (number of times)
2293 */
2294
2295
2296ENTRY(console_cls)
2297	push	%ebp
2298	push	%ebx                    /* save EBX */
2299
2300	call	EXT_C(prot_to_real)
2301	.code16
2302
2303	/* move the cursor to the beginning */
2304	movb	$0x02, %ah
2305	xorb	%bh, %bh
2306	xorw	%dx, %dx
2307	int	$0x10
2308
2309	/* write spaces to the entire screen */
2310	movw	$0x0920, %ax
2311	movw	$0x07, %bx
2312	movw	$(80 * 25), %cx
2313        int	$0x10
2314
2315	/* move back the cursor */
2316	movb	$0x02, %ah
2317	int	$0x10
2318
2319	DATA32	call	EXT_C(real_to_prot)
2320	.code32
2321
2322	pop	%ebx
2323	pop	%ebp
2324	ret
2325
2326
2327/*
2328 * int console_setcursor (int on)
2329 * BIOS call "INT 10H Function 01h" to set cursor type
2330 *      Call with       %ah = 0x01
2331 *                      %ch = cursor starting scanline
2332 *                      %cl = cursor ending scanline
2333 */
2334
2335console_cursor_state:
2336	.byte	1
2337console_cursor_shape:
2338	.word	0
2339
2340ENTRY(console_setcursor)
2341	push	%ebp
2342	push	%ebx
2343
2344	/* check if the standard cursor shape has already been saved */
2345	movw	console_cursor_shape, %ax
2346	testw	%ax, %ax
2347	jne	1f
2348
2349	call	EXT_C(prot_to_real)
2350	.code16
2351
2352	movb	$0x03, %ah
2353	xorb	%bh, %bh
2354	int	$0x10
2355
2356	DATA32	call	EXT_C(real_to_prot)
2357	.code32
2358
2359	movw	%cx, console_cursor_shape
23601:
2361	/* set %cx to the designated cursor shape */
2362	movw	$0x2000, %cx
2363	movl	0xc(%esp), %ebx
2364	testl	%ebx, %ebx
2365	jz	2f
2366	movw	console_cursor_shape, %cx
23672:
2368	call	EXT_C(prot_to_real)
2369	.code16
2370
2371	movb    $0x1, %ah
2372	int     $0x10
2373
2374	DATA32	call	EXT_C(real_to_prot)
2375	.code32
2376
2377	movzbl	console_cursor_state, %eax
2378	movb	%bl, console_cursor_state
2379
2380	pop	%ebx
2381	pop	%ebp
2382	ret
2383
2384/* graphics mode functions */
2385#ifdef SUPPORT_GRAPHICS
2386VARIABLE(cursorX)
2387.word	0
2388VARIABLE(cursorY)
2389.word	0
2390VARIABLE(cursorCount)
2391.word 0
2392VARIABLE(cursorBuf)
2393.byte	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
2394
2395
2396/*
2397 * int set_videomode(mode)
2398 * BIOS call "INT 10H Function 0h" to set video mode
2399 *	Call with	%ah = 0x0
2400 *			%al = video mode
2401 *      Returns old videomode.
2402 */
2403ENTRY(set_videomode)
2404	push	%ebp
2405	push	%ebx
2406	push	%ecx
2407
2408	movb	0x10(%esp), %cl
2409
2410	call	EXT_C(prot_to_real)
2411	.code16
2412
2413	xorw	%bx, %bx
2414	movb	$0xf, %ah
2415	int	$0x10			/* Get Current Video mode */
2416	movb	%al, %ch
2417	xorb	%ah, %ah
2418	movb	%cl, %al
2419        int	$0x10			/* Set Video mode */
2420
2421	DATA32	call	EXT_C(real_to_prot)
2422	.code32
2423
2424	xorb	%ah, %ah
2425	movb	%ch, %al
2426
2427	pop	%ecx
2428	pop	%ebx
2429	pop	%ebp
2430	ret
2431
2432
2433/*
2434 * unsigned char * graphics_get_font()
2435 * BIOS call "INT 10H Function 11h" to set font
2436 *      Call with       %ah = 0x11
2437 */
2438ENTRY(graphics_get_font)
2439	push	%ebp
2440	push	%ebx
2441	push	%ecx
2442	push	%edx
2443
2444	call	EXT_C(prot_to_real)
2445	.code16
2446
2447	movw	$0x1130, %ax
2448	movb	$6, %bh		/* font 8x16 */
2449	int	$0x10
2450	movw	%bp, %dx
2451	movw	%es, %cx
2452
2453	DATA32	call	EXT_C(real_to_prot)
2454	.code32
2455
2456	xorl	%eax, %eax
2457	movw	%cx, %ax
2458	shll	$4, %eax
2459	movw	%dx, %ax
2460
2461	pop	%edx
2462	pop	%ecx
2463	pop	%ebx
2464	pop	%ebp
2465	ret
2466
2467
2468
2469/*
2470 * graphics_set_palette(index, red, green, blue)
2471 * BIOS call "INT 10H Function 10h" to set individual dac register
2472 *	Call with	%ah = 0x10
2473 *			%bx = register number
2474 *			%ch = new value for green (0-63)
2475 *			%cl = new value for blue (0-63)
2476 *			%dh = new value for red (0-63)
2477 */
2478
2479ENTRY(graphics_set_palette)
2480	push	%ebp
2481	push	%eax
2482	push	%ebx
2483	push	%ecx
2484	push	%edx
2485
2486	movw	$0x3c8, %bx		/* address write mode register */
2487
2488	/* wait vertical retrace */
2489
2490	movw	$0x3da, %dx
2491l1b:	inb	%dx, %al	/* wait vertical active display */
2492	test	$8, %al
2493	jnz	l1b
2494
2495l2b:	inb	%dx, %al	/* wait vertical retrace */
2496	test	$8, %al
2497	jnz	l2b
2498
2499	mov	%bx, %dx
2500	movb	0x18(%esp), %al		/* index */
2501	outb	%al, %dx
2502	inc	%dx
2503
2504	movb	0x1c(%esp), %al		/* red */
2505	outb	%al, %dx
2506
2507	movb	0x20(%esp), %al		/* green */
2508	outb	%al, %dx
2509
2510	movb	0x24(%esp), %al		/* blue */
2511	outb	%al, %dx
2512
2513	movw	0x18(%esp), %bx
2514
2515	call	EXT_C(prot_to_real)
2516	.code16
2517
2518	movb	%bl, %bh
2519	movw	$0x1000, %ax
2520	int	$0x10
2521
2522	DATA32	call	EXT_C(real_to_prot)
2523	.code32
2524
2525	pop	%edx
2526	pop	%ecx
2527	pop	%ebx
2528	pop	%eax
2529	pop	%ebp
2530	ret
2531
2532#endif /* SUPPORT_GRAPHICS */
2533
2534/*
2535 * getrtsecs()
2536 *	if a seconds value can be read, read it and return it (BCD),
2537 *      otherwise return 0xFF
2538 * BIOS call "INT 1AH Function 02H" to check whether a character is pending
2539 *	Call with	%ah = 0x2
2540 *	Return:
2541 *		If RT Clock can give correct values
2542 *			%ch = hour (BCD)
2543 *			%cl = minutes (BCD)
2544 *                      %dh = seconds (BCD)
2545 *                      %dl = daylight savings time (00h std, 01h daylight)
2546 *			Carry flag = clear
2547 *		else
2548 *			Carry flag = set
2549 *                         (this indicates that the clock is updating, or
2550 *                          that it isn't running)
2551 */
2552ENTRY(getrtsecs)
2553	push	%ebp
2554
2555	call	EXT_C(prot_to_real)	/* enter real mode */
2556	.code16
2557
2558	movb	$0x2, %ah
2559	int	$0x1a
2560
2561	DATA32	jnc	gottime
2562	movb	$0xff, %dh
2563
2564gottime:
2565	DATA32	call	EXT_C(real_to_prot)
2566	.code32
2567
2568	movb	%dh, %al
2569
2570	pop	%ebp
2571	ret
2572
2573
2574/*
2575 * currticks()
2576 *	return the real time in ticks, of which there are about
2577 *	18-20 per second
2578 */
2579ENTRY(currticks)
2580	pushl	%ebp
2581
2582	call	EXT_C(prot_to_real)	/* enter real mode */
2583	.code16
2584
2585	/* %ax is already zero */
2586        int	$0x1a
2587
2588	DATA32	call	EXT_C(real_to_prot)
2589	.code32
2590
2591	movl	%ecx, %eax
2592	shll	$16, %eax
2593	movw	%dx, %ax
2594
2595	popl	%ebp
2596	ret
2597
2598ENTRY(amd64_rdmsr)
2599	movl	4(%esp), %ecx
2600	rdmsr
2601	movl	8(%esp), %ecx
2602	movl	%eax, (%ecx)
2603	movl	%edx, 4(%ecx)
2604	ret
2605
2606ENTRY(amd64_wrmsr)
2607	movl	8(%esp), %ecx
2608	movl	(%ecx), %eax
2609	movl	4(%ecx), %edx
2610	movl	4(%esp), %ecx
2611	wrmsr
2612	ret
2613
2614ENTRY(amd64_cpuid_insn)
2615	pushl	%ebp
2616	movl	%esp, %ebp
2617	pushl	%ebx
2618	pushl	%esi
2619	movl	0x8(%ebp), %eax
2620	movl	0xc(%ebp), %esi
2621	cpuid
2622	movl	%eax, 0x0(%esi)
2623	movl	%ebx, 0x4(%esi)
2624	movl	%ecx, 0x8(%esi)
2625	movl	%edx, 0xc(%esi)
2626	popl	%esi
2627	popl	%ebx
2628	popl	%ebp
2629	ret
2630
2631	/*
2632	 * Based on code from AMD64 Volume 3
2633	 */
2634ENTRY(amd64_cpuid_supported)
2635	pushf
2636	popl	%eax
2637	mov	%eax, %edx		/* save %eax for later */
2638	xorl	%eax, 0x200000		/* toggle bit 21 */
2639	pushl	%eax
2640	popf				/* save new %eax to EFLAGS */
2641	pushf				/* save new EFLAGS */
2642	popl	%ecx			/* copy EFLAGS to %eax */
2643	xorl	%eax, %eax
2644	cmpl	%ecx, %edx		/* see if bit 21 has changes */
2645	jne	1f
2646	incl	%eax
26471:
2648	ret
2649
2650ENTRY(get_target_operating_mode)
2651	pusha
2652
2653	call	EXT_C(prot_to_real)
2654	.code16
2655
2656	movw	$0xec00, %ax
2657	movw	$0x03, %bx
2658	int	$0x15
2659/* XXX still need to pass back return */
2660
2661	movw	%ax, %cx
2662
2663	DATA32	call	EXT_C(real_to_prot)
2664	.code32
2665
2666	xorl	%eax, %eax
2667	movw	%cx, %ax
2668
2669	popa
2670	ret
2671
2672#endif /* ! STAGE1_5 */
2673
2674/*
2675 *  This is the area for all of the special variables.
2676 */
2677
2678	.p2align	2	/* force 4-byte alignment */
2679
2680protstack:
2681	.long	PROTSTACKINIT
2682
2683VARIABLE(boot_drive)
2684#ifdef SUPPORT_DISKLESS
2685	.long	NETWORK_DRIVE
2686#else
2687	.long	0
2688#endif
2689
2690VARIABLE(install_second_sector)
2691	.long	0
2692
2693	/* an address can only be long-jumped to if it is in memory, this
2694	   is used by multiple routines */
2695offset:
2696	.long	0x8000
2697segment:
2698	.word	0
2699
2700VARIABLE(apm_bios_info)
2701	.word	0	/* version */
2702	.word	0	/* cseg */
2703	.long	0	/* offset */
2704	.word	0	/* cseg_16 */
2705	.word	0	/* dseg_16 */
2706	.word	0	/* cseg_len */
2707	.word	0	/* cseg_16_len */
2708	.word	0	/* dseg_16_len */
2709
2710/*
2711 * This is the Global Descriptor Table
2712 *
2713 *  An entry, a "Segment Descriptor", looks like this:
2714 *
2715 * 31          24         19   16                 7           0
2716 * ------------------------------------------------------------
2717 * |             | |B| |A|       | |   |1|0|E|W|A|            |
2718 * | BASE 31..24 |G|/|0|V| LIMIT |P|DPL|  TYPE   | BASE 23:16 |
2719 * |             | |D| |L| 19..16| |   |1|1|C|R|A|            |
2720 * ------------------------------------------------------------
2721 * |                             |                            |
2722 * |        BASE 15..0           |       LIMIT 15..0          |
2723 * |                             |                            |
2724 * ------------------------------------------------------------
2725 *
2726 *  Note the ordering of the data items is reversed from the above
2727 *  description.
2728 */
2729
2730	.p2align	2	/* force 4-byte alignment */
2731gdt:
2732	.word	0, 0
2733	.byte	0, 0, 0, 0
2734
2735	/* code segment */
2736	.word	0xFFFF, 0
2737	.byte	0, 0x9A, 0xCF, 0
2738
2739	/* data segment */
2740	.word	0xFFFF, 0
2741	.byte	0, 0x92, 0xCF, 0
2742
2743	/* 16 bit real mode CS */
2744	.word	0xFFFF, 0
2745	.byte	0, 0x9E, 0, 0
2746
2747	/* 16 bit real mode DS */
2748	.word	0xFFFF, 0
2749	.byte	0, 0x92, 0, 0
2750
2751
2752/* this is the GDT descriptor */
2753gdtdesc:
2754	.word	0x27			/* limit */
2755	.long	gdt			/* addr */
2756