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