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