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 73start: 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 89oemid: .space 0x08, 0x00 // OEM ID 90 91 .org 0x0b, 0x00 92bpb: .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 106ebpb: .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 119xread: push %ss // Address 120 pop %ds // data 121// 122// Setup an EDD disk packet and pass it to read 123// 124xread.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// 150main: 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 180main.1: mov $BOOT2_LOAD_BUF+PRT_OFF,%si // Partition table 181 movb $0x1,%dh // Partition 182main.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 186main.2a: jcxz main.5 // If second pass 187 testb $0x80,(%si) // Active? 188 jnz main.5 // Yes 189main.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// 203main.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// 229main.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 */ 276seta20: cli # Disable interrupts 277 xor %cx,%cx # Clear 278seta20.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 285seta20.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 290seta20.3: sti # Enable interrupts 291 retw # To caller 292 293// 294// Trampoline used to call read from within boot1. 295// 296nread: 297 mov 0x8(%si),%ax // Get 298 mov 0xa(%si),%cx // LBA 299nread_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// 309error: 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// 319putstr.0: mov $0x7,%bx // Page:attribute 320 movb $0xe,%ah // BIOS: Display 321 int $0x10 // character 322putstr: 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// 331ereturn: movb $0x1,%ah // Invalid 332 stc // argument 333return: 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 344read: 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 */ 378read.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 418read.2: mov $0x5,%di // Try count 419read.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 431read.4: movzbw %bl,%ax // Sectors read 432 add %ax,0x8(%bp) // Adjust 433 jnc read.5 // LBA, 434 incw 0xa(%bp) // transfer 435read.5: shlb %bl // buffer 436 add %bl,0x5(%bp) // pointer, 437 sub %al,0x2(%bp) // block count 438 ja read.1 // If not done 439read.6: retw // To caller 440 441// Messages 442 443prompt: .asciz " error\r\n" 444 445flags: .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 456part4: .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 462