Commit | Line | Data |
---|---|---|
409cbc03 | 1 | /* |
cacaceec MD |
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 | * | |
409cbc03 MD |
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 $ | |
7580e818 | 48 | * $DragonFly: src/sys/boot/pc32/boot2/boot1.S,v 1.10 2008/09/13 11:45:45 corecode Exp $ |
409cbc03 | 49 | */ |
984263bc | 50 | |
409cbc03 | 51 | #include "../bootasm.h" |
984263bc | 52 | |
5ee58eed MD |
53 | // Partition Constants |
54 | .set PRT_OFF,0x1be // Partition offset | |
55 | .set PRT_NUM,0x4 // Partitions | |
56 | .set PRT_BSD,0xa5 // Partition type | |
984263bc | 57 | |
5ee58eed MD |
58 | // Flag Bits |
59 | .set FL_PACKET,0x80 // Packet mode | |
984263bc | 60 | |
5ee58eed MD |
61 | // Misc. Constants |
62 | .set SIZ_PAG,0x1000 // Page size | |
63 | .set SIZ_SEC,0x200 // Sector size | |
7580e818 | 64 | #ifndef NSECT |
5ee58eed | 65 | .set NSECT,0x10 |
7580e818 | 66 | #endif |
409cbc03 | 67 | |
984263bc MD |
68 | .globl start |
69 | .globl xread | |
70 | .code16 | |
71 | ||
5ee58eed | 72 | start: jmp main // Start recognizably |
984263bc | 73 | |
5ee58eed MD |
74 | // This is the start of a standard BIOS Parameter Block (BPB). Most bootable |
75 | // FAT disks have this at the start of their MBR. While normal BIOS's will | |
76 | // work fine without this section, IBM's El Torito emulation "fixes" up the | |
77 | // BPB by writing into the memory copy of the MBR. Rather than have data | |
78 | // written into our xread routine, we'll define a BPB to work around it. | |
79 | // The data marked with (T) indicates a field required for a ThinkPad to | |
80 | // recognize the disk and (W) indicates fields written from IBM BIOS code. | |
81 | // The use of the BPB is based on what OpenBSD and NetBSD implemented in | |
82 | // their boot code but the required fields were determined by trial and error. | |
83 | // | |
84 | // Note: If additional space is needed in boot1, one solution would be to | |
85 | // move the "prompt" message data (below) to replace the OEM ID. | |
984263bc MD |
86 | |
87 | .org 0x03, 0x00 | |
5ee58eed | 88 | oemid: .space 0x08, 0x00 // OEM ID |
984263bc MD |
89 | |
90 | .org 0x0b, 0x00 | |
5ee58eed MD |
91 | bpb: .word 512 // sector size (T) |
92 | .byte 0 // sectors/clustor | |
93 | .word 0 // reserved sectors | |
94 | .byte 0 // number of FATs | |
95 | .word 0 // root entries | |
96 | .word 0 // small sectors | |
97 | .byte 0 // media type (W) | |
98 | .word 0 // sectors/fat | |
99 | .word 18 // sectors per track (T) | |
100 | .word 2 // number of heads (T) | |
101 | .long 0 // hidden sectors (W) | |
102 | .long 0 // large sectors | |
984263bc MD |
103 | |
104 | .org 0x24, 0x00 | |
5ee58eed | 105 | ebpb: .byte 0 // BIOS physical drive number (W) |
984263bc MD |
106 | |
107 | .org 0x25,0x90 | |
5ee58eed MD |
108 | // |
109 | // Trampoline used by boot2 to call read to read data from the disk via | |
110 | // the BIOS. Call with: | |
111 | // | |
112 | // %cx:%ax - long - LBA to read in | |
113 | // %es:(%bx) - caddr_t - buffer to read data into | |
114 | // %dl - byte - drive to read from | |
115 | // %dh - byte - num sectors to read | |
116 | // | |
984263bc | 117 | |
5ee58eed MD |
118 | xread: push %ss // Address |
119 | pop %ds // data | |
120 | // | |
121 | // Setup an EDD disk packet and pass it to read | |
122 | // | |
123 | xread.1: // Starting | |
124 | pushl $0x0 // absolute | |
125 | push %cx // block | |
126 | push %ax // number | |
127 | push %es // Address of | |
128 | push %bx // transfer buffer | |
129 | xor %ax,%ax // Number of | |
130 | movb %dh,%al // blocks to | |
131 | push %ax // transfer | |
132 | push $0x10 // Size of packet | |
133 | mov %sp,%bp // Packet pointer | |
134 | callw read // Read from disk | |
135 | lea 0x10(%bp),%sp // Clear stack | |
136 | lret // To far caller | |
137 | // | |
138 | // Load the rest of boot2 and BTX up, copy the parts to the right locations, | |
139 | // and start it all up. | |
140 | // | |
984263bc | 141 | |
5ee58eed MD |
142 | // |
143 | // Setup the segment registers to flat addressing (segment 0) and setup the | |
144 | // stack to end just below the start of our code. | |
145 | // | |
76adb3c3 MD |
146 | // XXX note - our origin (start) points to the MEM_BIOS_LADDR. We run |
147 | // from there but boot2 later on calls xread at BOOT1_ORIGIN. | |
148 | // | |
5ee58eed MD |
149 | main: cld // String ops inc |
150 | xor %cx,%cx // Zero | |
151 | mov %cx,%es // Address | |
152 | mov %cx,%ds // data | |
153 | mov %cx,%ss // Set up | |
154 | mov $start,%sp // stack | |
155 | // | |
cacaceec MD |
156 | // Relocate ourself to BOOT1_ORIGIN. Since %cx == 0, the inc %ch sets |
157 | // %cx == 0x100 (256 words == 512 bytes). | |
5ee58eed MD |
158 | // |
159 | mov %sp,%si // Source | |
cacaceec | 160 | mov $BOOT1_ORIGIN,%di // Destination |
5ee58eed MD |
161 | incb %ch // Word count |
162 | rep // Copy | |
163 | movsw // code | |
164 | // | |
165 | // If we are on a hard drive, then load the MBR and look for the first | |
76adb3c3 MD |
166 | // FreeBSD slice. |
167 | // | |
168 | // Note, we can't use the fake partition entry (part4), as it may contain | |
169 | // garbage if this is a normal boot1 on a slice, verses a dangerously | |
170 | // dedicated disk. Hardwire sector 0 to acquire the MBR | |
5ee58eed | 171 | // |
76adb3c3 MD |
172 | xor %ax,%ax |
173 | xor %cx,%cx | |
5ee58eed MD |
174 | cmpb $0x80,%dl // Hard drive? |
175 | jb main.4 // No | |
176 | movb $0x1,%dh // Block count | |
76adb3c3 | 177 | callw nread_alt // Read MBR |
5ee58eed | 178 | mov $0x1,%cx // Two passes |
04144d62 | 179 | main.1: mov $BOOT2_LOAD_BUF+PRT_OFF,%si // Partition table |
5ee58eed MD |
180 | movb $0x1,%dh // Partition |
181 | main.2: cmpb $PRT_BSD,0x4(%si) // Our partition type? | |
182 | jne main.3 // No | |
183 | jcxz main.5 // If second pass | |
184 | testb $0x80,(%si) // Active? | |
185 | jnz main.5 // Yes | |
186 | main.3: add $0x10,%si // Next entry | |
187 | incb %dh // Partition | |
188 | cmpb $0x1+PRT_NUM,%dh // In table? | |
189 | jb main.2 // Yes | |
190 | dec %cx // Do two | |
191 | jcxz main.1 // passes | |
192 | // | |
193 | // If we get here, we didn't find any FreeBSD slices at all, so print an | |
194 | // error message and die. | |
195 | // | |
196 | mov $msg_part,%si // Message | |
197 | jmp error // Error | |
198 | // | |
199 | // Floppies use partition 0 of drive 0. | |
200 | // | |
201 | main.4: xor %dx,%dx // Partition:drive | |
202 | // | |
203 | // Ok, we have a slice and drive in %dx now, so use that to locate and load | |
204 | // boot2. %si references the start of the slice we are looking for, so go | |
d64b2e33 MD |
205 | // ahead and load up the first N sectors (boot1 + boot2) from that. |
206 | // | |
207 | // N is 16 for boot1 in a disklabel32 and up to 32 in a disklabel64. The | |
208 | // disklabel64 can hold up to 64 sectors but MEM_BTX_USR+BOOT2_VORIGIN | |
209 | // will overflow the segment if we use more then 32 sectors. | |
04144d62 MD |
210 | // |
211 | // When we read it in, we conveniently use BOOT2_LOAD_BUF (0x8c00) as our | |
212 | // transfer buffer. Thus, boot1 ends up at 0x8c00, and boot2 starts at | |
213 | // 0x8c00 + 0x200 = 0x8e00. | |
214 | // | |
2d7f6790 | 215 | // The first part of boot2 is the disklabel, which is 0x200 bytes long. |
5ee58eed MD |
216 | // The second part is BTX, which is thus loaded into 0x9000, which is where |
217 | // it also runs from. The boot2.bin binary starts right after the end of | |
218 | // BTX, so we have to figure out where the start of it is and then move the | |
04144d62 MD |
219 | // binary to 0xc000. Normally, BTX clients start at MEM_BTX_USR, or 0xa000, |
220 | // but when we use btxld to create boot2, we use an entry point of 0x2000. | |
221 | // That entry point is relative to MEM_BTX_USR; thus boot2.bin starts | |
222 | // at 0xc000. | |
5ee58eed | 223 | // |
cacaceec MD |
224 | // MEM_BTX_USR_ARG will be overwritten by the disk read and the relocation |
225 | // loop, so we must store the argument after completing said loops. | |
226 | // | |
227 | main.5: pushw %dx // Save args | |
5ee58eed | 228 | movb $NSECT,%dh // Sector count |
6080181b SS |
229 | #ifdef DISKLABEL64 |
230 | // In disklabel64 boot2 starts | |
231 | addl $7,0x8(%si) // offset 0x1000. | |
232 | #endif | |
5ee58eed | 233 | callw nread // Read disk |
4e06dda7 MD |
234 | mov $MEM_BTX_ORG,%bx // Base of BTX header |
235 | mov 0xa(%bx),%si // Get BTX text length (btx.S) | |
236 | add %bx,%si // %si = start of boot2.bin | |
237 | // %di = relocation target | |
238 | mov $MEM_BTX_USR+BOOT2_VORIGIN,%di | |
239 | mov $MEM_BTX_ORG+(NSECT-1)*SIZ_SEC,%cx | |
240 | sub %si,%cx // %cx = Size of boot2 client | |
241 | rep // Relocate boot2 | |
242 | movsb | |
cacaceec | 243 | popw MEM_BTX_USR_ARG // save (disk,slice) for boot2 |
4e06dda7 MD |
244 | |
245 | #if 0 | |
246 | // XXX DISABLED. This makes incorrect assumptions about | |
247 | // where BSS begins, potentially leaving garbage in the BSS | |
248 | // space. The BSS zeroing code has been moved to | |
249 | // btx/lib/btxcsu.S (BTX client startup code) where we have | |
250 | // more definitive knowledge about where BSS resides. | |
251 | // | |
252 | // %cx now contains 0. Calculate 0x[1]0000 - %di to get a | |
253 | // count of assumed BSS bytes from the end of boot2.bin up | |
254 | // to 0x10000, then zero it out. | |
255 | // | |
256 | sub %di,%cx | |
257 | xorb %al,%al | |
258 | rep | |
259 | stosb | |
260 | #endif | |
5ee58eed | 261 | callw seta20 // Enable A20 |
cacaceec MD |
262 | |
263 | // YYY | |
264 | pushw $MEM_BTX_ENTRY // Start BTX | |
265 | retw | |
0d54de9f MD |
266 | |
267 | /* | |
268 | * Enable A20. Put upper limit on amount of time we wait for the | |
269 | * keyboard controller to get ready (65K x ISA access time). If | |
270 | * we wait more than that amount it's likely that the hardware | |
271 | * is legacy-free and simply doesn't have keyboard controller | |
272 | * and don't need enabling A20 at all. | |
273 | */ | |
274 | seta20: cli # Disable interrupts | |
275 | xor %cx,%cx # Clear | |
276 | seta20.1: inc %cx # Increment, overflow? | |
277 | jz seta20.3 # Yes | |
278 | inb $0x64,%al # Get status | |
279 | testb $0x2,%al # Busy? | |
280 | jnz seta20.1 # Yes | |
281 | movb $0xd1,%al # Command: Write | |
282 | outb %al,$0x64 # output port | |
283 | seta20.2: inb $0x64,%al # Get status | |
284 | testb $0x2,%al # Busy? | |
285 | jnz seta20.2 # Yes | |
286 | movb $0xdf,%al # Enable | |
287 | outb %al,$0x60 # A20 | |
288 | seta20.3: sti # Enable interrupts | |
289 | retw # To caller | |
290 | ||
5ee58eed MD |
291 | // |
292 | // Trampoline used to call read from within boot1. | |
293 | // | |
76adb3c3 | 294 | nread: |
5ee58eed MD |
295 | mov 0x8(%si),%ax // Get |
296 | mov 0xa(%si),%cx // LBA | |
76adb3c3 MD |
297 | nread_alt: |
298 | mov $BOOT2_LOAD_BUF,%bx // Transfer buffer | |
5ee58eed MD |
299 | push %cs // Read from |
300 | callw xread.1 // disk | |
301 | jnc return // If success, return | |
302 | mov $msg_read,%si // Otherwise, set the error | |
303 | // message and fall through to | |
304 | // the error routine | |
305 | // | |
306 | // Print out the error message pointed to by %ds:(%si) followed | |
307 | // by a prompt, wait for a keypress, and then reboot the machine. | |
308 | // | |
309 | error: callw putstr // Display message | |
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 | |
0d54de9f | 315 | ljmp $0xf000,$0xfff0 // reboot the machine |
5ee58eed MD |
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 | |
984263bc | 325 | |
5ee58eed MD |
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 | |
ed987dc9 MD |
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 | |
5ee58eed MD |
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 | |
ed987dc9 | 402 | ja ereturn // No, failed |
5ee58eed MD |
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 | |
ed987dc9 | 438 | ja read.1 // If not done |
5ee58eed | 439 | read.6: retw // To caller |
984263bc | 440 | |
5ee58eed | 441 | // Messages |
984263bc MD |
442 | |
443 | msg_read: .asciz "Read" | |
444 | msg_part: .asciz "Boot" | |
445 | ||
446 | prompt: .asciz " error\r\n" | |
447 | ||
5ee58eed | 448 | flags: .byte FLAGS // Flags |
984263bc MD |
449 | |
450 | .org PRT_OFF,0x90 | |
451 | ||
5ee58eed | 452 | // Partition table |
76adb3c3 MD |
453 | // |
454 | // THIS MAY NOT BE WRITTEN OUT TO THE BOOT1 AREA OF THE DISKLABEL. This | |
455 | // section is only written out when the disklabel is placed on the raw | |
456 | // disk instead of in a slice, when creating a dangerously dedicated disk. | |
984263bc MD |
457 | |
458 | .fill 0x30,0x1,0x0 | |
459 | part4: .byte 0x80, 0x00, 0x01, 0x00 | |
5ee58eed | 460 | .byte 0xa5, 0xfe, 0xff, 0xff |
984263bc | 461 | .byte 0x00, 0x00, 0x00, 0x00 |
5ee58eed | 462 | .byte 0x50, 0xc3, 0x00, 0x00 // 50000 sectors long, bleh |
984263bc | 463 | |
5ee58eed | 464 | .word 0xaa55 // Magic number |