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 */ 49 50#include "../bootasm.h" 51 52// Partition Constants 53 .set PRT_OFF,0x1be // Partition offset 54 .set PRT_NUM,0x4 // Partitions 55 .set PRT_BSD,0xa5 // Partition type 56 .set PRT_DFLY,0x6c // Partition type 57 58// Flag Bits 59 .set FL_PACKET,0x80 // Packet mode 60 61// Misc. Constants 62 .set SIZ_PAG,0x1000 // Page size 63 .set SIZ_SEC,0x200 // Sector size 64#ifndef NSECT 65 .set NSECT,0x10 66#endif 67 68 .globl start 69 .globl xread 70 .code16 71 72start: jmp main // Start recognizably 73 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. 86 87 .org 0x03, 0x00 88oemid: .space 0x08, 0x00 // OEM ID 89 90 .org 0x0b, 0x00 91bpb: .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 103 104 .org 0x24, 0x00 105ebpb: .byte 0 // BIOS physical drive number (W) 106 107 .org 0x25,0x90 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// 117 118xread: push %ss // Address 119 pop %ds // data 120// 121// Setup an EDD disk packet and pass it to read 122// 123xread.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// 141 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// 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// 149main: 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// 156// Relocate ourself to BOOT1_ORIGIN. Since %cx == 0, the inc %ch sets 157// %cx == 0x100 (256 words == 512 bytes). 158// 159 mov %sp,%si // Source 160 mov $BOOT1_ORIGIN,%di // Destination 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 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 171// 172 xor %ax,%ax 173 xor %cx,%cx 174 cmpb $0x80,%dl // Hard drive? 175 jb main.4 // No 176 movb $0x1,%dh // Block count 177 callw nread_alt // Read MBR 178 mov $0x1,%cx // Two passes 179main.1: mov $BOOT2_LOAD_BUF+PRT_OFF,%si // Partition table 180 movb $0x1,%dh // Partition 181main.2: cmpb $PRT_BSD,0x4(%si) // FreeBSD / old DFly 182 je main.2a 183 cmpb $PRT_DFLY,0x4(%si) // New DFly 184 jne main.3 // No 185main.2a: jcxz main.5 // If second pass 186 testb $0x80,(%si) // Active? 187 jnz main.5 // Yes 188main.3: add $0x10,%si // Next entry 189 incb %dh // Partition 190 cmpb $0x1+PRT_NUM,%dh // In table? 191 jb main.2 // Yes 192 dec %cx // Do two 193 jcxz main.1 // passes 194// 195// If we get here, we didn't find any FreeBSD slices at all, so print an 196// error message and die. 197// 198 jmp error // Error 199// 200// Floppies use partition 0 of drive 0. 201// 202main.4: xor %dx,%dx // Partition:drive 203// 204// Ok, we have a slice and drive in %dx now, so use that to locate and load 205// boot2. %si references the start of the slice we are looking for, so go 206// ahead and load up the first N sectors (boot1 + boot2) from that. 207// 208// N is 16 for boot1 in a disklabel32 and up to 32 in a disklabel64. The 209// disklabel64 can hold up to 64 sectors but MEM_BTX_USR+BOOT2_VORIGIN 210// will overflow the segment if we use more then 32 sectors. 211// 212// When we read it in, we conveniently use BOOT2_LOAD_BUF (0x8c00) as our 213// transfer buffer. Thus, boot1 ends up at 0x8c00, and boot2 starts at 214// 0x8c00 + 0x200 = 0x8e00. 215// 216// The first part of boot2 is the disklabel, which is 0x200 bytes long. 217// The second part is BTX, which is thus loaded into 0x9000, which is where 218// it also runs from. The boot2.bin binary starts right after the end of 219// BTX, so we have to figure out where the start of it is and then move the 220// binary to 0xc000. Normally, BTX clients start at MEM_BTX_USR, or 0xa000, 221// but when we use btxld to create boot2, we use an entry point of 0x2000. 222// That entry point is relative to MEM_BTX_USR; thus boot2.bin starts 223// at 0xc000. 224// 225// MEM_BTX_USR_ARG will be overwritten by the disk read and the relocation 226// loop, so we must store the argument after completing said loops. 227// 228main.5: pushw %dx // Save args 229 movb $NSECT,%dh // Sector count 230#ifdef DISKLABEL64 231 // In disklabel64 boot2 starts 232 addl $7,0x8(%si) // offset 0x1000. 233#endif 234 callw nread // Read disk 235 mov $MEM_BTX_ORG,%bx // Base of BTX header 236 mov 0xa(%bx),%si // Get BTX text length (btx.S) 237 add %bx,%si // %si = start of boot2.bin 238 // %di = relocation target 239 mov $MEM_BTX_USR+BOOT2_VORIGIN,%di 240 mov $MEM_BTX_ORG+(NSECT-1)*SIZ_SEC,%cx 241 sub %si,%cx // %cx = Size of boot2 client 242 rep // Relocate boot2 243 movsb 244 popw MEM_BTX_USR_ARG // save (disk,slice) for boot2 245 246#if 0 247 // XXX DISABLED. This makes incorrect assumptions about 248 // where BSS begins, potentially leaving garbage in the BSS 249 // space. The BSS zeroing code has been moved to 250 // btx/lib/btxcsu.S (BTX client startup code) where we have 251 // more definitive knowledge about where BSS resides. 252 // 253 // %cx now contains 0. Calculate 0x[1]0000 - %di to get a 254 // count of assumed BSS bytes from the end of boot2.bin up 255 // to 0x10000, then zero it out. 256 // 257 sub %di,%cx 258 xorb %al,%al 259 rep 260 stosb 261#endif 262 callw seta20 // Enable A20 263 264 // YYY 265 pushw $MEM_BTX_ENTRY // Start BTX 266 retw 267 268 /* 269 * Enable A20. Put upper limit on amount of time we wait for the 270 * keyboard controller to get ready (65K x ISA access time). If 271 * we wait more than that amount it's likely that the hardware 272 * is legacy-free and simply doesn't have keyboard controller 273 * and don't need enabling A20 at all. 274 */ 275seta20: cli # Disable interrupts 276 xor %cx,%cx # Clear 277seta20.1: inc %cx # Increment, overflow? 278 jz seta20.3 # Yes 279 inb $0x64,%al # Get status 280 testb $0x2,%al # Busy? 281 jnz seta20.1 # Yes 282 movb $0xd1,%al # Command: Write 283 outb %al,$0x64 # output port 284seta20.2: inb $0x64,%al # Get status 285 testb $0x2,%al # Busy? 286 jnz seta20.2 # Yes 287 movb $0xdf,%al # Enable 288 outb %al,$0x60 # A20 289seta20.3: sti # Enable interrupts 290 retw # To caller 291 292// 293// Trampoline used to call read from within boot1. 294// 295nread: 296 mov 0x8(%si),%ax // Get 297 mov 0xa(%si),%cx // LBA 298nread_alt: 299 mov $BOOT2_LOAD_BUF,%bx // Transfer buffer 300 push %cs // Read from 301 callw xread.1 // disk 302 jnc return // If success, return 303 304// Print that an error occurred (no room to determine which error 305// occurred) and the prompt. Then wait for a keypress, then reboot the 306// machine. 307// 308error: 309 mov $prompt,%si // Display 310 callw putstr // prompt 311 xorb %ah,%ah // BIOS: Get 312 int $0x16 // keypress 313 movw $0x1234, BDA_BOOT // Do a warm boot 314 ljmp $0xf000,$0xfff0 // reboot the machine 315// 316// Display a null-terminated string using the BIOS output. 317// 318putstr.0: mov $0x7,%bx // Page:attribute 319 movb $0xe,%ah // BIOS: Display 320 int $0x10 // character 321putstr: lodsb // Get char 322 testb %al,%al // End of string? 323 jne putstr.0 // No 324 325// 326// Overused return code. ereturn is used to return an error from the 327// read function. Since we assume putstr succeeds, we (ab)use the 328// same code when we return from putstr. 329// 330ereturn: movb $0x1,%ah // Invalid 331 stc // argument 332return: retw // To caller 333// 334// Reads sectors from the disk. If EDD is enabled, then check if it is 335// installed and use it if it is. If it is not installed or not enabled, then 336// fall back to using CHS. Since we use a LBA, if we are using CHS, we have to 337// fetch the drive parameters from the BIOS and divide it out ourselves. 338// Call with: 339// 340// %dl - byte - drive number 341// stack - 10 bytes - EDD Packet 342 343read: 344 /* 345 * Try EDD mode first. If not enabled or no BIOS support 346 * exists, fall back to CHS mode. 347 */ 348 testb $FL_PACKET,%cs:BOOT1_ORIGIN+flags-start 349 jz read.1 350 351 /* 352 * BIOS: check extensions present 353 */ 354 mov $0x55aa,%bx 355 push %dx 356 movb $0x41,%ah 357 int $0x13 358 pop %dx 359 jc read.1 /* BIOS error return */ 360 cmp $0xaa55,%bx /* check for proper magic */ 361 jne read.1 362 testb $0x1,%cl /* packet interface support? */ 363 jz read.1 364 365 /* 366 * Issue packet command. 367 * BIOS: Extended read command 368 */ 369 mov %bp,%si 370 movb $0x42,%ah 371 int $0x13 372 retw 373 374 /* 375 * Fallback to CHS mode 376 */ 377read.1: 378 push %dx // Save 379 movb $0x8,%ah // BIOS: Get drive 380 int $0x13 // parameters 381 movb %dh,%ch // Max head number 382 pop %dx // Restore 383 jc return // If error 384 andb $0x3f,%cl // Sectors per track 385 jz ereturn // If zero 386 cli // Disable interrupts 387 mov 0x8(%bp),%eax // Get LBA 388 push %dx // Save 389 movzbl %cl,%ebx // Divide by 390 xor %edx,%edx // sectors 391 div %ebx // per track 392 movb %ch,%bl // Max head number 393 movb %dl,%ch // Sector number 394 inc %bx // Divide by 395 xorb %dl,%dl // number 396 div %ebx // of heads 397 movb %dl,%bh // Head number 398 pop %dx // Restore 399 cmpl $0x3ff,%eax // Cylinder number supportable? 400 sti // Enable interrupts 401 ja ereturn // No, failed 402 xchgb %al,%ah // Set up cylinder 403 rorb $0x2,%al // number 404 orb %ch,%al // Merge 405 inc %ax // sector 406 xchg %ax,%cx // number 407 movb %bh,%dh // Head number 408 subb %ah,%al // Sectors this track 409 mov 0x2(%bp),%ah // Blocks to read 410 cmpb %ah,%al // To read 411 jb read.2 // this 412#ifdef TRACK_AT_A_TIME 413 movb %ah,%al // track 414#else 415 movb $1,%al // one sector 416#endif 417read.2: mov $0x5,%di // Try count 418read.3: les 0x4(%bp),%bx // Transfer buffer 419 push %ax // Save 420 movb $0x2,%ah // BIOS: Read 421 int $0x13 // from disk 422 pop %bx // Restore 423 jnc read.4 // If success 424 dec %di // Retry? 425 jz read.6 // No 426 xorb %ah,%ah // BIOS: Reset 427 int $0x13 // disk system 428 xchg %bx,%ax // Block count 429 jmp read.3 // Continue 430read.4: movzbw %bl,%ax // Sectors read 431 add %ax,0x8(%bp) // Adjust 432 jnc read.5 // LBA, 433 incw 0xa(%bp) // transfer 434read.5: shlb %bl // buffer 435 add %bl,0x5(%bp) // pointer, 436 sub %al,0x2(%bp) // block count 437 ja read.1 // If not done 438read.6: retw // To caller 439 440// Messages 441 442prompt: .asciz " error\r\n" 443 444flags: .byte FLAGS // Flags 445 446 .org PRT_OFF,0x90 447 448// Partition table 449// 450// THIS MAY NOT BE WRITTEN OUT TO THE BOOT1 AREA OF THE DISKLABEL. This 451// section is only written out when the disklabel is placed on the raw 452// disk instead of in a slice, when creating a dangerously dedicated disk. 453 454 .fill 0x30,0x1,0x0 455part4: .byte 0x80, 0x00, 0x01, 0x00 456 .byte 0xa5, 0xfe, 0xff, 0xff 457 .byte 0x00, 0x00, 0x00, 0x00 458 .byte 0x50, 0xc3, 0x00, 0x00 // 50000 sectors long, bleh 459 460 .word 0xaa55 // Magic number 461