1/* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22/* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * ident "%Z%%M% %I% %E% SMI" 27 */ 28 29/* 30 * SOLARIS MASTER BOOT: 31 * 32 * PURPOSE: loads the primary boot from the active fdisk partition. 33 * in effect, this routine mimics the functionality of INT 0x19. 34 * 35 * resides on the first physical sector of the hard drive media. 36 * loaded by INT 0x19 (ROM bootstrap loader) at address 0x7C00 37 * limited to 512 bytes total, including embedded fdisk table. 38 * 39 * for compatibility with the ROM BIOS, we contain standard DOS structures: 40 * 41 * the fdisk partition table (at offset 0x1BE-0x1FE) 42 * boot signature bytes (0x55, 0xAA at 0x1FE, 0x1FF) 43 * 44 * the above two entities are required in order to be compatible with 45 * the manner in which the DOS BIOS has always performed its boot operation. 46 * In the event that our master boot record is inadvertently replaced by 47 * a standard DOS boot sector, the booting operation will still succeed! 48 * 49 * This master boot record uses the relsect/numsect fields of the partition 50 * table entry, to compute the start of the active partition; therefore, 51 * it is geometry independent. This means that the drive could be "built" 52 * on a system with a disk controller that uses a given disk geometry, but 53 * would run on any other controller. 54 * 55 * SYNOPSIS: 56 * begins execution at 0:0x7C00 57 * relocates to 0:0x600 (to get out of the way!) 58 * reads fdisk table to locate bootable partition 59 * load boot record from the active fdisk partition at 0x7C00 60 * verify boot record signature bytes 61 * jump to/execute the SOLARIS PARTITION PRIMARY BOOT 62 * error handler - can either reboot, or invoke INT 0x18. 63 * 64 * interface from DOS INT 0x19: BootDev in DL 65 * (this fails sometimes, so we look for a signature to determine whether 66 * to rely on DL from the floppy boot, or if we should assume 0x80 from 67 * the BIOS) 68 * 69 * interface to partition boot: BootDev in DL 70 * 71 *============================================================================= 72 * Master boot record: resides on first physical sector of device 73 */ 74 75/* 76 * This file is written in GNU as syntax using Intel assembler syntax. The 77 * startup label _start will be executed at address PBOOT_ADDR (0x7C00), but 78 * the text section must be set at address RELOC_ADDR (0x600). With GNU ld 79 * this can be done using the "-Ttext 600" option. 80 */ 81 82 83#define PBOOT_ADDR 0x7C00 84#define RELOC_ADDR 0x600 85 86#define FDISK_START 0x1BE 87#define BOOT_SIG 0xAA55 88#define N_RETRIES 5 89 90#define FD_NUMPART 4 91#define FD_PTESIZE 0x10 92#define ACTIVE 0x80 93 94/* 95 * A convenience macro for declaring a message string (using .ascii directive-- 96 * NOT nul-terminated) surrounded by two labels, which can then be used with 97 * the SIZEOF() macro to get its length. 98 */ 99#define MSG(label, string) label: .ascii string; label##_end: 100 101/* 102 * Returns the length of some consecutive bytes. These bytes must be placed 103 * between two labels. The ending label must be the same as the starting label 104 * but with a suffix "_end". 105 */ 106#define SIZEOF(label) (offset label##_end - offset label) 107 108 109 .title "Solaris_Master_Boot" 110 111 .intel_syntax noprefix /* use Intel syntax */ 112 .code16 /* 16-bit mode (real mode) */ 113 114 .text /* code segment begins here */ 115 116 .global BootDev 117 .global _start 118 119_start: /* _start is loaded at PBOOT_ADDR */ 120 jmp bootrun 121 122Version: 123 .ascii "M3.0" /* ident string */ 124 125bootrun: 126 cli /* don't bother me now! */ 127 128 /* prepare to relocate ourselves */ 129 cld /* prepare for relocation */ 130 mov si, PBOOT_ADDR 131 mov di, RELOC_ADDR 132 133 /* set up segment registers */ 134 mov ax, cs /* initialize segment registers */ 135 mov ss, ax 136 mov sp, si /* stack starts down from 7C00 */ 137 mov es, ax 138 mov ds, ax 139 140 push cx /* save possible signature on stack */ 141 mov cx, 0x100 142 rep movsw 143 pop cx /* restore saved cx */ 144 145 /* running at PBOOT_ADDR, jump to RELOC_ADDR-rel addr */ 146 jmp (new_home - PBOOT_ADDR + RELOC_ADDR) 147 148new_home: 149 sti /* re-enable interrupts */ 150 151 /* 152 * assuming boot device number is in dl has caused problems in the past 153 * since we still don't absolutely have to rely on it, I've just 154 * removed the now-pointless code to check for the FACE-CAFE signature 155 * from mdexec, which doesn't do anything anymore, but left the 156 * assumption that BootDev is 0x80 and nothing but. If we ever need to 157 * have BIOS load us from a drive not numbered 0x80, we'll need to 158 * uncomment the following line; otherwise, the initialized value of 159 * BootDev, namely 0x80, will be used for disk accesses. 160 */ 161 /* mov BootDev, dl */ 162 163 /* set debug flag based on seeing "both shift down" */ 164 mov ah, 2 /* get shift state */ 165 int 0x16 166 and al, 3 /* isolate shift-key bits */ 167 cmp al, 3 168 jne nodbg 169 mov byte ptr [debugmode], 1 /* set to 1 */ 170 171nodbg: 172 /* 173 * Search the fdisk table sequentially to find a physical partition 174 * that is marked as "active" (bootable). 175 */ 176 mov bx, RELOC_ADDR + FDISK_START 177 mov cx, FD_NUMPART 178 179nxtpart: 180 cmp byte ptr [bx], ACTIVE 181 je got_active_part 182 add bx, FD_PTESIZE 183 loop nxtpart 184 185noparts: 186 mov bp, offset NoActiveErrMsg 187 mov cx, SIZEOF(NoActiveErrMsg) 188 jmp fatal_err 189 190got_active_part: 191 mov ah, 0 /* reset disk */ 192 int 0x13 193 194 push bx /* save partition pointer */ 195 196 /* Check for LBA BIOS */ 197 mov ah, 0x41 /* chkext function */ 198 mov bx, 0x55AA /* signature to change */ 199 mov cx, 0 200 int 0x13 201 jc noLBA /* carry == failure */ 202 cmp bx, 0xAA55 203 jne noLBA /* bad signature in BX == failure */ 204 test cx, 1 /* cx & 1 must be true, or... */ 205 jz noLBA /* ...no LBA */ 206 207 mov bp, offset lbastring 208 mov cx, SIZEOF(lbastring) 209 call debugout 210 211 /* 212 * LBA case: form a packet on the stack and call fn 0x42 to read 213 * packet, backwards (from hi to lo addresses): 214 * 8-byte LBA 215 * seg:ofs buffer address 216 * byte reserved 217 * byte nblocks 218 * byte reserved 219 * packet size in bytes (>= 0x10) 220 */ 221 222 pop bx /* restore partition pointer */ 223 push bx /* and save again */ 224 mov cx, N_RETRIES /* retry count */ 225retryLBA: 226 pushd 0 /* hi 32 bits of 64-bit sector number */ 227 push dword ptr [bx+8] /* relsect (lo 32 of 64-bit number) */ 228 push dword ptr [solaris_priboot] /* seg:ofs of buffer */ 229 push 1 /* reserved, one block */ 230 push 0x10 /* reserved, size (0x10) */ 231 mov ah, 0x42 /* "read LBA" */ 232 mov si, sp /* (ds already == ss) */ 233 int 0x13 234 lahf /* save flags */ 235 add sp, 16 /* restore stack */ 236 sahf /* restore flags */ 237 jnc readok /* got it */ 238 mov ah, 0 /* reset disk */ 239 int 0x13 240 loop retryLBA /* try again */ 241 jmp readerr /* exhausted retries; give up */ 242 243noLBA: 244 mov bp, offset chsstring 245 mov cx, SIZEOF(chsstring) 246 call debugout 247 248 pop bx /* restore partition pointer */ 249 push bx /* and save again */ 250 251 /* get BIOS disk parameters */ 252 mov dl, byte ptr [BootDev] 253 mov ah, 0x8 254 int 0x13 255 256 jnc geomok 257 258 /* error reading geom; die */ 259 mov bp, offset GeomErrMsg 260 mov cx, SIZEOF(GeomErrMsg) 261 jmp fatal_err 262 263geomok: 264 /* calculate sectors per track */ 265 mov al, cl /* ah doesn't matter; mul dh will set it */ 266 and al, 0x3F 267 mov byte ptr [secPerTrk], al 268 269 /* calculate sectors per cylinder */ 270 inc dh 271 mul dh 272 mov word ptr [secPerCyl], ax 273 274 /* calculate cylinder # */ 275 mov ax, [bx+8] /* ax = loword(relsect) */ 276 mov dx, [bx+10] /* dx:ax = relsect */ 277 div word ptr [secPerCyl] /* ax = cyl, */ 278 /* dx = sect in cyl (0 - cylsize-1) */ 279 mov bx, ax /* bx = cyl */ 280 281 /* calculate head/sector # */ 282 mov ax, dx /* ax = sect in cyl (0 - cylsize-1) */ 283 div byte ptr [secPerTrk] /* al = head, */ 284 /* ah = 0-rel sect in track */ 285 inc ah /* ah = 1-rel sector */ 286 287 xor cl,cl /* cl = 0 */ 288 mov ch, bh /* ch = hi bits of cyl (if any) */ 289 shr cx, 2 /* cl{7:6} = cyl{9:8} (if any) */ 290 and cl, 0xC0 /* cl = cyl{9:8} to merge with sect (if any) */ 291 292 or cl, ah /* cl{7:6} = cyl bits, cl{5:0} = sect */ 293 mov ch, bl /* ch = lo cyl bits */ 294 mov dh, al /* dh = head */ 295 mov dl, byte ptr [BootDev] /* dl = drivenum */ 296 les bx, solaris_priboot /* es:bx points to buffer */ 297 298 mov si, N_RETRIES 299retry_noLBA: 300 mov ax, 0x201 /* 02=read, sector count = 1 */ 301 302 int 0x13 303 jnc readok 304 mov ah, 0 /* reset disk */ 305 int 0x13 306 dec si 307 cmp si, 0 308 jne retry_noLBA /* retry, or fall through to read error */ 309 310readerr: 311 mov bp, offset ReadErrMsg 312 mov cx, SIZEOF(ReadErrMsg) 313 jmp fatal_err 314 315readok: 316 /* verify boot record signature */ 317 mov bx, PBOOT_ADDR 318 cmp word ptr [bx+0x1FE], BOOT_SIG 319 je sigok 320 321 mov bp, offset SigErrMsg 322 mov cx, SIZEOF(SigErrMsg) 323 jmp fatal_err 324 325sigok: 326 mov dl, byte ptr [BootDev] /* pass BootDev to next boot phase */ 327 pop si /* and pass partition pointer ds:si */ 328 call dword ptr [solaris_priboot] /* call doesn't return! */ 329 330 mov bp, offset ReturnErrMsg 331 mov cx, SIZEOF(ReturnErrMsg) 332 333fatal_err: /* land of no return....... */ 334 /* 335 * bp contains pointer to error message string, 336 * cx contains string length 337 */ 338 mov bx, 0x4F /* video page, attribute */ 339 call msgout 340 int 0x18 341 342debugout: 343 /* call with string pointer in es:bp, len in cx */ 344 cmp byte ptr [debugmode], 0 345 je debugout_ret /* skip if not in debug mode */ 346 347 mov bx, 0x1F /* page, attr (white on blue) */ 348 349 /* alternate entry for fatal_err */ 350msgout: 351 pusha 352 mov ax, 0x1301 353 mov dx, 0x1700 /* row, col */ 354 int 0x10 355 356 mov al, 7 /* BEL */ 357 mov cx, 1 358 int 0x10 359 360 mov ah, 0 /* get key */ 361 int 0x16 362 popa 363 364debugout_ret: 365 ret 366 367secPerTrk: 368 .byte 0 369secPerCyl: 370 .word 0 371solaris_priboot: 372 .long PBOOT_ADDR 373BootDev: 374 .byte 0x80 /* assumes drive 80 (see comment above) */ 375debugmode: 376 .byte 0 377 378MSG(GeomErrMsg, "Can't read geometry") 379MSG(NoActiveErrMsg, "No active partition") 380MSG(ReadErrMsg, "Can't read PBR") 381MSG(SigErrMsg, "Bad PBR sig") 382MSG(ReturnErrMsg, "!!!") 383MSG(lbastring, "LBA") 384MSG(chsstring, "CHS") 385 386/* 387 * For debugging: Here's a representative FDISK table entry 388 * 389 * .org 0x1BE 390 * .byte 0x80,1,1,0,0x82,0xfe,0x7f,4,0x3f,0,0,0,0x86,0xfa,0x3f,0 391 */ 392 .org 0x1FE 393 394 .word BOOT_SIG 395