* Use id(1) instead of grep(1) to detect the presence of the smmsp
[dragonfly.git] / sys / boot / pc32 / boot2 / boot1.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/boot2/boot1.s,v 1.23 2003/08/22 01:59:28 imp Exp $
17 // $DragonFly: src/sys/boot/pc32/boot2/boot1.S,v 1.3 2003/11/10 06:08:35 dillon Exp $
18
19 // Memory Locations
20                 .set MEM_REL,0x700              // Relocation address
21                 .set MEM_ARG,0x900              // Arguments
22                 .set MEM_ORG,0x7c00             // Origin
23                 .set MEM_BUF,0x8cec             // Load area
24                 .set MEM_BTX,0x9000             // BTX start
25                 .set MEM_JMP,0x9010             // BTX entry point
26                 .set MEM_USR,0xa000             // Client start
27                 .set BDA_BOOT,0x472             // Boot howto flag
28         
29 // Partition Constants 
30                 .set PRT_OFF,0x1be              // Partition offset
31                 .set PRT_NUM,0x4                // Partitions
32                 .set PRT_BSD,0xa5               // Partition type
33
34 // Flag Bits
35                 .set FL_PACKET,0x80             // Packet mode
36
37 // Misc. Constants
38                 .set SIZ_PAG,0x1000             // Page size
39                 .set SIZ_SEC,0x200              // Sector size
40
41                 .set NSECT,0x10
42                 .globl start
43                 .globl xread
44                 .code16
45
46 start:          jmp main                        // Start recognizably
47
48 // This is the start of a standard BIOS Parameter Block (BPB). Most bootable
49 // FAT disks have this at the start of their MBR. While normal BIOS's will
50 // work fine without this section, IBM's El Torito emulation "fixes" up the
51 // BPB by writing into the memory copy of the MBR. Rather than have data
52 // written into our xread routine, we'll define a BPB to work around it.
53 // The data marked with (T) indicates a field required for a ThinkPad to
54 // recognize the disk and (W) indicates fields written from IBM BIOS code.
55 // The use of the BPB is based on what OpenBSD and NetBSD implemented in
56 // their boot code but the required fields were determined by trial and error.
57 //
58 // Note: If additional space is needed in boot1, one solution would be to
59 // move the "prompt" message data (below) to replace the OEM ID.
60
61                 .org 0x03, 0x00
62 oemid:          .space 0x08, 0x00       // OEM ID
63
64                 .org 0x0b, 0x00
65 bpb:            .word   512             // sector size (T)
66                 .byte   0               // sectors/clustor
67                 .word   0               // reserved sectors
68                 .byte   0               // number of FATs
69                 .word   0               // root entries
70                 .word   0               // small sectors
71                 .byte   0               // media type (W)
72                 .word   0               // sectors/fat
73                 .word   18              // sectors per track (T)
74                 .word   2               // number of heads (T)
75                 .long   0               // hidden sectors (W)
76                 .long   0               // large sectors
77
78                 .org 0x24, 0x00
79 ebpb:           .byte   0               // BIOS physical drive number (W)
80
81                 .org 0x25,0x90
82 // 
83 // Trampoline used by boot2 to call read to read data from the disk via
84 // the BIOS.  Call with:
85 //
86 // %cx:%ax      - long    - LBA to read in
87 // %es:(%bx)    - caddr_t - buffer to read data into
88 // %dl          - byte    - drive to read from
89 // %dh          - byte    - num sectors to read
90 // 
91
92 xread:          push %ss                        // Address
93                 pop %ds                         //  data
94 //
95 // Setup an EDD disk packet and pass it to read
96 // 
97 xread.1:                                        // Starting
98                 pushl $0x0                      //  absolute
99                 push %cx                        //  block
100                 push %ax                        //  number
101                 push %es                        // Address of
102                 push %bx                        //  transfer buffer
103                 xor %ax,%ax                     // Number of
104                 movb %dh,%al                    //  blocks to
105                 push %ax                        //  transfer
106                 push $0x10                      // Size of packet
107                 mov %sp,%bp                     // Packet pointer
108                 callw read                      // Read from disk
109                 lea 0x10(%bp),%sp               // Clear stack
110                 lret                            // To far caller
111 // 
112 // Load the rest of boot2 and BTX up, copy the parts to the right locations,
113 // and start it all up.
114 //
115
116 //
117 // Setup the segment registers to flat addressing (segment 0) and setup the
118 // stack to end just below the start of our code.
119 // 
120 main:           cld                             // String ops inc
121                 xor %cx,%cx                     // Zero
122                 mov %cx,%es                     // Address
123                 mov %cx,%ds                     //  data
124                 mov %cx,%ss                     // Set up
125                 mov $start,%sp                  //  stack
126 //
127 // Relocate ourself to MEM_REL.  Since %cx == 0, the inc %ch sets
128 // %cx == 0x100.
129 // 
130                 mov %sp,%si                     // Source
131                 mov $MEM_REL,%di                // Destination
132                 incb %ch                        // Word count
133                 rep                             // Copy
134                 movsw                           //  code
135 //
136 // If we are on a hard drive, then load the MBR and look for the first
137 // FreeBSD slice.  We use the fake partition entry below that points to
138 // the MBR when we call nread.  The first pass looks for the first active
139 // FreeBSD slice.  The second pass looks for the first non-active FreeBSD
140 // slice if the first one fails.
141 // 
142                 mov $part4,%si                  // Partition
143                 cmpb $0x80,%dl                  // Hard drive?
144                 jb main.4                       // No
145                 movb $0x1,%dh                   // Block count
146                 callw nread                     // Read MBR
147                 mov $0x1,%cx                    // Two passes
148 main.1:         mov $MEM_BUF+PRT_OFF,%si        // Partition table
149                 movb $0x1,%dh                   // Partition
150 main.2:         cmpb $PRT_BSD,0x4(%si)          // Our partition type?
151                 jne main.3                      // No
152                 jcxz main.5                     // If second pass
153                 testb $0x80,(%si)               // Active?
154                 jnz main.5                      // Yes
155 main.3:         add $0x10,%si                   // Next entry
156                 incb %dh                        // Partition
157                 cmpb $0x1+PRT_NUM,%dh           // In table?
158                 jb main.2                       // Yes
159                 dec %cx                         // Do two
160                 jcxz main.1                     //  passes
161 //
162 // If we get here, we didn't find any FreeBSD slices at all, so print an
163 // error message and die.
164 // 
165                 mov $msg_part,%si               // Message
166                 jmp error                       // Error
167 //
168 // Floppies use partition 0 of drive 0.
169 // 
170 main.4:         xor %dx,%dx                     // Partition:drive
171 //
172 // Ok, we have a slice and drive in %dx now, so use that to locate and load
173 // boot2.  %si references the start of the slice we are looking for, so go
174 // ahead and load up the first 16 sectors (boot1 + boot2) from that.  When
175 // we read it in, we conveniently use 0x8cec as our transfer buffer.  Thus,
176 // boot1 ends up at 0x8cec, and boot2 starts at 0x8cec + 0x200 = 0x8eec.
177 // The first part of boot2 is the disklabel, which is 0x114 bytes long.
178 // The second part is BTX, which is thus loaded into 0x9000, which is where
179 // it also runs from.  The boot2.bin binary starts right after the end of
180 // BTX, so we have to figure out where the start of it is and then move the
181 // binary to 0xc000.  Normally, BTX clients start at MEM_USR, or 0xa000, but
182 // when we use btxld to create boot2, we use an entry point of 0x2000.  That
183 // entry point is relative to MEM_USR; thus boot2.bin starts at 0xc000.
184 // 
185 main.5:         mov %dx,MEM_ARG                 // Save args
186                 movb $NSECT,%dh                 // Sector count
187                 callw nread                     // Read disk
188                 mov $MEM_BTX,%bx                // BTX
189                 mov 0xa(%bx),%si                // Get BTX length and set
190                 add %bx,%si                     //  %si to start of boot2.bin
191                 mov $MEM_USR+SIZ_PAG*2,%di      // Client page 2
192                 mov $MEM_BTX+(NSECT-1)*SIZ_SEC,%cx // Byte
193                 sub %si,%cx                     //  count
194                 rep                             // Relocate
195                 movsb                           //  client
196                 sub %di,%cx                     // Byte count
197                 xorb %al,%al                    // Zero assumed bss from
198                 rep                             //  the end of boot2.bin
199                 stosb                           //  up to 0x10000
200                 callw seta20                    // Enable A20
201                 jmp start+MEM_JMP-MEM_ORG       // Start BTX
202 // 
203 // Enable A20 so we can access memory above 1 meg.
204 // 
205 seta20:         cli                             // Disable interrupts
206 seta20.1:       inb $0x64,%al                   // Get status
207                 testb $0x2,%al                  // Busy?
208                 jnz seta20.1                    // Yes
209                 movb $0xd1,%al                  // Command: Write
210                 outb %al,$0x64                  //  output port
211 seta20.2:       inb $0x64,%al                   // Get status
212                 testb $0x2,%al                  // Busy?
213                 jnz seta20.2                    // Yes
214                 movb $0xdf,%al                  // Enable
215                 outb %al,$0x60                  //  A20
216                 sti                             // Enable interrupts
217                 retw                            // To caller
218 // 
219 // Trampoline used to call read from within boot1.
220 // 
221 nread:          mov $MEM_BUF,%bx                // Transfer buffer
222                 mov 0x8(%si),%ax                // Get
223                 mov 0xa(%si),%cx                //  LBA
224                 push %cs                        // Read from
225                 callw xread.1                   //  disk
226                 jnc return                      // If success, return
227                 mov $msg_read,%si               // Otherwise, set the error
228                                                 //  message and fall through to
229                                                 //  the error routine
230 // 
231 // Print out the error message pointed to by %ds:(%si) followed
232 // by a prompt, wait for a keypress, and then reboot the machine.
233 // 
234 error:          callw putstr                    // Display message
235                 mov $prompt,%si                 // Display
236                 callw putstr                    //  prompt
237                 xorb %ah,%ah                    // BIOS: Get
238                 int $0x16                       //  keypress
239                 movw $0x1234, BDA_BOOT          // Do a warm boot
240                 ljmp $0xffff,$0x0               // reboot the machine
241 // 
242 // Display a null-terminated string using the BIOS output.
243 // 
244 putstr.0:       mov $0x7,%bx                    // Page:attribute
245                 movb $0xe,%ah                   // BIOS: Display
246                 int $0x10                       //  character
247 putstr:         lodsb                           // Get char
248                 testb %al,%al                   // End of string?
249                 jne putstr.0                    // No
250
251 //
252 // Overused return code.  ereturn is used to return an error from the
253 // read function.  Since we assume putstr succeeds, we (ab)use the
254 // same code when we return from putstr. 
255 // 
256 ereturn:        movb $0x1,%ah                   // Invalid
257                 stc                             //  argument
258 return:         retw                            // To caller
259 // 
260 // Reads sectors from the disk.  If EDD is enabled, then check if it is
261 // installed and use it if it is.  If it is not installed or not enabled, then
262 // fall back to using CHS.  Since we use a LBA, if we are using CHS, we have to
263 // fetch the drive parameters from the BIOS and divide it out ourselves.
264 // Call with:
265 //
266 // %dl  - byte     - drive number
267 // stack - 10 bytes - EDD Packet
268 //
269 read:           push %dx                        // Save
270                 movb $0x8,%ah                   // BIOS: Get drive
271                 int $0x13                       //  parameters
272                 movb %dh,%ch                    // Max head number
273                 pop %dx                         // Restore
274                 jc return                       // If error
275                 andb $0x3f,%cl                  // Sectors per track
276                 jz ereturn                      // If zero
277                 cli                             // Disable interrupts
278                 mov 0x8(%bp),%eax               // Get LBA
279                 push %dx                        // Save
280                 movzbl %cl,%ebx                 // Divide by
281                 xor %edx,%edx                   //  sectors
282                 div %ebx                        //  per track
283                 movb %ch,%bl                    // Max head number
284                 movb %dl,%ch                    // Sector number
285                 inc %bx                         // Divide by
286                 xorb %dl,%dl                    //  number
287                 div %ebx                        //  of heads
288                 movb %dl,%bh                    // Head number
289                 pop %dx                         // Restore
290                 cmpl $0x3ff,%eax                // Cylinder number supportable?
291                 sti                             // Enable interrupts
292                 ja read.7                       // No, try EDD
293                 xchgb %al,%ah                   // Set up cylinder
294                 rorb $0x2,%al                   //  number
295                 orb %ch,%al                     // Merge
296                 inc %ax                         //  sector
297                 xchg %ax,%cx                    //  number
298                 movb %bh,%dh                    // Head number
299                 subb %ah,%al                    // Sectors this track
300                 mov 0x2(%bp),%ah                // Blocks to read
301                 cmpb %ah,%al                    // To read
302                 jb read.2                       //  this
303 #ifdef  TRACK_AT_A_TIME
304                 movb %ah,%al                    //  track
305 #else
306                 movb $1,%al                     //  one sector
307 #endif
308 read.2:         mov $0x5,%di                    // Try count
309 read.3:         les 0x4(%bp),%bx                // Transfer buffer
310                 push %ax                        // Save
311                 movb $0x2,%ah                   // BIOS: Read
312                 int $0x13                       //  from disk
313                 pop %bx                         // Restore
314                 jnc read.4                      // If success
315                 dec %di                         // Retry?
316                 jz read.6                       // No
317                 xorb %ah,%ah                    // BIOS: Reset
318                 int $0x13                       //  disk system
319                 xchg %bx,%ax                    // Block count
320                 jmp read.3                      // Continue
321 read.4:         movzbw %bl,%ax                  // Sectors read
322                 add %ax,0x8(%bp)                // Adjust
323                 jnc read.5                      //  LBA,
324                 incw 0xa(%bp)                   //  transfer
325 read.5:         shlb %bl                        //  buffer
326                 add %bl,0x5(%bp)                //  pointer,
327                 sub %al,0x2(%bp)                //  block count
328                 ja read                         // If not done
329 read.6:         retw                            // To caller
330 read.7:         testb $FL_PACKET,%cs:MEM_REL+flags-start // LBA support enabled?
331                 jz ereturn                      // No, so return an error
332                 mov $0x55aa,%bx                 // Magic
333                 push %dx                        // Save
334                 movb $0x41,%ah                  // BIOS: Check
335                 int $0x13                       //  extensions present
336                 pop %dx                         // Restore
337                 jc return                       // If error, return an error
338                 cmp $0xaa55,%bx                 // Magic?
339                 jne ereturn                     // No, so return an error
340                 testb $0x1,%cl                  // Packet interface?
341                 jz ereturn                      // No, so return an error
342                 mov %bp,%si                     // Disk packet
343                 movb $0x42,%ah                  // BIOS: Extended
344                 int $0x13                       //  read
345                 retw                            // To caller
346
347 // Messages
348
349 msg_read:       .asciz "Read"
350 msg_part:       .asciz "Boot"
351
352 prompt:         .asciz " error\r\n"
353
354 flags:          .byte FLAGS                     // Flags
355
356                 .org PRT_OFF,0x90
357
358 // Partition table
359
360                 .fill 0x30,0x1,0x0
361 part4:          .byte 0x80, 0x00, 0x01, 0x00
362                 .byte 0xa5, 0xfe, 0xff, 0xff
363                 .byte 0x00, 0x00, 0x00, 0x00
364                 .byte 0x50, 0xc3, 0x00, 0x00    // 50000 sectors long, bleh
365
366                 .word 0xaa55                    // Magic number