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

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

1.1       nbrk        1: /*     $OpenBSD: biosboot.S,v 1.3 2007/05/31 18:08:13 tom Exp $        */
                      2:
                      3: /*
                      4:  * Copyright (c) 2003 Tobias Weingartner
                      5:  * Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com>
                      6:  * Copyright (c) 1997 Michael Shalayeff, Tobias Weingartner
                      7:  * All rights reserved.
                      8:  *
                      9:  * Redistribution and use in source and binary forms, with or without
                     10:  * modification, are permitted provided that the following conditions
                     11:  * are met:
                     12:  * 1. Redistributions of source code must retain the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer.
                     14:  * 2. Redistributions in binary form must reproduce the above copyright
                     15:  *    notice, this list of conditions and the following disclaimer in the
                     16:  *    documentation and/or other materials provided with the distribution.
                     17:  *
                     18:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
                     19:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
                     20:  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     21:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     22:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     23:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     24:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     25:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     26:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     27:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     28:  * SUCH DAMAGE.
                     29:  *
                     30:  */
                     31:        .file   "biosboot.S"
                     32:
                     33: #include <machine/asm.h>
                     34: #include <assym.h>
                     35:
                     36: /* Error indicators */
                     37: #define PBR_READ_ERROR                 'R'
                     38: #define PBR_CANT_BOOT                  'X'
                     39: #define PBR_BAD_MAGIC                  'M'
                     40: #define PBR_TOO_MANY_INDIRECTS         'I'
                     41:
                     42: #define CHAR_BLOCK_READ                '.'
                     43: #define CHAR_CHS_READ          ';'
                     44:
                     45: /*
                     46:  * Memory layout:
                     47:  *
                     48:  * 0x00000 -> 0x079FF  our stack               (to  30k5)
                     49:  * 0x07A00 -> 0x07BFF  typical MBR loc         (at  30k5)
                     50:  * 0x07C00 -> 0x07DFF  our code                (at  31k)
                     51:  * 0x07E00 -> ...      /boot inode block       (at  31k5)
                     52:  * 0x07E00 -> ...      (indirect block if nec)
                     53:  * 0x40000 -> ...      /boot                   (at 256k)
                     54:  *
                     55:  * The BIOS loads the MBR at physical address 0x07C00.  It then relocates
                     56:  * itself to (typically) 0x07A00.
                     57:  *
                     58:  * The MBR then loads us at physical address 0x07C00.
                     59:  *
                     60:  * We use a long jmp to normalise our address to seg:offset 07C0:0000.
                     61:  * (In real mode on x86, segment registers contain a base address in
                     62:  * paragraphs (16 bytes).  0000:00010 is the same as 0001:0000.)
                     63:  *
                     64:  * We set the stack to start at 0000:79FC (grows down on i386)
                     65:  *
                     66:  * We then read the inode for /boot into memory just above us at
                     67:  * 07E0:0000, and run through the direct block table (and the first
                     68:  * indirect block table, if necessary).
                     69:  *
                     70:  * We load /boot at seg:offset 4000:0000.
                     71:  *
                     72:  * Previous versions limited the size of /boot to 64k (loaded in a single
                     73:  * segment).  This version does not have this limitation.
                     74:  */
                     75: #define INODESEG       0x07e0  /* where we put /boot's inode's block */
                     76: #define INDIRECTSEG    0x07e0  /* where we put indirect table, if nec */
                     77: #define BOOTSEG                0x07c0  /* biosboot loaded here */
                     78: #define BOOTSTACKOFF  ((BOOTSEG << 4) - 4)  /* stack starts here, grows down */
                     79: #define LFMAGIC                0x464c  /* LFMAGIC (last two bytes of \7fELF) */
                     80: #define ELFMAGIC    0x464c457f  /* ELFMAGIC ("\7fELF") */
                     81:
                     82: #define INODEOFF  ((INODESEG-BOOTSEG) << 4)
                     83:
                     84: /*
                     85:  * The data passed by installboot is:
                     86:  *
                     87:  * inodeblk    uint32  the filesystem block that holds /boot's inode
                     88:  * inodedbl    uint32  the memory offset to the beginning of the
                     89:  *                     direct block list (di_db[]).  (This is the
                     90:  *                     offset within the block + $INODEOFF, which is
                     91:  *                     where we load the block to.)
                     92:  * fs_bsize_p  uint16  the filesystem block size _in paragraphs_
                     93:  *                     (i.e. fs_bsize / 16)
                     94:  * fs_bsize_s  uint16  the number of 512-byte sectors in a filesystem
                     95:  *                     block (i.e. fs_bsize / 512).  Directly written
                     96:  *                     into the LBA command block, at lba_count.
                     97:  *                     XXX LIMITED TO 127 BY PHOENIX EDD SPEC.
                     98:  * fsbtodb     uint8   shift count to convert filesystem blocks to
                     99:  *                     disk blocks (sectors).  Note that this is NOT
                    100:  *                     log2 fs_bsize, since fragmentation allows
                    101:  *                     the trailing part of a file to use part of a
                    102:  *                     filesystem block.  In other words, filesystem
                    103:  *                     block numbers can point into the middle of
                    104:  *                     filesystem blocks.
                    105:  * p_offset    uint32  the starting disk block (sector) of the
                    106:  *                     filesystem
                    107:  * nblocks     uint16  the number of filesystem blocks to read.
                    108:  *                     While this can be calculated as
                    109:  *                     howmany(di_size, fs_bsize) it takes us too
                    110:  *                     many code bytes to do it.
                    111:  *
                    112:  * All of these are patched directly into the code where they are used
                    113:  * (once only, each), to save space.
                    114:  *
                    115:  * One more symbol is exported, in anticipation of a "-c" flag in
                    116:  * installboot to force CHS reads:
                    117:  *
                    118:  * force_chs   uint8   set to the value 1 to force biosboot to use CHS
                    119:  *                     reads (this will of course cause the boot sequence
                    120:  *                     to fail if /boot is above 8 GB).
                    121:  */
                    122:
                    123:        .globl  inodeblk, inodedbl, fs_bsize_p, fsbtodb, p_offset, nblocks
                    124:        .globl  fs_bsize_s, force_chs
                    125:        .type   inodeblk, @function
                    126:        .type   inodedbl, @function
                    127:        .type   fs_bsize_p, @function
                    128:        .type   fs_bsize_s, @function
                    129:        .type   fsbtodb, @function
                    130:        .type   p_offset, @function
                    131:        .type   nblocks, @function
                    132:        .type   force_chs, @function
                    133:
                    134:
                    135: /* Clobbers %ax, maybe more */
                    136: #define        putc(c)         movb    $c, %al;        call    Lchr
                    137:
                    138: /* Clobbers %ax, %si, maybe more */
                    139: #define        puts(s)         movw    $s, %si;        call    Lmessage
                    140:
                    141:
                    142:        .text
                    143:        .code16
                    144:        .globl  _start
                    145: _start:
                    146:        jmp     begin
                    147:        nop
                    148:
                    149:        /*
                    150:         * BIOS Parameter Block.  Read by many disk utilities.
                    151:         *
                    152:         * We would have liked biosboot to go from the superblock to
                    153:         * the root directory to the inode for /boot, thence to read
                    154:         * its blocks into memory.
                    155:         *
                    156:         * As code and data space is quite tight in the 512-byte
                    157:         * partition boot sector, we instead get installboot to pass
                    158:         * us some pre-processed fields.
                    159:         *
                    160:         * We would have liked to put these in the BIOS parameter block,
                    161:         * as that seems to be the right place to put them (it's really
                    162:         * the equivalent of the superblock for FAT filesystems), but
                    163:         * caution prevents us.
                    164:         *
                    165:         * For now, these fields are either directly in the code (when they
                    166:         * are used once only) or at the end of this sector.
                    167:         */
                    168:
                    169:        . = _start + 3
                    170:
                    171:        .asciz  "OpenBSD"
                    172:
                    173:        /* BPB */
                    174:        . = _start + 0x0b
                    175: bpb:   .word   DEV_BSIZE                       /* sector size */
                    176:        .byte   2                               /* sectors/cluster */
                    177:        .word   0                               /* reserved sectors */
                    178:        .byte   0                               /* # of FAT */
                    179:        .word   0                               /* root entries */
                    180:        .word   0                               /* small sectors */
                    181:        .byte   0xf8                            /* media type (hd) */
                    182:        .word   0                               /* sectors/fat */
                    183:        .word   0                               /* sectors per track */
                    184:        .word   0                               /* # of heads */
                    185:
                    186:        /* EBPB */
                    187:        . = _start + 0x1c
                    188: ebpb:  .long   16                      /* hidden sectors */
                    189:        .long   0                       /* large sectors */
                    190:        .word   0                       /* physical disk */
                    191:        .byte   0x29                    /* signature, needed by NT */
                    192:        .space  4, 0                    /* volume serial number */
                    193:        .ascii  "UNIX LABEL"
                    194:        .asciz  "UFS 4.4"
                    195:
                    196:        /* boot code */
                    197:        . = _start + 0x3e
                    198:
                    199: begin:
                    200:        /* Fix up %cs just in case */
                    201:        ljmp    $BOOTSEG, $main
                    202:
                    203:        /*
                    204:         * Come here if we have to do a CHS boot, but we get an error from
                    205:         * BIOS get drive parameters, or it returns nsectors == 0 (in which
                    206:         * case we can't do the division we need to convert LBA sector
                    207:         * number to CHS).
                    208:         */
                    209: cant_boot:
                    210:        movb    $PBR_CANT_BOOT, %al
                    211:        jmp     err_print_crlf
                    212:
                    213: main:
                    214:        /* Set up stack */
                    215:        xorw    %ax, %ax
                    216:        movw    %ax, %ss
                    217:        movw    $BOOTSTACKOFF, %sp
                    218:
                    219:        /* Set up needed data segment reg */
                    220:        pushw   %cs
                    221:        popw    %ds                     /* Now %cs == %ds, != %ss (%ss == 0) */
                    222:
                    223: #ifdef SERIAL
                    224:        /* Initialize the serial port to 9600 baud, 8N1 */
                    225:        push    %dx
                    226:        movw    $0x00e3, %ax
                    227:        movw    SERIAL, %dx
                    228:        int     $0x14
                    229:        pop     %dx
                    230: #endif
                    231:
                    232: #ifdef BDEBUG
                    233:        putc('R')
                    234: #endif
                    235:
                    236:        /*
                    237:         * We're going to print our sign-on message.
                    238:         *
                    239:         * We're now LBA-aware, and will use LBA to load /boot if the
                    240:         * BIOS says it's available.  However, we have seen machines
                    241:         * where CHS is required even when LBA is available.  Therefore
                    242:         * we provide a way to force CHS use:
                    243:         *
                    244:         * If the SHIFT key is held down on entry, force CHS reads.
                    245:         */
                    246:        movw    $load_msg+1, %si        /* "Loading" */
                    247:        movb    %dl, %dh
                    248:
                    249:        /*
                    250:         * BIOS call "INT 0x16 Get Keyboard Shift Flags
                    251:         *      Call with       %ah = 0x02
                    252:         *      Return:
                    253:         *                      %al = shift flags
                    254:         *                      %ah - undefined by many BIOSes
                    255:         */
                    256:        movb    $0x02, %ah
                    257:        int     $0x16
                    258:
                    259:        /*
                    260:         * We provide the ability to force CHS use without having to hold
                    261:         * down the SHIFT key each boot.  Just set the byte at force_chs
                    262:         * to 1 (more accurately any value with either of the bottom two
                    263:         * bits set, but the use of 1 is recommended).
                    264:         */
                    265: force_chs = .+1
                    266:        orb     $0, %al
                    267:
                    268:        testb   $0x3, %al               /* Either shift key down? */
                    269:        jz      no_force_chs
                    270:
                    271:        decw    %si                     /* "!Loading" indicates forced CHS */
                    272:        xorb    %dh, %dh                /* Pretend a floppy, so no LBA use */
                    273:
                    274: no_force_chs:
                    275:        /* Print pretty message */
                    276:        call    Lmessage
                    277:
                    278:        /*
                    279:         * We will use LBA reads if we have LBA support, so find out.
                    280:         */
                    281:
                    282:        /*
                    283:         * But don't even try on floppies, OR if forcing to CHS.
                    284:         *
                    285:         * (We're really testing %dl, but use %dh so we can force the
                    286:         * top bit to zero to force CHS boot.)
                    287:         */
                    288:        testb   $0x80, %dh
                    289:        jz      no_lba
                    290:
                    291:        /*
                    292:         * BIOS call "INT 0x13 Extensions Installation Check"
                    293:         *      Call with       %ah = 0x41
                    294:         *                      %bx = 0x55AA
                    295:         *                      %dl = drive (0x80 for 1st hd, 0x81 for 2nd, etc)
                    296:         *      Return:
                    297:         *                      carry set: failure
                    298:         *                              %ah = error code (0x01, invalid func)
                    299:         *                      carry clear: success
                    300:         *                              %bx = 0xAA55 (must verify)
                    301:         *                              %ah = major version of extensions
                    302:         *                              %al   (internal use)
                    303:         *                              %cx = capabilities bitmap
                    304:         *                                      0x0001 - extnd disk access funcs
                    305:         *                                      0x0002 - rem. drive ctrl funcs
                    306:         *                                      0x0004 - EDD functions with EBP
                    307:         *                              %dx   (extension version?)
                    308:         */
                    309:
                    310:        pushw   %dx                     /* Save the drive number (%dl) */
                    311:        movw    $0x55AA, %bx
                    312:        movb    $0x41, %ah
                    313:        int     $0x13
                    314:        popw    %dx                     /* Retrieve drive number */
                    315:
                    316:        jc      no_lba                  /* Did the command work? Jump if not */
                    317:        cmpw    $0xAA55, %bx            /* Check that bl, bh exchanged */
                    318:        jne     no_lba                  /* If not, don't have EDD extensions */
                    319:        testb   $0x01, %cl              /* And do we have "read" available? */
                    320:        jz      no_lba                  /* Again, use CHS if not */
                    321:
                    322:        /* We have LBA support, so that's the vector to use */
                    323:
                    324:        movw    $load_lba, load_fsblock
                    325:        jmp     get_going
                    326:
                    327: no_lba:
                    328:        pushw   %dx
                    329:
                    330:        /*
                    331:         * BIOS call "INT 0x13 Function 0x08" to get drive parameters
                    332:         *      Call with        %ah = 0x08
                    333:         *                       %dl = drive (0x80 for 1st hd, 0x81 for 2nd...)
                    334:         *       Return:
                    335:         *                       carry set: failure
                    336:         *                           %ah = err code
                    337:         *                       carry clear: success
                    338:         *                           %ah = 0x00
                    339:         *                           %al = 0x00 (some BIOSes)
                    340:         *                           %ch = 0x00 (some BIOSes)
                    341:         *                           %ch = max-cylinder & 0xFF
                    342:         *                           %cl = max sector | rest of max-cyl bits
                    343:         *                           %dh = max head number
                    344:         *                           %dl = number of drives
                    345:         *                                 (according to Ralph Brown Int List)
                    346:         */
                    347:        movb    $0x08, %ah
                    348:        int     $0x13                   /* We need to know heads & sectors */
                    349:
                    350:        jc      cant_boot               /* If error, can't boot */
                    351:
                    352:        movb    %dh, maxheads           /* Remember this */
                    353:
                    354:        andb    $0x3F, %cl
                    355:        jz      cant_boot
                    356:        movb    %cl, nsectors
                    357:
                    358:        putc(CHAR_CHS_READ)             /* Indicate (subtly) CHS reads */
                    359:
                    360:        popw    %dx                     /* Retrieve the drive number */
                    361:
                    362: get_going:
                    363:        /*
                    364:         * Older versions of biosboot used to set up the destination
                    365:         * segment, and increase the target offset every time a number
                    366:         * of blocks was read.  That limits /boot to 64k.
                    367:         *
                    368:         * In order to support /boots > 64k, we always read to offset
                    369:         * 0000 in the target segment, and just increase the target segment
                    370:         * each time.
                    371:         */
                    372:
                    373:        /*
                    374:         * We would do movl inodeblk, %eax  here, but that instruction
                    375:         * is 4 bytes long; add 4 bytes for data takes 8 bytes.  Using
                    376:         * a load immediate takes 6 bytes, and we just get installboot
                    377:         * to patch here, rather than data anywhere else.
                    378:         */
                    379: inodeblk = .+2
                    380:        movl    $0x90909090, %eax       /* mov $inodeblk, %eax */
                    381:
                    382:        movw    $INODESEG, %bx          /* Where to put /boot's inode */
                    383:
                    384:        /*
                    385:         * %eax - filesystem block to read
                    386:         * %bx  - target segment (target offset is 0000)
                    387:         * %dl  - BIOS drive number
                    388:         */
                    389:        call    *load_fsblock           /* This will crash'n'burn on errs */
                    390:
                    391:        /*
                    392:         * We now have /boot's inode in memory.
                    393:         *
                    394:         * /usr/include/ufs/ufs/dinode.h for the details:
                    395:         *
                    396:         * Offset  8 (decimal): 64-bit file size (only use low 32 bits)
                    397:         * Offset 40 (decimal): list of NDADDR (12) direct disk blocks
                    398:         * Offset 88 (decimal): list of NIADDR (3) indirect disk blocks
                    399:         *
                    400:         * NOTE: list of indirect blocks immediately follows list of
                    401:         * direct blocks.  We use this fact in the code.
                    402:         *
                    403:         * We only support loading from direct blocks plus the first
                    404:         * indirect block.  This is the same as the previous biosboot/
                    405:         * installboot limit.  Note that, with default 16,384-bytes
                    406:         * filesystem blocks, the direct block list supports files up
                    407:         * to 192 KB.  /boot is currently around 60 KB.
                    408:         *
                    409:         * The on-disk format can't change (filesystems with this format
                    410:         * already exist) so okay to hardcode offsets here.
                    411:         *
                    412:         * The nice thing about doing things with filesystem blocks
                    413:         * rather than sectors is that filesystem blocks numbers have
                    414:         * 32 bits, so fit into a single register (even if "e"d).
                    415:         *
                    416:         * Note that this code does need updating if booting from a new
                    417:         * filesystem is required.
                    418:         */
                    419: #define NDADDR 12
                    420: #define di_db  40                      /* Not used; addr put in by instboot */
                    421: #define di_ib  88                      /* Not used; run on from direct blks */
                    422:
                    423:        /*
                    424:         * Register usage:
                    425:         *
                    426:         * %eax - block number for load_fsblock
                    427:         * %bx  - target segment (target offset is 0000) for load_fsblock
                    428:         * %dl  - BIOS drive number for load_fsblock
                    429:         * %esi - points to block table in inode/indirect block
                    430:         * %cx  - number of blocks to load within loop (i.e. from current
                    431:         *        block list, which is either the direct block list di_db[]
                    432:         *        or the indirect block list)
                    433:         * %di  - total number of blocks to load
                    434:         */
                    435:
                    436:        /*
                    437:         * We would do movl inodedbl, %esi  here, but that instruction
                    438:         * is 4 bytes long; add 4 bytes for data takes 8 bytes.  Using
                    439:         * a load immediate takes 6 bytes, and we just get installboot
                    440:         * to patch here, rather than in data anywhere else.
                    441:         */
                    442: inodedbl = .+2
                    443:        movl    $0x90909090, %esi       /* mov $inodedbl, %esi */
                    444:                                        /* Now esi -> di_db[] */
                    445:
                    446: nblocks = .+1
                    447:        movw    $0x9090, %di            /* mov nblocks, %di */
                    448:        movw    %di, %cx
                    449:        cmpw    $NDADDR, %cx
                    450:        jc      1f
                    451:        movw    $NDADDR, %cx
                    452: 1:                                     /* %cx = min(nblocks, $NADDR) */
                    453:
                    454:        movw    $(LOADADDR >> 4), %bx   /* Target segment for /boot */
                    455:
                    456: load_blocks:
                    457:        putc(CHAR_BLOCK_READ)           /* Show progress indicator */
                    458:
                    459:        cld
                    460:
                    461:        /* Get the next filesystem block number into %eax */
                    462:        lodsl                   /* %eax = *(%si++), make sure 0x66 0xad */
                    463:
                    464:        pushal                          /* Save all 32-bit registers */
                    465:
                    466:        /*
                    467:         * Read a single filesystem block (will almost certainly be multiple
                    468:         * disk sectors)
                    469:         *
                    470:         * %eax - filesystem block to read
                    471:         * %bx  - target segment (target offset is 0000)
                    472:         * %dl  - BIOS drive number
                    473:         */
                    474:        call    *load_fsblock           /* This will crash'n'burn on errs */
                    475:
                    476:        popal                           /* Restore 32-bit registers */
                    477:
                    478:        /*
                    479:         * We want to put addw fs_bsize_p, %bx, which takes 4 bytes
                    480:         * of code and two bytes of data.
                    481:         *
                    482:         * Instead, use an immediate load, and have installboot patch
                    483:         * here directly.
                    484:         */
                    485:        /* Move on one filesystem block */
                    486: fs_bsize_p = .+2
                    487:        addw    $0x9090, %bx            /* addw $fs_bsize_p, %bx */
                    488:
                    489:        decw    %di
                    490:        loop    load_blocks
                    491:
                    492:        /* %cx == 0 ... important it stays this way (used later) */
                    493:
                    494:        /*
                    495:         * Finished reading a set of blocks.
                    496:         *
                    497:         * This was either the direct blocks, and there may or may not
                    498:         * be indirect blocks to read, or it was the indirect blocks,
                    499:         * and we may or may not have read in all of /boot.  (Ideally
                    500:         * will have read in all of /boot.)
                    501:         */
                    502:        orw     %di, %di
                    503:        jz      done_load               /* No more sectors to read */
                    504:
                    505:        /* We have more blocks to load */
                    506:
                    507:        /* We only support a single indirect block (the same as previous
                    508:         * versions of installboot.  This is required for the boot floppies.
                    509:         *
                    510:         * We use a bit of the code to store a flag that indicates
                    511:         * whether we have read the first indirect block or not.
                    512:         *
                    513:         * If we've already read the indirect list, we can't load this /boot.
                    514:         *
                    515:         * indirect     uint8   0 => running through load_blocks loop reading
                    516:         *                      direct blocks.  If != 0, we're reading the
                    517:         *                      indirect blocks.  Must use a field that is
                    518:         *                      initialised to 0.
                    519:         */
                    520: indirect = .+2
                    521:        movw    $PBR_TOO_MANY_INDIRECTS, %ax    /* movb $PRB_TOO..., %al */
                    522:                                                /* movb indirect, %ah */
                    523:        orb     %ah, %ah
                    524:        jnz     err_print_crlf
                    525:
                    526:        incb    indirect                /* No need to worry about wrap */
                    527:                                        /* around, as this will only be done */
                    528:                                        /* once before we fail */
                    529:
                    530:        /* Okay, let's read in the indirect block */
                    531:
                    532:        lodsl                           /* Get blk num of 1st indirect blk */
                    533:
                    534:        pushw   %bx                     /* Remember where we got to */
                    535:        movw    $INODESEG, %bx
                    536:        call    *load_fsblock           /* This will crash'n'burn on errs */
                    537:        popw    %bx                     /* Indirect blocks get added on to */
                    538:                                        /* just after where we got to */
                    539:        movl    $INODEOFF, %esi
                    540:        movw    %di, %cx                /* How many blocks left to read */
                    541:
                    542:        jmp     load_blocks
                    543:
                    544: done_load:
                    545:        puts(crlf)
                    546:
                    547:        /* %cx == 0 from loop above... keep it that way */
                    548:
                    549:        /*
                    550:         * Check the magic signature at the beginning of /boot.
                    551:         * Since /boot is now ELF, this should be 0xFF E L F.
                    552:         */
                    553:        movw    $(LOADADDR >> 4), %ax   /* Target segment */
                    554:        movw    %ax, %es
                    555:
                    556:        /*
                    557:         * We cheat a little here, and only check the L and F.
                    558:         *
                    559:         * (Saves 3 bytes of code... the two signature bytes we
                    560:         * don't check, and the operand size prefix that's not
                    561:         * needed.)
                    562:         */
                    563:        cmpw    $LFMAGIC, %es:2(,1)
                    564:        je      exec_boot
                    565:
                    566:        movb    $PBR_BAD_MAGIC, %al
                    567:
                    568: err_print:
                    569:        movw    $err_txt, %si
                    570: err_print2:
                    571:        movb    %al, err_id
                    572: err_stop:
                    573:        call    Lmessage
                    574: stay_stopped:
                    575:        sti                             /* Ensure Ctl-Alt-Del will work */
                    576:        hlt                             /* (don't require power cycle) */
                    577:        jmp     stay_stopped            /* Just to make sure :-) */
                    578:
                    579: exec_boot:
                    580:        /* At this point we could try to use the entry point in
                    581:         * the image we just loaded.  But if we do that, we also
                    582:         * have to potentially support loading that image where it
                    583:         * is supposed to go.  Screw it, just assume that the image
                    584:         * is sane.
                    585:         */
                    586: #ifdef BDEBUG
                    587:        putc('P')
                    588: #endif
                    589:
                    590:        /* %cx == 0 from loop above... keep it that way */
                    591:
                    592:        /*
                    593:         * We want to do movzbl %dl, %eax ; pushl %eax to zero-extend the
                    594:         * drive number to 32 bits and pass it to /boot.  However, this
                    595:         * takes 6 bytes.
                    596:         *
                    597:         * Doing it this way saves 2 bytes.
                    598:         */
                    599:        pushw   %cx
                    600:        movb    %dl, %cl
                    601:        pushw   %cx
                    602:
                    603:        pushl   $BOOTMAGIC      /* use some magic */
                    604:
                    605:        /* jmp  /boot */
                    606:        ljmp $(LINKADDR >> 4), $0
                    607:        /* not reached */
                    608:
                    609:
                    610: /*
                    611:  * Load a single filesystem block into memory using CHS calls.
                    612:  *
                    613:  * Input:      %eax - 32-bit filesystem block number
                    614:  *             %bx  - target segment (target offset is 0000)
                    615:  *             %dl  - BIOS drive number
                    616:  *
                    617:  * Output:     block successfully read in (panics if not)
                    618:  *             all general purpose registers may have been trashed
                    619:  */
                    620: load_chs:
                    621:        /*
                    622:         * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into
                    623:         * memory.
                    624:         *      Call with        %ah = 0x42
                    625:         *                       %ah = 0x2
                    626:         *                       %al = number of sectors
                    627:         *                       %ch = cylinder & 0xFF
                    628:         *                       %cl = sector (0-63) | rest of cylinder bits
                    629:         *                       %dh = head
                    630:         *                       %dl = drive (0x80 for 1st hd, 0x81 for 2nd...)
                    631:         *                       %es:%bx = segment:offset of buffer
                    632:         *       Return:
                    633:         *                       carry set: failure
                    634:         *                           %ah = err code
                    635:         *                           %al = number of sectors transferred
                    636:         *                       carry clear: success
                    637:         *                           %al = 0x0 OR number of sectors transferred
                    638:         *                                 (depends on BIOS!)
                    639:         *                                 (according to Ralph Brown Int List)
                    640:         */
                    641:
                    642:        /* Convert the filesystem block into a sector value */
                    643:        call    fsbtosector
                    644:        movl    lba_sector, %eax        /* we can only use 24 bits, really */
                    645:
                    646:        movw    fs_bsize_s, %cx /* sectors per filesystem block */
                    647:
                    648:        /*
                    649:         * Some BIOSes require that reads don't cross track boundaries.
                    650:         * Therefore we do all CHS reads single-sector.
                    651:         */
                    652: calc_chs:
                    653:        pushal
                    654:        movw    %bx, %es        /* Set up target segment */
                    655:
                    656:        pushw   %dx             /* Save drive number (in %dl) */
                    657:        xorl    %edx, %edx
                    658:        movl    %edx, %ecx
                    659:
                    660: nsectors = .+1
                    661:        movb    $0x90, %cl      /* movb $nsectors, %cl */
                    662:                                /* Doing it this way saves 4-2 = 2 bytes code */
                    663:                                /* bytes (no data, since we would overload) */
                    664:
                    665:        divl    %ecx, %eax
                    666:                                /* Now have sector number in %dl */
                    667:        pushw   %dx             /* Remember for later */
                    668:
                    669:        xorl    %edx, %edx
                    670:
                    671: maxheads = .+1
                    672:        movb    $0x90, %cl      /* movb $maxheads, %cl; 0 <= maxheads <= 255 */
                    673:                                /* Doing it this way saves 4-2 = 2 code */
                    674:                                /* bytes (no data, since we would overload */
                    675:
                    676:        incw    %cx             /* Number of heads is 1..256, no "/0" worries */
                    677:
                    678:        divl    %ecx, %eax
                    679:                                /* Have head number in %dl */
                    680:                                /* Cylinder number in %ax */
                    681:        movb    %al, %ch        /* Bottom 8 bits of cyl number */
                    682:        shlb    $6, %ah         /* Move up top 2 bits of cyl number */
                    683:        movb    %ah, %cl        /* Top 2 bits of cyl number in here */
                    684:
                    685:        popw    %bx             /* (pushed %dx, but need %dl for now */
                    686:        incb    %bl             /* Sector numbers run from 1, not 0 */
                    687:        orb     %bl, %cl        /* Or the sector number into top bits cyl */
                    688:
                    689:                                /* Remember, %dl has head number */
                    690:        popw    %ax
                    691:                                /* %al has BIOS drive number -> %dl */
                    692:
                    693:        movb    %dl, %dh        /* Now %dh has head number (from 0) */
                    694:        movb    %al, %dl        /* Now %dl has BIOS drive number */
                    695:
                    696:        xorw    %bx, %bx        /* Set up target offset */
                    697:
                    698:        movw    $0x0201, %ax    /* %al = 1 - read one sector at a time */
                    699:                                /* %ah = 2 - int 0x13 function for CHS read */
                    700:
                    701:        call    do_int_13       /* saves us 1 byte :-) */
                    702:
                    703:        /* Get the next sector */
                    704:
                    705:        popal
                    706:        incl    %eax
                    707:        addw    $32, %bx        /* Number of segments/paras in a sector */
                    708:        loop    calc_chs
                    709:
                    710:        ret
                    711:
                    712:        /* read error */
                    713: read_error:
                    714:        movb    $PBR_READ_ERROR, %al
                    715: err_print_crlf:
                    716:        movw    $err_txt_crlf, %si
                    717:        jmp     err_print2
                    718:
                    719:
                    720: /*
                    721:  * Load a single filesystem block into memory using LBA calls.
                    722:  *
                    723:  * Input:      %eax - 32-bit filesystem block number
                    724:  *             %bx  - target segment (target offset is 0000)
                    725:  *             %dl  - BIOS drive number
                    726:  *
                    727:  * Output:     block successfully read in (panics if not)
                    728:  *             all general purpose registers may have been trashed
                    729:  */
                    730: load_lba:
                    731:        /*
                    732:         * BIOS call "INT 0x13 Extensions Extended Read"
                    733:         *      Call with       %ah = 0x42
                    734:         *                      %dl = drive (0x80 for 1st hd, 0x81 for 2nd, etc)
                    735:         *                      %ds:%si = segment:offset of command packet
                    736:         *      Return:
                    737:         *                      carry set: failure
                    738:         *                              %ah = error code (0x01, invalid func)
                    739:         *                              command packet's sector count field set
                    740:         *                              to the number of sectors successfully
                    741:         *                              transferred
                    742:         *                      carry clear: success
                    743:         *                              %ah = 0 (success)
                    744:         *      Command Packet:
                    745:         *                      0x0000  BYTE    packet size (0x10 or 0x18)
                    746:         *                      0x0001  BYTE    reserved (should be 0)
                    747:         *                      0x0002  WORD    sectors to transfer (max 127)
                    748:         *                      0x0004  DWORD   seg:offset of transfer buffer
                    749:         *                      0x0008  QWORD   starting sector number
                    750:         */
                    751:        call    fsbtosector             /* Set up lba_sector & lba_sector+4 */
                    752:
                    753:        /* movb %dh, lba_count          <- XXX done by installboot */
                    754:        movw    %bx, lba_seg
                    755:        movw    $lba_command, %si
                    756:        movb    $0x42, %ah
                    757: do_int_13:
                    758:        int     $0x13
                    759:        jc      read_error
                    760:
                    761:        ret
                    762:
                    763:
                    764: /*
                    765:  * Converts a given filesystem block number into a disk sector
                    766:  * at lba_sector and lba_sector+4.
                    767:  *
                    768:  * Input:      %eax - 32-bit filesystem block number
                    769:  *
                    770:  * Output:     lba_sector and lba_sector+4 set up
                    771:  *             XXX
                    772:  */
                    773: fsbtosector:
                    774:        /*
                    775:         * We want to do
                    776:         *
                    777:         * movb fsbtodb, %ch            /# Shift counts we'll need #/
                    778:         * movb $32, %cl
                    779:         *
                    780:         * which is 6 bytes of code + 1 byte of data.
                    781:         *
                    782:         * We'll actually code it with an immediate 16-bit load into %cx,
                    783:         * which is just 3 bytes of data (saves 4 bytes).
                    784:         */
                    785: fsbtodb = .+2
                    786:        movw    $0x9020, %cx            /* %ch = fsbtodb, %cl = 0x20 */
                    787:
                    788:        pushl   %eax
                    789:        subb    %ch, %cl
                    790:        shrl    %cl, %eax
                    791:        movl    %eax, lba_sector+4
                    792:        popl    %eax
                    793:
                    794:        movb    %ch, %cl
                    795:        shll    %cl, %eax
                    796:
                    797:        /*
                    798:         * And add p_offset, which is the block offset to the start
                    799:         * of the filesystem.
                    800:         *
                    801:         * We would do addl p_offset, %eax, which is 5 bytes of code
                    802:         * and 4 bytes of data, but it's more efficient to have
                    803:         * installboot patch directly in the code (this variable is
                    804:         * only used here) for 6 bytes of code (but no data).
                    805:         */
                    806: p_offset = .+2
                    807:        addl    $0x90909090, %eax       /* addl $p_offset, %eax */
                    808:
                    809:        movl    %eax, lba_sector
                    810:        jnc     1f
                    811:
                    812:        incl    lba_sector+4
                    813: 1:
                    814:        ret
                    815:
                    816:
                    817: /*
                    818:  * Display string
                    819:  */
                    820: Lmessage:
                    821:        cld
                    822: 1:
                    823:        lodsb                   /* load a byte into %al */
                    824:        orb     %al, %al
                    825:        jz      1f
                    826:        call    Lchr
                    827:        jmp     1b
                    828:
                    829: /*
                    830:  *     Lchr: write the character in %al to console
                    831:  */
                    832: Lchr:
                    833: #ifdef SERIAL
                    834:        pushw   %dx
                    835:        movb    $0x01, %ah
                    836:        xorw    %dx, %dx
                    837:        movb    SERIAL, %dl
                    838:        int     $0x14
                    839:        popw    %dx
                    840: #else
                    841:        pushw   %bx
                    842:        movb    $0x0e, %ah
                    843:        xorw    %bx, %bx
                    844:        incw    %bx             /* movw $0x01, %bx */
                    845:        int     $0x10
                    846:        popw    %bx
                    847: #endif
                    848: 1:
                    849:        ret
                    850:
                    851:        /* .data */
                    852:
                    853: /* vector to the routine to read a particular filesystem block for us */
                    854: load_fsblock:
                    855:        .word   load_chs
                    856:
                    857:
                    858: /* This next block is used for the EDD command packet used to read /boot
                    859:  * sectors.
                    860:  *
                    861:  * lba_count is set up for us by installboot.  It is the number of sectors
                    862:  * in a filesystem block.  (Max value 127.)
                    863:  *
                    864:  * XXX The EDD limit of 127 sectors in one read means that we currently
                    865:  *     restrict filesystem blocks to 127 sectors, or < 64 KB.  That is
                    866:  *     effectively a 32 KB block limit, as filesystem block sizes are
                    867:  *     powers of two.  The default filesystem block size is 16 KB.
                    868:  *
                    869:  *     I say we run with this limitation and see where it bites us...
                    870:  */
                    871:
                    872: lba_command:
                    873:        .byte   0x10                    /* size of command packet */
                    874:        .byte   0x00                    /* reserved */
                    875: fs_bsize_s:
                    876: lba_count:
                    877:        .word   0                       /* sectors to transfer, max 127 */
                    878:        .word   0                       /* target buffer, offset */
                    879: lba_seg:
                    880:        .word   0                       /* target buffer, segment */
                    881: lba_sector:
                    882:        .long   0, 0                    /* sector number */
                    883:
                    884: load_msg:
                    885:        .asciz  "!Loading"
                    886: err_txt_crlf:
                    887:        .ascii  "\r\n"
                    888: err_txt:
                    889:        .ascii  "ERR "
                    890: err_id:
                    891:        .ascii  "?"
                    892: crlf:  .asciz  "\r\n"
                    893:
                    894:        . = 0x200 - 2
                    895:        /* a little signature */
                    896:        .word   DOSMBR_SIGNATURE

CVSweb