1/* -----------------------------------------------------------------------
2 *
3 *   Copyright 2007-2009 H. Peter Anvin - All Rights Reserved
4 *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
5 *
6 *   Permission is hereby granted, free of charge, to any person
7 *   obtaining a copy of this software and associated documentation
8 *   files (the "Software"), to deal in the Software without
9 *   restriction, including without limitation the rights to use,
10 *   copy, modify, merge, publish, distribute, sublicense, and/or
11 *   sell copies of the Software, and to permit persons to whom
12 *   the Software is furnished to do so, subject to the following
13 *   conditions:
14 *
15 *   The above copyright notice and this permission notice shall
16 *   be included in all copies or substantial portions of the Software.
17 *
18 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 *   OTHER DEALINGS IN THE SOFTWARE.
26 *
27 * ----------------------------------------------------------------------- */
28
29#include "adjust.h"
30
31	.code16
32	.text
33
34	.globl	bootsec
35stack		= 0x7c00
36driveno		= (stack-6)
37sectors		= (stack-8)
38secpercyl	= (stack-12)
39
40BIOS_kbdflags	= 0x417
41BIOS_page	= 0x462
42
43	/* gas/ld has issues with doing this as absolute addresses... */
44	.section ".bootsec", "a", @nobits
45	.globl	bootsec
46bootsec:
47	.space	512
48
49	.text
50	.globl	_start
51_start:
52	.byte	0x33, 0xc0	/* xorw	%ax, %ax */
53	cli
54	movw	%ax, %ds
55	movw	%ax, %ss
56	movw	$stack, %sp
57	movw	%sp, %si
58	pushw	%es		/* es:di -> $PnP header */
59	pushw	%di
60	movw	%ax, %es
61	sti
62	cld
63
64	/* Copy down to 0:0x600 */
65	movw	$_start, %di
66	movw	$(512/2), %cx
67	rep; movsw
68
69	ljmpw	$0, $next
70next:
71
72	ADJUST_DRIVE
73	pushw	%dx		/* dl -> drive number */
74
75	/* Check to see if we have EBIOS */
76	pushw	%dx		/* drive number */
77	movb	$0x41, %ah	/* %al == 0 already */
78	movw	$0x55aa, %bx
79	xorw	%cx, %cx
80	xorb	%dh, %dh
81	stc
82	int	$0x13
83	jc	1f
84	cmpw	$0xaa55, %bx
85	jne	1f
86	shrw	%cx		/* Bit 0 = fixed disk subset */
87	jnc	1f
88
89	/* We have EBIOS; patch in the following code at
90	   read_sector_cbios: movb $0x42, %ah ;  jmp read_common */
91	movl	$0xeb42b4+((read_common-read_sector_cbios-4) << 24), \
92		(read_sector_cbios)
93
941:
95	popw	%dx
96
97	/* Get (C)HS geometry */
98	movb	$0x08, %ah
99	int	$0x13
100	andw	$0x3f, %cx	/* Sector count */
101	pushw	%cx		/* Save sectors on the stack */
102	movzbw	%dh, %ax	/* dh = max head */
103	incw	%ax		/* From 0-based max to count */
104	mulw	%cx		/* Heads*sectors -> sectors per cylinder */
105
106	/* Save sectors/cylinder on the stack */
107	pushw	%dx		/* High word */
108	pushw	%ax		/* Low word */
109
110	xorl	%eax, %eax	/* Base */
111	cdq			/* Root (%edx <- 0) */
112	call	scan_partition_table
113
114	/* If we get here, we have no OS */
115missing_os:
116	call	error
117	.ascii	"Missing operating system.\r\n"
118
119/*
120 * read_sector: read a single sector pointed to by %eax to 0x7c00.
121 * CF is set on error.  All registers saved.
122 */
123read_sector:
124	pushal
125	xorl	%edx, %edx
126	movw	$bootsec, %bx
127	pushl	%edx	/* MSW of LBA */
128	pushl	%eax	/* LSW of LBA */
129	pushw	%es	/* Buffer segment */
130	pushw	%bx	/* Buffer offset */
131	pushw	$1	/* Sector count */
132	pushw	$16	/* Size of packet */
133	movw	%sp, %si
134
135	/* This chunk is skipped if we have ebios */
136	/* Do not clobber %eax before this chunk! */
137	/* This also relies on %bx and %edx as set up above. */
138read_sector_cbios:
139	divl	(secpercyl)
140	shlb	$6, %ah
141	movb	%ah, %cl
142	movb	%al, %ch
143	xchgw	%dx, %ax
144	divb	(sectors)
145	movb	%al, %dh
146	orb	%ah, %cl
147	incw	%cx	/* Sectors are 1-based */
148	movw	$0x0201, %ax
149
150read_common:
151	movb	(driveno), %dl
152	int	$0x13
153	leaw	16(%si), %sp	/* Drop DAPA */
154	popal
155	ret
156
157/*
158 * read_partition_table:
159 *	Read a partition table (pointed to by %eax), and copy
160 *	the partition table into the ptab buffer.
161 *
162 *	Clobbers %si, %di, and %cx, other registers preserved.
163 *	%cx = 0 on exit.
164 *
165 *	On error, CF is set and ptab is overwritten with junk.
166 */
167ptab	= _start+446
168
169read_partition_table:
170	call	read_sector
171	movw	$bootsec+446, %si
172	movw	$ptab, %di
173	movw	$(16*4/2), %cx
174	rep ; movsw
175	ret
176
177/*
178 * scan_partition_table:
179 *	Scan a partition table currently loaded in the partition table
180 *	area.  Preserve all registers.
181 *
182 *      On entry:
183 *	  %eax - base (location of this partition table)
184 *	  %edx - root (offset from MBR, or 0 for MBR)
185 *
186 *      These get pushed into stack slots:
187 *        28(%bp) - %eax - base
188 *	  20(%bp) - %edx - root
189 */
190
191scan_partition_table:
192	pushal
193	movw	%sp, %bp
194
195	/* Search for active partitions */
196	movw	$ptab, %bx
197	movw	$4, %cx
198	xorw	%ax, %ax
199	push	%bx
200	push	%cx
2015:
202	testb	$0x80, (%bx)
203	jz	6f
204	incw	%ax
205	movw	%bx, %si
2066:
207	addw	$16, %bx
208	loopw	5b
209
210	decw	%ax		/* Number of active partitions found */
211	jz	boot
212	jns	too_many_active
213
214	/* No active partitions found, look for extended partitions */
215	popw	%cx		/* %cx <- 4    */
216	popw	%bx		/* %bx <- ptab */
2177:
218	movb	4(%bx), %al
219	cmpb	$0x0f, %al	/* 0x0f = Win9x extended */
220	je	8f
221	andb	$~0x80, %al	/* 0x85 = Linux extended */
222	cmpb	$0x05, %al	/* 0x05 = MS-DOS extended */
223	jne	9f
224
225	/* It is an extended partition.  Read the extended partition and
226	   try to scan it.  If the scan returns, re-load the current
227	   partition table and resume scan. */
2288:
229	movl	8(%bx), %eax		/* Partition table offset */
230	movl	20(%bp), %edx		/* "Root" */
231	addl	%edx, %eax		/* Compute location of new ptab */
232	andl	%edx, %edx		/* Is this the MBR? */
233	jnz	10f
234	movl	%eax, %edx		/* Offset -> root if this was MBR */
23510:
236	call	read_partition_table
237	jc	11f
238	call	scan_partition_table
23911:
240	/* This returned, so we need to reload the current partition table */
241	movl	28(%bp), %eax		/* "Base" */
242	call	read_partition_table
243
244	/* fall through */
2459:
246	/* Not an extended partition */
247	addw	$16, %bx
248	loopw	7b
249
250	/* Nothing found, return */
251	popal
252	ret
253
254too_many_active:
255	call	error
256	.ascii	"Multiple active partitions.\r\n"
257
258/*
259 * boot: invoke the actual bootstrap. (%si) points to the partition
260 *	 table entry, and 28(%bp) has the partition table base.
261 */
262boot:
263	movl	8(%si), %eax
264	addl	28(%bp), %eax
265	movl	%eax, 8(%si)	/* Adjust in-memory partition table entry */
266	call	read_sector
267	jc	disk_error
268
269	/* Check if the read sector is a XFS superblock */
270	cmpl	$0x42534658, (bootsec) /* "XFSB" */
271	jne	no_xfs
272
273	/* We put the Syslinux boot sector at offset 0x800 (4 sectors), so we
274	 * need to adjust %eax (%eax + 4) to read the right sector into 0x7C00.
275	 */
276	addl	$0x800 >> 0x09, %eax /* plus 4 sectors */
277	call	read_sector
278	jc	disk_error
279
280no_xfs:
281	cmpw	$0xaa55, (bootsec+510)
282	jne	missing_os		/* Not a valid boot sector */
283	movw	$driveno, %sp	/* driveno == bootsec-6 */
284	popw	%dx		/* dl -> drive number */
285	popw	%di		/* es:di -> $PnP vector */
286	popw	%es
287	cli
288	jmpw	*%sp		/* %sp == bootsec */
289
290disk_error:
291	call	error
292	.ascii	"Operating system load error.\r\n"
293
294/*
295 * Print error messages.  This is invoked with "call", with the
296 * error message at the return address.
297 */
298error:
299	popw	%si
3002:
301	lodsb
302	movb	$0x0e, %ah
303	movb	(BIOS_page), %bh
304	movb	$0x07, %bl
305	int	$0x10		/* May destroy %bp */
306	cmpb	$10, %al	/* Newline? */
307	jne	2b
308
309	int	$0x18		/* Boot failure */
310die:
311	hlt
312	jmp	die
313