boot0.S revision 138048
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 138048 2004-11-24 15:39:04Z jhb $
17128723Sru */
18128722Sru
19128723Sru/* A 512-byte boot manager. */
20128722Sru#ifdef SIO
21128723Sru/* ... using a serial console on COM1. */
22138048Sjhb#endif
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:
95138048Sjhb#if defined(SIO) && COMSPEED != 0
96128723Sru/*
97130343Sphk * Initialize the serial port.  bioscom preserves the driver number in DX.
98128723Sru */
99130632Sphk		movw COMSPEED,%ax		# defined by Makefile
100130632Sphk		callw bioscom
101138048Sjhb#endif
102128723Sru/*
103137298Skeramida * Check what flags were loaded with us, specifically if a predefined drive
104137298Skeramida * number should be used.  If what the bios gives us is bad, use the '0' in
105137298Skeramida * the block instead.
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
121137298Skeramida * 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).
123137298Skeramida * Remember that the table starts 2 bytes earlier than you would expect
124137298Skeramida * 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/*
136137298Skeramida * If any of the entries in the table are the same as the 'type' in the slice
137137298Skeramida * table entry, then this is an empty or non bootable partition. Skip it.
138128723Sru */
139129239Sru		movb (%bx),%al			# Load type
140129239Sru		movw $tables,%di		# Lookup tables
141129239Sru		movb $TBL0SZ,%cl		# Number of entries
142129239Sru		repne				# Exclude
143129239Sru		scasb				#  partition?
144129239Sru		je main.5			# Yes
145128723Sru/*
146128723Sru * Now scan the table of known types
147128723Sru */
148129239Sru		movb $TBL1SZ,%cl		# Number of entries
149129239Sru		repne				# Known
150129239Sru		scasb				#  type?
151129239Sru		jne main.4			# No
152128723Sru/*
153137298Skeramida * If it matches get the matching element in the next array.  If it doesn't,
154137298Skeramida * we are already pointing at its first element which points to a "?".
155128723Sru */
156129239Sru		addw $TBL1SZ,%di		# Adjust
157129239Srumain.4:		movb (%di),%cl			# Partition
158129239Sru		addw %cx,%di			#  description
159129239Sru		callw putx			# Display it
160129239Srumain.5:		incw %dx			# Next item
161129239Sru		addb $0x10,%bl			# Next entry
162129239Sru		jnc main.3			# Till done
163128723Sru/*
164137298Skeramida * Passed a 256 byte boundary; the table is finished.
165137298Skeramida * Add one to the drive number and check it is valid.
166128723Sru */
167129239Sru		popw %ax			# Drive number
168129239Sru		subb $0x80-0x1,%al		# Does next
169129239Sru		cmpb NHRDRV,%al			#  drive exist? (from BIOS?)
170129239Sru		jb main.6			# Yes
171128723Sru/*
172137298Skeramida * If this is the only drive, don't display it as an option.
173128723Sru */
174129239Sru		decw %ax			# Already drive 0?
175129239Sru		jz main.7			# Yes
176128723Sru/*
177137298Skeramida * If it was illegal or we cycled through them, go back to drive 0.
178128723Sru */
179129239Sru		xorb %al,%al			# Drive 0
180128723Sru/*
181137298Skeramida * Whatever drive we selected, make it an ascii digit and save it back to the
182137298Skeramida * "next drive" location in the loaded block in case we want to save it later
183137298Skeramida * for next time.  This also is part of the printed drive string so add 0x80
184137298Skeramida * to indicate end of string.
185128723Sru */
186129239Srumain.6:		addb $'0'|0x80,%al		# Save next
187129239Sru		movb %al,_NXTDRV(%bp)		#  drive number
188129239Sru		movw $drive,%di			# Display
189129239Sru		callw putx			#  item
190128723Sru/*
191128723Sru * Now that we've printed the drive (if we needed to), display a prompt.
192128723Sru */
193129239Srumain.7:		movw $prompt,%si		# Display
194129239Sru		callw putstr			#  prompt
195129239Sru		movb _OPT(%bp),%dl		# Display
196129239Sru		decw %si			#  default
197129239Sru		callw putkey			#  key
198130343Sphk/*
199130343Sphk * Start of input loop.  Beep and take note of time
200130343Sphk */
201130343Sphkmain.10:	movb $ASCII_BEL,%al		# Signal
202130343Sphk		callw putchr			#  beep!
203129239Sru		xorb %ah,%ah			# BIOS: Get
204129239Sru		int $0x1a			#  system time
205129239Sru		movw %dx,%di			# Ticks when
206130343Sphk		addw _TICKS(%bp),%di		#  timeout
207128723Sru/*
208137298Skeramida * Busy loop, looking for keystrokes but keeping one eye on the time.
209128723Sru */
210128722Srumain.8:
211128722Sru#ifndef SIO
212129239Sru		movb $0x1,%ah			# BIOS: Check
213129239Sru		int $0x16			#  for keypress
214129239Sru		jnz main.11			# Have one
215128722Sru#else /* SIO */
216129239Sru		movb $0x03,%ah			# BIOS: Read COM
217130343Sphk		call bioscom
218129239Sru		testb $0x01,%ah			# Check line status
219129239Sru		jnz main.11 			# (bit 1 indicates input)
220128722Sru#endif /* SIO */
221129239Sru		xorb %ah,%ah			# BIOS: Get
222129239Sru		int $0x1a			#  system time
223129239Sru		cmpw %di,%dx			# Timeout?
224129239Sru		jb main.8			# No
225128723Sru/*
226128723Sru * If timed out or defaulting, come here.
227128723Sru */
228129239Srumain.9:		movb _OPT(%bp),%al		# Load default
229129239Sru		jmp main.12			# Join common code
230128723Sru/*
231128723Sru * Get the keystroke.
232128723Sru */
233128722Srumain.11:
234128722Sru#ifndef SIO
235129239Sru		xorb %ah,%ah			# BIOS: Get
236129239Sru		int $0x16			#  keypress
237129239Sru		movb %ah,%al			# Scan code
238138048Sjhb#else
239129239Sru		movb $0x02,%ah			# BIOS: Receive
240130343Sphk		call bioscom
241138048Sjhb#endif
242128723Sru/*
243128723Sru * If it's CR act as if timed out.
244128723Sru */
245128722Sru#ifndef SIO
246129239Sru		cmpb $KEY_ENTER,%al		# Enter pressed?
247138048Sjhb#else
248129239Sru		cmpb $ASCII_CR,%al		# Enter pressed?
249138048Sjhb#endif
250129239Sru		je main.9			# Yes
251128723Sru/*
252137298Skeramida * Otherwise check if legal. If not ask again.
253128723Sru */
254128722Sru#ifndef SIO
255129239Sru		subb $KEY_F1,%al		# Less F1 scan code
256129239Sru		cmpb $0x4,%al			# F1..F5?
257129239Sru		jna main.12			# Yes
258129239Sru		subb $(KEY_1 - KEY_F1),%al	# Less #1 scan code
259138048Sjhb#else
260129239Sru		subb $'1',%al			# Less '1' ascii character
261138048Sjhb#endif
262129239Sru		cmpb $0x4,%al			# #1..#5?
263129239Sru		ja main.10			# No
264128723Sru/*
265137298Skeramida * We have a selection.  If it's a bad selection go back to complain.
266128723Sru * The bits in MNUOPT were set when the options were printed.
267128723Sru * Anything not printed is not an option.
268128723Sru */
269129239Srumain.12:	cbtw				# Option
270129239Sru		btw %ax,_MNUOPT(%bp)	 	#  enabled?
271129239Sru		jnc main.10			# No
272128723Sru/*
273128723Sru * Save the info in the original tables
274128723Sru * for rewriting to the disk.
275128723Sru */
276129239Sru		movb %al,_OPT(%bp)		# Save option
277129239Sru		movw $FAKE,%si			# Partition for write
278129239Sru		movb (%si),%dl			# Drive number
279129239Sru		movw %si,%bx			# Partition for read
280129239Sru		cmpb $0x4,%al			# F5/#5 pressed?
281129239Sru		pushf				# Save
282129239Sru		je main.13			# Yes
283129239Sru		shlb $0x4,%al			# Point to
284129239Sru		addw $partbl,%ax		#  selected
285129239Sru		xchgw %bx,%ax	 		#  partition
286129239Sru		movb $0x80,(%bx)		# Flag active
287128723Sru/*
288128723Sru * If not asked to do a write-back (flags 0x40) don't do one.
289128723Sru */
290129239Srumain.13:	pushw %bx			# Save
291129239Sru		testb $0x40,_FLAGS(%bp)		# No updates?
292129239Sru		jnz main.14			# Yes
293129239Sru		movw $start,%bx			# Data to write
294129239Sru		movb $0x3,%ah			# Write sector
295129239Sru		callw intx13			#  to disk
296129239Srumain.14:	popw %si			# Restore
297129239Sru		popf				# Restore
298128723Sru/*
299128723Sru * If going to next drive, replace drive with selected one.
300128723Sru * Remember to un-ascii it. Hey 0x80 is already set, cool!
301128723Sru */
302129239Sru		jne main.15			# If not F5/#5
303129239Sru		movb _NXTDRV(%bp),%dl		# Next drive
304129239Sru		subb $'0',%dl			#  number
305128723Sru/*
306137298Skeramida * Load selected bootsector to the LOAD location in RAM.
307137298Skeramida * If it fails to read or isn't marked bootable, treat it as a bad selection.
308137298Skeramida * XXX: What does %si carry?
309128723Sru */
310129239Srumain.15:	movw $LOAD,%bx			# Address for read
311129239Sru		movb $0x2,%ah			# Read sector
312129239Sru		callw intx13			#  from disk
313129239Sru		jc main.10			# If error
314129239Sru		cmpw $MAGIC,0x1fe(%bx)		# Bootable?
315129239Sru		jne main.10			# No
316129239Sru		pushw %si			# Save
317129239Sru		movw $crlf,%si			# Leave some
318129239Sru		callw puts			#  space
319129239Sru		popw %si			# Restore
320129239Sru		jmp *%bx			# Invoke bootstrap
321128722Sru
322128723Sru/*
323128723Sru * Display routines
324128723Sru */
325128722Sruputkey:
326128722Sru#ifndef SIO
327129239Sru		movb $'F',%al			# Display
328129239Sru		callw putchr			#  'F'
329138048Sjhb#endif
330129239Sru		movb $'1',%al			# Prepare
331129239Sru		addb %dl,%al			#  digit
332129239Sru		jmp putstr.1			# Display the rest
333128722Sru
334128723Sru/*
335128723Sru * Display the option and note that it is a valid option.
336128723Sru * That last point is a bit tricky..
337128723Sru */
338129239Sruputx:		btsw %dx,_MNUOPT(%bp)		# Enable menu option
339129239Sru		movw $item,%si			# Display
340129239Sru		callw putkey			#  key
341129239Sru		movw %di,%si			# Display the rest
342128722Sru
343129239Sruputs:		callw putstr			# Display string
344128722Sru
345129239Sruputn:		movw $crlf,%si			# To next line
346128722Sru
347129239Sruputstr:		lodsb				# Get byte
348129239Sru		testb $0x80,%al 		# End of string?
349129239Sru		jnz putstr.2			# Yes
350129239Sruputstr.1:	callw putchr			# Display char
351129239Sru		jmp putstr			# Continue
352129239Sruputstr.2:	andb $~0x80,%al 		# Clear MSB
353128722Sru
354130343Sphk#ifndef SIO
355128722Sruputchr:
356129239Sru		pushw %bx			# Save
357129239Sru		movw $0x7,%bx	 		# Page:attribute
358129239Sru		movb $0xe,%ah			# BIOS: Display
359129239Sru		int $0x10			#  character
360129239Sru		popw %bx			# Restore
361130343Sphk		retw				# To caller
362128722Sru#else /* SIO */
363130343Sphkputchr:
364130343Sphk		movb $0x01,%ah			# BIOS: Send
365130343Sphkbioscom:
366129239Sru		pushw %dx			# Save
367129239Sru		xorw %dx,%dx 			# Use COM1
368129239Sru		int $0x14			#  Character
369129239Sru		popw %dx			# Restore
370130343Sphk		retw				# To caller
371128722Sru#endif /* SIO */
372128722Sru
373128723Sru/* One-sector disk I/O routine */
374128722Sru
375129239Sruintx13:		movb 0x1(%si),%dh		# Load head
376129239Sru		movw 0x2(%si),%cx		# Load cylinder:sector
377129239Sru		movb $0x1,%al			# Sector count
378129239Sru		pushw %si			# Save
379129239Sru		movw %sp,%di			# Save
380129239Sru		testb $0x80,_FLAGS(%bp)		# Use packet interface?
381129239Sru		jz intx13.1			# No
382129239Sru		pushl $0x0			# Set the
383129239Sru		pushl 0x8(%si)			# LBA address
384129239Sru		pushw %es			# Set the transfer
385129239Sru		pushw %bx			#  buffer address
386129239Sru		push  $0x1			# Block count
387129239Sru		push  $0x10			# Packet size
388129239Sru		movw %sp,%si			# Packet pointer
389129239Sru		decw %ax			# Verify off
390129239Sru		orb $0x40,%ah			# Use disk packet
391129239Sruintx13.1:	int $0x13			# BIOS: Disk I/O
392129239Sru		movw %di,%sp			# Restore
393129239Sru		popw %si			# Restore
394129239Sru		retw				# To caller
395128722Sru
396128723Sru/* Menu strings */
397128722Sru
398128722Sruitem:		.ascii "  ";	     .byte ' '|0x80
399128722Sruprompt:		.ascii "\nDefault:"; .byte ' '|0x80
400128722Srucrlf:		.ascii "\r";	     .byte '\n'|0x80
401128722Sru
402128723Sru/* Partition type tables */
403128722Sru
404128722Srutables:
405128723Sru/*
406128723Sru * These entries identify invalid or NON BOOT types and partitions.
407128723Sru */
408128722Sru		.byte 0x0, 0x5, 0xf
409128723Sru/*
410137298Skeramida * These values indicate bootable types we know the names of.
411128723Sru */
412128722Sru		.byte 0x1, 0x4, 0x6, 0xb, 0xc, 0xe, 0x83
413128722Sru		.byte 0x9f, 0xa5, 0xa6, 0xa9
414128723Sru/*
415128723Sru * These are offsets that match the known names above and point to the strings
416128723Sru * that will be printed.
417128723Sru */
418129239Sru		.byte os_misc-. 		# Unknown
419129239Sru		.byte os_dos-.			# DOS
420129239Sru		.byte os_dos-.			# DOS
421129239Sru		.byte os_dos-.			# DOS
422129239Sru		.byte os_dos-.			# Windows
423129239Sru		.byte os_dos-.			# Windows
424129239Sru		.byte os_dos-.			# Windows
425129239Sru		.byte os_linux-.		# Linux
426129239Sru		.byte os_bsd-.			# BSD/OS
427129239Sru		.byte os_freebsd-.		# FreeBSD
428129239Sru		.byte os_bsd-.			# OpenBSD
429129239Sru		.byte os_bsd-.			# NetBSD
430128723Sru/*
431128723Sru * And here are the strings themselves. 0x80 or'd into a byte indicates
432128723Sru * the end of the string. (not so great for Russians but...)
433128723Sru */
434128722Sruos_misc:	.ascii "?";    .byte '?'|0x80
435128722Sruos_dos:		.ascii "DO";   .byte 'S'|0x80
436128722Sruos_linux:	.ascii "Linu"; .byte 'x'|0x80
437128722Sruos_freebsd:	.ascii "Free"
438128722Sruos_bsd:		.ascii "BS";   .byte 'D'|0x80
439128722Sru
440128722Sru		.org PRT_OFF-0xe,0x90
441128722Sru
442129239Sru		.word B0MAGIC			# Magic number
443128722Sru
444128723Sru/*
445128723Sru * These values are sometimes changed before writing back to the drive
446128723Sru * Be especially careful that nxtdrv: must come after drive:, as it
447128723Sru * is part of the same string.
448128723Sru */
449128722Srudrive:		.ascii "Drive "
450129239Srunxtdrv:		.byte 0x0			# Next drive number
451129239Sruopt:		.byte 0x0			# Option
452129239Srusetdrv:		.byte 0x80			# Drive to force
453129239Sruflags:		.byte FLAGS			# Flags
454129239Sruticks:		.word TICKS			# Delay
455128722Sru
456128723Sru/*
457137298Skeramida * Here is the 64 byte partition table that fdisk would fiddle with.
458128723Sru */
459129239Srupartbl:		.fill 0x40,0x1,0x0		# Partition table
460129239Sru		.word MAGIC			# Magic number
461