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