1/* $MirOS: src/sys/arch/i386/stand/boot/srt0.S,v 1.85 2012/09/02 22:08:48 tg Exp $ */
2
3/*-
4 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2012
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#include "stand/boot/cmd.h"
24
25	.intel_syntax noprefix
26	.text
27	.code16
28
29	/* extern */
30	.globl	real_to_prot
31	.globl	prot_to_real
32	.globl	boot
33	.globl	_edata
34	.globl	_end
35	.globl	_rtt
36#ifdef USE_PXE
37	.globl	have_pxe
38#endif
39	/* global */
40	.globl	_start
41	.globl	bios_bootpte
42	.globl	i386_userpt
43	.globl	i386_biosdev
44	.globl	i386_biosflags
45	.globl	i386_dosdev
46	.globl	pxe_bang
47	.globl	pxe_plus
48	.globl	pxecall_addr
49	.globl	lmbm_num
50	.globl	lmbm_ofs
51
52_start:	cli
53	call	Linit
54Lrval:	.p2align 2
55	. = _start + 4
56	/*	  ffffddpp	(f)lags user(p)artype (d)rive */
57Ldrvi:	.long	0x696D4F00	/* drive information and magic */
58	i386_userpt = Ldrvi
59	i386_biosdev = Ldrvi + 1
60	i386_biosflags = Ldrvi + 2
61		/*-
62		 * 01101101 = flags invalid
63		 * xxxxxx00 = nothing special about drives
64		 * xxxxxx01 = i386_dosdev valid, DOS interface
65		 * xxxxxx10 = pxelinux or invalid FOOlinux
66		 * xxxxxx11 = syslinux/extlinux/isolinux
67		 * xxxxx1xx = probably booted via PXE
68		 */
69	i386_dosdev = Ldrvi + 3
70		/*-
71		 * if PXE: 0 = do not scan, 1 = do scan
72		 */
73	.size	i386_userpt,1
74	.size	i386_biosdev,1
75	.size	i386_biosflags,1
76	.size	i386_dosdev,1
77	. = _start + 8
78pxe_bang:
79	.size	pxe_bang,4
80	.long	0		/* address of !PXE structure */
81	. = _start + 12
82pxe_plus:
83	.size	pxe_plus,4
84	.long	0		/* address of PXENV+ structure */
85	. = _start + 16
86bios_bootpte:
87	.size	bios_bootpte,16
88	.long	0, 0, 0, 0	/* 16 bytes from initial DS:SI */
89	. = _start + 32
90pxecall_addr:
91	.size	pxecall_addr,4
92	.long	0		/* PXE RM entry point (FAR pointer) */
93lmbm_num:
94	.size	lmbm_num,4
95	.long	0		/* number of Loadable MultiBoot Modules */
96lmbm_ofs:
97	.size	lmbm_ofs,4
98	.long	0		/* address of LMBM table, if loaded */
99
100#ifndef SMALL_BOOT
101	/* Multiboot header */
102	.p2align 2
103Lmbhdr:	.long	0x1BADB002		/* magic */
104	.long	0x00010000		/* flags */
105	.long	-0x1BADB002-0x00010000	/* checksum */
106	/* the addresses are all bounced to 1 MiB */
107	.long	Lmbhdr - SA_LINKADDR + 0x00100000	/* header_addr */
108	.long	0x00100000				/* load_addr */
109	.long	0					/* load_end_addr */
110	.long	0					/* bss_end_addr */
111	.long	Lmbrun - SA_LINKADDR + 0x00100000	/* entry_addr */
112#endif
113
114	/* pointer to ldbsd.com command line or 1 (DOS/COMBOOT PSP) */
115Largp:	.long	0
116
117Linit:	/* qemu sucks, int3 doesn’t work */
118	push	ds
119	push	si
120	xor	si,si
121	mov	ds,si
122	mov	cl,0xCB
123	xchg	ds:[si],cl
124	/* set a “b *0” to stop at this point */
125	lcall	0x0000,0x0000
126	xchg	ds:[si],cl
127	pop	si
128	pop	ds
129
130#ifndef SMALL_BOOT
131	mov	cx,es
132	shl	ecx,16
133	mov	cx,bx		/* ECX: PXENV+ */
134	mov	bp,sp
135	mov	edi,[bp+6]	/* EDI: !PXE */
136	rol	esi,16
137	mov	si,ds		/* ESI: SI:DS */
138#endif
139
140	xor	ebp,ebp
141	push	ebp
142	popfd
143	pop	bp
144
145	/* make cs:(e)bx = offset _start */
146	lea	ebx,[ebp + offset _start - offset Lrval]
147	/* make ebp = flat offset _start */
148	mov	bp,cs
149	shl	ebp,4
150	add	ebp,ebx
151	/* make ax = _start + 64K (approx.) */
152	mov	eax,ebp
153	shr	eax,4
154	add	ax,0x1000
155	/* set up initial stack */
156	mov	ss,ax
157	mov	esp,0x00003FEC
158
159	/* first stage: before relocation */
160
161	/* set up segment registers */
162	mov	ax,cs
163	mov	ds,ax
164	mov	es,ax
165
166#ifdef SMALL_BOOT
167	xor	eax,eax
168	mov	ah,dl
169	mov	al,ds:[ebx + offset Ldrvi - offset _start]
170	mov	ds:[ebx + offset Ldrvi - offset _start],eax
171#else
172	/* store away structure pointers */
173	mov	ds:[ebx + offset pxe_plus - offset _start],ecx
174	mov	ds:[ebx + offset pxe_bang - offset _start],edi
175	cmp	byte ptr ds:[ebx + offset i386_biosflags - offset _start],0x6D
176	je	1f
177	mov	ds,si
178	rol	esi,16
179	lea	di,[ebx + offset bios_bootpte - offset _start]
180	movsd
181	movsd
182	movsd
183	movsd
184	mov	ax,cs
185	mov	ds,ax
186	jmp	3f
1871:	xor	eax,eax
188	mov	ah,dl
189	cmp	ebp,0x00007C00
190	jne	2f
191	or	eax,0x01040000		/* PXE, do scan */
1922:	mov	al,ds:[ebx + offset Ldrvi - offset _start]
193	mov	ds:[ebx + offset Ldrvi - offset _start],eax
1943:	/* find out if we're a DOS or SYSLINUX COMBOOT programme */
195	cmp	bx,0x0100
196	jne	Lnocom
197	cmp	word ptr ds:[0],0x20CD
198	jne	Lnocom
199
200	/* store magic flag to Largp for using the command line */
201	inc	byte ptr ds:[ebx + offset Largp - offset _start]
202
203	push	ebp
204	push	ebx
205
206	xor	eax,eax
207	/* from DOS or SYSLINUX: no PXE or bootpte */
208	lea	edi,[ebx + offset pxe_bang - offset _start]
209	mov	cx,6
210	rep	stosd
211
212	mov	ah,0x30		/* get DOS version */
213	int	0x21
214	cmp	eax,0x59530000
215	jne	Lnolx
216	cmp	ecx,0x4E490000
217	jne	Lnolx
218	cmp	edx,0x58550000
219	jne	Lnolx
220	cmp	ebx,0x4C530000
221	jne	Lnolx
222
223	/* SYSLINUX */
224	mov	ax,0x0005	/* SYSLINUX: force text mode */
225	int	0x22
226	mov	ax,0x000A	/* SYSLINUX: get information */
227	int	0x22
228	push	cs
229	pop	ds
230	pop	ecx
231	cmp	al,0x31
232	jb	Linvlinux
233	je	Lsyslinux
234	cmp	al,0x34
235	ja	Linvlinux
236	je	Lextlinux
237	cmp	al,0x33
238	je	Lisolinux
239
240Lpxelinux:
241	mov	ax,es
242	shl	eax,16
243	mov	ax,bx
244	mov	ds:[ecx + offset pxe_bang - offset _start],eax
245	mov	ds:[ecx + offset pxe_plus - offset _start],eax
246	xor	edx,edx
247	mov	dh,4		/* probably PXE :) but do not scan */
248	mov	bx,3		/* clean up but retain PXE and UNDI */
249	jmp	Lislx
250
251Lsyslinux:
252Lextlinux:
253	mov	eax,es:[bx]
254	mov	ds:[ecx + offset bios_bootpte - offset _start],eax
255	mov	eax,es:[bx+4]
256	mov	ds:[ecx + offset bios_bootpte - offset _start + 4],eax
257	mov	eax,es:[bx+8]
258	mov	ds:[ecx + offset bios_bootpte - offset _start + 8],eax
259	mov	eax,es:[bx+12]
260	mov	ds:[ecx + offset bios_bootpte - offset _start + 12],eax
261Lisolinux:
262Linvlinux:
263	and	edx,0xFF
264	mov	dh,1
265	xor	bx,bx		/* clean up everything */
266Lislx:	or	dh,2
267	shl	edx,8
268	mov	dl,ds:[ecx + offset Ldrvi - offset _start]
269	mov	ds:[ecx + offset Ldrvi - offset _start],edx
270
271	mov	ax,0x000C	/* SYSLINUX: final cleanup */
272	mov	dx,bx		/* see above */
273	int	0x22
274	jmp	Liscom
275
276Lnolx:	/* DOS */
277	mov	ah,0x19
278	int	0x21
279	push	cs
280	pop	ds
281	pop	ecx
282	lea	ebx,[ecx + offset Ldrvi - offset _start]
283	mov	ah,al		/* ign ign dosdrv dosdrv */
284	cmp	al,2
285	jb	1f		/* floppy (BIOS 00h, 01h) for DOS A:, B: */
286	mov	al,0x80		/* fake BIOS 80h for DOS C:, D:, E:, ... */
2871:	shl	eax,8		/* ign dosdrv biosdrv zero */
288	inc	ax		/* 01 = flag: DOS drive valid */
289	xchg	ah,al		/* ign dosdrv flag=1 biosdrv */
290	shl	eax,8		/* dosdrv flag biosdrv zero */
291	mov	al,ds:[bx]	/* dosdrv flag biosdrv partp */
292	mov	ds:[bx],eax
293
294Liscom:	pop	ebp
295	xor	eax,eax
296	push	eax
297	popfd
298Lnocom:	/* flags are (already) okay */
299#endif
300
301	/* load source address (_start) normalised */
302	mov	ebx,ebp
303	shr	ebx,4
304	/* subtract 128 for DOS/COMBOOT PSP command line */
305	sub	bx,(128/16)
306	mov	ds,bx
307	mov	si,bp
308	and	si,0x000F
309
310	/* check if we need to relocate */
311	cmp	ebp,SA_LINKADDR
312	je	LdoRel		/* to set up the stack */
313	cmp	ebp,0x1C000
314	jbe	LdoRel		/* way below target */
315	cmp	ebp,0x50000
316	jae	LdoRel		/* somewhat above target */
317
318	/* eek, relocate twice */
319	mov	ax,0x7000
320	mov	ss,ax
321	mov	sp,0x3FFC
322	mov	ax,0x6000
323	push	ax
324	/* subtract 128 for DOS/COMBOOT PSP command line */
325	sub	ax,(128/16)
326	mov	es,ax
327	xor	di,di
328	mov	eax,offset LdoRel - offset _start
329	push	ax
330	push	es
331	push	di
332	/* min. 0xFF00 max. code size + 0x80 PSP cmdline */
333	mov	cx,0xFFF0
334	rep	movsb
335	pop	si
336	pop	ds
337	lret
338
339LdoRel:	mov	ax,0x3000
340	mov	ss,ax
341	mov	esp,0x0000FF7C
342	cmp	ebp,SA_LINKADDR
343	je	LisRel
344
345	/* subtract 128 for DOS/COMBOOT PSP command line */
346	mov	ax,(SA_LINKSEG - (128/16))
347	mov	es,ax
348	xor	di,di
349	/* same as above */
350	mov	cx,0xFFF0
351	rep	movsb
352
353	/* assumes flags=0, SS:ESP set up to 3000:0000FF7Ch */
354LisRel:	mov	ax,SA_LINKSEG
355	mov	ds,ax
356	mov	es,ax
357	push	ax
358	mov	eax,offset Lstart - offset _start
359	push	ax
360	lret
361
362	/* whew, we're relocated */
363Lstart:
364	/* check for presence of command line */
365	mov	esi,offset Largp - offset _start
366	mov	eax,[si]
367	or	eax,eax
368	jz	Lnocmd
369#ifdef BOOTSELECT_HOOK
370	xor	ebx,ebx
371#endif
372	cmp	eax,1		/* magic */
373	jne	Liscmd
374	/* we have a DOS/COMBOOT PSP, analyse it */
375	mov	ax,ds
376	push	ax
377	sub	ax,(128/16)
378	mov	ds,ax
379	xor	cx,cx
380	mov	cl,byte ptr ds:[0]
381	mov	si,1
3821:	or	cx,cx
383	jz	Lpspno
384	lodsb
385	cmp	al,0x09		/* tab */
386	je	2f
387	cmp	al,0x0D		/* CR */
388	je	2f
389	cmp	al,0x20		/* space */
390	jne	3f
3912:	dec	cx
392	jmp	1b
3933:	dec	si
394	mov	di,si
395	add	di,cx
396	dec	di
3974:	mov	al,[di]
398	cmp	al,0x09		/* tab */
399	je	5f
400	cmp	al,0x0D		/* CR */
401	je	5f
402	cmp	al,0x20		/* space */
403	jne	6f
4045:	dec	cx
405	jz	Lpspno
406	dec	di
407	jmp	4b
4086:	inc	di
409	xor	eax,eax
410	mov	[di],al		/* convert to NUL-terminated */
411	/* DS:SI now points to NUL-terminated argv (SI < 0x80) */
412	mov	ax,ds
413	shl	eax,4
414	add	ax,si
415	pop	ds
416	/* store flat pointer to NUL-terminated argv into Largp */
417	mov	esi,offset Largp - offset _start
418	mov	[si],eax
419#ifdef BOOTSELECT_HOOK
420	xor	ebx,ebx
421#endif
422	jmp	Liscmd
423
424Lpspno:	pop	ds
425	xor	eax,eax
426	mov	esi,offset Largp - offset _start
427	mov	[si],eax
428Lnocmd:	/* no command line found */
429#ifdef BOOTSELECT_HOOK
430	sti
431	.globl	Lhook
432	call	Lhook
433	mov	ebx,eax
434#endif
435Liscmd:	/* a command line was found */
436
437	call	real_to_prot
438Lsta32:	.code32
439	/* ensure stack is 32-bit aligned */
440	mov	esp,0x0003FF7C
441	/* zero out .bss section */
442	xor	eax,eax
443	push	eax
444	popfd
445	mov	ecx,offset _end
446	mov	edi,offset _edata
447	sub	ecx,edi
448	rep	stosb
449
450	/* zero out stack segment lower parts */
451	mov	edi,offset ssbss_beg
452	mov	ecx,offset ssbss_end - offset ssbss_beg
453	rep	stosb
454
455#ifdef BOOTSELECT_HOOK
456	/* store user choice */
457	mov	[hook_value],ebx
458#endif
459
460	/* store command line, if any */
461	mov	esi,[Largp]
462	or	esi,esi
463	jz	1f
464	mov	ecx,(CMD_BUFF_SIZE - 1)
465	mov	edi,offset cmd_buf
466	rep	movsb
467	/* NUL-terminate potentially too long multiboot cmdline */
468	mov	al,0
469	stosb
4701:
471
472	movzx	eax,byte ptr [i386_biosdev]
473	push	eax
474	call	boot
475	jmp	_rtt
476
477#ifndef SMALL_BOOT
478	/* Multiboot entry point */
479	.p2align 4,0x90
480Lmbrun:	mov	esp,0x0003FF7C
481	xor	eax,eax
482	push	eax
483	popfd
484
485	/* set a “b *0” to stop at this point */
486	push	[eax]
487	mov	byte ptr [eax],0xC3
488	call	eax
489	pop	[eax]
490
491	mov	esi,0x00100000
492	mov	edi,SA_LINKADDR
493	mov	ecx,0x4000
494	rep	movsd
495	mov	eax,offset Lmb2rm
496	jmp	eax
497	/* relocated */
498Lmb2rm:	mov	edi,offset Ldrvi
499	mov	edx,[ebx+12]		/* boot_device */
500	shr	edx,16
501	mov	eax,edx
502	mov	al,[edi]	/* user partition type */
503	stosd			/* drive information */
504	xor	eax,eax
505	stosd			/* pxe_bang */
506	stosd			/* pxe_plus */
507	stosd			/* partition */
508	stosd			/* partition */
509	stosd			/* partition */
510	stosd			/* partition */
511#ifdef USE_PXE
512	mov	[have_pxe],eax	/* do not even try */
513#endif
514
515	mov	eax,[ebx]
516	and	eax,(1 << 2)	/* do we have command line? */
517	jz	1f		/* nope */
518	mov	esi,[ebx+16]	/* pointer */
519	/*
520	 * annoyingly enough, the first word on the command line
521	 * is our own pathname, so we must skip it
522	 */
5234:	lodsb
524	or	al,al		/* NUL */
525	jz	1f
526	cmp	al,0x09		/* tab */
527	je	2f
528	cmp	al,0x0A		/* LF */
529	je	2f
530	cmp	al,0x0D		/* CR */
531	je	2f
532	cmp	al,0x20		/* space */
533	jne	4b
5342:	lodsb
535	or	al,al		/* NUL */
536	jz	1f
537	cmp	al,0x09		/* tab */
538	je	2b
539	cmp	al,0x0A		/* LF */
540	je	2b
541	cmp	al,0x0D		/* CR */
542	je	2b
543	cmp	al,0x20		/* space */
544	je	2b
545	dec	esi
546	mov	edi,esi
547	mov	ecx,CMD_BUFF_SIZE
548	xor	eax,eax
549	repne	scasb
5503:	dec	edi
551	mov	al,[edi]
552	cmp	al,0x09		/* tab */
553	je	3b
554	cmp	al,0x0A		/* LF */
555	je	3b
556	cmp	al,0x0D		/* CR */
557	je	3b
558	cmp	al,0x20		/* space */
559	je	3b
560
561	/* got a non-empty command line */
562	mov	byte ptr [edi+1],0
563	mov	[Largp],esi
564
565	/* got no command line */
5661:	mov	eax,[ebx]
567	and	eax,(1 << 3)	/* do we have modules? */
568	jz	Lnombm		/* nope */
569	mov	eax,[ebx+20]	/* how many? */
570	mov	[lmbm_num],eax
571	mov	eax,[ebx+24]	/* module information */
572	mov	[lmbm_ofs],eax
573Lnombm:
574
575	lgdt	Gdtr
576	call	prot_to_real	/* converts stack */
577	.code16
578	jmp	LisRel
579#endif
580
581#ifdef USE_PXE
582	.code32
583	.globl	pxecall_bang
584pxecall_bang:
585	push	ebp
586	mov	ebp,esp
587	pushfd
588	push	ebx
589	push	esi
590	push	edi
591
592	mov	ebx,[ebp+8]
593	mov	edi,offset pxe_command_buf - offset bounce_buf
594
595	call	prot_to_real
596	.code16
597	sti
598
599	push	ss
600	push	di
601	push	bx
602	lcall	ds:[32]		/* pxecall_addr */
603	add	sp,6
604	movzx	ebx,ax
605
606	call	real_to_prot
607	.code32
608
609	mov	eax,ebx
610	pop	edi
611	pop	esi
612	pop	ebx
613	popfd
614	pop	ebp
615	ret
616
617	.globl	pxecall_plus
618pxecall_plus:
619	push	ebp
620	mov	ebp,esp
621	pushfd
622	push	ebx
623	push	esi
624	push	edi
625
626	mov	ebx,[ebp+8]
627	mov	edi,offset pxe_command_buf - offset bounce_buf
628
629	call	prot_to_real
630	.code16
631	sti
632
633	push	ss
634	pop	es
635	lcall	ds:[32]		/* pxecall_addr */
636	movzx	ebx,ax
637
638	call	real_to_prot
639	.code32
640
641	mov	eax,ebx
642	pop	edi
643	pop	esi
644	pop	ebx
645	popfd
646	pop	ebp
647	ret
648#endif
649
650
651	bounce_buf = 0x30000
652	.globl	bounce_buf
653	.size	bounce_buf, 4096
654
655	crc_table = bounce_buf + 4096
656	.globl	crc_table
657	.size	crc_table, 1024
658
659	pxe_command_buf = crc_table + 1024
660	.globl	pxe_command_buf
661	.size	pxe_command_buf, 256
662
663	sa_fixed_table = pxe_command_buf + 256
664	.globl	sa_fixed_table
665	.size	sa_fixed_table, 2176
666
667	biosdev_lba_buf = sa_fixed_table + 2176
668	.globl	biosdev_lba_buf
669	.size	biosdev_lba_buf, 16
670
671	cmd_buf = biosdev_lba_buf + 16
672	.globl	cmd_buf
673	.size	cmd_buf, CMD_BUFF_SIZE
674
675	/* last; size unknown (<0x1000) */
676	cmd = cmd_buf + CMD_BUFF_SIZE
677	.globl	cmd
678	.size	cmd, CMD_STRUCT_SIZE
679
680	ssbss_beg = bounce_buf
681	ssbss_end = cmd + 0x0900
682