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

Annotation of sys/arch/i386/stand/biosboot/biosboot.S, Revision 1.1

1.1     ! nbrk        1: /*     $OpenBSD: biosboot.S,v 1.38 2007/05/31 23:34:46 weingart 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:        .asciz  "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