boot0.S revision 130343
1128723Sru/*
2128723Sru * Copyright (c) 2002 Bruce M. Simpson
3128723Sru * Copyright (c) 1998 Robert Nordier
4128723Sru * All rights reserved.
5128723Sru *
6128723Sru * Redistribution and use in source and binary forms are freely
7128723Sru * permitted provided that the above copyright notice and this
8128723Sru * paragraph and the following disclaimer are duplicated in all
9128723Sru * such forms.
10128723Sru *
11128723Sru * This software is provided "AS IS" and without any express or
12128723Sru * implied warranties, including, without limitation, the implied
13128723Sru * warranties of merchantability and fitness for a particular
14128723Sru * purpose.
15128723Sru *
16128723Sru * $FreeBSD: head/sys/boot/i386/boot0/boot0.S 130343 2004-06-11 09:45:18Z phk $
17128723Sru */
18128722Sru
19128723Sru/* A 512-byte boot manager. */
20128722Sru#ifdef SIO
21128723Sru/* ... using a serial console on COM1. */
22128722Sru#endif /* SIO */
23128722Sru
24129239Sru		.set NHRDRV,0x475		# Number of hard drives
25129239Sru		.set ORIGIN,0x600		# Execution address
26129239Sru		.set FAKE,0x800 		# Partition entry
27129239Sru		.set LOAD,0x7c00		# Load address
28128722Sru
29129239Sru		.set PRT_OFF,0x1be		# Partition table
30128722Sru
31129239Sru		.set TBL0SZ,0x3 		# Table 0 size
32129239Sru		.set TBL1SZ,0xb 		# Table 1 size
33128722Sru
34129239Sru		.set MAGIC,0xaa55		# Magic: bootable
35129239Sru		.set B0MAGIC,0xbb66		# Identification
36128722Sru
37129239Sru		.set KEY_ENTER,0x1c		# Enter key scan code
38129239Sru		.set KEY_F1,0x3b		# F1 key scan code
39129239Sru		.set KEY_1,0x02			# #1 key scan code
40128722Sru
41129239Sru		.set ASCII_BEL,0x07		# ASCII code for <BEL>
42129239Sru		.set ASCII_CR,0x0D		# ASCII code for <CR>
43128722Sru
44128723Sru/*
45128723Sru * Addresses in the sector of embedded data values.
46128723Sru * Accessed with negative offsets from the end of the relocated sector (%ebp).
47128723Sru */
48129239Sru		.set _NXTDRV,-0x48		# Next drive
49129239Sru		.set _OPT,-0x47 		# Default option
50129239Sru		.set _SETDRV,-0x46		# Drive to force
51129239Sru		.set _FLAGS,-0x45		# Flags
52129239Sru		.set _TICKS,-0x44		# Timeout ticks
53129239Sru		.set _FAKE,0x0			# Fake partition entry
54129239Sru		.set _MNUOPT,0xc		# Menu options
55128722Sru
56129239Sru		.globl start			# Entry point
57129239Sru		.code16				# This runs in real mode
58128722Sru
59128723Sru/*
60128723Sru * Initialise segments and registers to known values.
61128723Sru * segments start at 0.
62128723Sru * The stack is immediately below the address we were loaded to.
63128723Sru */
64129239Srustart:		cld				# String ops inc
65129239Sru		xorw %ax,%ax			# Zero
66129239Sru		movw %ax,%es			# Address
67129239Sru		movw %ax,%ds			#  data
68129239Sru		movw %ax,%ss			# Set up
69129239Sru		movw $LOAD,%sp			#  stack
70128722Sru
71128723Sru/*
72128723Sru * Copy this code to the address it was linked for
73128723Sru */
74129239Sru		movw %sp,%si			# Source
75129239Sru		movw $start,%di			# Destination
76129239Sru		movw $0x100,%cx			# Word count
77129239Sru		rep				# Relocate
78129239Sru		movsw				#  code
79128723Sru/*
80128723Sru * Set address for variable space beyond code, and clear it.
81128723Sru * Notice that this is also used to point to the values embedded in the block,
82128723Sru * by using negative offsets.
83128723Sru */
84129239Sru		movw %di,%bp			# Address variables
85129239Sru		movb $0x8,%cl			# Words to clear
86129239Sru		rep				# Zero
87129239Sru		stosw				#  them
88128723Sru/*
89128723Sru * Relocate to the new copy of the code.
90128723Sru */
91129239Sru		incb -0xe(%di)			# Sector number
92129239Sru		jmp main-LOAD+ORIGIN		# To relocated code
93128722Sru
94128722Srumain:
95128722Sru#ifdef SIO
96128723Sru/*
97130343Sphk * Initialize the serial port.  bioscom preserves the driver number in DX.
98128723Sru */
99129239Sru		movb COMSPEED,%al		# defined by Makefile
100129239Sru		movb $0x00,%ah			# BIOS: Set COM Port
101130343Sphk		call bioscom
102128722Sru#endif /* SIO */
103128723Sru/*
104128723Sru * Check what flags were loaded with us, specifically, Use a predefined Drive.
105128723Sru * If what the bios gives us is bad, use the '0' in the block instead, as well.
106128723Sru */
107129239Sru		testb $0x20,_FLAGS(%bp)		# Set number drive?
108129239Sru		jnz main.1			# Yes
109129239Sru		testb %dl,%dl			# Drive number valid?
110129239Sru		js main.2			# Possibly (0x80 set)
111129239Srumain.1:		movb _SETDRV(%bp),%dl		# Drive number to use
112128723Sru/*
113128723Sru * Whatever we decided to use, now store it into the fake
114128723Sru * partition entry that lives in the data space above us.
115128723Sru */
116129239Srumain.2:		movb %dl,_FAKE(%bp)		# Save drive number
117129239Sru		callw putn			# To new line
118129239Sru		pushw %dx			# Save drive number
119128723Sru/*
120128723Sru * Start out with a pointer to the 4th byte of the first table entry
121128723Sru * so that after 4 iterations it's beyond the end of the sector.
122128723Sru * and beyond a 256 byte boundary and has overflowed 8 bits (see next comment).
123128723Sru * (remember that the table starts 2 bytes earlier than you would expect
124128723Sru * as the bootable flag is after it in the block)
125128723Sru */
126129239Sru		movw $(partbl+0x4),%bx		# Partition table (+4)
127129239Sru		xorw %dx,%dx			# Item number
128128723Sru/*
129128723Sru * Loop around on the partition table, printing values until we
130128723Sru * pass a 256 byte boundary. The end of loop test is at main.5.
131128723Sru */
132129239Srumain.3:		movb %ch,-0x4(%bx)		# Zero active flag (ch == 0)
133129239Sru		btw %dx,_FLAGS(%bp)		# Entry enabled?
134129239Sru		jnc main.5			# No
135128723Sru/*
136128723Sru * If any of the entries in the table are
137128723Sru * the same as the 'type' in the slice table entry,
138128723Sru * then this is an empty or non bootable partition. Skip it.
139128723Sru */
140129239Sru		movb (%bx),%al			# Load type
141129239Sru		movw $tables,%di		# Lookup tables
142129239Sru		movb $TBL0SZ,%cl		# Number of entries
143129239Sru		repne				# Exclude
144129239Sru		scasb				#  partition?
145129239Sru		je main.5			# Yes
146128723Sru/*
147128723Sru * Now scan the table of known types
148128723Sru */
149129239Sru		movb $TBL1SZ,%cl		# Number of entries
150129239Sru		repne				# Known
151129239Sru		scasb				#  type?
152129239Sru		jne main.4			# No
153128723Sru/*
154128723Sru * If it matches get the matching element in the
155128723Sru * next array. if it doesn't, we are already
156128723Sru * pointing at its first element which points to a "?".
157128723Sru */
158129239Sru		addw $TBL1SZ,%di		# Adjust
159129239Srumain.4:		movb (%di),%cl			# Partition
160129239Sru		addw %cx,%di			#  description
161129239Sru		callw putx			# Display it
162129239Srumain.5:		incw %dx			# Next item
163129239Sru		addb $0x10,%bl			# Next entry
164129239Sru		jnc main.3			# Till done
165128723Sru/*
166128723Sru * Passed a 256 byte boundary..
167128723Sru * table is finished.
168128723Sru * Add one to the drive number and check it is valid,
169128723Sru */
170129239Sru		popw %ax			# Drive number
171129239Sru		subb $0x80-0x1,%al		# Does next
172129239Sru		cmpb NHRDRV,%al			#  drive exist? (from BIOS?)
173129239Sru		jb main.6			# Yes
174128723Sru/*
175128723Sru * If not then if there is only one drive,
176128723Sru * Don't display drive as an option.
177128723Sru */
178129239Sru		decw %ax			# Already drive 0?
179129239Sru		jz main.7			# Yes
180128723Sru/*
181128723Sru * If it was illegal or we cycled through them,
182128723Sru * then go back to drive 0.
183128723Sru */
184129239Sru		xorb %al,%al			# Drive 0
185128723Sru/*
186128723Sru * Whatever drive we selected, make it an ascii digit and save it back
187128723Sru * to the "next drive" location in the loaded block in case we
188128723Sru * want to save it for next time.
189128723Sru * This also is part of the printed drive string so add 0x80 to indicate
190128723Sru * end of string.
191128723Sru */
192129239Srumain.6:		addb $'0'|0x80,%al		# Save next
193129239Sru		movb %al,_NXTDRV(%bp)		#  drive number
194129239Sru		movw $drive,%di			# Display
195129239Sru		callw putx			#  item
196128723Sru/*
197128723Sru * Now that we've printed the drive (if we needed to), display a prompt.
198128723Sru */
199129239Srumain.7:		movw $prompt,%si		# Display
200129239Sru		callw putstr			#  prompt
201129239Sru		movb _OPT(%bp),%dl		# Display
202129239Sru		decw %si			#  default
203129239Sru		callw putkey			#  key
204130343Sphk/*
205130343Sphk * Start of input loop.  Beep and take note of time
206130343Sphk */
207130343Sphkmain.10:	movb $ASCII_BEL,%al		# Signal
208130343Sphk		callw putchr			#  beep!
209129239Sru		xorb %ah,%ah			# BIOS: Get
210129239Sru		int $0x1a			#  system time
211129239Sru		movw %dx,%di			# Ticks when
212130343Sphk		addw _TICKS(%bp),%di		#  timeout
213128723Sru/*
214128723Sru * Busy loop, looking for keystrokes but
215128723Sru * keeping one eye on the time.
216128723Sru */
217128722Srumain.8:
218128722Sru#ifndef SIO
219129239Sru		movb $0x1,%ah			# BIOS: Check
220129239Sru		int $0x16			#  for keypress
221129239Sru		jnz main.11			# Have one
222128722Sru#else /* SIO */
223129239Sru		movb $0x03,%ah			# BIOS: Read COM
224130343Sphk		call bioscom
225129239Sru		testb $0x01,%ah			# Check line status
226129239Sru		jnz main.11 			# (bit 1 indicates input)
227128722Sru#endif /* SIO */
228129239Sru		xorb %ah,%ah			# BIOS: Get
229129239Sru		int $0x1a			#  system time
230129239Sru		cmpw %di,%dx			# Timeout?
231129239Sru		jb main.8			# No
232128723Sru/*
233128723Sru * If timed out or defaulting, come here.
234128723Sru */
235129239Srumain.9:		movb _OPT(%bp),%al		# Load default
236129239Sru		jmp main.12			# Join common code
237128723Sru/*
238128723Sru * Get the keystroke.
239128723Sru */
240128722Srumain.11:
241128722Sru#ifndef SIO
242129239Sru		xorb %ah,%ah			# BIOS: Get
243129239Sru		int $0x16			#  keypress
244129239Sru		movb %ah,%al			# Scan code
245128722Sru#else /* SIO */
246129239Sru		movb $0x02,%ah			# BIOS: Receive
247130343Sphk		call bioscom
248128722Sru#endif /* SIO */
249128723Sru/*
250128723Sru * If it's CR act as if timed out.
251128723Sru */
252128722Sru#ifndef SIO
253129239Sru		cmpb $KEY_ENTER,%al		# Enter pressed?
254128722Sru#else /* SIO */
255129239Sru		cmpb $ASCII_CR,%al		# Enter pressed?
256128722Sru#endif /* SIO */
257129239Sru		je main.9			# Yes
258128723Sru/*
259128723Sru * Otherwise check if legal
260128723Sru * If not ask again.
261128723Sru */
262128722Sru#ifndef SIO
263129239Sru		subb $KEY_F1,%al		# Less F1 scan code
264129239Sru		cmpb $0x4,%al			# F1..F5?
265129239Sru		jna main.12			# Yes
266129239Sru		subb $(KEY_1 - KEY_F1),%al	# Less #1 scan code
267128722Sru#else /* SIO */
268129239Sru		subb $'1',%al			# Less '1' ascii character
269128722Sru#endif /* SIO */
270129239Sru		cmpb $0x4,%al			# #1..#5?
271129239Sru		ja main.10			# No
272128723Sru/*
273128723Sru * We have a selection.
274128723Sru * but if it's a bad selection go back to complain.
275128723Sru * The bits in MNUOPT were set when the options were printed.
276128723Sru * Anything not printed is not an option.
277128723Sru */
278129239Srumain.12:	cbtw				# Option
279129239Sru		btw %ax,_MNUOPT(%bp)	 	#  enabled?
280129239Sru		jnc main.10			# No
281128723Sru/*
282128723Sru * Save the info in the original tables
283128723Sru * for rewriting to the disk.
284128723Sru */
285129239Sru		movb %al,_OPT(%bp)		# Save option
286129239Sru		movw $FAKE,%si			# Partition for write
287129239Sru		movb (%si),%dl			# Drive number
288129239Sru		movw %si,%bx			# Partition for read
289129239Sru		cmpb $0x4,%al			# F5/#5 pressed?
290129239Sru		pushf				# Save
291129239Sru		je main.13			# Yes
292129239Sru		shlb $0x4,%al			# Point to
293129239Sru		addw $partbl,%ax		#  selected
294129239Sru		xchgw %bx,%ax	 		#  partition
295129239Sru		movb $0x80,(%bx)		# Flag active
296128723Sru/*
297128723Sru * If not asked to do a write-back (flags 0x40) don't do one.
298128723Sru */
299129239Srumain.13:	pushw %bx			# Save
300129239Sru		testb $0x40,_FLAGS(%bp)		# No updates?
301129239Sru		jnz main.14			# Yes
302129239Sru		movw $start,%bx			# Data to write
303129239Sru		movb $0x3,%ah			# Write sector
304129239Sru		callw intx13			#  to disk
305129239Srumain.14:	popw %si			# Restore
306129239Sru		popf				# Restore
307128723Sru/*
308128723Sru * If going to next drive, replace drive with selected one.
309128723Sru * Remember to un-ascii it. Hey 0x80 is already set, cool!
310128723Sru */
311129239Sru		jne main.15			# If not F5/#5
312129239Sru		movb _NXTDRV(%bp),%dl		# Next drive
313129239Sru		subb $'0',%dl			#  number
314128723Sru/*
315128723Sru * load  selected bootsector to the LOAD location in RAM.
316128723Sru * If it fails to read or isn't marked bootable, treat it
317128723Sru * as a bad selection.
318128723Sru * XXX what does %si carry?
319128723Sru */
320129239Srumain.15:	movw $LOAD,%bx			# Address for read
321129239Sru		movb $0x2,%ah			# Read sector
322129239Sru		callw intx13			#  from disk
323129239Sru		jc main.10			# If error
324129239Sru		cmpw $MAGIC,0x1fe(%bx)		# Bootable?
325129239Sru		jne main.10			# No
326129239Sru		pushw %si			# Save
327129239Sru		movw $crlf,%si			# Leave some
328129239Sru		callw puts			#  space
329129239Sru		popw %si			# Restore
330129239Sru		jmp *%bx			# Invoke bootstrap
331128722Sru
332128723Sru/*
333128723Sru * Display routines
334128723Sru */
335128722Sruputkey:
336128722Sru#ifndef SIO
337129239Sru		movb $'F',%al			# Display
338129239Sru		callw putchr			#  'F'
339128722Sru#endif /* SIO */
340129239Sru		movb $'1',%al			# Prepare
341129239Sru		addb %dl,%al			#  digit
342129239Sru		jmp putstr.1			# Display the rest
343128722Sru
344128723Sru/*
345128723Sru * Display the option and note that it is a valid option.
346128723Sru * That last point is a bit tricky..
347128723Sru */
348129239Sruputx:		btsw %dx,_MNUOPT(%bp)		# Enable menu option
349129239Sru		movw $item,%si			# Display
350129239Sru		callw putkey			#  key
351129239Sru		movw %di,%si			# Display the rest
352128722Sru
353129239Sruputs:		callw putstr			# Display string
354128722Sru
355129239Sruputn:		movw $crlf,%si			# To next line
356128722Sru
357129239Sruputstr:		lodsb				# Get byte
358129239Sru		testb $0x80,%al 		# End of string?
359129239Sru		jnz putstr.2			# Yes
360129239Sruputstr.1:	callw putchr			# Display char
361129239Sru		jmp putstr			# Continue
362129239Sruputstr.2:	andb $~0x80,%al 		# Clear MSB
363128722Sru
364130343Sphk#ifndef SIO
365128722Sruputchr:
366129239Sru		pushw %bx			# Save
367129239Sru		movw $0x7,%bx	 		# Page:attribute
368129239Sru		movb $0xe,%ah			# BIOS: Display
369129239Sru		int $0x10			#  character
370129239Sru		popw %bx			# Restore
371130343Sphk		retw				# To caller
372128722Sru#else /* SIO */
373130343Sphkputchr:
374130343Sphk		movb $0x01,%ah			# BIOS: Send
375130343Sphk		xorw %cx,%cx 			# No timeout
376130343Sphkbioscom:
377129239Sru		pushw %dx			# Save
378129239Sru		xorw %dx,%dx 			# Use COM1
379129239Sru		int $0x14			#  Character
380129239Sru		popw %dx			# Restore
381130343Sphk		retw				# To caller
382128722Sru#endif /* SIO */
383128722Sru
384128723Sru/* One-sector disk I/O routine */
385128722Sru
386129239Sruintx13:		movb 0x1(%si),%dh		# Load head
387129239Sru		movw 0x2(%si),%cx		# Load cylinder:sector
388129239Sru		movb $0x1,%al			# Sector count
389129239Sru		pushw %si			# Save
390129239Sru		movw %sp,%di			# Save
391129239Sru		testb $0x80,_FLAGS(%bp)		# Use packet interface?
392129239Sru		jz intx13.1			# No
393129239Sru		pushl $0x0			# Set the
394129239Sru		pushl 0x8(%si)			# LBA address
395129239Sru		pushw %es			# Set the transfer
396129239Sru		pushw %bx			#  buffer address
397129239Sru		push  $0x1			# Block count
398129239Sru		push  $0x10			# Packet size
399129239Sru		movw %sp,%si			# Packet pointer
400129239Sru		decw %ax			# Verify off
401129239Sru		orb $0x40,%ah			# Use disk packet
402129239Sruintx13.1:	int $0x13			# BIOS: Disk I/O
403129239Sru		movw %di,%sp			# Restore
404129239Sru		popw %si			# Restore
405129239Sru		retw				# To caller
406128722Sru
407128723Sru/* Menu strings */
408128722Sru
409128722Sruitem:		.ascii "  ";	     .byte ' '|0x80
410128722Sruprompt:		.ascii "\nDefault:"; .byte ' '|0x80
411128722Srucrlf:		.ascii "\r";	     .byte '\n'|0x80
412128722Sru
413128723Sru/* Partition type tables */
414128722Sru
415128722Srutables:
416128723Sru/*
417128723Sru * These entries identify invalid or NON BOOT types and partitions.
418128723Sru */
419128722Sru		.byte 0x0, 0x5, 0xf
420128723Sru/*
421128723Sru * These values indicate bootable types we know the names of
422128723Sru */
423128722Sru		.byte 0x1, 0x4, 0x6, 0xb, 0xc, 0xe, 0x83
424128722Sru		.byte 0x9f, 0xa5, 0xa6, 0xa9
425128723Sru/*
426128723Sru * These are offsets that match the known names above and point to the strings
427128723Sru * that will be printed.
428128723Sru */
429129239Sru		.byte os_misc-. 		# Unknown
430129239Sru		.byte os_dos-.			# DOS
431129239Sru		.byte os_dos-.			# DOS
432129239Sru		.byte os_dos-.			# DOS
433129239Sru		.byte os_dos-.			# Windows
434129239Sru		.byte os_dos-.			# Windows
435129239Sru		.byte os_dos-.			# Windows
436129239Sru		.byte os_linux-.		# Linux
437129239Sru		.byte os_bsd-.			# BSD/OS
438129239Sru		.byte os_freebsd-.		# FreeBSD
439129239Sru		.byte os_bsd-.			# OpenBSD
440129239Sru		.byte os_bsd-.			# NetBSD
441128723Sru/*
442128723Sru * And here are the strings themselves. 0x80 or'd into a byte indicates
443128723Sru * the end of the string. (not so great for Russians but...)
444128723Sru */
445128722Sruos_misc:	.ascii "?";    .byte '?'|0x80
446128722Sruos_dos:		.ascii "DO";   .byte 'S'|0x80
447128722Sruos_linux:	.ascii "Linu"; .byte 'x'|0x80
448128722Sruos_freebsd:	.ascii "Free"
449128722Sruos_bsd:		.ascii "BS";   .byte 'D'|0x80
450128722Sru
451128722Sru		.org PRT_OFF-0xe,0x90
452128722Sru
453129239Sru		.word B0MAGIC			# Magic number
454128722Sru
455128723Sru/*
456128723Sru * These values are sometimes changed before writing back to the drive
457128723Sru * Be especially careful that nxtdrv: must come after drive:, as it
458128723Sru * is part of the same string.
459128723Sru */
460128722Srudrive:		.ascii "Drive "
461129239Srunxtdrv:		.byte 0x0			# Next drive number
462129239Sruopt:		.byte 0x0			# Option
463129239Srusetdrv:		.byte 0x80			# Drive to force
464129239Sruflags:		.byte FLAGS			# Flags
465129239Sruticks:		.word TICKS			# Delay
466128722Sru
467128723Sru/*
468128723Sru * here is the 64 byte partition table that fdisk would fiddle with.
469128723Sru */
470129239Srupartbl:		.fill 0x40,0x1,0x0		# Partition table
471129239Sru		.word MAGIC			# Magic number
472