Merge from vendor branch OPENSSH:
[dragonfly.git] / sys / boot / i386 / bootn0 / boot0.S
1 /*
2  * Copyright (c) 1998 Robert Nordier
3  * Copyright (c) 2004 by Matthew Dillon, 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  * $DragonFly: src/sys/boot/i386/bootn0/Attic/boot0.S,v 1.2 2004/07/18 18:42:08 dillon Exp $
16  *
17  * A 512-byte boot manager.
18  */
19
20                 .set NHRDRV,0x475       # Number of hard drives
21                 .set FAKE,0x800         # Partition entry
22                 .set BIOS_LOAD,0x7c00   # BIOS Load address
23                 .set ORG_ADDR,0x91000   # 'start' symbol
24                 .set ORG_SEG,0x900      # leave room in segment for stk
25                 .set ORG_OFF,0x1000
26
27                 .set PRT_OFF,0x1be      # Partition table
28
29                 .set TBL0SZ,3           # Table 0 size
30                 .set TBL1SZ,9           # Table 1 size
31
32                 .set MAGIC,0xaa55       # Magic: bootable
33                 .set B0MAGIC,0xbb66     # Identification
34
35                 .set KEY_ENTER,0x1c     # Enter key scan code
36                 .set KEY_F1,0x3b        # F1 key scan code
37                 .set KEY_1,0x02         # #1 key scan code
38
39                 #
40                 # Addresses in the sector of embedded data values.  Accessed
41                 # with negative offsets from the end of the relocated sector
42                 # (%ebp)
43                 #
44                 .set _NXTDRV,-0x48      # Next drive
45                 .set _OPT,-0x47         # Default option
46                 .set _SETDRV,-0x46      # Drive to force
47                 .set _FLAGS,-0x45       # Flags
48                 .set _TICKS,-0x44       # Timeout ticks
49                 .set _FAKE,0x0          # Fake partition entry
50                 .set _MNUOPT,0xc        # Menu options
51
52 #define DRELOC(symbol)  (symbol-start+ORG_OFF)
53
54                 .globl start            # Entry point
55                 .code16                 # This runs in real mode
56
57                 # Initialize segments and relocate the boot sector.  Our
58                 # stack starts at our target relocation address and grows
59                 # downward while the copy of boot0 grows upwards.
60                 #
61
62 start:          cld                     # String ops inc
63                 movw $ORG_SEG,%ax       # Setup various segments
64                 movw %ax,%es
65                 movw %ax,%ss            # (coupled with next instruction)
66                 movw $ORG_OFF,%sp       # stack downward from start
67
68                 xorw %ax,%ax
69                 movw %ax,%ds            # source is in segment 0
70                 movw $BIOS_LOAD,%si     # source is the BIOS load address
71                 movw %sp,%di
72                 movw $0x100,%cx         # 256x2 = 512 bytes
73                 rep
74                 movsw
75
76                 # Note: %ds points at segment 0
77                 #       %es points at our relocated segment
78                 #       %ss points at our relocated segment
79                 #       (%ax still 0)
80                 #
81                 # Remember that %bp relative addresses uses the stack segment
82
83                 movw %di,%bp            # address variables
84                 movb $8,%cl             # clear 16 bytes
85                 rep
86                 stosw
87
88                 # Point %ds at our relocated segment, cleanup, and
89                 # jump to our relocated segment.
90                 #
91                 movw %ss,%ax
92                 movw %ax,%ds
93                 incb _FAKE+2(%bp)       # Start with sector 1
94                 ljmp $ORG_SEG,$DRELOC(main)
95
96                 # Check what flags were loaded with us.  Specifically, use a
97                 # predefined drive.  If the BIOS gives us junk use the '0'
98                 # in the block instead, as well.
99                 #
100 main:           testb $0x20,_FLAGS(%bp) # Set number drive?
101                 jnz main.1              # Yes
102                 testb %dl,%dl           # Drive number valid?
103                 js main.2               # Possibly (0x80 set)
104 main.1:         movb _SETDRV(%bp),%dl   # Drive number to use
105
106                 # Save our working drive in a temporary variable
107                 #
108 main.2:         movb %dl,_FAKE(%bp)     # Save drive number
109                 callw putn              # To new line
110                 pushw %dx               # Save drive number
111
112                 # Start out with a pointer to the 4th byte of the first table
113                 # entry so that after 4 iterations it is beyond the end of
114                 # the sector (hits a 256 byte boundary and overflows 8 bits)
115                 #
116                 # Remember that the table starts 2 bytes earlier then you
117                 # would expect as the bootable flag is after it in the block.
118
119                 movw $DRELOC(partbl+0x4),%bx    # Partition table (+4)
120                 xorw %dx,%dx            # Item number
121
122                 # Loop on the partition table printing values until we 
123                 # hit the 256 byte boundary (at main.5).
124                 #
125 main.3:         movb %ch,-4(%bx)        # Zero active flag (ch == 0)
126                 btw %dx,_FLAGS(%bp)     # Entry enabled?
127                 jnc main.5              # No
128
129                 # If any of the table entries match 'type' in the slice
130                 # table entry, then this is an empty or non bootable partition.
131                 # Skip it.
132                 #
133                 movb (%bx),%al          # Load type
134                 movw $DRELOC(tables),%di # Lookup tables
135                 movb $TBL0SZ,%cl        # Number of entries
136                 repne                   # Exclude partition?
137                 scasb                   # (note: uses %es by default)
138                 je main.5               # Yes
139
140                 #
141                 # Now scan the table of known types
142                 #
143                 movb $TBL1SZ,%cl        # Number of entries
144                 repne                   # Known type ?
145                 scasb
146                 jne main.4              # No
147
148                 /*
149                  * If it matches get the matching element in the next
150                  * array.  If it doesn't we are already pointing at the
151                  * first element, which points to a "?".
152                  */
153                 addw $TBL1SZ,%di        # Adjust
154 main.4:         movb (%di),%cl          # Partition description
155                 addw %cx,%di
156                 callw putx              # Display it
157 main.5:         incw %dx                # Next item 
158                 addb $0x10,%bl          # Next entry
159                 jnc main.3              # Till done
160
161                 # End of loop.  Add one to the drive number and check validity.
162                 # 
163                 popw %ax                # Drive number
164                 subb $0x80-0x1,%al      # Does next drive exist? (BIOS)
165                 cmpb NHRDRV,%al
166                 jb main.6               # Yes
167
168                 # If %ax is 1 there is only one drive in the system and we
169                 # skip the drive selection code.  Note that %ax becomes an
170                 # index.
171                 #
172                 decw %ax                # Already drive 0?
173                 jz main.7               # Yes
174
175                 # No, loop on drive selection starting with 0
176                 #
177                 xorb %al,%al            # Drive 0
178 main.6:         addb $'0'|0x80,%al      # Save next drive number
179                 movb %al,_NXTDRV(%bp)
180                 movw $DRELOC(drive),%di # Display item
181                 callw putx
182
183                 # Prompt and setup for input but do not yet initiate the
184                 # input.
185                 #
186 main.7:         movw $DRELOC(prompt),%si # Display prompt
187                 callw putstr
188                 movb _OPT(%bp),%dl      # Display default key
189                 decw %si
190                 callw putkey
191                 xorb %ah,%ah            # BIOS: Get system time
192                 int $0x1a
193                 movw %dx,%di            # Figure out tick value of timeout
194                 addw _TICKS(%bp),%di
195
196                 # Initiate input
197                 #
198 main.8:         movb $0x1,%ah           # BIOS: Check for keypress
199                 int $0x16
200                 jnz main.11
201                 xorb %ah,%ah            # BIOS: Get system time
202                 int $0x1a
203                 cmpw %di,%dx            # loop if no timeout
204                 jb main.8
205
206                 # A timeout or default comes to here
207                 #
208 main.9:         movb _OPT(%bp),%al      # Load default
209                 jmp main.12             # Join common code
210
211                 /* 
212                  * If the user's last try was bad, beep.  Since nothing was
213                  * printed, continue as if the user hadn't done anything.
214                  */
215 main.10:        movb $0x7,%al           # Signal error
216                 callw putchr
217
218                 # Obtain the keystroke we previously polled successfully for
219                 #
220 main.11:        xorb %ah,%ah            # BIOS: Get keypress
221                 int $0x16
222                 movb %ah,%al            # Scan code
223
224                 # An ENTER is equivalent to a timeout
225                 #
226                 cmpb $KEY_ENTER,%al     # Enter pressed?
227                 je main.9               # Yes
228
229                 # Look for a legal keystroke, jump to main.10 (beep) and
230                 # retry if it is not legal.
231                 #
232                 subb $KEY_F1,%al        # Less F1 scan code
233                 cmpb $0x4,%al           # F1..F5?
234                 jna main.12
235                 subb $(KEY_1-KEY_F1),%al # Less #1 scan code
236                 cmpb $0x4,%al           # #1..#5?
237                 ja main.10
238
239                 # Check the selection, complain if its bad.  Only options
240                 # that were printed (MNUOPT bit set) are valid.
241                 #
242 main.12:        cbtw                    # Option enabled?
243                 btw %ax,_MNUOPT(%bp)
244                 jnc main.10             # No
245
246                 /*
247                  * We're good.  Save the data for later rewriting back to disk
248                  */
249                 movb %al,_OPT(%bp)      # Save for future action
250                 leaw _FAKE(%bp),%si     # Partition for write
251                 movb (%si),%dl          # Drive number
252                 movw %si,%bx            # Partition for read
253                 cmpb $0x4,%al           # F5 pressed?
254                 pushf                   # Save result of check
255                 je main.13              # Yes
256                 shlb $0x4,%al           # Point to selected partition
257                 addw $DRELOC(partbl),%ax
258                 xchgw %bx,%ax
259                 movb $0x80,(%bx)        # Flag active
260
261                 # Only writeback if we were told to in _FLAGS
262                 #
263 main.13:        pushw %bx               # Save
264                 testb $0x40,_FLAGS(%bp) # No updates?
265                 jnz main.14             # Yes
266                 movw $DRELOC(start),%bx # Data to write (relative to %es)
267                 movb $0x3,%ah           # Write sector to disk
268                 callw intx13
269 main.14:        popw %si                # Restore %si for next intx13
270
271                 # Retrieve the perviously saved F5 (next drive) request
272                 # status and move us to the next drive if necessary
273                 #
274                 popf
275                 jne main.15
276                 movb _NXTDRV(%bp),%dl
277                 subb $'0',%dl
278
279                 /*
280                  * Load the selected bootsector to the LOAD location in RAM.
281                  * We are already relocated so it will not overwrite us
282                  * in particular.  If it fails to read or isn't marked bootable,
283                  * treat it was a bad selection.
284                  *
285                  * note: drive number in %dl
286                  */
287 main.15:        movw $BIOS_LOAD,%bx     # Address for read
288                 pushw $0
289                 popw %es
290                 movb $0x2,%ah           # Read sector from disk
291                 callw intx13
292                 movw %es:0x1fe(%bx),%ax # (these do not effect carry)
293                 pushw %ss
294                 popw %es
295                 jc main.10              # error on carry
296                 subw $MAGIC,%ax         # Bootable?
297                 jnz main.10
298                 pushw %ax               # seg: setup for lret (note %ax is 0)
299                 pushw %bx               # offset: setup for lret
300                 pushw %ax               # (so we can clear %ds)
301                 callw putn
302                 popw %ds
303                 lret
304
305                 # SUPPORT ROUTINES
306                 #
307                 # Display functions.  %bx is not modified by any of these
308                 # calls.
309                 #
310 putkey:         movb $'F',%al           # Display 'F'
311                 callw putchr
312                 movb $'1',%al           # Display digit and remainder of prompt
313                 addb %dl,%al
314                 jmp putstr.1
315
316                 # Display the option and note that it is a valid option
317                 # by setting the appropriate bit in _MNUOPT.
318                 #
319 putx:           btsw %dx,_MNUOPT(%bp)   # Enable menu option
320                 movw $DRELOC(item),%si  # Display key
321                 callw putkey
322                 movw %di,%si            # Display the option string
323 puts:           callw putstr            # Display string in %si
324
325 putn:           movw $DRELOC(crlf),%si  # Output a CR+LF
326                 lodsb
327 putstr.1:       callw putchr            # Output %al + string at (%si)
328 putstr:         lodsb                   # Output string at (%si)
329                 testb %al,%al
330                 jns   putstr.1
331                 andb $0x7f,%al
332 putchr:         pusha                   # %bx is not modified by this call
333                 movw $0x7,%bx           # Page:attribute
334                 movb $0xe,%ah           # BIOS: Display character
335                 int $0x10
336                 popa
337                 retw
338
339                 # One-sector disk I/O routine
340                 #
341                 # note: drive number in %dl, %si points to local variable
342                 # area holding (driveno, head, w:cylinder/sector)
343                 #
344 intx13:         movb 0x1(%si),%dh       # Load head
345                 movw 0x2(%si),%cx       # Load cylinder:sector
346                 movb $0x1,%al           # Sector count
347                 pusha
348                 pushl $0x0              # Set the LBA Address
349                 pushl 0x8(%si)
350                 pushw %es               # Set the Transfer Buffer Address
351                 pushw %bx
352                 pushw $0x1              # Block count
353                 pushw $0x10             # Packet size
354                 testb $0x80,_FLAGS(%bp) # Use packet interface?
355                 jz intx13.1
356                 movw %sp,%si            # Packet pointer (%es?)
357                 decw %ax                # Verify off
358                 orb $0x40,%ah           # Use disk packet
359 intx13.1:       int $0x13               # BIOS: Disk I/O
360                 movw %sp,%si
361                 lea  16(%si),%sp
362                 popa
363                 retw                    # WARNING: carry must be preserved
364
365                 /*
366                  * MENU STRINGS
367                  */
368
369 item:           .ascii "  "
370                 .byte ' '|0x80
371 prompt:         .ascii "\nDefault:"
372                 .byte ' '|0x80
373 crlf:           .ascii "\r"
374                 .byte '\n'|0x80
375
376                 /*
377                  * Partition table types
378                  */
379 tables:
380                 /*
381                  * These entries identify invalid or NON BOOT types and
382                  * partitions.
383                  */
384                 .byte 0x0, 0x5, 0xf
385
386                 /*
387                  * These values indicate bootable types we know the names of
388                  */
389                 .byte 0xb, 0xc, 0xe, 0x83
390                 .byte 0xa5, 0xa6, 0xa9
391
392                 /*
393                  * These are offsets that match the known names above and
394                  * point to the strings that will be printed.
395                  */
396                 .byte os_misc-.                 # Unknown
397                 .byte os_dos-.                  # Windows
398                 .byte os_dos-.                  # Windows
399                 .byte os_dos-.                  # Windows
400                 .byte os_linux-.                # Linux
401                 .byte os_bsd-.                  # FreeBSD/DragonFly
402                 .byte os_bsd-.                  # OpenBSD
403                 .byte os_bsd-.                  # NetBSD
404
405                 /*
406                  * And here are the strings themselves. 0x80 or'd into a 
407                  * byte indicates the end of the string. (not so great
408                  * for Russians but...)
409                  */
410 os_misc:        .ascii "?";    .byte '?'|0x80
411 os_dos:         .ascii "DO";   .byte 'S'|0x80
412 os_linux:       .ascii "Linu"; .byte 'x'|0x80
413 os_bsd:         .ascii "BS";   .byte 'D'|0x80
414
415                 .org PRT_OFF-0xe,0x90
416
417                 /*
418                  * 14 bytes then partition table.  Some of these values may
419                  * be adjusted by the above code and then written back to the
420                  * drive.  Be especially careful in that nxtdrv: must come 
421                  * after drive:, as it is part of the same string.
422                  */
423                 .word B0MAGIC                   # Magic number
424
425 drive:          .ascii "Drive "
426 nxtdrv:         .byte 0x0                       # Next drive number
427 opt:            .byte 0x0                       # Option
428 setdrv:         .byte 0x80                      # Drive to force
429 flags:          .byte FLAGS                     # Flags
430 ticks:          .word TICKS                     # Delay
431
432                 /*
433                  * here is the 64 byte partition table that fdisk would
434                  * fiddle with.
435                  */
436 partbl:         .fill 0x40,0x1,0x0              # Partition table
437                 .word MAGIC                     # Magic number