1/* NOTE: this boot sector contains instructions that need at least an 80186.
2 * Yes, as86 has a bug somewhere in the valid instruction set checks.
3 *
4 */
5
6/*	floppyload.S Copyright (C) 1991, 1992 Linus Torvalds
7 *	modified by Drew Eckhardt
8 *	modified by Bruce Evans (bde)
9 *
10 * floppyprefix.S is loaded at 0x0000:0x7c00 by the bios-startup routines.
11 *
12 * It then loads the system at SYSSEG<<4, using BIOS interrupts.
13 *
14 * The loader has been made as simple as possible, and continuous read errors
15 * will result in a unbreakable loop. Reboot by hand. It loads pretty fast by
16 * getting whole tracks at a time whenever possible.
17 */
18
19FILE_LICENCE ( GPL2_ONLY )
20
21.equ	BOOTSEG, 0x07C0			/* original address of boot-sector */
22
23.equ	SYSSEG, 0x1000			/* system loaded at SYSSEG<<4 */
24
25	.org	0
26	.arch i386
27	.text
28	.section ".prefix", "ax", @progbits
29	.code16
30
31	jmp	$BOOTSEG, $go		/* reload cs:ip to match relocation addr */
32go:
33	movw	$0x2000-12, %di		/* 0x2000 is arbitrary value >= length */
34					/* of bootsect + room for stack + 12 for */
35					/* saved disk parm block */
36
37	movw	$BOOTSEG, %ax
38	movw	%ax,%ds
39	movw	%ax,%es
40	movw	%ax,%ss			/* put stack at BOOTSEG:0x4000-12. */
41	movw	%di,%sp
42
43/* Many BIOS's default disk parameter tables will not recognize multi-sector
44 * reads beyond the maximum sector number specified in the default diskette
45 * parameter tables - this may mean 7 sectors in some cases.
46 *
47 * Since single sector reads are slow and out of the question, we must take care
48 * of this by creating new parameter tables (for the first disk) in RAM.  We
49 * will set the maximum sector count to 36 - the most we will encounter on an
50 * ED 2.88.  High doesn't hurt.	Low does.
51 *
52 * Segments are as follows: ds=es=ss=cs - BOOTSEG
53 */
54
55	xorw	%cx,%cx
56	movw	%cx,%es			/* access segment 0 */
57	movw	$0x78, %bx		/* 0:bx is parameter table address */
58	pushw	%ds			/* save ds */
59/* 0:bx is parameter table address */
60	ldsw	%es:(%bx),%si		/* loads ds and si */
61
62	movw	%ax,%es			/* ax is BOOTSECT (loaded above) */
63	movb	$6, %cl			/* copy 12 bytes */
64	cld
65	pushw	%di			/* keep a copy for later */
66	rep
67	movsw				/* ds:si is source, es:di is dest */
68	popw	%di
69
70	movb	$36,%es:4(%di)
71
72	movw	%cx,%ds			/* access segment 0 */
73	xchgw	%di,(%bx)
74	movw	%es,%si
75	xchgw	%si,2(%bx)
76	popw	%ds			/* restore ds */
77	movw	%di, dpoff		/* save old parameters */
78	movw	%si, dpseg		/* to restore just before finishing */
79	pushw	%ds
80	popw	%es			/* reload es */
81
82/* Note that es is already set up.  Also cx is 0 from rep movsw above. */
83
84	xorb	%ah,%ah			/* reset FDC */
85	xorb	%dl,%dl
86	int	$0x13
87
88/* Get disk drive parameters, specifically number of sectors/track.
89 *
90 * It seems that there is no BIOS call to get the number of sectors.  Guess
91 * 36 sectors if sector 36 can be read, 18 sectors if sector 18 can be read,
92 * 15 if sector 15 can be read.	Otherwise guess 9.
93 */
94
95	movw	$disksizes, %si		/* table of sizes to try */
96
97probe_loop:
98	lodsb
99	cbtw				/* extend to word */
100	movw	%ax, sectors
101	cmpw	$disksizes+4, %si
102	jae	got_sectors		/* if all else fails, try 9 */
103	xchgw	%cx,%ax			/* cx = track and sector */
104	xorw	%dx,%dx			/* drive 0, head 0 */
105	movw	$0x0200, %bx		/* address after boot sector */
106					/*   (512 bytes from origin, es = cs) */
107	movw	$0x0201, %ax		/* service 2, 1 sector */
108	int	$0x13
109	jc	probe_loop		/* try next value */
110
111got_sectors:
112	movw	$msg1end-msg1, %cx
113	movw	$msg1, %si
114	call	print_str
115
116/* ok, we've written the Loading... message, now we want to load the system */
117
118	movw	$SYSSEG, %ax
119	movw	%ax,%es			/* segment of SYSSEG<<4 */
120	pushw	%es
121	call	read_it
122
123/* This turns off the floppy drive motor, so that we enter the kernel in a
124 * known state, and don't have to worry about it later.
125 */
126	movw	$0x3f2, %dx
127	xorb	%al,%al
128	outb	%al,%dx
129
130	call	print_nl
131	pop	%es			/* = SYSSEG */
132
133/* Restore original disk parameters */
134	movw	$0x78, %bx
135	movw	dpoff, %di
136	movw	dpseg, %si
137	xorw	%ax,%ax
138	movw	%ax,%ds
139	movw	%di,(%bx)
140	movw	%si,2(%bx)
141
142	/* Everything now loaded.  %es = SYSSEG, so %es:0000 points to
143	 * start of loaded image.
144	 */
145
146	/* Jump to loaded copy */
147	ljmp	$SYSSEG, $start_runtime
148
149endseg:	.word SYSSEG
150	.section ".zinfo.fixup", "a", @progbits	/* Compressor fixups */
151	.ascii	"ADDW"
152	.long	endseg
153	.long	16
154	.long	0
155	.previous
156
157/* This routine loads the system at address SYSSEG<<4, making sure no 64kB
158 * boundaries are crossed. We try to load it as fast as possible, loading whole
159 * tracks whenever we can.
160 *
161 * in:	es - starting address segment (normally SYSSEG)
162 */
163read_it:
164	movw	$0,sread		/* load whole image including prefix */
165	movw	%es,%ax
166	testw	$0x0fff, %ax
167die:	jne	die			/* es must be at 64kB boundary */
168	xorw	%bx,%bx			/* bx is starting address within segment */
169rp_read:
170	movw	%es,%ax
171	movw	%bx,%dx
172	movb	$4, %cl
173	shrw	%cl,%dx			/* bx is always divisible by 16 */
174	addw	%dx,%ax
175	cmpw	endseg, %ax	/* have we loaded all yet? */
176	jb	ok1_read
177	ret
178ok1_read:
179	movw	sectors, %ax
180	subw	sread, %ax
181	movw	%ax,%cx
182	shlw	$9, %cx
183	addw	%bx,%cx
184	jnc	ok2_read
185	je	ok2_read
186	xorw	%ax,%ax
187	subw	%bx,%ax
188	shrw	$9, %ax
189ok2_read:
190	call	read_track
191	movw	%ax,%cx
192	addw	sread, %ax
193	cmpw	sectors, %ax
194	jne	ok3_read
195	movw	$1, %ax
196	subw	head, %ax
197	jne	ok4_read
198	incw	track
199ok4_read:
200	movw	%ax, head
201	xorw	%ax,%ax
202ok3_read:
203	movw	%ax, sread
204	shlw	$9, %cx
205	addw	%cx,%bx
206	jnc	rp_read
207	movw	%es,%ax
208	addb	$0x10, %ah
209	movw	%ax,%es
210	xorw	%bx,%bx
211	jmp	rp_read
212
213read_track:
214	pusha
215	pushw	%ax
216	pushw	%bx
217	pushw	%bp			/* just in case the BIOS is buggy */
218	movw	$0x0e2e, %ax		/* 0x2e = . */
219	movw	$0x0007, %bx
220	int	$0x10
221	popw	%bp
222	popw	%bx
223	popw	%ax
224
225	movw	track, %dx
226	movw	sread, %cx
227	incw	%cx
228	movb	%dl,%ch
229	movw	head, %dx
230	movb	%dl,%dh
231	andw	$0x0100, %dx
232	movb	$2, %ah
233
234	pushw	%dx			/* save for error dump */
235	pushw	%cx
236	pushw	%bx
237	pushw	%ax
238
239	int	$0x13
240	jc	bad_rt
241	addw	$8, %sp
242	popa
243	ret
244
245bad_rt: pushw	%ax			/* save error code */
246	call	print_all		/* ah = error, al = read */
247
248	xorb	%ah,%ah
249	xorb	%dl,%dl
250	int	$0x13
251
252	addw	$10, %sp
253	popa
254	jmp	read_track
255
256/* print_all is for debugging purposes.	It will print out all of the registers.
257 * The assumption is that this is called from a routine, with a stack frame like
258 *	dx
259 *	cx
260 *	bx
261 *	ax
262 *	error
263 *	ret <- sp
264 */
265
266print_all:
267	call	print_nl		/* nl for readability */
268	movw	$5, %cx			/* error code + 4 registers */
269	movw	%sp,%bp
270
271print_loop:
272	pushw	%cx			/* save count left */
273
274	cmpb	$5, %cl
275	jae	no_reg			/* see if register name is needed */
276
277	movw	$0x0007, %bx		/* page 0, attribute 7 (normal) */
278	movw	$0xe05+0x41-1, %ax
279	subb	%cl,%al
280	int	$0x10
281
282	movb	$0x58, %al		/* 'X' */
283	int	$0x10
284
285	movb	$0x3A, %al		/* ':' */
286	int	$0x10
287
288no_reg:
289	addw	$2, %bp			/* next register */
290	call	print_hex		/* print it */
291	movb	$0x20, %al		/* print a space */
292	int	$0x10
293	popw	%cx
294	loop	print_loop
295	call	print_nl		/* nl for readability */
296	ret
297
298print_str:
299	movw	$0x0007, %bx		/* page 0, attribute 7 (normal) */
300	movb	$0x0e, %ah		/* write char, tty mode */
301prloop:
302	lodsb
303	int	$0x10
304	loop	prloop
305	ret
306
307print_nl:
308	movw	$0x0007, %bx		/* page 0, attribute 7 (normal) */
309	movw	$0xe0d, %ax		/* CR */
310	int	$0x10
311	movb	$0xa, %al		/* LF */
312	int	$0x10
313	ret
314
315/* print_hex prints the word pointed to by ss:bp in hexadecimal. */
316
317print_hex:
318	movw	(%bp),%dx		/* load word into dx */
319	movb	$4, %cl
320	movb	$0x0e, %ah		/* write char, tty mode */
321	movw	$0x0007, %bx		/* page 0, attribute 7 (normal) */
322	call	print_digit
323	call	print_digit
324	call	print_digit
325/* fall through */
326print_digit:
327	rol	%cl,%dx			/* rotate so that lowest 4 bits are used */
328	movb	$0x0f, %al		/* mask for nybble */
329	andb	%dl,%al
330	addb	$0x90, %al		/* convert al to ascii hex (four instructions) */
331	daa
332	adcb	$0x40, %al
333	daa
334	int	$0x10
335	ret
336
337sread:	.word 0				/* sectors read of current track */
338head:	.word 0				/* current head */
339track:	.word 0				/* current track */
340
341sectors:
342	.word 0
343
344dpseg:	.word 0
345dpoff:	.word 0
346
347disksizes:
348	.byte 36,18,15,9
349
350msg1:
351	.ascii "Loading ROM image"
352msg1end:
353
354	.org 510, 0
355	.word 0xAA55
356
357start_runtime:
358	/* Install gPXE */
359	call	install
360
361	/* Set up real-mode stack */
362	movw	%bx, %ss
363	movw	$_estack16, %sp
364
365	/* Jump to .text16 segment */
366	pushw	%ax
367	pushw	$1f
368	lret
369	.section ".text16", "awx", @progbits
3701:
371	pushl	$main
372	pushw	%cs
373	call	prot_call
374	popl	%ecx /* discard */
375
376	/* Uninstall gPXE */
377	call	uninstall
378
379	/* Boot next device */
380	int $0x18
381
382