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