summaryrefslogtreecommitdiff
path: root/sys/arch/amd64/stand/mbr
diff options
context:
space:
mode:
authorMichael Shalayeff <mickey@cvs.openbsd.org>2004-02-03 12:09:48 +0000
committerMichael Shalayeff <mickey@cvs.openbsd.org>2004-02-03 12:09:48 +0000
commitee64ff9774a8a97d8ac9159076e5aa096f4b0465 (patch)
tree56c0a44d5a11182f89b55b68c79f750f9a093bc6 /sys/arch/amd64/stand/mbr
parent74d9ef0d94f422fd01f5c329723d4a06537c0884 (diff)
das boot; das cloned das from das i386
Diffstat (limited to 'sys/arch/amd64/stand/mbr')
-rw-r--r--sys/arch/amd64/stand/mbr/Makefile29
-rw-r--r--sys/arch/amd64/stand/mbr/mbr.S570
2 files changed, 599 insertions, 0 deletions
diff --git a/sys/arch/amd64/stand/mbr/Makefile b/sys/arch/amd64/stand/mbr/Makefile
new file mode 100644
index 00000000000..670c731871c
--- /dev/null
+++ b/sys/arch/amd64/stand/mbr/Makefile
@@ -0,0 +1,29 @@
+# $OpenBSD: Makefile,v 1.1 2004/02/03 12:09:47 mickey Exp $
+#
+
+PROG= mbr
+SRCS= mbr.S
+AFLAGS+=-m32 -I${.CURDIR} -I${.CURDIR}/../../.. #-Wa,-a
+LD=ld
+LDFLAGS=-melf_i386 -nostdlib -Ttext 0 -x -N -s -Bstatic -e start
+
+NOMAN=
+#MAN+= mbr.8
+
+INSTALL_STRIP=
+SADIR=${.CURDIR}/..
+S= ${.CURDIR}/../../../..
+
+# Uncomment this to make mbr talk to a serial port.
+#CPPFLAGS+=-DSERIAL=0
+
+${PROG}: $(OBJS) $(DPADD)
+ $(LD) $(LDFLAGS) -o $(PROG) $(OBJS) $(LDADD)
+ #@size $(PROG)
+ @if [ -x ${.OBJDIR}/${PROG} ]; then \
+ objcopy -O binary ${PROG} ${.OBJDIR}/.tmp;\
+ mv -f ${.OBJDIR}/.tmp ${.OBJDIR}/${PROG}; \
+ ls -l ${.OBJDIR}/${PROG}; \
+ fi
+
+.include <bsd.prog.mk>
diff --git a/sys/arch/amd64/stand/mbr/mbr.S b/sys/arch/amd64/stand/mbr/mbr.S
new file mode 100644
index 00000000000..b7553ead71a
--- /dev/null
+++ b/sys/arch/amd64/stand/mbr/mbr.S
@@ -0,0 +1,570 @@
+/* $OpenBSD: mbr.S,v 1.1 2004/02/03 12:09:47 mickey Exp $ */
+
+/*
+ * Copyright (c) 1997 Michael Shalayeff and Tobias Weingartner
+ * Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+/* Copyright (c) 1996 VaX#n8 (vax@linkdead.paranoia.com)
+ * last edited 9 July 1996
+ * many thanks to Erich Boleyn (erich@uruk.org) for putting up with
+ * all my questions, and for his work on GRUB
+ * You may use this code or fragments thereof in a manner consistent
+ * with the other copyrights as long as you retain my pseudonym and
+ * this copyright notice in the file.
+ */
+
+ .file "mbr.S"
+
+#include <machine/asm.h>
+#include <assym.h>
+
+/*
+ * Memory layout:
+ *
+ * 0x07C00 -> 0x07DFF BIOS loads us here (at 31k)
+ * 0x07E00 -> 0x17BFC our stack (to 95k)
+ *
+ * 0x07A00 -> 0x07BFF we relocate to here (at 30k5)
+ *
+ * 0x07C00 -> 0x07DFF we load PBR here (at 31k)
+ *
+ * The BIOS loads us at physical address 0x07C00. We use a long jmp to
+ * normalise our address to seg:offset 07C0:0000. We then relocate to
+ * 0x07A00, seg:offset 07A0:0000.
+ *
+ * We use a long jmp to normalise our address to seg:offset 07A0:0000
+ * We set the stack to start at 07C0:FFFC (grows down on i386)
+ * We load the partition boot record (PBR) /boot at seg:offset 4000:0000
+ */
+#define BOOTSEG 0x7c0 /* segment where we are loaded */
+#define BOOTRELOCSEG 0x7a0 /* segment where we relocate to */
+#define BOOTSTACKOFF 0xfffc /* stack starts here, grows down */
+#define PARTSZ 16 /* each partition table entry is 16 bytes */
+
+#define CHAR_LBA_READ '.'
+#define CHAR_CHS_READ ';'
+#define CHAR_CHS_FORCE '!'
+#define CHAR_SHIFT_SEEN 0x07 /* Use BEL */
+
+#define MBR_FLAGS_FORCE_CHS 0x0001
+
+#ifdef DEBUG
+#define CHAR_S 'S' /* started */
+#define CHAR_R 'R' /* relocated */
+#define CHAR_L 'L' /* looking for bootable partition */
+#define CHAR_B 'B' /* loading boot */
+#define CHAR_G 'G' /* jumping to boot */
+
+#define DBGMSG(c) movb $c, %al; call Lchr
+#else /* !DEBUG */
+#define DBGMSG(c)
+#endif /* !DEBUG */
+
+/* Clobbers %al - maybe more */
+#define putc(c) movb $c, %al; call Lchr
+
+/* Clobbers %esi - maybe more */
+#define puts(s) movw $s, %si; call Lmessage
+
+
+ .text
+ .code16
+
+ .globl start
+start:
+ /* Adjust %cs to be right */
+ ljmp $BOOTSEG, $1f
+1:
+ /* Set up stack */
+ movw %cs, %ax
+
+ /*
+ * We don't need to disable and re-enable interrupts around the
+ * the load of ss and sp.
+ *
+ * From 80386 Programmer's Reference Manual:
+ * "A MOV into SS inhibits all interrupts until after the execution
+ * of the next instruction (which is presumably a MOV into eSP)"
+ *
+ * According to Hamarsoft's 86BUGS list (which is distributed with
+ * Ralph Brown's Interrupt List), some early 8086/88 processors
+ * failed to disable interrupts following a load into a segment
+ * register, but this was fixed with later steppings.
+ *
+ * Accordingly, this code will fail on very early 8086/88s, but
+ * nick@ will just have to live with it. Others will note that
+ * we require an 80386 (or compatible) or above processor, anyway.
+ */
+ /* cli */
+ movw %ax, %ss
+ movw $BOOTSTACKOFF, %sp
+ /* sti */ /* XXX not necessary; see above */
+
+ /* Set up data segment */
+ movw %ax, %ds
+ DBGMSG(CHAR_S)
+
+ /*
+ * On the PC architecture, the boot record (originally on a floppy
+ * disk) is loaded at 0000:7C00 (hex) and execution starts at the
+ * beginning.
+ *
+ * When hard disk support was added, a scheme to partition disks into
+ * four separate partitions was used, to allow multiple operating
+ * systems to be installed on the one disk. The boot sectors of the
+ * operating systems on each partition would of course expect to be
+ * loaded at 0000:7C00.
+ *
+ * The first sector of the hard disk is the master boot record (MBR).
+ * It is this which defines the partitions and says which one is
+ * bootable. Of course, the BIOS loads the MBR at 0000:7C00, the
+ * same location where the MBR needs to load the partition boot
+ * record (PBR, called biosboot in OpenBSD).
+ *
+ * Therefore, the MBR needs to relocate itself before loading the PBR.
+ *
+ * Make it so.
+ */
+ movw $BOOTRELOCSEG, %ax
+ movw %ax, %es
+ xorw %si, %si
+ xorw %di, %di
+ movw $0x200, %cx /* Bytes in MBR, relocate it all */
+ cld
+ rep
+ movsb
+
+ /* Jump to relocated self */
+ ljmp $BOOTRELOCSEG, $reloc
+reloc:
+ DBGMSG(CHAR_R)
+
+ /* Set up %es and %ds */
+ pushw %ds
+ popw %es /* next boot is at the same place as we were loaded */
+ pushw %cs
+ popw %ds /* and %ds is at the %cs */
+
+#ifdef SERIAL
+ /* Initialize the serial port to 9600 baud, 8N1.
+ */
+ xorw %ax, %ax
+ movb $0xe3, %ax
+ movw $SERIAL, %dx
+ int $0x14
+#endif
+
+ /*
+ * If the SHIFT key is held down on entry, force CHS read
+ */
+
+ /*
+ * BIOS call "INT 0x16 Get Keyboard Shift Flags
+ * Call with %ah = 0x02
+ * Return:
+ * %al = shift flags
+ * %ah - undefined by many BIOSes
+ */
+ movb $0x02, %ah
+ int $0x16
+ testb $0x3, %al /* Either shift key down? */
+ jz no_shift
+
+ putc(CHAR_SHIFT_SEEN) /* Signal that shift key was seen */
+
+ orb $MBR_FLAGS_FORCE_CHS, flags
+
+no_shift:
+ /* BIOS passes us drive number in %dl
+ *
+ * XXX - This is not always true. We currently check if %dl
+ * points to a HD, and if not we complain, and set it to point
+ * to the first HDD. Note, this is not 100% correct, since
+ * there is a possibility that you boot of of HD #2, and still
+ * get (%dl & 0x80) == 0x00, these type of systems will lose.
+ */
+ testb $0x80, %dl
+ jnz drive_ok
+
+ /* MBR on floppy or old BIOS
+ * Note: MBR (this code) should never be on a floppy. It does
+ * not belong there, so %dl should never be 0x00.
+ *
+ * Here we simply complain (should we?), and then hardcode the
+ * boot drive to 0x80.
+ */
+ puts(efdmbr)
+
+ /* If we are passed bogus data, set it to HD #1
+ */
+ movb $0x80, %dl
+
+drive_ok:
+ /* Find the first active partition.
+ * Note: this should be the only active partition. We currently
+ * don't check for that.
+ */
+ movw $pt, %si
+
+ movw $NDOSPART, %cx
+find_active:
+ DBGMSG(CHAR_L)
+ movb (%si), %al
+
+ cmpb $DOSACTIVE, %al
+ je found
+
+ addw $PARTSZ, %si
+ loop find_active
+
+ /* No bootable partition */
+no_part:
+ movw $enoboot, %si
+
+err_stop:
+ call Lmessage
+
+stay_stopped:
+ cli
+ hlt
+ /* Just to make sure */
+ jmp stay_stopped
+
+found:
+ /*
+ * Found bootable partition
+ */
+
+ DBGMSG(CHAR_B)
+
+ /* Store the drive number (from %dl) in decimal */
+ movb %dl, %al
+ andb $0x0F, %al
+ addb $'0', %al
+ movb %al, drive_num
+
+ /*
+ * Store the partition number, in decimal.
+ *
+ * We started with cx = 4; if found we want part '0'
+ * cx = 3; part '1'
+ * cx = 2; part '2'
+ * cx = 1; part '3'
+ *
+ * We'll come into this with no other values for cl.
+ */
+ movb $'0'+4, %al
+ subb %cl, %al
+ movb %al, part_num
+
+ /*
+ * Tell operator what partition we're trying to boot.
+ *
+ * Using drive X, partition Y
+ * - this used to be printed out after successfully loading the
+ * partition boot record; we now print it out before
+ */
+ pushw %si
+ movw $info, %si
+ testb $MBR_FLAGS_FORCE_CHS, flags
+ jnz 1f
+ incw %si
+1:
+ call Lmessage
+ popw %si
+
+ /*
+ * Partition table entry format:
+ *
+ * 0x00 BYTE boot indicator (0x80 = active, 0x00 = inactive)
+ * 0x01 BYTE start head
+ * 0x02 WORD start cylinder, sector
+ * 0x04 BYTE system type (0xA6 = OpenBSD)
+ * 0x05 BYTE end head
+ * 0x06 WORD end cylinder, sector
+ * 0x08 LONG start LBA sector
+ * 0x0C LONG number of sectors in partition
+ *
+ * In the case of a partition that extends beyond the 8GB boundary,
+ * the LBA values will be correct, the CHS values will have their
+ * maximums (typically (C,H,S) = (1023,255,63)).
+ *
+ * %ds:%si points to the active partition table entry.
+ */
+
+ /* We will load the partition boot sector (biosboot) where we
+ * were originally loaded. We'll check to make sure something
+ * valid comes in. So that we don't find ourselves, zero out
+ * the signature at the end.
+ */
+ movw $0, %es:signature(,1)
+
+ /*
+ * Have we been instructed to ignore LBA?
+ */
+ testb $MBR_FLAGS_FORCE_CHS, flags
+ jnz do_chs
+
+ /*
+ * We will use the LBA sector number if we have LBA support,
+ * so find out.
+ */
+
+ /*
+ * BIOS call "INT 0x13 Extensions Installation Check"
+ * Call with %ah = 0x41
+ * %bx = 0x55AA
+ * %dl = drive (0x80 for 1st hd, 0x81 for 2nd, etc)
+ * Return:
+ * carry set: failure
+ * %ah = error code (0x01, invalid func)
+ * carry clear: success
+ * %bx = 0xAA55 (must verify)
+ * %ah = major version of extensions
+ * %al (internal use)
+ * %cx = capabilities bitmap
+ * 0x0001 - extnd disk access funcs
+ * 0x0002 - rem. drive ctrl funcs
+ * 0x0004 - EDD functions with EBP
+ * %dx (extension version?)
+ */
+
+ movb %dl, (%si) /* Store drive here temporarily */
+ /* (This call trashes %dl) */
+ /*
+ * XXX This is actually the correct
+ * place to store this. The 0x80
+ * value used to indicate the
+ * active partition is by intention
+ * the same as the BIOS drive value
+ * for the first hard disk (0x80).
+ * At one point, 0x81 would go here
+ * for the second hard disk; the
+ * 0x80 value is often used as a
+ * bit flag for testing, rather
+ * than an exact byte value.
+ */
+ movw $0x55AA, %bx
+ movb $0x41, %ah
+ int $0x13
+
+ movb (%si), %dl /* Get back drive number */
+
+ jc do_chs /* Did the command work? Jump if not */
+ cmpw $0xAA55, %bx /* Check that bl, bh exchanged */
+ jne do_chs /* If not, don't have EDD extensions */
+ testb $0x01, %cl /* And do we have "read" available? */
+ jz do_chs /* Again, use CHS if not */
+
+do_lba:
+ /*
+ * BIOS call "INT 0x13 Extensions Extended Read"
+ * Call with %ah = 0x42
+ * %dl = drive (0x80 for 1st hd, 0x81 for 2nd, etc)
+ * %ds:%si = segment:offset of command packet
+ * Return:
+ * carry set: failure
+ * %ah = error code (0x01, invalid func)
+ * command packet's sector count field set
+ * to the number of sectors successfully
+ * transferred
+ * carry clear: success
+ * %ah = 0 (success)
+ * Command Packet:
+ * 0x0000 BYTE packet size (0x10 or 0x18)
+ * 0x0001 BYTE reserved (should be 0)
+ * 0x0002 WORD sectors to transfer (max 127)
+ * 0x0004 DWORD seg:offset of transfer buffer
+ * 0x0008 QWORD starting sector number
+ */
+ movb $CHAR_LBA_READ, %al
+ call Lchr
+
+ /* Load LBA sector number from active partition table entry */
+ movl 8(%si), %ecx
+ movl %ecx, lba_sector
+
+ pushw %si /* We'll need %si later */
+
+ movb $0x42, %ah
+ movw $lba_command, %si
+ int $0x13
+
+ popw %si /* (get back %si) flags unchanged */
+
+ jnc booting_os /* If it worked, run the pbr we got */
+
+ /*
+ * LBA read failed, fall through to try CHS read
+ */
+
+do_chs:
+ /*
+ * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into
+ * memory
+ * Call with %ah = 0x2
+ * %al = number of sectors
+ * %ch = cylinder & 0xFF
+ * %cl = sector (0-63) | rest of cylinder bits
+ * %dh = head
+ * %dl = drive (0x80 for hard disk)
+ * %es:%bx = segment:offset of buffer
+ * Return:
+ * carry set: failure
+ * %ah = err code
+ * %al = number of sectors transferred
+ * carry clear: success
+ * %al = 0x0 OR number of sectors transferred
+ * (depends on BIOS!)
+ * (according to Ralph Brown Int List)
+ */
+ movb $CHAR_CHS_READ, %al
+ call Lchr
+
+ /* Load values from active partition table entry */
+ movb 1(%si), %dh /* head */
+ movw 2(%si), %cx /* sect, cyl */
+ movw $0x201, %ax /* function and number of blocks */
+ xorw %bx, %bx /* put it at %es:0 */
+ int $0x13
+ jnc booting_os
+
+read_error:
+ movw $eread, %si
+ jmp err_stop
+
+booting_os:
+ puts(crlf)
+ DBGMSG(CHAR_G)
+
+ /*
+ * Make sure the pbr we loaded has a valid signature at the end.
+ * This also ensures that something did load where we were expecting
+ * it, as there's still a copy of our code there...
+ */
+ cmpw $DOSMBR_SIGNATURE, %es:signature(,1)
+ jne missing_os
+
+ /* jump to the new code (%ds:%si is at the right point) */
+ ljmp $0, $BOOTSEG << 4
+ /* not reached */
+
+missing_os:
+ movw $enoos, %si
+ jmp err_stop
+
+/*
+ * Display string
+ */
+Lmessage:
+ pushw %ax
+ cld
+1:
+ lodsb /* %al = *%si++ */
+ testb %al, %al
+ jz 1f
+ call Lchr
+ jmp 1b
+
+/*
+ * Lchr: write the error message in %ds:%si to console
+ */
+Lchr:
+ pushw %ax
+
+#ifdef SERIAL
+ pushw %dx
+ movb $0x01, %ah
+ movw SERIAL, %dx
+ int $0x14
+ popw %dx
+#else
+ pushw %bx
+ movb $0x0e, %ah
+ movw $1, %bx
+ int $0x10
+ popw %bx
+#endif
+1: popw %ax
+ ret
+
+/* command packet for LBA read of boot sector */
+lba_command:
+ .byte 0x10 /* size of command packet */
+ .byte 0x00 /* reserved */
+ .word 0x0001 /* sectors to transfer, just 1 */
+ .word 0 /* target buffer, offset */
+ .word BOOTSEG /* target buffer, segment */
+lba_sector:
+ .long 0, 0 /* sector number */
+
+/* Info messages */
+info: .ascii "!Using drive "
+drive_num:
+ .byte 'X'
+ .ascii ", partition "
+part_num:
+ .asciz "Y"
+
+/* Error messages */
+efdmbr: .asciz "MBR on floppy or old BIOS\r\n"
+eread: .asciz "\r\nRead error\r\n"
+enoos: .asciz "No O/S\r\n"
+enoboot: .ascii "No active partition" /* runs into crlf... */
+crlf: .asciz "\r\n"
+
+endofcode:
+ nop
+
+/* We're going to store a flags word here */
+
+ . = 0x1b4
+flags:
+ .word 0x0000
+ .ascii "Ox" /* Indicate that the two bytes */
+ /* before us are the flags word */
+
+/* (MBR) NT disk signature offset */
+ . = 0x1b8
+ .space 4, 0
+
+/* partition table */
+/* flag, head, sec, cyl, type, ehead, esect, ecyl, start, len */
+ . = DOSPARTOFF /* starting address of partition table */
+pt:
+ .byte 0x0,0,0,0,0,0,0,0
+ .long 0,0
+ .byte 0x0,0,0,0,0,0,0,0
+ .long 0,0
+ .byte 0x0,0,0,0,0,0,0,0
+ .long 0,0
+ .byte DOSACTIVE,0,1,0,DOSPTYP_OPENBSD,255,255,255
+ .long 0,0x7FFFFFFF
+/* the last 2 bytes in the sector 0 contain the signature */
+ . = 0x1fe
+signature:
+ .short DOSMBR_SIGNATURE
+ . = 0x200