Upgrade less(1). 1/2
[dragonfly.git] / sys / boot / pc32 / boot2 / boot1.S
1 /*
2  * Copyright (c) 2003,2004 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  * 
34  * Copyright (c) 1998 Robert Nordier
35  * All rights reserved.
36  *
37  * Redistribution and use in source and binary forms are freely
38  * permitted provided that the above copyright notice and this
39  * paragraph and the following disclaimer are duplicated in all
40  * such forms.
41  *
42  * This software is provided "AS IS" and without any express or
43  * implied warranties, including, without limitation, the implied
44  * warranties of merchantability and fitness for a particular
45  * purpose.
46  *
47  * $FreeBSD: src/sys/boot/i386/boot2/boot1.s,v 1.23 2003/08/22 01:59:28 imp Exp $
48  * $DragonFly: src/sys/boot/pc32/boot2/boot1.S,v 1.10 2008/09/13 11:45:45 corecode Exp $
49  */
50
51 #include "../bootasm.h"
52         
53 // Partition Constants 
54                 .set PRT_OFF,0x1be              // Partition offset
55                 .set PRT_NUM,0x4                // Partitions
56                 .set PRT_BSD,0xa5               // Partition type
57                 .set PRT_DFLY,0x6c              // Partition type
58
59 // Flag Bits
60                 .set FL_PACKET,0x80             // Packet mode
61
62 // Misc. Constants
63                 .set SIZ_PAG,0x1000             // Page size
64                 .set SIZ_SEC,0x200              // Sector size
65 #ifndef NSECT
66                 .set NSECT,0x10
67 #endif
68
69                 .globl start
70                 .globl xread
71                 .code16
72
73 start:          jmp main                        // Start recognizably
74
75 // This is the start of a standard BIOS Parameter Block (BPB). Most bootable
76 // FAT disks have this at the start of their MBR. While normal BIOS's will
77 // work fine without this section, IBM's El Torito emulation "fixes" up the
78 // BPB by writing into the memory copy of the MBR. Rather than have data
79 // written into our xread routine, we'll define a BPB to work around it.
80 // The data marked with (T) indicates a field required for a ThinkPad to
81 // recognize the disk and (W) indicates fields written from IBM BIOS code.
82 // The use of the BPB is based on what OpenBSD and NetBSD implemented in
83 // their boot code but the required fields were determined by trial and error.
84 //
85 // Note: If additional space is needed in boot1, one solution would be to
86 // move the "prompt" message data (below) to replace the OEM ID.
87
88                 .org 0x03, 0x00
89 oemid:          .space 0x08, 0x00       // OEM ID
90
91                 .org 0x0b, 0x00
92 bpb:            .word   512             // sector size (T)
93                 .byte   0               // sectors/clustor
94                 .word   0               // reserved sectors
95                 .byte   0               // number of FATs
96                 .word   0               // root entries
97                 .word   0               // small sectors
98                 .byte   0               // media type (W)
99                 .word   0               // sectors/fat
100                 .word   18              // sectors per track (T)
101                 .word   2               // number of heads (T)
102                 .long   0               // hidden sectors (W)
103                 .long   0               // large sectors
104
105                 .org 0x24, 0x00
106 ebpb:           .byte   0               // BIOS physical drive number (W)
107
108                 .org 0x25,0x90
109 // 
110 // Trampoline used by boot2 to call read to read data from the disk via
111 // the BIOS.  Call with:
112 //
113 // %cx:%ax      - long    - LBA to read in
114 // %es:(%bx)    - caddr_t - buffer to read data into
115 // %dl          - byte    - drive to read from
116 // %dh          - byte    - num sectors to read
117 // 
118
119 xread:          push %ss                        // Address
120                 pop %ds                         //  data
121 //
122 // Setup an EDD disk packet and pass it to read
123 // 
124 xread.1:                                        // Starting
125                 pushl $0x0                      //  absolute
126                 push %cx                        //  block
127                 push %ax                        //  number
128                 push %es                        // Address of
129                 push %bx                        //  transfer buffer
130                 xor %ax,%ax                     // Number of
131                 movb %dh,%al                    //  blocks to
132                 push %ax                        //  transfer
133                 push $0x10                      // Size of packet
134                 mov %sp,%bp                     // Packet pointer
135                 callw read                      // Read from disk
136                 lea 0x10(%bp),%sp               // Clear stack
137                 lret                            // To far caller
138 // 
139 // Load the rest of boot2 and BTX up, copy the parts to the right locations,
140 // and start it all up.
141 //
142
143 //
144 // Setup the segment registers to flat addressing (segment 0) and setup the
145 // stack to end just below the start of our code.
146 // 
147 // XXX note - our origin (start) points to the MEM_BIOS_LADDR.  We run
148 // from there but boot2 later on calls xread at BOOT1_ORIGIN.
149 //
150 main:           cld                             // String ops inc
151                 xor %cx,%cx                     // Zero
152                 mov %cx,%es                     // Address
153                 mov %cx,%ds                     //  data
154                 mov %cx,%ss                     // Set up
155                 mov $start,%sp                  //  stack
156 //
157 // Relocate ourself to BOOT1_ORIGIN.  Since %cx == 0, the inc %ch sets
158 // %cx == 0x100 (256 words == 512 bytes).
159 // 
160                 mov %sp,%si                     // Source
161                 mov $BOOT1_ORIGIN,%di           // Destination
162                 incb %ch                        // Word count
163                 rep                             // Copy
164                 movsw                           //  code
165 //
166 // If we are on a hard drive, then load the MBR and look for the first
167 // FreeBSD slice.
168 //
169 // Note, we can't use the fake partition entry (part4), as it may contain
170 // garbage if this is a normal boot1 on a slice, verses a dangerously
171 // dedicated disk.  Hardwire sector 0 to acquire the MBR
172 // 
173                 xor %ax,%ax
174                 xor %cx,%cx
175                 cmpb $0x80,%dl                  // Hard drive?
176                 jb main.4                       // No
177                 movb $0x1,%dh                   // Block count
178                 callw nread_alt                 // Read MBR
179                 mov $0x1,%cx                    // Two passes
180 main.1:         mov $BOOT2_LOAD_BUF+PRT_OFF,%si // Partition table
181                 movb $0x1,%dh                   // Partition
182 main.2:         cmpb $PRT_BSD,0x4(%si)          // FreeBSD / old DFly
183                 je main.2a
184                 cmpb $PRT_DFLY,0x4(%si)         // New DFly
185                 jne main.3                      // No
186 main.2a:        jcxz main.5                     // If second pass
187                 testb $0x80,(%si)               // Active?
188                 jnz main.5                      // Yes
189 main.3:         add $0x10,%si                   // Next entry
190                 incb %dh                        // Partition
191                 cmpb $0x1+PRT_NUM,%dh           // In table?
192                 jb main.2                       // Yes
193                 dec %cx                         // Do two
194                 jcxz main.1                     //  passes
195 //
196 // If we get here, we didn't find any FreeBSD slices at all, so print an
197 // error message and die.
198 // 
199                 jmp error                       // Error
200 //
201 // Floppies use partition 0 of drive 0.
202 // 
203 main.4:         xor %dx,%dx                     // Partition:drive
204 //
205 // Ok, we have a slice and drive in %dx now, so use that to locate and load
206 // boot2.  %si references the start of the slice we are looking for, so go
207 // ahead and load up the first N sectors (boot1 + boot2) from that.
208 //
209 // N is 16 for boot1 in a disklabel32 and up to 32 in a disklabel64.  The
210 // disklabel64 can hold up to 64 sectors but MEM_BTX_USR+BOOT2_VORIGIN
211 // will overflow the segment if we use more then 32 sectors.
212 //
213 // When we read it in, we conveniently use BOOT2_LOAD_BUF (0x8c00) as our
214 // transfer buffer.  Thus, boot1 ends up at 0x8c00, and boot2 starts at
215 // 0x8c00 + 0x200 = 0x8e00.
216 //
217 // The first part of boot2 is the disklabel, which is 0x200 bytes long.
218 // The second part is BTX, which is thus loaded into 0x9000, which is where
219 // it also runs from.  The boot2.bin binary starts right after the end of
220 // BTX, so we have to figure out where the start of it is and then move the
221 // binary to 0xc000.  Normally, BTX clients start at MEM_BTX_USR, or 0xa000,
222 // but when we use btxld to create boot2, we use an entry point of 0x2000. 
223 // That entry point is relative to MEM_BTX_USR; thus boot2.bin starts
224 // at 0xc000.
225 // 
226 // MEM_BTX_USR_ARG will be overwritten by the disk read and the relocation
227 // loop, so we must store the argument after completing said loops.
228 //
229 main.5:         pushw %dx                       // Save args
230                 movb $NSECT,%dh                 // Sector count
231 #ifdef DISKLABEL64
232                                                 // In disklabel64 boot2 starts
233                 addl $7,0x8(%si)                // offset 0x1000.
234 #endif
235                 callw nread                     // Read disk
236                 mov $MEM_BTX_ORG,%bx            // Base of BTX header
237                 mov 0xa(%bx),%si                // Get BTX text length (btx.S)
238                 add %bx,%si                     // %si = start of boot2.bin
239                                                 // %di = relocation target
240                 mov $MEM_BTX_USR+BOOT2_VORIGIN,%di 
241                 mov $MEM_BTX_ORG+(NSECT-1)*SIZ_SEC,%cx
242                 sub %si,%cx                     // %cx = Size of boot2 client
243                 rep                             // Relocate boot2
244                 movsb
245                 popw MEM_BTX_USR_ARG            // save (disk,slice) for boot2
246
247 #if 0
248                 // XXX DISABLED.  This makes incorrect assumptions about
249                 // where BSS begins, potentially leaving garbage in the BSS
250                 // space.  The BSS zeroing code has been moved to
251                 // btx/lib/btxcsu.S (BTX client startup code) where we have
252                 // more definitive knowledge about where BSS resides.
253                 //
254                 // %cx now contains 0.  Calculate 0x[1]0000 - %di to get a
255                 // count of assumed BSS bytes from the end of boot2.bin up
256                 // to 0x10000, then zero it out.
257                 //
258                 sub %di,%cx
259                 xorb %al,%al
260                 rep
261                 stosb
262 #endif
263                 callw seta20                    // Enable A20
264
265                 // YYY
266                 pushw $MEM_BTX_ENTRY            // Start BTX
267                 retw
268
269                 /*
270                  * Enable A20. Put upper limit on amount of time we wait for the
271                  * keyboard controller to get ready (65K x ISA access time). If
272                  * we wait more than that amount it's likely that the hardware
273                  * is legacy-free and simply doesn't have keyboard controller
274                  * and don't need enabling A20 at all.
275                  */
276 seta20:         cli                             # Disable interrupts
277                 xor %cx,%cx                     # Clear
278 seta20.1:       inc %cx                         # Increment, overflow?
279                 jz seta20.3                     # Yes
280                 inb $0x64,%al                   # Get status
281                 testb $0x2,%al                  # Busy?
282                 jnz seta20.1                    # Yes
283                 movb $0xd1,%al                  # Command: Write
284                 outb %al,$0x64                  #  output port
285 seta20.2:       inb $0x64,%al                   # Get status
286                 testb $0x2,%al                  # Busy?
287                 jnz seta20.2                    # Yes
288                 movb $0xdf,%al                  # Enable
289                 outb %al,$0x60                  #  A20
290 seta20.3:       sti                             # Enable interrupts
291                 retw                            # To caller
292
293 // 
294 // Trampoline used to call read from within boot1.
295 // 
296 nread:
297                 mov 0x8(%si),%ax                // Get
298                 mov 0xa(%si),%cx                //  LBA
299 nread_alt:
300                 mov $BOOT2_LOAD_BUF,%bx         // Transfer buffer
301                 push %cs                        // Read from
302                 callw xread.1                   //  disk
303                 jnc return                      // If success, return
304
305 // Print that an error occured (no room to determine which error
306 // occured) and the prompt.  Then wait for a keypress, then reboot the
307 // machine.
308 // 
309 error:
310                 mov $prompt,%si                 // Display
311                 callw putstr                    //  prompt
312                 xorb %ah,%ah                    // BIOS: Get
313                 int $0x16                       //  keypress
314                 movw $0x1234, BDA_BOOT          // Do a warm boot
315                 ljmp $0xf000,$0xfff0            // reboot the machine
316 // 
317 // Display a null-terminated string using the BIOS output.
318 // 
319 putstr.0:       mov $0x7,%bx                    // Page:attribute
320                 movb $0xe,%ah                   // BIOS: Display
321                 int $0x10                       //  character
322 putstr:         lodsb                           // Get char
323                 testb %al,%al                   // End of string?
324                 jne putstr.0                    // No
325
326 //
327 // Overused return code.  ereturn is used to return an error from the
328 // read function.  Since we assume putstr succeeds, we (ab)use the
329 // same code when we return from putstr. 
330 // 
331 ereturn:        movb $0x1,%ah                   // Invalid
332                 stc                             //  argument
333 return:         retw                            // To caller
334 // 
335 // Reads sectors from the disk.  If EDD is enabled, then check if it is
336 // installed and use it if it is.  If it is not installed or not enabled, then
337 // fall back to using CHS.  Since we use a LBA, if we are using CHS, we have to
338 // fetch the drive parameters from the BIOS and divide it out ourselves.
339 // Call with:
340 //
341 // %dl  - byte     - drive number
342 // stack - 10 bytes - EDD Packet
343
344 read:
345                 /*
346                  * Try EDD mode first.  If not enabled or no BIOS support
347                  * exists, fall back to CHS mode.
348                  */
349                 testb   $FL_PACKET,%cs:BOOT1_ORIGIN+flags-start
350                 jz      read.1
351
352                 /*
353                  * BIOS: check extensions present
354                  */
355                 mov     $0x55aa,%bx
356                 push    %dx
357                 movb    $0x41,%ah
358                 int     $0x13
359                 pop     %dx
360                 jc      read.1                  /* BIOS error return */
361                 cmp     $0xaa55,%bx             /* check for proper magic */
362                 jne     read.1
363                 testb $0x1,%cl                  /* packet interface support? */
364                 jz      read.1
365
366                 /*
367                  * Issue packet command.
368                  * BIOS: Extended read command
369                  */
370                 mov     %bp,%si
371                 movb    $0x42,%ah
372                 int     $0x13
373                 retw
374
375                 /*
376                  * Fallback to CHS mode
377                  */
378 read.1:
379                 push %dx                        // Save
380                 movb $0x8,%ah                   // BIOS: Get drive
381                 int $0x13                       //  parameters
382                 movb %dh,%ch                    // Max head number
383                 pop %dx                         // Restore
384                 jc return                       // If error
385                 andb $0x3f,%cl                  // Sectors per track
386                 jz ereturn                      // If zero
387                 cli                             // Disable interrupts
388                 mov 0x8(%bp),%eax               // Get LBA
389                 push %dx                        // Save
390                 movzbl %cl,%ebx                 // Divide by
391                 xor %edx,%edx                   //  sectors
392                 div %ebx                        //  per track
393                 movb %ch,%bl                    // Max head number
394                 movb %dl,%ch                    // Sector number
395                 inc %bx                         // Divide by
396                 xorb %dl,%dl                    //  number
397                 div %ebx                        //  of heads
398                 movb %dl,%bh                    // Head number
399                 pop %dx                         // Restore
400                 cmpl $0x3ff,%eax                // Cylinder number supportable?
401                 sti                             // Enable interrupts
402                 ja ereturn                      // No, failed
403                 xchgb %al,%ah                   // Set up cylinder
404                 rorb $0x2,%al                   //  number
405                 orb %ch,%al                     // Merge
406                 inc %ax                         //  sector
407                 xchg %ax,%cx                    //  number
408                 movb %bh,%dh                    // Head number
409                 subb %ah,%al                    // Sectors this track
410                 mov 0x2(%bp),%ah                // Blocks to read
411                 cmpb %ah,%al                    // To read
412                 jb read.2                       //  this
413 #ifdef  TRACK_AT_A_TIME
414                 movb %ah,%al                    //  track
415 #else
416                 movb $1,%al                     //  one sector
417 #endif
418 read.2:         mov $0x5,%di                    // Try count
419 read.3:         les 0x4(%bp),%bx                // Transfer buffer
420                 push %ax                        // Save
421                 movb $0x2,%ah                   // BIOS: Read
422                 int $0x13                       //  from disk
423                 pop %bx                         // Restore
424                 jnc read.4                      // If success
425                 dec %di                         // Retry?
426                 jz read.6                       // No
427                 xorb %ah,%ah                    // BIOS: Reset
428                 int $0x13                       //  disk system
429                 xchg %bx,%ax                    // Block count
430                 jmp read.3                      // Continue
431 read.4:         movzbw %bl,%ax                  // Sectors read
432                 add %ax,0x8(%bp)                // Adjust
433                 jnc read.5                      //  LBA,
434                 incw 0xa(%bp)                   //  transfer
435 read.5:         shlb %bl                        //  buffer
436                 add %bl,0x5(%bp)                //  pointer,
437                 sub %al,0x2(%bp)                //  block count
438                 ja read.1                       // If not done
439 read.6:         retw                            // To caller
440
441 // Messages
442
443 prompt:         .asciz " error\r\n"
444
445 flags:          .byte FLAGS                     // Flags
446
447                 .org PRT_OFF,0x90
448
449 // Partition table
450 //
451 // THIS MAY NOT BE WRITTEN OUT TO THE BOOT1 AREA OF THE DISKLABEL.  This
452 // section is only written out when the disklabel is placed on the raw
453 // disk instead of in a slice, when creating a dangerously dedicated disk.
454
455                 .fill 0x30,0x1,0x0
456 part4:          .byte 0x80, 0x00, 0x01, 0x00
457                 .byte 0xa5, 0xfe, 0xff, 0xff
458                 .byte 0x00, 0x00, 0x00, 0x00
459                 .byte 0x50, 0xc3, 0x00, 0x00    // 50000 sectors long, bleh
460
461                 .word 0xaa55                    // Magic number