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