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