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