boot0.S revision 130632
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 130632 2004-06-17 12:02:25Z 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 */
99130632Sphk		movw COMSPEED,%ax		# defined by Makefile
100130632Sphk		callw bioscom
101128722Sru#endif /* SIO */
102128723Sru/*
103128723Sru * Check what flags were loaded with us, specifically, Use a predefined Drive.
104128723Sru * If what the bios gives us is bad, use the '0' in the block instead, as well.
105128723Sru */
106129239Sru		testb $0x20,_FLAGS(%bp)		# Set number drive?
107129239Sru		jnz main.1			# Yes
108129239Sru		testb %dl,%dl			# Drive number valid?
109129239Sru		js main.2			# Possibly (0x80 set)
110129239Srumain.1:		movb _SETDRV(%bp),%dl		# Drive number to use
111128723Sru/*
112128723Sru * Whatever we decided to use, now store it into the fake
113128723Sru * partition entry that lives in the data space above us.
114128723Sru */
115129239Srumain.2:		movb %dl,_FAKE(%bp)		# Save drive number
116129239Sru		callw putn			# To new line
117129239Sru		pushw %dx			# Save drive number
118128723Sru/*
119128723Sru * Start out with a pointer to the 4th byte of the first table entry
120128723Sru * so that after 4 iterations it's beyond the end of the sector.
121128723Sru * and beyond a 256 byte boundary and has overflowed 8 bits (see next comment).
122128723Sru * (remember that the table starts 2 bytes earlier than you would expect
123128723Sru * as the bootable flag is after it in the block)
124128723Sru */
125129239Sru		movw $(partbl+0x4),%bx		# Partition table (+4)
126129239Sru		xorw %dx,%dx			# Item number
127128723Sru/*
128128723Sru * Loop around on the partition table, printing values until we
129128723Sru * pass a 256 byte boundary. The end of loop test is at main.5.
130128723Sru */
131129239Srumain.3:		movb %ch,-0x4(%bx)		# Zero active flag (ch == 0)
132129239Sru		btw %dx,_FLAGS(%bp)		# Entry enabled?
133129239Sru		jnc main.5			# No
134128723Sru/*
135128723Sru * If any of the entries in the table are
136128723Sru * the same as the 'type' in the slice table entry,
137128723Sru * 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/*
153128723Sru * If it matches get the matching element in the
154128723Sru * next array. if it doesn't, we are already
155128723Sru * pointing at its first element which points to a "?".
156128723Sru */
157129239Sru		addw $TBL1SZ,%di		# Adjust
158129239Srumain.4:		movb (%di),%cl			# Partition
159129239Sru		addw %cx,%di			#  description
160129239Sru		callw putx			# Display it
161129239Srumain.5:		incw %dx			# Next item
162129239Sru		addb $0x10,%bl			# Next entry
163129239Sru		jnc main.3			# Till done
164128723Sru/*
165128723Sru * Passed a 256 byte boundary..
166128723Sru * table is finished.
167128723Sru * Add one to the drive number and check it is valid,
168128723Sru */
169129239Sru		popw %ax			# Drive number
170129239Sru		subb $0x80-0x1,%al		# Does next
171129239Sru		cmpb NHRDRV,%al			#  drive exist? (from BIOS?)
172129239Sru		jb main.6			# Yes
173128723Sru/*
174128723Sru * If not then if there is only one drive,
175128723Sru * Don't display drive as an option.
176128723Sru */
177129239Sru		decw %ax			# Already drive 0?
178129239Sru		jz main.7			# Yes
179128723Sru/*
180128723Sru * If it was illegal or we cycled through them,
181128723Sru * then go back to drive 0.
182128723Sru */
183129239Sru		xorb %al,%al			# Drive 0
184128723Sru/*
185128723Sru * Whatever drive we selected, make it an ascii digit and save it back
186128723Sru * to the "next drive" location in the loaded block in case we
187128723Sru * want to save it for next time.
188128723Sru * This also is part of the printed drive string so add 0x80 to indicate
189128723Sru * end of string.
190128723Sru */
191129239Srumain.6:		addb $'0'|0x80,%al		# Save next
192129239Sru		movb %al,_NXTDRV(%bp)		#  drive number
193129239Sru		movw $drive,%di			# Display
194129239Sru		callw putx			#  item
195128723Sru/*
196128723Sru * Now that we've printed the drive (if we needed to), display a prompt.
197128723Sru */
198129239Srumain.7:		movw $prompt,%si		# Display
199129239Sru		callw putstr			#  prompt
200129239Sru		movb _OPT(%bp),%dl		# Display
201129239Sru		decw %si			#  default
202129239Sru		callw putkey			#  key
203130343Sphk/*
204130343Sphk * Start of input loop.  Beep and take note of time
205130343Sphk */
206130343Sphkmain.10:	movb $ASCII_BEL,%al		# Signal
207130343Sphk		callw putchr			#  beep!
208129239Sru		xorb %ah,%ah			# BIOS: Get
209129239Sru		int $0x1a			#  system time
210129239Sru		movw %dx,%di			# Ticks when
211130343Sphk		addw _TICKS(%bp),%di		#  timeout
212128723Sru/*
213128723Sru * Busy loop, looking for keystrokes but
214128723Sru * keeping one eye on the time.
215128723Sru */
216128722Srumain.8:
217128722Sru#ifndef SIO
218129239Sru		movb $0x1,%ah			# BIOS: Check
219129239Sru		int $0x16			#  for keypress
220129239Sru		jnz main.11			# Have one
221128722Sru#else /* SIO */
222129239Sru		movb $0x03,%ah			# BIOS: Read COM
223130343Sphk		call bioscom
224129239Sru		testb $0x01,%ah			# Check line status
225129239Sru		jnz main.11 			# (bit 1 indicates input)
226128722Sru#endif /* SIO */
227129239Sru		xorb %ah,%ah			# BIOS: Get
228129239Sru		int $0x1a			#  system time
229129239Sru		cmpw %di,%dx			# Timeout?
230129239Sru		jb main.8			# No
231128723Sru/*
232128723Sru * If timed out or defaulting, come here.
233128723Sru */
234129239Srumain.9:		movb _OPT(%bp),%al		# Load default
235129239Sru		jmp main.12			# Join common code
236128723Sru/*
237128723Sru * Get the keystroke.
238128723Sru */
239128722Srumain.11:
240128722Sru#ifndef SIO
241129239Sru		xorb %ah,%ah			# BIOS: Get
242129239Sru		int $0x16			#  keypress
243129239Sru		movb %ah,%al			# Scan code
244128722Sru#else /* SIO */
245129239Sru		movb $0x02,%ah			# BIOS: Receive
246130343Sphk		call bioscom
247128722Sru#endif /* SIO */
248128723Sru/*
249128723Sru * If it's CR act as if timed out.
250128723Sru */
251128722Sru#ifndef SIO
252129239Sru		cmpb $KEY_ENTER,%al		# Enter pressed?
253128722Sru#else /* SIO */
254129239Sru		cmpb $ASCII_CR,%al		# Enter pressed?
255128722Sru#endif /* SIO */
256129239Sru		je main.9			# Yes
257128723Sru/*
258128723Sru * Otherwise check if legal
259128723Sru * If not ask again.
260128723Sru */
261128722Sru#ifndef SIO
262129239Sru		subb $KEY_F1,%al		# Less F1 scan code
263129239Sru		cmpb $0x4,%al			# F1..F5?
264129239Sru		jna main.12			# Yes
265129239Sru		subb $(KEY_1 - KEY_F1),%al	# Less #1 scan code
266128722Sru#else /* SIO */
267129239Sru		subb $'1',%al			# Less '1' ascii character
268128722Sru#endif /* SIO */
269129239Sru		cmpb $0x4,%al			# #1..#5?
270129239Sru		ja main.10			# No
271128723Sru/*
272128723Sru * We have a selection.
273128723Sru * but if it's a bad selection go back to complain.
274128723Sru * The bits in MNUOPT were set when the options were printed.
275128723Sru * Anything not printed is not an option.
276128723Sru */
277129239Srumain.12:	cbtw				# Option
278129239Sru		btw %ax,_MNUOPT(%bp)	 	#  enabled?
279129239Sru		jnc main.10			# No
280128723Sru/*
281128723Sru * Save the info in the original tables
282128723Sru * for rewriting to the disk.
283128723Sru */
284129239Sru		movb %al,_OPT(%bp)		# Save option
285129239Sru		movw $FAKE,%si			# Partition for write
286129239Sru		movb (%si),%dl			# Drive number
287129239Sru		movw %si,%bx			# Partition for read
288129239Sru		cmpb $0x4,%al			# F5/#5 pressed?
289129239Sru		pushf				# Save
290129239Sru		je main.13			# Yes
291129239Sru		shlb $0x4,%al			# Point to
292129239Sru		addw $partbl,%ax		#  selected
293129239Sru		xchgw %bx,%ax	 		#  partition
294129239Sru		movb $0x80,(%bx)		# Flag active
295128723Sru/*
296128723Sru * If not asked to do a write-back (flags 0x40) don't do one.
297128723Sru */
298129239Srumain.13:	pushw %bx			# Save
299129239Sru		testb $0x40,_FLAGS(%bp)		# No updates?
300129239Sru		jnz main.14			# Yes
301129239Sru		movw $start,%bx			# Data to write
302129239Sru		movb $0x3,%ah			# Write sector
303129239Sru		callw intx13			#  to disk
304129239Srumain.14:	popw %si			# Restore
305129239Sru		popf				# Restore
306128723Sru/*
307128723Sru * If going to next drive, replace drive with selected one.
308128723Sru * Remember to un-ascii it. Hey 0x80 is already set, cool!
309128723Sru */
310129239Sru		jne main.15			# If not F5/#5
311129239Sru		movb _NXTDRV(%bp),%dl		# Next drive
312129239Sru		subb $'0',%dl			#  number
313128723Sru/*
314128723Sru * load  selected bootsector to the LOAD location in RAM.
315128723Sru * If it fails to read or isn't marked bootable, treat it
316128723Sru * as a bad selection.
317128723Sru * XXX what does %si carry?
318128723Sru */
319129239Srumain.15:	movw $LOAD,%bx			# Address for read
320129239Sru		movb $0x2,%ah			# Read sector
321129239Sru		callw intx13			#  from disk
322129239Sru		jc main.10			# If error
323129239Sru		cmpw $MAGIC,0x1fe(%bx)		# Bootable?
324129239Sru		jne main.10			# No
325129239Sru		pushw %si			# Save
326129239Sru		movw $crlf,%si			# Leave some
327129239Sru		callw puts			#  space
328129239Sru		popw %si			# Restore
329129239Sru		jmp *%bx			# Invoke bootstrap
330128722Sru
331128723Sru/*
332128723Sru * Display routines
333128723Sru */
334128722Sruputkey:
335128722Sru#ifndef SIO
336129239Sru		movb $'F',%al			# Display
337129239Sru		callw putchr			#  'F'
338128722Sru#endif /* SIO */
339129239Sru		movb $'1',%al			# Prepare
340129239Sru		addb %dl,%al			#  digit
341129239Sru		jmp putstr.1			# Display the rest
342128722Sru
343128723Sru/*
344128723Sru * Display the option and note that it is a valid option.
345128723Sru * That last point is a bit tricky..
346128723Sru */
347129239Sruputx:		btsw %dx,_MNUOPT(%bp)		# Enable menu option
348129239Sru		movw $item,%si			# Display
349129239Sru		callw putkey			#  key
350129239Sru		movw %di,%si			# Display the rest
351128722Sru
352129239Sruputs:		callw putstr			# Display string
353128722Sru
354129239Sruputn:		movw $crlf,%si			# To next line
355128722Sru
356129239Sruputstr:		lodsb				# Get byte
357129239Sru		testb $0x80,%al 		# End of string?
358129239Sru		jnz putstr.2			# Yes
359129239Sruputstr.1:	callw putchr			# Display char
360129239Sru		jmp putstr			# Continue
361129239Sruputstr.2:	andb $~0x80,%al 		# Clear MSB
362128722Sru
363130343Sphk#ifndef SIO
364128722Sruputchr:
365129239Sru		pushw %bx			# Save
366129239Sru		movw $0x7,%bx	 		# Page:attribute
367129239Sru		movb $0xe,%ah			# BIOS: Display
368129239Sru		int $0x10			#  character
369129239Sru		popw %bx			# Restore
370130343Sphk		retw				# To caller
371128722Sru#else /* SIO */
372130343Sphkputchr:
373130343Sphk		movb $0x01,%ah			# BIOS: Send
374130343Sphkbioscom:
375129239Sru		pushw %dx			# Save
376129239Sru		xorw %dx,%dx 			# Use COM1
377129239Sru		int $0x14			#  Character
378129239Sru		popw %dx			# Restore
379130343Sphk		retw				# To caller
380128722Sru#endif /* SIO */
381128722Sru
382128723Sru/* One-sector disk I/O routine */
383128722Sru
384129239Sruintx13:		movb 0x1(%si),%dh		# Load head
385129239Sru		movw 0x2(%si),%cx		# Load cylinder:sector
386129239Sru		movb $0x1,%al			# Sector count
387129239Sru		pushw %si			# Save
388129239Sru		movw %sp,%di			# Save
389129239Sru		testb $0x80,_FLAGS(%bp)		# Use packet interface?
390129239Sru		jz intx13.1			# No
391129239Sru		pushl $0x0			# Set the
392129239Sru		pushl 0x8(%si)			# LBA address
393129239Sru		pushw %es			# Set the transfer
394129239Sru		pushw %bx			#  buffer address
395129239Sru		push  $0x1			# Block count
396129239Sru		push  $0x10			# Packet size
397129239Sru		movw %sp,%si			# Packet pointer
398129239Sru		decw %ax			# Verify off
399129239Sru		orb $0x40,%ah			# Use disk packet
400129239Sruintx13.1:	int $0x13			# BIOS: Disk I/O
401129239Sru		movw %di,%sp			# Restore
402129239Sru		popw %si			# Restore
403129239Sru		retw				# To caller
404128722Sru
405128723Sru/* Menu strings */
406128722Sru
407128722Sruitem:		.ascii "  ";	     .byte ' '|0x80
408128722Sruprompt:		.ascii "\nDefault:"; .byte ' '|0x80
409128722Srucrlf:		.ascii "\r";	     .byte '\n'|0x80
410128722Sru
411128723Sru/* Partition type tables */
412128722Sru
413128722Srutables:
414128723Sru/*
415128723Sru * These entries identify invalid or NON BOOT types and partitions.
416128723Sru */
417128722Sru		.byte 0x0, 0x5, 0xf
418128723Sru/*
419128723Sru * These values indicate bootable types we know the names of
420128723Sru */
421128722Sru		.byte 0x1, 0x4, 0x6, 0xb, 0xc, 0xe, 0x83
422128722Sru		.byte 0x9f, 0xa5, 0xa6, 0xa9
423128723Sru/*
424128723Sru * These are offsets that match the known names above and point to the strings
425128723Sru * that will be printed.
426128723Sru */
427129239Sru		.byte os_misc-. 		# Unknown
428129239Sru		.byte os_dos-.			# DOS
429129239Sru		.byte os_dos-.			# DOS
430129239Sru		.byte os_dos-.			# DOS
431129239Sru		.byte os_dos-.			# Windows
432129239Sru		.byte os_dos-.			# Windows
433129239Sru		.byte os_dos-.			# Windows
434129239Sru		.byte os_linux-.		# Linux
435129239Sru		.byte os_bsd-.			# BSD/OS
436129239Sru		.byte os_freebsd-.		# FreeBSD
437129239Sru		.byte os_bsd-.			# OpenBSD
438129239Sru		.byte os_bsd-.			# NetBSD
439128723Sru/*
440128723Sru * And here are the strings themselves. 0x80 or'd into a byte indicates
441128723Sru * the end of the string. (not so great for Russians but...)
442128723Sru */
443128722Sruos_misc:	.ascii "?";    .byte '?'|0x80
444128722Sruos_dos:		.ascii "DO";   .byte 'S'|0x80
445128722Sruos_linux:	.ascii "Linu"; .byte 'x'|0x80
446128722Sruos_freebsd:	.ascii "Free"
447128722Sruos_bsd:		.ascii "BS";   .byte 'D'|0x80
448128722Sru
449128722Sru		.org PRT_OFF-0xe,0x90
450128722Sru
451129239Sru		.word B0MAGIC			# Magic number
452128722Sru
453128723Sru/*
454128723Sru * These values are sometimes changed before writing back to the drive
455128723Sru * Be especially careful that nxtdrv: must come after drive:, as it
456128723Sru * is part of the same string.
457128723Sru */
458128722Srudrive:		.ascii "Drive "
459129239Srunxtdrv:		.byte 0x0			# Next drive number
460129239Sruopt:		.byte 0x0			# Option
461129239Srusetdrv:		.byte 0x80			# Drive to force
462129239Sruflags:		.byte FLAGS			# Flags
463129239Sruticks:		.word TICKS			# Delay
464128722Sru
465128723Sru/*
466128723Sru * here is the 64 byte partition table that fdisk would fiddle with.
467128723Sru */
468129239Srupartbl:		.fill 0x40,0x1,0x0		# Partition table
469129239Sru		.word MAGIC			# Magic number
470