1/*
2 *  GRUB  --  GRand Unified Bootloader
3 *  Copyright (C) 1994-2002  H. Peter Anvin
4 *  Copyright (C) 1999,2000,2001,2004	Free Software Foundation, Inc.
5 *
6 *  This program is free software; you can redistribute it and/or modify
7 *  it under the terms of the GNU General Public License as published by
8 *  the Free Software Foundation; either version 2 of the License, or
9 *  (at your option) any later version.
10 *
11 *  This program is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 *  GNU General Public License for more details.
15 *
16 *  You should have received a copy of the GNU General Public License
17 *  along with this program; if not, write to the Free Software
18 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 */
21
22/*
23 Most of this file was originally "isolinux.asm" from SYSLINUX package.
24 It has been very heavily modified.
25*/
26
27#define ASM_FILE
28#include "stage1.h"
29#include "shared.h"
30#include "iso9660.h"
31
32#ifndef STAGE1_5
33#include "stage2_size.h"
34#endif
35
36
37	/* Absolute addresses
38	   This makes the assembler generate the address without support
39	   from the linker. (ELF can't relocate 16-bit addresses!) */
40#define ABS(x)			(x-_start+BOOTSEC_LOCATION)
41
42#ifdef STAGE1_5
43# define STAGE_ADDR		0x2000
44#else
45# define STAGE_ADDR		0x8000
46#endif /* STAGE1_5 */
47
48	/* Print message string */
49#define MSG(x)			mov $ABS(x), %si; call message;
50
51	.file	"start_eltorito.S"
52
53	.text
54
55	/* Tell GAS to generate 16-bit instructions so that this code works
56	   in real mode. */
57	.code16
58
59	.globl	start, _start
60
61/*
62 * Primary entry point.	 Because BIOSes are buggy, we only load the first
63 * CD-ROM sector (2K) of the file, so the number one priority is actually
64 * loading the rest.
65 */
66start:
67_start:
68	cli
69	ljmp	$0, $ABS(real_start)
70
71	. = _start + 8			    /* Pad to file offset 8 */
72
73		/* This table gets filled in by mkisofs using the
74		   -boot-info-table option */
75bi_pvd:		.long 0xDEADBEEF	    /* LBA of primary volume descript */
76bi_file:	.long 0xDEADBEEF	    /* LBA of boot file */
77bi_length:	.long 0xDEADBEEF	    /* Length of boot file */
78bi_csum:	.long 0xDEADBEEF	    /* Checksum of boot file */
79bi_reserved:	.space (10*4)		    /* Reserved */
80
81real_start:
82	xor	%ax, %ax
83	mov	%ax, %ss
84	mov	%ax, %ds
85	mov	%ax, %es
86	mov	%ax, %fs
87	mov	%ax, %gs
88	mov	$STAGE1_STACKSEG, %sp	    /* set up the REAL stack */
89	sti
90	cld
91
92	/* save drive reference first thing! */
93	mov	%dl, ABS(BootDrive)
94
95	/* print a notification message on the screen */
96	MSG(notification_string)
97
98load_image:
99	/* Set up boot file sector, size, load address */
100	mov	ABS(bi_length), %eax
101	add	$(ISO_SECTOR_SIZE-1), %eax
102	shr	$ISO_SECTOR_BITS, %eax	    /* dwords->sectors */
103	mov	%ax, %bp		    /* boot file sectors */
104	mov	$(STAGE_ADDR >> 4), %bx
105	mov	%bx, %es
106	xor	%bx, %bx
107	mov	ABS(bi_file), %eax
108	call	getlinsec
109	mov	%ds, %ax
110	mov	%ax, %es
111
112	MSG(notification_done)
113bootit:
114	/* save the sector number of the second sector in %ebp */
115	mov	$ABS(firstlist - BOOTSEC_LISTSIZE), %si
116	mov	(%si), %ebp
117	mov	ABS(BootDrive), %dl	    /* this makes sure %dl is our "boot" drive */
118	ljmp	$0, $(STAGE_ADDR+SECTOR_SIZE)  /* jump to main() in asm.S */
119
120/* go here when you need to stop the machine hard after an error condition */
121stop:	jmp	stop
122
123
124/*
125 * Get linear sectors - EBIOS LBA addressing, 2048-byte sectors.
126 *
127 * Note that we can't always do this as a single request, because at least
128 * Phoenix BIOSes has a 127-sector limit.  To be on the safe side, stick
129 * to 16 sectors (32K) per request.
130 *
131 * Input:
132 *	 EAX	 - Linear sector number
133 *	 ES:BX	 - Target buffer
134 *	 BP	 - Sector count
135 */
136getlinsec:
137	mov	$ABS(dapa), %si		   /* Load up the DAPA */
138	mov	%bx, 4(%si)
139	mov	%es, %bx
140	mov	%bx, 6(%si)
141	mov	%eax, 8(%si)
1421:
143	push	%bp
144	push	%si
145	cmp	ABS(MaxTransfer), %bp
146	jbe	2f
147	mov	ABS(MaxTransfer), %bp
1482:
149	mov	%bp, 2(%si)
150	mov	ABS(BootDrive), %dl
151	mov	$0x42, %ah		    /* Extended Read */
152	call	xint13
153	pop	%si
154	pop	%bp
155	movzwl	2(%si), %eax		    /* Sectors we read */
156	add	%eax, 8(%si)		    /* Advance sector pointer */
157	sub	%ax, %bp		    /* Sectors left */
158	shl	$(ISO_SECTOR_BITS-4), %ax   /* 2048-byte sectors -> segment */
159	add	%ax, 6(%si)		    /* Advance buffer pointer */
160
161	pushal
162	MSG(notification_step)
163	popal
164	cmp	$0, %bp
165	ja	1b
166	mov	8(%si), %eax		    /* Return next sector */
167	ret
168
169/*
170 * INT 13h with retry
171 */
172xint13:
173	movb	$6, ABS(RetryCount)
174.try:
175	pushal
176	int	$0x13
177	jc	1f
178	add	$(8*4), %sp		    /* Clean up stack */
179	ret
1801:
181	mov	%ah, %dl		    /* Save error code */
182	decb	ABS(RetryCount)
183	jz	.real_error
184	mov	ABS(RetryCount), %al
185	mov	ABS(dapa+2), %ah	    /* Sector transfer count */
186	cmp	$2, %al			    /* Only 2 attempts left */
187	ja	2f
188	mov	$1, %ah			    /* Drop transfer size to 1 */
189	jmp	.setmaxtr
1902:
191	cmp	$3, %al
192	ja	3f			    /* First time, just try again */
193	shr	$1, %ah			    /* Otherwise, try to reduce */
194	adc	$0, %ah			    /* the max transfer size, but not */
195.setmaxtr:
196	mov	%ah, ABS(MaxTransfer)
197	mov	%ah, ABS(dapa+2)
1983:
199	popal
200	jmp	.try
201
202.real_error:
203	MSG(read_error_string)
204	mov	%dl, %al
205	call	printhex2
206	popal
207	jmp	stop
208
209
210
211/*
212 * message: write the string pointed to by %si
213 *
214 *   WARNING: trashes %si, %ax, and %bx
215 */
216
217	/*
218	 * Use BIOS "int 10H Function 0Eh" to write character in teletype mode
219	 *	%ah = 0xe	%al = character
220	 *	%bh = page	%bl = foreground color (graphics modes)
221	 */
2221:
223	mov	$0x0001, %bx
224	mov	$0x0E, %ah
225	int	$0x10		/* display a byte */
226
227message:
228	lodsb
229	or	%al, %al
230	jne	1b		/* if not end of string, jmp to display */
231	ret
232
233/*
234 * printhex[248]: Write a hex number in (AL, AX, EAX) to the console
235 */
236printhex2:
237	pushal
238	rol	$24, %eax
239	mov	$2, %cx
240	jmp	1f
241printhex4:
242	pushal
243	rol	$16, %eax
244	mov	$4, %cx
245	jmp	1f
246printhex8:
247	pushal
248	mov	$8, %cx
2491:
250	rol	$4, %eax
251	push	%eax
252	and	$0x0F, %al
253	cmp	$10, %al
254	jae	.high
255.low:	add	$('0'), %al
256	jmp	2f
257.high:	add	$('A'-10), %al
2582:
259	mov	$0x0001, %bx
260	mov	$0x0E, %ah
261	int	$0x10		/* display a char */
262	pop	%eax
263	loop	1b
264	popal
265	ret
266
267/**************************************************************************/
268#ifdef STAGE1_5
269notification_string:	.string "Loading stage1.5 "
270#else
271notification_string:	.string "Loading stage2 "
272#endif
273
274notification_step:	.string "."
275notification_done:	.string "\r\n"
276
277read_error_string:	.string "Read error 0x"
278
279/*
280 * EBIOS disk address packet
281 */
282		.align 8
283dapa:		.byte 16		   /* Packet size */
284		.byte 0			   /* reserved */
285		.word 0			   /* +2 Block count */
286		.word 0			   /* +4 Offset of buffer */
287		.word 0			   /* +6 Segment of buffer */
288		.long 0			   /* +8 LBA (LSW) */
289		.long 0			   /* +C LBA (MSW) */
290
291VARIABLE(BootDrive)
292	.byte 0xFF
293VARIABLE(MaxTransfer)
294	.word 16			   /* Max sectors per transfer (32Kb) */
295VARIABLE(RetryCount)
296	.byte 0
297
298
299/*
300 *  This area is an empty space between the main body of code below which
301 *  grows up (fixed after compilation, but between releases it may change
302 *  in size easily), and the lists of sectors to read, which grows down
303 *  from a fixed top location.
304 */
305
306	.word 0
307	.word 0
308
309	. = _start + SECTOR_SIZE - BOOTSEC_LISTSIZE
310
311	/* fill the first data listing with the default */
312blocklist_default_start:/* this is the sector start parameter, in logical
313			   sectors from the start of the disk, sector 0 */
314	.long 0
315
316blocklist_default_len:	/* this is the number of sectors to read */
317#ifdef STAGE1_5
318	.word 0
319#else
320	.word (STAGE2_SIZE + ISO_SECTOR_SIZE - 1) >> ISO_SECTOR_BITS
321#endif
322blocklist_default_seg:	/* this is the segment of the starting address
323			   to load the data into */
324	.word (STAGE_ADDR + SECTOR_SIZE) >> 4
325
326firstlist:	/* this label has to be after the list data!!! */
327