altmbr: an alternative MBR which ignores the active flag
authorH. Peter Anvin <hpa@linux.intel.com>
Tue, 31 Mar 2009 01:17:05 +0000 (18:17 -0700)
committerH. Peter Anvin <hpa@linux.intel.com>
Tue, 31 Mar 2009 01:17:05 +0000 (18:17 -0700)
Impact: new feature

Create an alternative MBR which takes a partition number in byte 439
instead of looking at the active flag.  This is useful when
dual-booting legacy operating systems which assume that their
particular partition must be the active partition.

Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
mbr/Makefile
mbr/altmbr.S [new file with mode: 0644]
mbr/checksize.pl

index 0bdf7e3..e9fc387 100644 (file)
@@ -17,7 +17,7 @@
 topdir = ..
 include $(topdir)/MCONFIG.embedded
 
-all:   mbr.bin gptmbr.bin isohdpfx.bin
+all:   mbr.bin altmbr.bin gptmbr.bin isohdpfx.bin
 
 .PRECIOUS: %.o
 %.o: %.S
@@ -31,6 +31,10 @@ mbr.bin: mbr.elf checksize.pl
        $(OBJCOPY) -O binary $< $@
        $(PERL) checksize.pl $@ 440
 
+altmbr.bin: altmbr.elf checksize.pl
+       $(OBJCOPY) -O binary $< $@
+       $(PERL) checksize.pl $@ 439 440
+
 isohdpfx.bin: isohdpfx.elf checksize.pl
        $(OBJCOPY) -O binary $< $@
        $(PERL) checksize.pl $@ 432
diff --git a/mbr/altmbr.S b/mbr/altmbr.S
new file mode 100644 (file)
index 0000000..4d9d7f3
--- /dev/null
@@ -0,0 +1,307 @@
+/* -----------------------------------------------------------------------
+ *
+ *   Copyright 2007-2009 H. Peter Anvin - All Rights Reserved
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+       .code16
+       .text
+
+       .globl  bootsec
+stack          = 0x7c00
+driveno                = (stack-6)
+sectors                = (stack-8)
+secpercyl      = (stack-12)
+
+BIOS_kbdflags  = 0x417
+BIOS_page      = 0x462
+
+       /* gas/ld has issues with doing this as absolute addresses... */
+       .section ".bootsec", "a", @nobits
+       .globl  bootsec
+bootsec:
+       .space  512
+
+       .text
+       .globl  _start
+_start:
+       cli
+       xorw    %ax, %ax
+       movw    %ax, %ds
+       movw    %ax, %ss
+       movw    $stack, %sp
+       movw    %sp, %si
+       pushw   %es             /* es:di -> $PnP header */
+       pushw   %di
+       movw    %ax, %es
+       sti
+       cld
+
+       /* Copy down to 0:0x600 */
+       movw    $_start, %di
+       movw    $(512/2), %cx
+       rep; movsw
+
+       ljmpw   $0, $next
+next:
+       
+       /* Escape for the user: if Ctrl is pressed, assume drive hd0 */
+       testb   $0x04, BIOS_kbdflags    /* Ctrl pressed */
+       jz      1f
+       movb    $0x80, %dl
+1:
+       pushw   %dx             /* dl -> drive number */
+
+       /* Check to see if we have EBIOS */
+       pushw   %dx             /* drive number */
+       movb    $0x41, %ah      /* %al == 0 already */
+       movw    $0x55aa, %bx
+       xorw    %cx, %cx
+       xorb    %dh, %dh
+       stc
+       int     $0x13
+       jc      1f
+       cmpw    $0xaa55, %bx
+       jne     1f
+       shrw    %cx             /* Bit 0 = fixed disk subset */
+       jnc     1f
+
+       /* We have EBIOS; patch in the following code at
+          read_sector_cbios: movb $0x42, %ah ;  jmp read_common */
+       movl    $0xeb42b4+((read_common-read_sector_cbios-4) << 24), \
+               (read_sector_cbios)
+
+1:
+       popw    %dx
+
+       /* Get (C)HS geometry */
+       movb    $0x08, %ah
+       int     $0x13
+       andw    $0x3f, %cx      /* Sector count */
+       pushw   %cx             /* Save sectors on the stack */
+       movzbw  %dh, %ax        /* dh = max head */
+       incw    %ax             /* From 0-based max to count */
+       mulw    %cx             /* Heads*sectors -> sectors per cylinder */
+
+       /* Save sectors/cylinder on the stack */
+       pushw   %dx             /* High word */
+       pushw   %ax             /* Low word */
+
+       xorl    %eax, %eax      /* Base */
+       cdq                     /* Root (%edx <- 0) */
+       call    scan_partition_table
+
+       /* If we get here, we have no OS */
+missing_os:
+       call    error
+       .ascii  "Missing operating system.\r\n"
+
+/*
+ * read_sector: read a single sector pointed to by %eax to 0x7c00.
+ * CF is set on error.  All registers saved.
+ */
+read_sector:
+       pushal
+       xorl    %edx, %edx
+       movw    $bootsec, %bx
+       pushl   %edx    /* MSW of LBA */
+       pushl   %eax    /* LSW of LBA */
+       pushw   %es     /* Buffer segment */
+       pushw   %bx     /* Buffer offset */
+       pushw   $1      /* Sector count */
+       pushw   $16     /* Size of packet */
+       movw    %sp, %si
+
+       /* This chunk is skipped if we have ebios */
+       /* Do not clobber %eax before this chunk! */
+       /* This also relies on %bx and %edx as set up above. */
+read_sector_cbios:
+       divl    (secpercyl)
+       shlb    $6, %ah
+       movb    %ah, %cl
+       movb    %al, %ch
+       xchgw   %dx, %ax
+       divb    (sectors)
+       movb    %al, %dh
+       orb     %ah, %cl
+       incw    %cx     /* Sectors are 1-based */
+       movw    $0x0201, %ax
+
+read_common:
+       movb    (driveno), %dl
+       int     $0x13
+       leaw    16(%si), %sp    /* Drop DAPA */
+       popal
+       ret
+
+/*
+ * read_partition_table:
+ *     Read a partition table (pointed to by %eax), and copy
+ *     the partition table into the ptab buffer.
+ *
+ *     Clobbers %si, %di, and %cx, other registers preserved.
+ *     %cx = 0 on exit.
+ *
+ *     On error, CF is set and ptab is overwritten with junk.
+ */
+ptab   = _start+446
+
+read_partition_table:
+       call    read_sector
+       movw    $bootsec+446, %si
+       movw    $ptab, %di
+       movw    $(16*4/2), %cx
+       rep ; movsw
+       ret
+
+/*
+ * scan_partition_table:
+ *     Scan a partition table currently loaded in the partition table
+ *     area.  Preserve all registers.
+ *
+ *      On entry:
+ *       %eax - base (location of this partition table)
+ *       %edx - root (offset from MBR, or 0 for MBR)
+ *
+ *      These get pushed into stack slots:
+ *        28(%bp) - %eax - base
+ *       20(%bp) - %edx - root
+ */
+
+scan_partition_table:
+       pushal
+       movw    %sp, %bp
+
+       /* Scan the primary partition table */
+       movw    $ptab, %si
+       movw    $4, %cx
+       push    %si
+       push    %cx
+
+       /* Is it a primary partition? */
+       andl    %edx, %edx
+       jnz     6f
+
+5:
+       decb    (partition)
+       jz      boot
+       addw    $16, %bx
+       loopw   5b
+
+6:
+       /* No active partitions found, look for extended partitions */
+       popw    %cx             /* %cx <- 4    */
+       popw    %si             /* %si <- ptab */
+7:
+       movl    20(%bp), %edx           /* "Root" */
+
+       movb    4(%si), %al
+       cmpb    $0x0f, %al      /* 0x0f = Win9x extended */
+       je      8f
+       andb    $~0x80, %al     /* 0x85 = Linux extended */
+       cmpb    $0x05, %al      /* 0x05 = MS-DOS extended */
+       jne     9f
+
+       /* It is an extended partition.  Read the extended partition and
+          try to scan it.  If the scan returns, re-load the current
+          partition table and resume scan. */
+8:
+       movl    8(%si), %eax            /* Partition table offset */
+       addl    %edx, %eax              /* Compute location of new ptab */
+       andl    %edx, %edx              /* Is this the MBR? */
+       jnz     10f
+       movl    %eax, %edx              /* Offset -> root if this was MBR */
+10:
+       call    read_partition_table
+       jc      11f
+       call    scan_partition_table
+11:
+       /* This returned, so we need to reload the current partition table */
+       movl    28(%bp), %eax           /* "Base" */
+       call    read_partition_table
+
+       /* fall through */
+9:
+       /* Not an extended partition */
+       andb    %al, %al
+       jz      12f                     /* Not a valid partition */
+       andl    %edx, %edx              /* Are we inside an extended part? */
+       /* If so, this is a logical partition */
+       decb    (partition)
+       je      boot
+12:
+       addw    $16, %si
+       loopw   7b
+
+       /* Nothing found, return */
+       popal
+       ret
+
+/*
+ * boot: invoke the actual bootstrap. (%si) points to the partition
+ *      table entry, and 28(%bp) has the partition table base.
+ */
+boot:
+       cmpb    $0, 4(%si)
+       je      missing_os
+       movl    8(%si), %eax
+       addl    28(%bp), %eax
+       movl    %eax, 8(%si)    /* Adjust in-memory partition table entry */
+       call    read_sector
+       jc      disk_error
+       cmpw    $0xaa55, (bootsec+510)
+       jne     missing_os              /* Not a valid boot sector */
+       movw    $driveno, %sp   /* driveno == bootsec-6 */
+       popw    %dx             /* dl -> drive number */
+       popw    %di             /* es:di -> $PnP vector */
+       popw    %es
+       cli
+       jmpw    *%sp            /* %sp == bootsec */
+
+disk_error:
+       call    error
+       .ascii  "Operating system load error.\r\n"
+
+/*
+ * Print error messages.  This is invoked with "call", with the
+ * error message at the return address.
+ */
+error:
+       popw    %si
+2:
+       lodsb
+       movb    $0x0e, %ah
+       movb    (BIOS_page), %bh
+       movb    $0x07, %bl
+       int     $0x10           /* May destroy %bp */
+       cmpb    $10, %al        /* Newline? */
+       jne     2b
+
+       int     $0x18           /* Boot failure */
+die:
+       hlt
+       jmp     die
+
+/* Location of the partition configuration byte */
+partition = _start + 439
index 68a035b..a364310 100755 (executable)
@@ -1,6 +1,6 @@
 ## -----------------------------------------------------------------------
 ##
-##   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
+##   Copyright 2007-2009 H. Peter Anvin - All Rights Reserved
 ##
 ##   This program is free software; you can redistribute it and/or modify
 ##   it under the terms of the GNU General Public License as published by
 ##
 ## checksize.pl
 ##
-## Check the size of a binary file
+## Check the size of a binary file and pad it with zeroes to that size
 ##
 
-($file, $maxsize) = @ARGV;
+use bytes;
 
-@st = stat($file);
+($file, $maxsize, $padsize) = @ARGV;
+
+$padsize = $maxsize unless(defined($padsize));
+
+open(FILE, '+<', $file) or die;
+@st = stat(FILE);
 if (!defined($size = $st[7])) {
     die "$0: $file: $!\n";
 }
 if ($size > $maxsize) {
     print STDERR "$file: too big ($size > $maxsize)\n";
     exit 1;
-} else {
-    exit 0;
+} elsif ($size < $padsize) {
+    seek(FILE, $size, 0);
+    print FILE "\0" x ($padsize-$size);
 }
+
+exit 0;