boot0.S revision 128723
1/*
2 * Copyright (c) 2002 Bruce M. Simpson
3 * Copyright (c) 1998 Robert Nordier
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms are freely
7 * permitted provided that the above copyright notice and this
8 * paragraph and the following disclaimer are duplicated in all
9 * such forms.
10 *
11 * This software is provided "AS IS" and without any express or
12 * implied warranties, including, without limitation, the implied
13 * warranties of merchantability and fitness for a particular
14 * purpose.
15 *
16 * $FreeBSD: head/sys/boot/i386/boot0/boot0.S 128723 2004-04-28 20:55:01Z ru $
17 */
18
19/* A 512-byte boot manager. */
20#ifdef SIO
21/* ... using a serial console on COM1. */
22#endif /* SIO */
23
24		.set NHRDRV,0x475		// Number of hard drives
25		.set ORIGIN,0x600		// Execution address
26		.set FAKE,0x800 		// Partition entry
27		.set LOAD,0x7c00		// Load address
28
29		.set PRT_OFF,0x1be		// Partition table
30
31		.set TBL0SZ,0x3 		// Table 0 size
32		.set TBL1SZ,0xb 		// Table 1 size
33
34		.set MAGIC,0xaa55		// Magic: bootable
35		.set B0MAGIC,0xbb66		// Identification
36
37		.set KEY_ENTER,0x1c		// Enter key scan code
38		.set KEY_F1,0x3b		// F1 key scan code
39		.set KEY_1,0x02			// #1 key scan code
40
41		.set ASCII_BEL,0x07		// ASCII code for <BEL>
42		.set ASCII_CR,0x0D		// ASCII code for <CR>
43
44/*
45 * Addresses in the sector of embedded data values.
46 * Accessed with negative offsets from the end of the relocated sector (%ebp).
47 */
48		.set _NXTDRV,-0x48		// Next drive
49		.set _OPT,-0x47 		// Default option
50		.set _SETDRV,-0x46		// Drive to force
51		.set _FLAGS,-0x45		// Flags
52		.set _TICKS,-0x44		// Timeout ticks
53		.set _FAKE,0x0			// Fake partition entry
54		.set _MNUOPT,0xc		// Menu options
55
56		.globl start			// Entry point
57		.code16				// This runs in real mode
58
59/*
60 * Initialise segments and registers to known values.
61 * segments start at 0.
62 * The stack is immediately below the address we were loaded to.
63 */
64start:		cld				// String ops inc
65		xorw %ax,%ax			// Zero
66		movw %ax,%es			// Address
67		movw %ax,%ds			//  data
68		movw %ax,%ss			// Set up
69		movw $LOAD,%sp			//  stack
70
71/*
72 * Copy this code to the address it was linked for
73 */
74		movw %sp,%si			// Source
75		movw $start,%di			// Destination
76		movw $0x100,%cx			// Word count
77		rep				// Relocate
78		movsw				//  code
79/*
80 * Set address for variable space beyond code, and clear it.
81 * Notice that this is also used to point to the values embedded in the block,
82 * by using negative offsets.
83 */
84		movw %di,%bp			// Address variables
85		movb $0x8,%cl			// Words to clear
86		rep				// Zero
87		stosw				//  them
88/*
89 * Relocate to the new copy of the code.
90 */
91		incb -0xe(%di)			// Sector number
92		jmp main-LOAD+ORIGIN		// To relocated code
93
94main:
95#ifdef SIO
96/*
97 * Initialize the serial port.
98 * Must save DX (contains drive number)
99 */
100		pushw %dx			// Save
101		xorw %dx,%dx			// Port: COM1
102		movb COMSPEED,%al		// defined by Makefile
103		movb $0x00,%ah			// BIOS: Set COM Port
104		int $0x14			//  Parameters
105		popw %dx			// Restore
106#endif /* SIO */
107/*
108 * Check what flags were loaded with us, specifically, Use a predefined Drive.
109 * If what the bios gives us is bad, use the '0' in the block instead, as well.
110 */
111		testb $0x20,_FLAGS(%bp)		// Set number drive?
112		jnz main.1			// Yes
113		testb %dl,%dl			// Drive number valid?
114		js main.2			// Possibly (0x80 set)
115main.1:		movb _SETDRV(%bp),%dl		// Drive number to use
116/*
117 * Whatever we decided to use, now store it into the fake
118 * partition entry that lives in the data space above us.
119 */
120main.2:		movb %dl,_FAKE(%bp)		// Save drive number
121		callw putn			// To new line
122		pushw %dx			// Save drive number
123/*
124 * Start out with a pointer to the 4th byte of the first table entry
125 * so that after 4 iterations it's beyond the end of the sector.
126 * and beyond a 256 byte boundary and has overflowed 8 bits (see next comment).
127 * (remember that the table starts 2 bytes earlier than you would expect
128 * as the bootable flag is after it in the block)
129 */
130		movw $(partbl+0x4),%bx		// Partition table (+4)
131		xorw %dx,%dx			// Item number
132/*
133 * Loop around on the partition table, printing values until we
134 * pass a 256 byte boundary. The end of loop test is at main.5.
135 */
136main.3:		movb %ch,-0x4(%bx)		// Zero active flag (ch == 0)
137		btw %dx,_FLAGS(%bp)		// Entry enabled?
138		jnc main.5			// No
139/*
140 * If any of the entries in the table are
141 * the same as the 'type' in the slice table entry,
142 * then this is an empty or non bootable partition. Skip it.
143 */
144		movb (%bx),%al			// Load type
145		movw $tables,%di		// Lookup tables
146		movb $TBL0SZ,%cl		// Number of entries
147		repne				// Exclude
148		scasb				//  partition?
149		je main.5			// Yes
150/*
151 * Now scan the table of known types
152 */
153		movb $TBL1SZ,%cl		// Number of entries
154		repne				// Known
155		scasb				//  type?
156		jne main.4			// No
157/*
158 * If it matches get the matching element in the
159 * next array. if it doesn't, we are already
160 * pointing at its first element which points to a "?".
161 */
162		addw $TBL1SZ,%di		// Adjust
163main.4:		movb (%di),%cl			// Partition
164		addw %cx,%di			//  description
165		callw putx			// Display it
166main.5:		incw %dx			// Next item
167		addb $0x10,%bl			// Next entry
168		jnc main.3			// Till done
169/*
170 * Passed a 256 byte boundary..
171 * table is finished.
172 * Add one to the drive number and check it is valid,
173 */
174		popw %ax			// Drive number
175		subb $0x80-0x1,%al		// Does next
176		cmpb NHRDRV,%al			//  drive exist? (from BIOS?)
177		jb main.6			// Yes
178/*
179 * If not then if there is only one drive,
180 * Don't display drive as an option.
181 */
182		decw %ax			// Already drive 0?
183		jz main.7			// Yes
184/*
185 * If it was illegal or we cycled through them,
186 * then go back to drive 0.
187 */
188		xorb %al,%al			// Drive 0
189/*
190 * Whatever drive we selected, make it an ascii digit and save it back
191 * to the "next drive" location in the loaded block in case we
192 * want to save it for next time.
193 * This also is part of the printed drive string so add 0x80 to indicate
194 * end of string.
195 */
196main.6:		addb $'0'|0x80,%al		// Save next
197		movb %al,_NXTDRV(%bp)		//  drive number
198		movw $drive,%di			// Display
199		callw putx			//  item
200/*
201 * Now that we've printed the drive (if we needed to), display a prompt.
202 * Get ready for the input by noting the time.
203 */
204main.7:		movw $prompt,%si		// Display
205		callw putstr			//  prompt
206		movb _OPT(%bp),%dl		// Display
207		decw %si			//  default
208		callw putkey			//  key
209main.7_1:
210		xorb %ah,%ah			// BIOS: Get
211		int $0x1a			//  system time
212#ifndef SIO
213		movw %dx,%di			// Ticks when
214		addw _TICKS(%bp),%di	 	//  timeout
215#else /* SIO */
216		movw %dx,%si			// Ticks when
217		addw _TICKS(%bp),%si		//  timeout
218#endif /* SIO */
219/*
220 * Busy loop, looking for keystrokes but
221 * keeping one eye on the time.
222 */
223main.8:
224#ifndef SIO
225		movb $0x1,%ah			// BIOS: Check
226		int $0x16			//  for keypress
227		jnz main.11			// Have one
228#else /* SIO */
229		xorw %dx,%dx			// Use COM1
230		movb $0x03,%ah			// BIOS: Read COM
231		int $0x14			//  Status
232		testb $0x01,%ah			// Check line status
233		jnz main.11 			// (bit 1 indicates input)
234#endif /* SIO */
235		xorb %ah,%ah			// BIOS: Get
236		int $0x1a			//  system time
237#ifndef SIO
238		cmpw %di,%dx			// Timeout?
239#else /* SIO */
240		cmpw %si,%dx			// Timeout?
241#endif /* SIO */
242		jb main.8			// No
243/*
244 * If timed out or defaulting, come here.
245 */
246main.9:		movb _OPT(%bp),%al		// Load default
247		jmp main.12			// Join common code
248/*
249 * User's last try was bad, beep in displeasure.
250 * Since nothing was printed, just continue on as if the user
251 * hadn't done anything. This gives the effect of the user getting a beep
252 * for all bad keystrokes but no action until either the timeout
253 * occurs or the user hits a good key.
254 */
255main.10:	movb $ASCII_BEL,%al		// Signal
256		callw putchr			//  error
257#ifdef SIO
258		jmp main.7_1			// Go back
259#endif /* SIO */
260/*
261 * Get the keystroke.
262 */
263main.11:
264#ifndef SIO
265		xorb %ah,%ah			// BIOS: Get
266		int $0x16			//  keypress
267		movb %ah,%al			// Scan code
268#else /* SIO */
269		movb $0x02,%ah			// BIOS: Receive
270		int $0x14			//  COM Byte
271#endif /* SIO */
272/*
273 * If it's CR act as if timed out.
274 */
275#ifndef SIO
276		cmpb $KEY_ENTER,%al		// Enter pressed?
277#else /* SIO */
278		cmpb $ASCII_CR,%al		// Enter pressed?
279#endif /* SIO */
280		je main.9			// Yes
281/*
282 * Otherwise check if legal
283 * If not ask again.
284 */
285#ifndef SIO
286		subb $KEY_F1,%al		// Less F1 scan code
287		cmpb $0x4,%al			// F1..F5?
288		jna main.12			// Yes
289		subb $(KEY_1 - KEY_F1),%al	// Less #1 scan code
290#else /* SIO */
291		subb $'1',%al			// Less '1' ascii character
292#endif /* SIO */
293		cmpb $0x4,%al			// #1..#5?
294		ja main.10			// No
295/*
296 * We have a selection.
297 * but if it's a bad selection go back to complain.
298 * The bits in MNUOPT were set when the options were printed.
299 * Anything not printed is not an option.
300 */
301main.12:	cbtw				// Option
302		btw %ax,_MNUOPT(%bp)	 	//  enabled?
303		jnc main.10			// No
304/*
305 * Save the info in the original tables
306 * for rewriting to the disk.
307 */
308		movb %al,_OPT(%bp)		// Save option
309		movw $FAKE,%si			// Partition for write
310		movb (%si),%dl			// Drive number
311		movw %si,%bx			// Partition for read
312		cmpb $0x4,%al			// F5/#5 pressed?
313		pushf				// Save
314		je main.13			// Yes
315		shlb $0x4,%al			// Point to
316		addw $partbl,%ax		//  selected
317		xchgw %bx,%ax	 		//  partition
318		movb $0x80,(%bx)		// Flag active
319/*
320 * If not asked to do a write-back (flags 0x40) don't do one.
321 */
322main.13:	pushw %bx			// Save
323		testb $0x40,_FLAGS(%bp)		// No updates?
324		jnz main.14			// Yes
325		movw $start,%bx			// Data to write
326		movb $0x3,%ah			// Write sector
327		callw intx13			//  to disk
328main.14:	popw %si			// Restore
329		popf				// Restore
330/*
331 * If going to next drive, replace drive with selected one.
332 * Remember to un-ascii it. Hey 0x80 is already set, cool!
333 */
334		jne main.15			// If not F5/#5
335		movb _NXTDRV(%bp),%dl		// Next drive
336		subb $'0',%dl			//  number
337/*
338 * load  selected bootsector to the LOAD location in RAM.
339 * If it fails to read or isn't marked bootable, treat it
340 * as a bad selection.
341 * XXX what does %si carry?
342 */
343main.15:	movw $LOAD,%bx			// Address for read
344		movb $0x2,%ah			// Read sector
345		callw intx13			//  from disk
346		jc main.10			// If error
347		cmpw $MAGIC,0x1fe(%bx)		// Bootable?
348		jne main.10			// No
349		pushw %si			// Save
350		movw $crlf,%si			// Leave some
351		callw puts			//  space
352		popw %si			// Restore
353		jmp *%bx			// Invoke bootstrap
354
355/*
356 * Display routines
357 */
358putkey:
359#ifndef SIO
360		movb $'F',%al			// Display
361		callw putchr			//  'F'
362#endif /* SIO */
363		movb $'1',%al			// Prepare
364		addb %dl,%al			//  digit
365		jmp putstr.1			// Display the rest
366
367/*
368 * Display the option and note that it is a valid option.
369 * That last point is a bit tricky..
370 */
371putx:		btsw %dx,_MNUOPT(%bp)		// Enable menu option
372		movw $item,%si			// Display
373		callw putkey			//  key
374		movw %di,%si			// Display the rest
375
376puts:		callw putstr			// Display string
377
378putn:		movw $crlf,%si			// To next line
379
380putstr:		lodsb				// Get byte
381		testb $0x80,%al 		// End of string?
382		jnz putstr.2			// Yes
383putstr.1:	callw putchr			// Display char
384		jmp putstr			// Continue
385putstr.2:	andb $~0x80,%al 		// Clear MSB
386
387putchr:
388#ifndef SIO
389		pushw %bx			// Save
390		movw $0x7,%bx	 		// Page:attribute
391		movb $0xe,%ah			// BIOS: Display
392		int $0x10			//  character
393		popw %bx			// Restore
394#else /* SIO */
395		pushw %dx			// Save
396		xorw %dx,%dx 			// Use COM1
397		xorw %cx,%cx 			// No timeout
398		movb $0x01,%ah			// BIOS: Send
399		int $0x14			//  Character
400		popw %dx			// Restore
401#endif /* SIO */
402		retw				// To caller
403
404/* One-sector disk I/O routine */
405
406intx13:		movb 0x1(%si),%dh		// Load head
407		movw 0x2(%si),%cx		// Load cylinder:sector
408		movb $0x1,%al			// Sector count
409		pushw %si			// Save
410		movw %sp,%di			// Save
411		testb $0x80,_FLAGS(%bp)		// Use packet interface?
412		jz intx13.1			// No
413		pushl $0x0			// Set the
414		pushl 0x8(%si)			// LBA address
415		pushw %es			// Set the transfer
416		pushw %bx			//  buffer address
417		push  $0x1			// Block count
418		push  $0x10			// Packet size
419		movw %sp,%si			// Packet pointer
420		decw %ax			// Verify off
421		orb $0x40,%ah			// Use disk packet
422intx13.1:	int $0x13			// BIOS: Disk I/O
423		movw %di,%sp			// Restore
424		popw %si			// Restore
425		retw				// To caller
426
427/* Menu strings */
428
429#ifndef SIO
430item:		.ascii "  ";	     .byte ' '|0x80
431prompt:		.ascii "\nDefault:"; .byte ' '|0x80
432#else /* SIO */
433item:		.ascii " ";	     .byte ' '|0x80
434prompt:		.ascii "\nDef:";     .byte ' '|0x80
435#endif /* SIO */
436crlf:		.ascii "\r";	     .byte '\n'|0x80
437
438/* Partition type tables */
439
440tables:
441/*
442 * These entries identify invalid or NON BOOT types and partitions.
443 */
444		.byte 0x0, 0x5, 0xf
445/*
446 * These values indicate bootable types we know the names of
447 */
448		.byte 0x1, 0x4, 0x6, 0xb, 0xc, 0xe, 0x83
449		.byte 0x9f, 0xa5, 0xa6, 0xa9
450/*
451 * These are offsets that match the known names above and point to the strings
452 * that will be printed.
453 */
454		.byte os_misc-. 		// Unknown
455		.byte os_dos-.			// DOS
456		.byte os_dos-.			// DOS
457		.byte os_dos-.			// DOS
458		.byte os_dos-.			// Windows
459		.byte os_dos-.			// Windows
460		.byte os_dos-.			// Windows
461		.byte os_linux-.		// Linux
462		.byte os_bsd-.			// BSD/OS
463		.byte os_freebsd-.		// FreeBSD
464		.byte os_bsd-.			// OpenBSD
465		.byte os_bsd-.			// NetBSD
466/*
467 * And here are the strings themselves. 0x80 or'd into a byte indicates
468 * the end of the string. (not so great for Russians but...)
469 */
470os_misc:	.ascii "?";    .byte '?'|0x80
471os_dos:		.ascii "DO";   .byte 'S'|0x80
472os_linux:	.ascii "Linu"; .byte 'x'|0x80
473os_freebsd:	.ascii "Free"
474os_bsd:		.ascii "BS";   .byte 'D'|0x80
475
476		.org PRT_OFF-0xe,0x90
477
478		.word B0MAGIC			// Magic number
479
480/*
481 * These values are sometimes changed before writing back to the drive
482 * Be especially careful that nxtdrv: must come after drive:, as it
483 * is part of the same string.
484 */
485drive:		.ascii "Drive "
486nxtdrv:		.byte 0x0			// Next drive number
487opt:		.byte 0x0			// Option
488setdrv:		.byte 0x80			// Drive to force
489flags:		.byte FLAGS			// Flags
490ticks:		.word TICKS			// Delay
491
492/*
493 * here is the 64 byte partition table that fdisk would fiddle with.
494 */
495partbl:		.fill 0x40,0x1,0x0		// Partition table
496		.word MAGIC			// Magic number
497