/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * ident "%Z%%M% %I% %E% SMI" */ /* * SOLARIS MASTER BOOT: * * PURPOSE: loads the primary boot from the active fdisk partition. * in effect, this routine mimics the functionality of INT 0x19. * * resides on the first physical sector of the hard drive media. * loaded by INT 0x19 (ROM bootstrap loader) at address 0x7C00 * limited to 512 bytes total, including embedded fdisk table. * * for compatibility with the ROM BIOS, we contain standard DOS structures: * * the fdisk partition table (at offset 0x1BE-0x1FE) * boot signature bytes (0x55, 0xAA at 0x1FE, 0x1FF) * * the above two entities are required in order to be compatible with * the manner in which the DOS BIOS has always performed its boot operation. * In the event that our master boot record is inadvertently replaced by * a standard DOS boot sector, the booting operation will still succeed! * * This master boot record uses the relsect/numsect fields of the partition * table entry, to compute the start of the active partition; therefore, * it is geometry independent. This means that the drive could be "built" * on a system with a disk controller that uses a given disk geometry, but * would run on any other controller. * * SYNOPSIS: * begins execution at 0:0x7C00 * relocates to 0:0x600 (to get out of the way!) * reads fdisk table to locate bootable partition * load boot record from the active fdisk partition at 0x7C00 * verify boot record signature bytes * jump to/execute the SOLARIS PARTITION PRIMARY BOOT * error handler - can either reboot, or invoke INT 0x18. * * interface from DOS INT 0x19: BootDev in DL * (this fails sometimes, so we look for a signature to determine whether * to rely on DL from the floppy boot, or if we should assume 0x80 from * the BIOS) * * interface to partition boot: BootDev in DL * *============================================================================= * Master boot record: resides on first physical sector of device */ /* * This file is written in GNU as syntax using Intel assembler syntax. The * startup label _start will be executed at address PBOOT_ADDR (0x7C00), but * the text section must be set at address RELOC_ADDR (0x600). With GNU ld * this can be done using the "-Ttext 600" option. */ #define PBOOT_ADDR 0x7C00 #define RELOC_ADDR 0x600 #define FDISK_START 0x1BE #define BOOT_SIG 0xAA55 #define N_RETRIES 5 #define FD_NUMPART 4 #define FD_PTESIZE 0x10 #define ACTIVE 0x80 /* * A convenience macro for declaring a message string (using .ascii directive-- * NOT nul-terminated) surrounded by two labels, which can then be used with * the SIZEOF() macro to get its length. */ #define MSG(label, string) label: .ascii string; label##_end: /* * Returns the length of some consecutive bytes. These bytes must be placed * between two labels. The ending label must be the same as the starting label * but with a suffix "_end". */ #define SIZEOF(label) (offset label##_end - offset label) .title "Solaris_Master_Boot" .intel_syntax noprefix /* use Intel syntax */ .code16 /* 16-bit mode (real mode) */ .text /* code segment begins here */ .global BootDev .global _start _start: /* _start is loaded at PBOOT_ADDR */ jmp bootrun Version: .ascii "M3.0" /* ident string */ bootrun: cli /* don't bother me now! */ /* prepare to relocate ourselves */ cld /* prepare for relocation */ mov si, PBOOT_ADDR mov di, RELOC_ADDR /* set up segment registers */ mov ax, cs /* initialize segment registers */ mov ss, ax mov sp, si /* stack starts down from 7C00 */ mov es, ax mov ds, ax push cx /* save possible signature on stack */ mov cx, 0x100 rep movsw pop cx /* restore saved cx */ /* running at PBOOT_ADDR, jump to RELOC_ADDR-rel addr */ jmp (new_home - PBOOT_ADDR + RELOC_ADDR) new_home: sti /* re-enable interrupts */ /* * assuming boot device number is in dl has caused problems in the past * since we still don't absolutely have to rely on it, I've just * removed the now-pointless code to check for the FACE-CAFE signature * from mdexec, which doesn't do anything anymore, but left the * assumption that BootDev is 0x80 and nothing but. If we ever need to * have BIOS load us from a drive not numbered 0x80, we'll need to * uncomment the following line; otherwise, the initialized value of * BootDev, namely 0x80, will be used for disk accesses. */ /* mov BootDev, dl */ /* set debug flag based on seeing "both shift down" */ mov ah, 2 /* get shift state */ int 0x16 and al, 3 /* isolate shift-key bits */ cmp al, 3 jne nodbg mov byte ptr [debugmode], 1 /* set to 1 */ nodbg: /* * Search the fdisk table sequentially to find a physical partition * that is marked as "active" (bootable). */ mov bx, RELOC_ADDR + FDISK_START mov cx, FD_NUMPART nxtpart: cmp byte ptr [bx], ACTIVE je got_active_part add bx, FD_PTESIZE loop nxtpart noparts: mov bp, offset NoActiveErrMsg mov cx, SIZEOF(NoActiveErrMsg) jmp fatal_err got_active_part: mov ah, 0 /* reset disk */ int 0x13 push bx /* save partition pointer */ /* Check for LBA BIOS */ mov ah, 0x41 /* chkext function */ mov bx, 0x55AA /* signature to change */ mov cx, 0 int 0x13 jc noLBA /* carry == failure */ cmp bx, 0xAA55 jne noLBA /* bad signature in BX == failure */ test cx, 1 /* cx & 1 must be true, or... */ jz noLBA /* ...no LBA */ mov bp, offset lbastring mov cx, SIZEOF(lbastring) call debugout /* * LBA case: form a packet on the stack and call fn 0x42 to read * packet, backwards (from hi to lo addresses): * 8-byte LBA * seg:ofs buffer address * byte reserved * byte nblocks * byte reserved * packet size in bytes (>= 0x10) */ pop bx /* restore partition pointer */ push bx /* and save again */ mov cx, N_RETRIES /* retry count */ retryLBA: pushd 0 /* hi 32 bits of 64-bit sector number */ push dword ptr [bx+8] /* relsect (lo 32 of 64-bit number) */ push dword ptr [solaris_priboot] /* seg:ofs of buffer */ push 1 /* reserved, one block */ push 0x10 /* reserved, size (0x10) */ mov ah, 0x42 /* "read LBA" */ mov si, sp /* (ds already == ss) */ int 0x13 lahf /* save flags */ add sp, 16 /* restore stack */ sahf /* restore flags */ jnc readok /* got it */ mov ah, 0 /* reset disk */ int 0x13 loop retryLBA /* try again */ jmp readerr /* exhausted retries; give up */ noLBA: mov bp, offset chsstring mov cx, SIZEOF(chsstring) call debugout pop bx /* restore partition pointer */ push bx /* and save again */ /* get BIOS disk parameters */ mov dl, byte ptr [BootDev] mov ah, 0x8 int 0x13 jnc geomok /* error reading geom; die */ mov bp, offset GeomErrMsg mov cx, SIZEOF(GeomErrMsg) jmp fatal_err geomok: /* calculate sectors per track */ mov al, cl /* ah doesn't matter; mul dh will set it */ and al, 0x3F mov byte ptr [secPerTrk], al /* calculate sectors per cylinder */ inc dh mul dh mov word ptr [secPerCyl], ax /* calculate cylinder # */ mov ax, [bx+8] /* ax = loword(relsect) */ mov dx, [bx+10] /* dx:ax = relsect */ div word ptr [secPerCyl] /* ax = cyl, */ /* dx = sect in cyl (0 - cylsize-1) */ mov bx, ax /* bx = cyl */ /* calculate head/sector # */ mov ax, dx /* ax = sect in cyl (0 - cylsize-1) */ div byte ptr [secPerTrk] /* al = head, */ /* ah = 0-rel sect in track */ inc ah /* ah = 1-rel sector */ xor cl,cl /* cl = 0 */ mov ch, bh /* ch = hi bits of cyl (if any) */ shr cx, 2 /* cl{7:6} = cyl{9:8} (if any) */ and cl, 0xC0 /* cl = cyl{9:8} to merge with sect (if any) */ or cl, ah /* cl{7:6} = cyl bits, cl{5:0} = sect */ mov ch, bl /* ch = lo cyl bits */ mov dh, al /* dh = head */ mov dl, byte ptr [BootDev] /* dl = drivenum */ les bx, solaris_priboot /* es:bx points to buffer */ mov si, N_RETRIES retry_noLBA: mov ax, 0x201 /* 02=read, sector count = 1 */ int 0x13 jnc readok mov ah, 0 /* reset disk */ int 0x13 dec si cmp si, 0 jne retry_noLBA /* retry, or fall through to read error */ readerr: mov bp, offset ReadErrMsg mov cx, SIZEOF(ReadErrMsg) jmp fatal_err readok: /* verify boot record signature */ mov bx, PBOOT_ADDR cmp word ptr [bx+0x1FE], BOOT_SIG je sigok mov bp, offset SigErrMsg mov cx, SIZEOF(SigErrMsg) jmp fatal_err sigok: mov dl, byte ptr [BootDev] /* pass BootDev to next boot phase */ pop si /* and pass partition pointer ds:si */ call dword ptr [solaris_priboot] /* call doesn't return! */ mov bp, offset ReturnErrMsg mov cx, SIZEOF(ReturnErrMsg) fatal_err: /* land of no return....... */ /* * bp contains pointer to error message string, * cx contains string length */ mov bx, 0x4F /* video page, attribute */ call msgout int 0x18 debugout: /* call with string pointer in es:bp, len in cx */ cmp byte ptr [debugmode], 0 je debugout_ret /* skip if not in debug mode */ mov bx, 0x1F /* page, attr (white on blue) */ /* alternate entry for fatal_err */ msgout: pusha mov ax, 0x1301 mov dx, 0x1700 /* row, col */ int 0x10 mov al, 7 /* BEL */ mov cx, 1 int 0x10 mov ah, 0 /* get key */ int 0x16 popa debugout_ret: ret secPerTrk: .byte 0 secPerCyl: .word 0 solaris_priboot: .long PBOOT_ADDR BootDev: .byte 0x80 /* assumes drive 80 (see comment above) */ debugmode: .byte 0 MSG(GeomErrMsg, "Can't read geometry") MSG(NoActiveErrMsg, "No active partition") MSG(ReadErrMsg, "Can't read PBR") MSG(SigErrMsg, "Bad PBR sig") MSG(ReturnErrMsg, "!!!") MSG(lbastring, "LBA") MSG(chsstring, "CHS") /* * For debugging: Here's a representative FDISK table entry * * .org 0x1BE * .byte 0x80,1,1,0,0x82,0xfe,0x7f,4,0x3f,0,0,0,0x86,0xfa,0x3f,0 */ .org 0x1FE .word BOOT_SIG