1/* $MirOS: src/sys/arch/i386/stand/bootxx/bootxx.S,v 1.26 2010/12/01 19:56:58 tg Exp $ */
2
3/*-
4 * Copyright (c) 2009
5 *	Thorsten Glaser <tg@mirbsd.org>
6 *
7 * Provided that these terms and disclaimer and all copyright notices
8 * are retained or reproduced in an accompanying document, permission
9 * is granted to deal in this work without restriction, including un-
10 * limited rights to use, publicly perform, distribute, sell, modify,
11 * merge, give away, or sublicence.
12 *
13 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
14 * the utmost extent permitted by applicable law, neither express nor
15 * implied; without malicious intent or gross negligence. In no event
16 * may a licensor, author or contributor be held liable for indirect,
17 * direct, other damage, loss, or other issues arising in any way out
18 * of dealing in the work, even if advised of the possibility of such
19 * damage or existence of a defect, except proven that it results out
20 * of said person's immediate fault when using the work as intended.
21 */
22
23	.intel_syntax noprefix
24	.section .comment
25	.ascii	"$MirOS: src/sys/arch/i386/stand/bootxx/bootxx.S,v 1.26 2010/12/01 19:56:58 tg Exp $"
26#if defined(BOOT_GRUB)
27	.ascii	" +t:GRUB"
28#elif defined(BOOT_ISOLINUX)
29	.ascii	" +t:ILNX"
30#else
31	.ascii	" +t:MBSD"
32#endif
33#ifdef BPB_SPACE
34	.ascii	" +s:BPB"
35#else
36	.ascii	" +s:MBR"
37#endif
38	.byte	0
39
40	.code16
41	.text
42
43	.globl	_start
44	.type	_start,@function
45_start:	/* bootxx entry point 07C00 */
46#ifdef BPB_SPACE
47	jmp	Linit
48	nop
49	. = _start + 3
50L_BPB:	.ascii	"MirOSBSD"
51	. = L_BPB + 0x3F
52	.byte	0x29
53	. = L_BPB + 0x44
54	.ascii	"MirOSBSDldr"
55	.ascii	"FATnn   "
56#endif
57Linit:	xor	ecx,ecx
58	mov	ss,cx
59	mov	sp,0x7BFC
60	push	ecx
61	popfd
62#ifdef BOOT_ISOLINUX
63	push	es		/* $PnP header */
64	push	di
65#endif
66	mov	es,cx
67#if defined(BOOT_GRUB) || defined(BOOT_ISOLINUX)
68	mov	si,0x7C00	/* 0000:_start pre-reloc */
69#else
70	mov	di,0x7C00	/* DS:SI is set by BIOS! */
71	push	di
72	/* save partition table entry */
73	mov	cl,16
74	rep	movsb
75	pop	si
76#endif
77	mov	ds,cx
78	mov	bx,SA_LINKSEG
79	push	bx
80	push	bx
81	mov	es,bx
82	mov	di,offset _start
83	mov	ch,2		/* 512 bytes */
84	rep	movsb
85	pop	ds
86	push	offset Lmain
87	lret
88
89	Lpblk = _start + 0x10
90	Lebss = Lpblk + 0x18	/* make sure this is < Lmmsg */
91
92Lemsg:	.ascii	__BOOT_VER
93	.asciz	" Loading "
94#ifndef BOOT_GRUB
95Lmmsg:	.ascii	"bad magic"
96#endif
97Lfmsg:	.ascii	" error"
98Lbmsg:	.asciz	"\r\n"
99
100	.globl	bkcnt
101	.type	bkcnt,@object
102	.size	bkcnt,1
103bkcnt:	.byte	(bkend - bktbl)
104
105	.globl	geomh
106	.type	geomh,@object
107	.size	geomh,2
108geomh:	.word	2		/* 1..256 = tracks per cylinder */
109
110	.globl	geoms
111	.type	geoms,@object
112	.size	geoms,2
113	/* set to 0 for auto geometry */
114geoms:	.word	18		/* 1..63 = sectors per track */
115
116	.globl	partp
117	.type	partp,@object
118	.size	partp,1
119partp:	.byte	0
120Ldrv:	.byte	0x80		/* must be at partp + 1 */
121
122Lload:	.word	LsLBA
123
124#ifdef BOOT_ISOLINUX
125Lpnp:	.long	0
126#endif
127
128	/* output NUL-terminated string from ds:si */
129Lotxt0:	mov	ah,0x0E
130	mov	bx,7
131	int	0x10
132Lotxt:	lodsb
133	or	al,al
134	jnz	Lotxt0
135	ret
136
137Lbarf:	call	Lotxt
138	mov	ax,offset LsCHS
139	xchg	ax,[Lload]
140	cmp	ax,offset LsCHS
141	jne	Lretr
142	xor	ax,ax
143	int	0x16
144	jmp	0xF000,0xFFF0
145
146Lmain:	sti
147	mov	[Ldrv],dl
148#ifdef BOOT_ISOLINUX
149	pop	eax		/* $PnP header */
150	mov	[Lpnp],eax
151#endif
152	mov	si,offset Lemsg
153	cmp	dl,0x80
154	jb	Lbarf		/* floppy: only try CHS */
155	call	Lotxt
156	/* FALLTHROUGH */
157
158Lretr:	xor	bx,bx		/* load offset (begin) */
159	mov	si,offset bktbl
160	movzx	bp,byte ptr [bkcnt]
161Lloop:	lodsb
162	/*-
163	 * AL contains bitmasked:  aaabbbbb
164	 * -> a = (number of bytes - 1) that follow	(-> CX)
165	 * -> b = (number of sectors - 1) to load	(-> DI)
166	 * BX contains current load address offset
167	 * SI contains pointer to data being processed
168	 * BP contains number of entries to process
169	 */
170
171	/* decode */
172	movzx	cx,al
173	shr	cl,5
174	and	ax,0x1F
175	inc	ax
176	inc	cx
177	push	ax		/* later DI */
178
179	/* create LBA parameter block */
180	mov	di,offset Lpblk
181	mov	ax,0x0010
182	stosw
183	mov	al,1
184	stosw
185	mov	ax,bx
186	stosw
187	mov	ax,cs
188	stosw
189	rep	movsb		/* copy CX bytes */
190	xor	ax,ax		/* pad with 8 (up to 7 needed) NUL bytes */
191	stosw
192	stosw
193	stosw
194	stosw
195
196	pop	di
197	/*-
198	 * Lpblk, Ldrv are filled in completely
199	 * CS = DS = ES = SA_LINKSEG = 0x4000
200	 * DI = number of sectors to still load
201	 * BX = load offset (segment CS)
202	 * BP = number of entries to load, including this one
203	 * SI = pointer to next entry to load
204	 */
205	push	bp
206	push	si
207Lld1s:	mov	si,offset Lpblk
208	call	[Lload]
209	mov	bp,4		/* number of retries */
210	mov	dl,[Ldrv]
211Lldlp:	pusha
212	stc
213	int	0x13
214	pushf
215	mov	ax,0x0E2E	/* "dot" as progress meter */
216	mov	bx,7
217	int	0x10
218	popf
219	sti
220	popa
221	jnc	Lldok
222	dec	bp
223	pusha
224	pushf
225	xor	ax,ax		/* reset drive */
226	int	0x13
227	popf
228	mov	si,offset Lfmsg
229	jz	Lbarf
230	mov	ax,0x0E30	/* number as fail meter */
231	add	ax,bp
232	mov	bx,7
233	int	0x10
234	popa
235	jmp	Lldlp
236Lldok:	/* increment load address by sector size */
237	mov	ah,2		/* 512 bytes */
238	.globl	secsz
239	.type	secsz,@object
240	.size	secsz,1
241	secsz = . - 1		/* the "2" above */
242L...1:	add	bh,ah		/* add the 512 bytes */
243	mov	4[si],bx
244	add	ah,bh		/* again */
245	jc	Ldone		/* last block completes (64 KiB - bootxx) */
246	/* increment quadword by one */
247	add	dword ptr 8[si],1
248	adc	dword ptr 12[si],0
249	/* load next sector */
250	dec	di
251	jnz	Lld1s
252	pop	si
253	pop	bp
254	dec	bp
255	jnz	Lloop
256	/* FALLTHROUGH */
257
258Ldone:	/* check bootloader magic */
259#if defined(BOOT_GRUB)
260	/* no magic */
261#elif defined(BOOT_ISOLINUX)
262	xor	ecx,ecx
263	push	ecx		/* partition offset (qword) */
264	push	ecx		/* partition offset (qword) */
265	mov	eax,[Lpnp]
266	push	eax		/* ES, DI */
267	mov	ax,[Ldrv]
268	push	ax		/* DX */
269	mov	ax,[geomh]
270	push	ax		/* # of heads */
271	mov	ax,[geoms]
272	push	ax		/* # of sectors per track */
273	xor	bx,bx
274	mov	ax,[Lload]
275	cmp	ax,offset LsCHS	/* so-called CBIOS */
276	je	Lilx1
277	inc	bx		/* so-called EBIOS */
278Lilx1:	push	bx		/* ebios flag */
279	push	cx		/* target CS */
280	mov	eax,ds:[0x40]	/* location of magic in ISOLINUX.BIN */
281	mov	si,offset Lmmsg
282	cmp	eax,0x7078C0FB	/* magic */
283	jne	Lbarf
284#else
285	xor	eax,eax
286	push	ax		/* return address */
287	mov	ax,[partp]
288	xchg	eax,ds:[4]	/* location of magic in /boot */
289	cmp	eax,0x696D4F00
290	mov	si,offset Lmmsg
291	jne	Lbarf
292#endif
293	mov	si,offset Lbmsg
294	call	Lotxt
295#if defined(BOOT_GRUB) || defined(BOOT_ISOLINUX)
296#if defined(BOOT_GRUB)
297	xor	edx,edx
298	push	dx		/* initial CS */
299	dec	edx		/* EDX := FFFFFFFF */
300	mov	dl,[Ldrv]	/* boot BIOS drive number */
301	mov	ax,0x8200
302#elif defined(BOOT_ISOLINUX)
303	mov	ax,0x7C44
304#endif
305	push	ax		/* initial IP */
306	cli			/* be nice to a GNU every once in a while */
307	lret
308#else
309	mov	si,offset _start
310	ret
311#endif
312
313	/* obtain drive geometry */
314Lgeom:	pusha
315	push	es
316#if 0
317	/*
318	 * According to RBIL, this is needed to guard against
319	 * BIOS bugs, but we don’t read out the table anyway.
320	 */
321	xor	di,di
322	mov	es,di
323#endif
324	mov	ah,8
325	mov	dl,[Ldrv]
326	stc
327	int	0x13
328	sti
329	pop	es
330	mov	si,offset Lfmsg
331	jc	Lbarf
332	/* process returned values */
333	and	cl,0x3F		/* number of sectors per track */
334	mov	[geoms],cl	/* high byte is 0 anyway */
335	movzx	ax,dh
336	inc	ax		/* maximum index -> number of heads */
337	mov	[geomh],ax
338	popa
339	/* FALLTHROUGH */
340
341LsCHS:	mov	cx,[geoms]
342	jcxz	Lgeom
343	mov	ax,8[si]
344	mov	dx,10[si]
345	div	cx
346	inc	dx		/* remainder: sector */
347	xor	cx,cx
348	xchg	cx,dx
349	div	word ptr [geomh]
350	shl	ah,6		/* quotient: cylinder */
351	xchg	ah,al
352	or	cx,ax
353	mov	dh,dl		/* remainder: head */
354	mov	ax,0x0201
355	ret
356
357LsLBA:	mov	ah,0x42
358	ret
359
360	.globl	bktbl
361	.type	bktbl,@object
362bktbl:	.long	0xCAFEBABE
363
364#ifndef BPB_SPACE
365	/* ensure we have space for a partition table */
366	. = _start + 0x01BE
367	.byte	0
368#endif
369
370	/* bxinst requires bktbl to end at 510 */
371	. = _start + 0x01FE
372	/* bkend is required by installboot(8) still */
373	.globl	bkend
374	.type	bkend,@object
375bkend:	.word	0xAA55
376