[BACK]Return to mbr.S CVS log [TXT][DIR] Up to [local] / sys / arch / amd64 / stand / mbr

Annotation of sys/arch/amd64/stand/mbr/mbr.S, Revision 1.1.1.1

1.1       nbrk        1: /*     $OpenBSD: mbr.S,v 1.3 2007/06/25 14:10:17 tom Exp $     */
                      2:
                      3: /*
                      4:  * Copyright (c) 1997 Michael Shalayeff and Tobias Weingartner
                      5:  * Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com>
                      6:  * All rights reserved.
                      7:  *
                      8:  * Redistribution and use in source and binary forms, with or without
                      9:  * modification, are permitted provided that the following conditions
                     10:  * are met:
                     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 the
                     15:  *    documentation and/or other materials provided with the distribution.
                     16:  *
                     17:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
                     18:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
                     19:  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     20:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     21:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     22:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     23:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     24:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     25:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     26:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     27:  * SUCH DAMAGE.
                     28:  *
                     29:  */
                     30: /* Copyright (c) 1996 VaX#n8 (vax@linkdead.paranoia.com)
                     31:  * last edited 9 July 1996
                     32:  * many thanks to Erich Boleyn (erich@uruk.org) for putting up with
                     33:  * all my questions, and for his work on GRUB
                     34:  * You may use this code or fragments thereof in a manner consistent
                     35:  * with the other copyrights as long as you retain my pseudonym and
                     36:  * this copyright notice in the file.
                     37:  */
                     38:
                     39:        .file   "mbr.S"
                     40:
                     41: #include <machine/asm.h>
                     42: #include <assym.h>
                     43:
                     44: /*
                     45:  * Memory layout:
                     46:  *
                     47:  * 0x07C00 -> 0x07DFF  BIOS loads us here      (at  31k)
                     48:  * 0x07E00 -> 0x17BFC  our stack               (to  95k)
                     49:  *
                     50:  * 0x07A00 -> 0x07BFF  we relocate to here     (at  30k5)
                     51:  *
                     52:  * 0x07C00 -> 0x07DFF  we load PBR here        (at  31k)
                     53:  *
                     54:  * The BIOS loads us at physical address 0x07C00.  We use a long jmp to
                     55:  * normalise our address to seg:offset 07C0:0000.  We then relocate to
                     56:  * 0x07A00, seg:offset 07A0:0000.
                     57:  *
                     58:  * We use a long jmp to normalise our address to seg:offset 07A0:0000
                     59:  * We set the stack to start at 07C0:FFFC (grows down on i386)
                     60:  * The partition boot record (PBR) loads /boot at seg:offset 4000:0000
                     61:  */
                     62: #define BOOTSEG                0x7c0   /* segment where we are loaded */
                     63: #define BOOTRELOCSEG   0x7a0   /* segment where we relocate to */
                     64: #define BOOTSTACKOFF   0xfffc  /* stack starts here, grows down */
                     65: #define PARTSZ         16      /* each partition table entry is 16 bytes */
                     66:
                     67: #define CHAR_LBA_READ  '.'
                     68: #define CHAR_CHS_READ  ';'
                     69: #define CHAR_CHS_FORCE '!'
                     70: #define CHAR_SHIFT_SEEN        0x07    /* Use BEL */
                     71:
                     72: #define MBR_FLAGS_FORCE_CHS    0x0001
                     73:
                     74: #ifdef DEBUG
                     75: #define CHAR_S         'S'     /* started */
                     76: #define CHAR_R         'R'     /* relocated */
                     77: #define CHAR_L         'L'     /* looking for bootable partition */
                     78: #define CHAR_B         'B'     /* loading boot */
                     79: #define CHAR_G         'G'     /* jumping to boot */
                     80:
                     81: #define DBGMSG(c)      movb    $c, %al;        call    Lchr
                     82: #else /* !DEBUG */
                     83: #define DBGMSG(c)
                     84: #endif /* !DEBUG */
                     85:
                     86: /* Clobbers %al - maybe more */
                     87: #define        putc(c)         movb    $c, %al;        call    Lchr
                     88:
                     89: /* Clobbers %esi - maybe more */
                     90: #define        puts(s)         movw    $s, %si;        call    Lmessage
                     91:
                     92:
                     93:        .text
                     94:        .code16
                     95:
                     96:        .globl  start
                     97: start:
                     98:        /* Adjust %cs to be right */
                     99:        ljmp    $BOOTSEG, $1f
                    100: 1:
                    101:        /* Set up stack */
                    102:        movw    %cs, %ax
                    103:
                    104:        /*
                    105:         * We don't need to disable and re-enable interrupts around the
                    106:         * the load of ss and sp.
                    107:         *
                    108:         * From 80386 Programmer's Reference Manual:
                    109:         * "A MOV into SS inhibits all interrupts until after the execution
                    110:         * of the next instruction (which is presumably a MOV into eSP)"
                    111:         *
                    112:         * According to Hamarsoft's 86BUGS list (which is distributed with
                    113:         * Ralph Brown's Interrupt List), some early 8086/88 processors
                    114:         * failed to disable interrupts following a load into a segment
                    115:         * register, but this was fixed with later steppings.
                    116:         *
                    117:         * Accordingly, this code will fail on very early 8086/88s, but
                    118:         * nick@ will just have to live with it.  Others will note that
                    119:         * we require an 80386 (or compatible) or above processor, anyway.
                    120:         */
                    121:        /* cli */
                    122:        movw    %ax, %ss
                    123:        movw    $BOOTSTACKOFF, %sp
                    124:        /* sti */                       /* XXX not necessary; see above */
                    125:
                    126:        /* Set up data segment */
                    127:        movw    %ax, %ds
                    128:        DBGMSG(CHAR_S)
                    129:
                    130:        /*
                    131:         * On the PC architecture, the boot record (originally on a floppy
                    132:         * disk) is loaded at 0000:7C00 (hex) and execution starts at the
                    133:         * beginning.
                    134:         *
                    135:         * When hard disk support was added, a scheme to partition disks into
                    136:         * four separate partitions was used, to allow multiple operating
                    137:         * systems to be installed on the one disk.  The boot sectors of the
                    138:         * operating systems on each partition would of course expect to be
                    139:         * loaded at 0000:7C00.
                    140:         *
                    141:         * The first sector of the hard disk is the master boot record (MBR).
                    142:         * It is this which defines the partitions and says which one is
                    143:         * bootable.  Of course, the BIOS loads the MBR at 0000:7C00, the
                    144:         * same location where the MBR needs to load the partition boot
                    145:         * record (PBR, called biosboot in OpenBSD).
                    146:         *
                    147:         * Therefore, the MBR needs to relocate itself before loading the PBR.
                    148:         *
                    149:         * Make it so.
                    150:         */
                    151:        movw    $BOOTRELOCSEG, %ax
                    152:        movw    %ax, %es
                    153:        xorw    %si, %si
                    154:        xorw    %di, %di
                    155:        movw    $0x200, %cx             /* Bytes in MBR, relocate it all */
                    156:        cld
                    157:        rep
                    158:        movsb
                    159:
                    160:        /* Jump to relocated self */
                    161:        ljmp $BOOTRELOCSEG, $reloc
                    162: reloc:
                    163:        DBGMSG(CHAR_R)
                    164:
                    165:        /* Set up %es and %ds */
                    166:        pushw   %ds
                    167:        popw    %es     /* next boot is at the same place as we were loaded */
                    168:        pushw   %cs
                    169:        popw    %ds     /* and %ds is at the %cs */
                    170:
                    171: #ifdef SERIAL
                    172:        /* Initialize the serial port to 9600 baud, 8N1.
                    173:         */
                    174:        xorw    %ax, %ax
                    175:        movb    $0xe3, %ax
                    176:        movw    $SERIAL, %dx
                    177:        int     $0x14
                    178: #endif
                    179:
                    180:        /*
                    181:         * If the SHIFT key is held down on entry, force CHS read
                    182:         */
                    183:
                    184:        /*
                    185:         * BIOS call "INT 0x16 Get Keyboard Shift Flags
                    186:         *      Call with       %ah = 0x02
                    187:         *      Return:
                    188:         *                      %al = shift flags
                    189:         *                      %ah - undefined by many BIOSes
                    190:         */
                    191:        movb    $0x02, %ah
                    192:        int     $0x16
                    193:        testb   $0x3, %al       /* Either shift key down? */
                    194:        jz      no_shift
                    195:
                    196:        putc(CHAR_SHIFT_SEEN)   /* Signal that shift key was seen */
                    197:
                    198:        orb     $MBR_FLAGS_FORCE_CHS, flags
                    199:
                    200: no_shift:
                    201:        /* BIOS passes us drive number in %dl
                    202:         *
                    203:         * XXX - This is not always true.  We currently check if %dl
                    204:         * points to a HD, and if not we complain, and set it to point
                    205:         * to the first HDD.  Note, this is not 100% correct, since
                    206:         * there is a possibility that you boot from HD #2, and still
                    207:         * get (%dl & 0x80) == 0x00, these type of systems will lose.
                    208:         */
                    209:        testb   $0x80, %dl
                    210:        jnz     drive_ok
                    211:
                    212:        /* MBR on floppy or old BIOS
                    213:         * Note: MBR (this code) should never be on a floppy.  It does
                    214:         * not belong there, so %dl should never be 0x00.
                    215:         *
                    216:         * Here we simply complain (should we?), and then hardcode the
                    217:         * boot drive to 0x80.
                    218:         */
                    219:        puts(efdmbr)
                    220:
                    221:        /* If we are passed bogus data, set it to HD #1
                    222:         */
                    223:        movb    $0x80, %dl
                    224:
                    225: drive_ok:
                    226:        /* Find the first active partition.
                    227:         * Note: this should be the only active partition.  We currently
                    228:         * don't check for that.
                    229:         */
                    230:        movw    $pt, %si
                    231:
                    232:        movw    $NDOSPART, %cx
                    233: find_active:
                    234:        DBGMSG(CHAR_L)
                    235:        movb    (%si), %al
                    236:
                    237:        cmpb    $DOSACTIVE, %al
                    238:        je      found
                    239:
                    240:        addw    $PARTSZ, %si
                    241:        loop    find_active
                    242:
                    243:        /* No bootable partition */
                    244: no_part:
                    245:        movw    $enoboot, %si
                    246:
                    247: err_stop:
                    248:        call    Lmessage
                    249:
                    250: stay_stopped:
                    251:        sti                             /* Ensure Ctl-Alt-Del will work */
                    252:        hlt
                    253:        /* Just to make sure */
                    254:        jmp     stay_stopped
                    255:
                    256: found:
                    257:        /*
                    258:         * Found bootable partition
                    259:         */
                    260:
                    261:        DBGMSG(CHAR_B)
                    262:
                    263:        /* Store the drive number (from %dl) in decimal */
                    264:        movb    %dl, %al
                    265:        andb    $0x0F, %al
                    266:        addb    $'0', %al
                    267:        movb    %al, drive_num
                    268:
                    269:        /*
                    270:         * Store the partition number, in decimal.
                    271:         *
                    272:         * We started with cx = 4; if found we want part '0'
                    273:         *                 cx = 3;                  part '1'
                    274:         *                 cx = 2;                  part '2'
                    275:         *                 cx = 1;                  part '3'
                    276:         *
                    277:         * We'll come into this with no other values for cl.
                    278:         */
                    279:        movb    $'0'+4, %al
                    280:        subb    %cl, %al
                    281:        movb    %al, part_num
                    282:
                    283:        /*
                    284:         * Tell operator what partition we're trying to boot.
                    285:         *
                    286:         * Using drive X, partition Y
                    287:         * - this used to be printed out after successfully loading the
                    288:         *   partition boot record; we now print it out before
                    289:         */
                    290:        pushw   %si
                    291:        movw    $info, %si
                    292:        testb   $MBR_FLAGS_FORCE_CHS, flags
                    293:        jnz     1f
                    294:        incw    %si
                    295: 1:
                    296:        call    Lmessage
                    297:        popw    %si
                    298:
                    299:        /*
                    300:         * Partition table entry format:
                    301:         *
                    302:         * 0x00 BYTE boot indicator (0x80 = active, 0x00 = inactive)
                    303:         * 0x01 BYTE start head
                    304:         * 0x02 WORD start cylinder, sector
                    305:         * 0x04 BYTE system type (0xA6 = OpenBSD)
                    306:         * 0x05 BYTE end head
                    307:         * 0x06 WORD end cylinder, sector
                    308:         * 0x08 LONG start LBA sector
                    309:         * 0x0C LONG number of sectors in partition
                    310:         *
                    311:         * In the case of a partition that extends beyond the 8GB boundary,
                    312:         * the LBA values will be correct, the CHS values will have their
                    313:         * maximums (typically (C,H,S) = (1023,255,63)).
                    314:         *
                    315:         * %ds:%si points to the active partition table entry.
                    316:         */
                    317:
                    318:        /* We will load the partition boot sector (biosboot) where we
                    319:         * were originally loaded.  We'll check to make sure something
                    320:         * valid comes in.  So that we don't find ourselves, zero out
                    321:         * the signature at the end.
                    322:         */
                    323:        movw    $0, %es:signature(,1)
                    324:
                    325:        /*
                    326:         * Have we been instructed to ignore LBA?
                    327:         */
                    328:        testb   $MBR_FLAGS_FORCE_CHS, flags
                    329:        jnz     do_chs
                    330:
                    331:        /*
                    332:         * We will use the LBA sector number if we have LBA support,
                    333:         * so find out.
                    334:         */
                    335:
                    336:        /*
                    337:         * BIOS call "INT 0x13 Extensions Installation Check"
                    338:         *      Call with       %ah = 0x41
                    339:         *                      %bx = 0x55AA
                    340:         *                      %dl = drive (0x80 for 1st hd, 0x81 for 2nd, etc)
                    341:         *      Return:
                    342:         *                      carry set: failure
                    343:         *                              %ah = error code (0x01, invalid func)
                    344:         *                      carry clear: success
                    345:         *                              %bx = 0xAA55 (must verify)
                    346:         *                              %ah = major version of extensions
                    347:         *                              %al   (internal use)
                    348:         *                              %cx = capabilities bitmap
                    349:         *                                      0x0001 - extnd disk access funcs
                    350:         *                                      0x0002 - rem. drive ctrl funcs
                    351:         *                                      0x0004 - EDD functions with EBP
                    352:         *                              %dx   (extension version?)
                    353:         */
                    354:
                    355:        movb    %dl, (%si)              /* Store drive here temporarily */
                    356:                                        /* (This call trashes %dl) */
                    357:                                        /*
                    358:                                         * XXX This is actually the correct
                    359:                                         *     place to store this.  The 0x80
                    360:                                         *     value used to indicate the
                    361:                                         *     active partition is by intention
                    362:                                         *     the same as the BIOS drive value
                    363:                                         *     for the first hard disk (0x80).
                    364:                                         *     At one point, 0x81 would go here
                    365:                                         *     for the second hard disk; the
                    366:                                         *     0x80 value is often used as a
                    367:                                         *     bit flag for testing, rather
                    368:                                         *     than an exact byte value.
                    369:                                         */
                    370:        movw    $0x55AA, %bx
                    371:        movb    $0x41, %ah
                    372:        int     $0x13
                    373:
                    374:        movb    (%si), %dl              /* Get back drive number */
                    375:
                    376:        jc      do_chs                  /* Did the command work? Jump if not */
                    377:        cmpw    $0xAA55, %bx            /* Check that bl, bh exchanged */
                    378:        jne     do_chs                  /* If not, don't have EDD extensions */
                    379:        testb   $0x01, %cl              /* And do we have "read" available? */
                    380:        jz      do_chs                  /* Again, use CHS if not */
                    381:
                    382: do_lba:
                    383:        /*
                    384:         * BIOS call "INT 0x13 Extensions Extended Read"
                    385:         *      Call with       %ah = 0x42
                    386:         *                      %dl = drive (0x80 for 1st hd, 0x81 for 2nd, etc)
                    387:         *                      %ds:%si = segment:offset of command packet
                    388:         *      Return:
                    389:         *                      carry set: failure
                    390:         *                              %ah = error code (0x01, invalid func)
                    391:         *                              command packet's sector count field set
                    392:         *                              to the number of sectors successfully
                    393:         *                              transferred
                    394:         *                      carry clear: success
                    395:         *                              %ah = 0 (success)
                    396:         *      Command Packet:
                    397:         *                      0x0000  BYTE    packet size (0x10 or 0x18)
                    398:         *                      0x0001  BYTE    reserved (should be 0)
                    399:         *                      0x0002  WORD    sectors to transfer (max 127)
                    400:         *                      0x0004  DWORD   seg:offset of transfer buffer
                    401:         *                      0x0008  QWORD   starting sector number
                    402:         */
                    403:        movb    $CHAR_LBA_READ, %al
                    404:        call    Lchr
                    405:
                    406:        /* Load LBA sector number from active partition table entry */
                    407:        movl    8(%si), %ecx
                    408:        movl    %ecx, lba_sector
                    409:
                    410:        pushw   %si                     /* We'll need %si later */
                    411:
                    412:        movb    $0x42, %ah
                    413:        movw    $lba_command, %si
                    414:        int     $0x13
                    415:
                    416:        popw    %si                     /* (get back %si) flags unchanged */
                    417:
                    418:        jnc     booting_os              /* If it worked, run the pbr we got */
                    419:
                    420:        /*
                    421:         * LBA read failed, fall through to try CHS read
                    422:         */
                    423:
                    424: do_chs:
                    425:        /*
                    426:         * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into
                    427:         * memory
                    428:         *       Call with       %ah = 0x2
                    429:         *                       %al = number of sectors
                    430:         *                       %ch = cylinder & 0xFF
                    431:         *                       %cl = sector (0-63) | rest of cylinder bits
                    432:         *                       %dh = head
                    433:         *                       %dl = drive (0x80 for hard disk)
                    434:         *                       %es:%bx = segment:offset of buffer
                    435:         *       Return:
                    436:         *                       carry set: failure
                    437:         *                           %ah = err code
                    438:         *                           %al = number of sectors transferred
                    439:         *                       carry clear: success
                    440:         *                           %al = 0x0 OR number of sectors transferred
                    441:         *                                 (depends on BIOS!)
                    442:         *                                 (according to Ralph Brown Int List)
                    443:         */
                    444:        movb    $CHAR_CHS_READ, %al
                    445:        call    Lchr
                    446:
                    447:        /* Load values from active partition table entry */
                    448:        movb    1(%si), %dh             /* head */
                    449:        movw    2(%si), %cx             /* sect, cyl */
                    450:        movw    $0x201, %ax             /* function and number of blocks */
                    451:        xorw    %bx, %bx                /* put it at %es:0 */
                    452:        int     $0x13
                    453:        jnc     booting_os
                    454:
                    455: read_error:
                    456:        movw    $eread, %si
                    457:        jmp     err_stop
                    458:
                    459: booting_os:
                    460:        puts(crlf)
                    461:        DBGMSG(CHAR_G)
                    462:
                    463:        /*
                    464:         * Make sure the pbr we loaded has a valid signature at the end.
                    465:         * This also ensures that something did load where we were expecting
                    466:         * it, as there's still a copy of our code there...
                    467:         */
                    468:        cmpw    $DOSMBR_SIGNATURE, %es:signature(,1)
                    469:        jne     missing_os
                    470:
                    471:        /* jump to the new code (%ds:%si is at the right point) */
                    472:        ljmp    $0, $BOOTSEG << 4
                    473:        /* not reached */
                    474:
                    475: missing_os:
                    476:        movw    $enoos, %si
                    477:        jmp     err_stop
                    478:
                    479: /*
                    480:  * Display string
                    481:  */
                    482: Lmessage:
                    483:        pushw   %ax
                    484:        cld
                    485: 1:
                    486:        lodsb                   /* %al = *%si++ */
                    487:        testb   %al, %al
                    488:        jz      1f
                    489:        call    Lchr
                    490:        jmp     1b
                    491:
                    492: /*
                    493:  *     Lchr: write the error message in %ds:%si to console
                    494:  */
                    495: Lchr:
                    496:        pushw   %ax
                    497:
                    498: #ifdef SERIAL
                    499:        pushw   %dx
                    500:        movb    $0x01, %ah
                    501:        movw    SERIAL, %dx
                    502:        int     $0x14
                    503:        popw    %dx
                    504: #else
                    505:        pushw   %bx
                    506:        movb    $0x0e, %ah
                    507:        movw    $1, %bx
                    508:        int     $0x10
                    509:        popw    %bx
                    510: #endif
                    511: 1:     popw    %ax
                    512:        ret
                    513:
                    514: /* command packet for LBA read of boot sector */
                    515: lba_command:
                    516:        .byte   0x10                    /* size of command packet */
                    517:        .byte   0x00                    /* reserved */
                    518:        .word   0x0001                  /* sectors to transfer, just 1 */
                    519:        .word   0                       /* target buffer, offset */
                    520:        .word   BOOTSEG                 /* target buffer, segment */
                    521: lba_sector:
                    522:        .long   0, 0                    /* sector number */
                    523:
                    524: /* Info messages */
                    525: info:  .ascii          "!Using drive "
                    526: drive_num:
                    527:        .byte           'X'
                    528:        .ascii          ", partition "
                    529: part_num:
                    530:        .asciz          "Y"
                    531:
                    532: /* Error messages */
                    533: efdmbr:        .asciz          "MBR on floppy or old BIOS\r\n"
                    534: eread: .asciz          "\r\nRead error\r\n"
                    535: enoos: .asciz          "No O/S\r\n"
                    536: enoboot: .ascii                "No active partition"   /* runs into crlf... */
                    537: crlf:  .asciz          "\r\n"
                    538:
                    539: endofcode:
                    540:        nop
                    541:
                    542: /* We're going to store a flags word here */
                    543:
                    544:        . = 0x1b4
                    545: flags:
                    546:        .word   0x0000
                    547:        .ascii  "Ox"                    /* Indicate that the two bytes */
                    548:                                        /* before us are the flags word */
                    549:
                    550: /* (MBR) NT disk signature offset */
                    551:        . = 0x1b8
                    552:        .space  4, 0
                    553:
                    554: /* partition table */
                    555: /* flag, head, sec, cyl, type, ehead, esect, ecyl, start, len */
                    556:        . = DOSPARTOFF  /* starting address of partition table */
                    557: pt:
                    558:        .byte   0x0,0,0,0,0,0,0,0
                    559:        .long   0,0
                    560:        .byte   0x0,0,0,0,0,0,0,0
                    561:        .long   0,0
                    562:        .byte   0x0,0,0,0,0,0,0,0
                    563:        .long   0,0
                    564:        .byte   DOSACTIVE,0,1,0,DOSPTYP_OPENBSD,255,255,255
                    565:        .long   0,0x7FFFFFFF
                    566: /* the last 2 bytes in the sector 0 contain the signature */
                    567:        . = 0x1fe
                    568: signature:
                    569:        .short  DOSMBR_SIGNATURE
                    570:        . = 0x200

CVSweb