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