1/*
2 *  GRUB  --  GRand Unified Bootloader
3 *  Copyright (C) 1999,2000,2001   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#define ASM_FILE
21#include <shared.h>
22
23#ifndef STAGE1_5
24#include <stage2_size.h>
25#endif
26
27/*
28 *  defines for the code go here
29 */
30
31	/* Absolute addresses
32	   This makes the assembler generate the address without support
33	   from the linker. (ELF can't relocate 16-bit addresses!) */
34#ifdef STAGE1_5
35# define ABS(x) (x-_start+0x2000)
36#else
37# define ABS(x) (x-_start+0x8000)
38#endif /* STAGE1_5 */
39
40	/* Print message string */
41#define MSG(x)	movw $ABS(x), %si; call message
42
43	.file	"start.S"
44
45	.text
46
47	/* Tell GAS to generate 16-bit instructions so that this code works
48	   in real mode. */
49	.code16
50
51	.globl	start, _start
52start:
53_start:
54	/*
55	 * _start is loaded at 0x8000 and is jumped to with
56	 * CS:IP 0:0x8000 in stage2.
57	 */
58
59	/*
60	 * we continue to use the stack for stage1 and assume that
61	 * some registers are set to correct values. See stage1.S
62	 * for more information.
63	 */
64
65	/* save drive reference first thing! */
66	pushw	%dx
67
68	/* print a notification message on the screen */
69	pushw	%si
70	MSG(notification_string)
71	popw	%si
72
73	/* this sets up for the first run through "bootloop" */
74	movw	$ABS(firstlist - BOOTSEC_LISTSIZE), %di
75
76	/* save the sector number of the second sector in %ebp */
77	movl	(%di), %ebp
78
79        /* this is the loop for reading the secondary boot-loader in */
80bootloop:
81
82	/* check the number of sectors to read */
83	cmpw	$0, 4(%di)
84
85	/* if zero, go to the start function */
86	je	bootit
87
88setup_sectors:
89	/* check if we use LBA or CHS */
90	cmpb	$0, -1(%si)
91
92	/* jump to chs_mode if zero */
93	je	chs_mode
94
95lba_mode:
96	/* load logical sector start */
97	movl	(%di), %ebx
98
99	/* the maximum is limited to 0x7f because of Phoenix EDD */
100	xorl	%eax, %eax
101	movb	$0x7f, %al
102
103	/* how many do we really want to read? */
104	cmpw	%ax, 4(%di)	/* compare against total number of sectors */
105
106	/* which is greater? */
107	jg	1f
108
109	/* if less than, set to total */
110	movw	4(%di), %ax
111
1121:
113	/* subtract from total */
114	subw	%ax, 4(%di)
115
116	/* add into logical sector start */
117	addl	%eax, (%di)
118
119	/* set up disk address packet */
120
121	/* the size and the reserved byte */
122	movw	$0x0010, (%si)
123
124	/* the number of sectors */
125	movw	%ax, 2(%si)
126
127	/* the absolute address (low 32 bits) */
128	movl	%ebx, 8(%si)
129
130	/* the segment of buffer address */
131	movw	$BUFFERSEG, 6(%si)
132
133	/* save %ax from destruction! */
134	pushw	%ax
135
136	/* zero %eax */
137	xorl	%eax, %eax
138
139	/* the offset of buffer address */
140	movw	%ax, 4(%si)
141
142	/* the absolute address (high 32 bits) */
143	movl	%eax, 12(%si)
144
145
146/*
147 * BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory
148 *	Call with	%ah = 0x42
149 *			%dl = drive number
150 *			%ds:%si = segment:offset of disk address packet
151 *	Return:
152 *			%al = 0x0 on success; err code on failure
153 */
154
155	movb	$0x42, %ah
156	int	$0x13
157
158	jc	read_error
159
160	movw	$BUFFERSEG, %bx
161	jmp	copy_buffer
162
163chs_mode:
164	/* load logical sector start (bottom half) */
165	movl	(%di), %eax
166
167	/* zero %edx */
168	xorl	%edx, %edx
169
170	/* divide by number of sectors */
171	divl	(%si)
172
173	/* save sector start */
174	movb	%dl, 10(%si)
175
176	xorl	%edx, %edx	/* zero %edx */
177	divl	4(%si)		/* divide by number of heads */
178
179	/* save head start */
180	movb	%dl, 11(%si)
181
182	/* save cylinder start */
183	movw	%ax, 12(%si)
184
185	/* do we need too many cylinders? */
186	cmpw	8(%si), %ax
187	jge	geometry_error
188
189	/* determine the maximum sector length of this read */
190	movw	(%si), %ax	/* get number of sectors per track/head */
191
192	/* subtract sector start */
193	subb	10(%si), %al
194
195	/* how many do we really want to read? */
196	cmpw	%ax, 4(%di)	/* compare against total number of sectors */
197
198
199	/* which is greater? */
200	jg	2f
201
202	/* if less than, set to total */
203	movw	4(%di), %ax
204
2052:
206	/* subtract from total */
207	subw	%ax, 4(%di)
208
209	/* add into logical sector start */
210	addl	%eax, (%di)
211
212/*
213 *  This is the loop for taking care of BIOS geometry translation (ugh!)
214 */
215
216	/* get high bits of cylinder */
217	movb	13(%si), %dl
218
219	shlb	$6, %dl		/* shift left by 6 bits */
220	movb	10(%si), %cl	/* get sector */
221
222	incb	%cl		/* normalize sector (sectors go
223					from 1-N, not 0-(N-1) ) */
224	orb	%dl, %cl	/* composite together */
225	movb	12(%si), %ch	/* sector+hcyl in cl, cylinder in ch */
226
227	/* restore %dx */
228	popw	%dx
229	pushw	%dx
230
231	/* head number */
232	movb	11(%si), %dh
233
234	pushw	%ax	/* save %ax from destruction! */
235
236/*
237 * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
238 *	Call with	%ah = 0x2
239 *			%al = number of sectors
240 *			%ch = cylinder
241 *			%cl = sector (bits 6-7 are high bits of "cylinder")
242 *			%dh = head
243 *			%dl = drive (0x80 for hard disk, 0x0 for floppy disk)
244 *			%es:%bx = segment:offset of buffer
245 *	Return:
246 *			%al = 0x0 on success; err code on failure
247 */
248
249	movw	$BUFFERSEG, %bx
250	movw	%bx, %es	/* load %es segment with disk buffer */
251
252	xorw	%bx, %bx	/* %bx = 0, put it at 0 in the segment */
253	movb	$0x2, %ah	/* function 2 */
254	int	$0x13
255
256	jc	read_error
257
258	/* save source segment */
259	movw	%es, %bx
260
261copy_buffer:
262
263	/* load addresses for copy from disk buffer to destination */
264	movw	6(%di), %es	/* load destination segment */
265
266	/* restore %ax */
267	popw	%ax
268
269	/* determine the next possible destination address (presuming
270		512 byte sectors!) */
271	shlw	$5, %ax		/* shift %ax five bits to the left */
272	addw	%ax, 6(%di)	/* add the corrected value to the destination
273				   address for next time */
274
275	/* save addressing regs */
276	pusha
277	pushw	%ds
278
279	/* get the copy length */
280	shlw	$4, %ax
281	movw	%ax, %cx
282
283	xorw	%di, %di	/* zero offset of destination addresses */
284	xorw	%si, %si	/* zero offset of source addresses */
285	movw	%bx, %ds	/* restore the source segment */
286
287	cld		/* sets the copy direction to forward */
288
289	/* perform copy */
290	rep		/* sets a repeat */
291	movsb		/* this runs the actual copy */
292
293	/* restore addressing regs and print a dot with correct DS
294	   (MSG modifies SI, which is saved, and unused AX and BX) */
295	popw	%ds
296	MSG(notification_step)
297	popa
298
299	/* check if finished with this dataset */
300	cmpw	$0, 4(%di)
301	jne	setup_sectors
302
303	/* update position to load from */
304	subw	$BOOTSEC_LISTSIZE, %di
305
306	/* jump to bootloop */
307	jmp	bootloop
308
309/* END OF MAIN LOOP */
310
311bootit:
312	/* print a newline */
313	MSG(notification_done)
314	popw	%dx	/* this makes sure %dl is our "boot" drive */
315#ifdef STAGE1_5
316	ljmp	$0, $0x2200
317#else /* ! STAGE1_5 */
318	ljmp	$0, $0x8200
319#endif /* ! STAGE1_5 */
320
321
322/*
323 * BIOS Geometry translation error (past the end of the disk geometry!).
324 */
325geometry_error:
326	MSG(geometry_error_string)
327	jmp	general_error
328
329/*
330 * Read error on the disk.
331 */
332read_error:
333	MSG(read_error_string)
334
335general_error:
336	MSG(general_error_string)
337
338/* go here when you need to stop the machine hard after an error condition */
339stop:	jmp	stop
340
341#ifdef STAGE1_5
342notification_string:	.string "Loading stage1.5"
343#else
344notification_string:	.string "Loading stage2"
345#endif
346
347notification_step:	.string "."
348notification_done:	.string "\r\n"
349
350geometry_error_string:	.string "Geom"
351read_error_string:	.string "Read"
352general_error_string:	.string " Error"
353
354/*
355 * message: write the string pointed to by %si
356 *
357 *   WARNING: trashes %si, %ax, and %bx
358 */
359
360	/*
361	 * Use BIOS "int 10H Function 0Eh" to write character in teletype mode
362	 *	%ah = 0xe	%al = character
363	 *	%bh = page	%bl = foreground color (graphics modes)
364	 */
3651:
366	movw	$0x0001, %bx
367	movb	$0xe, %ah
368	int	$0x10		/* display a byte */
369
370	incw	%si
371message:
372	movb	(%si), %al
373	cmpb	$0, %al
374	jne	1b	/* if not end of string, jmp to display */
375	ret
376lastlist:
377
378/*
379 *  This area is an empty space between the main body of code below which
380 *  grows up (fixed after compilation, but between releases it may change
381 *  in size easily), and the lists of sectors to read, which grows down
382 *  from a fixed top location.
383 */
384
385	.word 0
386	.word 0
387
388	. = _start + 0x200 - BOOTSEC_LISTSIZE
389
390        /* fill the first data listing with the default */
391blocklist_default_start:
392	.long 2		/* this is the sector start parameter, in logical
393			   sectors from the start of the disk, sector 0 */
394blocklist_default_len:
395			/* this is the number of sectors to read */
396#ifdef STAGE1_5
397	.word 0		/* the command "install" will fill this up */
398#else
399	.word (STAGE2_SIZE + 511) >> 9
400#endif
401blocklist_default_seg:
402#ifdef STAGE1_5
403	.word 0x220
404#else
405	.word 0x820	/* this is the segment of the starting address
406			   to load the data into */
407#endif
408
409firstlist:	/* this label has to be after the list data!!! */
410