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