summaryrefslogtreecommitdiffstats
path: root/sys/boot/i386
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2007-10-24 21:33:00 +0000
committerjhb <jhb@FreeBSD.org>2007-10-24 21:33:00 +0000
commit2f8a906c36edd256f39a8d2ef209fef1a548d79e (patch)
tree2e0ad7e3920a2c896b216cf32db1db922c1f0e29 /sys/boot/i386
parent81c7dc737f4505dc976b462a40fdd5832e63a765 (diff)
downloadFreeBSD-src-2f8a906c36edd256f39a8d2ef209fef1a548d79e.zip
FreeBSD-src-2f8a906c36edd256f39a8d2ef209fef1a548d79e.tar.gz
First cut at support for booting a GPT labeled disk via the BIOS bootstrap
on i386 and amd64 machines. The overall process is that /boot/pmbr lives in the PMBR (similar to /boot/mbr for MBR disks) and is responsible for locating and loading /boot/gptboot. /boot/gptboot is similar to /boot/boot except that it groks GPT rather than MBR + bsdlabel. Unlike /boot/boot, /boot/gptboot lives in its own dedicated GPT partition with a new "FreeBSD boot" type. This partition does not have a fixed size in that /boot/pmbr will load the entire partition into the lower 640k. However, it is limited in that it can only be 545k. That's still a lot better than the current 7.5k limit for boot2 on MBR. gptboot mostly acts just like boot2 in that it reads /boot.config and loads up /boot/loader. Some more details: - Include uuid_equal() and uuid_is_nil() in libstand. - Add a new 'boot' command to gpt(8) which makes a GPT disk bootable using /boot/pmbr and /boot/gptboot. Note that the disk must have some free space for the boot partition. - This required exposing the backend of the 'add' function as a gpt_add_part() function to the rest of gpt(8). 'boot' uses this to create a boot partition if needed. - Don't cripple cgbase() in the UFS boot code for /boot/gptboot so that it can handle a filesystem > 1.5 TB. - /boot/gptboot has a simple loader (gptldr) that doesn't do any I/O unlike boot1 since /boot/pmbr loads all of gptboot up front. The C portion of gptboot (gptboot.c) has been repocopied from boot2.c. The primary changes are to parse the GPT to find a root filesystem and to use 64-bit disk addresses. Currently gptboot assumes that the first UFS partition on the disk is the / filesystem, but this algorithm will likely be improved in the future. - Teach the biosdisk driver in /boot/loader to understand GPT tables. GPT partitions are identified as 'disk0pX:' (e.g. disk0p2:) which is similar to the /dev names the kernel uses (e.g. /dev/ad0p2). - Add a new "freebsd-boot" alias to g_part() for the new boot UUID. MFC after: 1 month Discussed with: marcel (some things might still change, but am committing what I have so far)
Diffstat (limited to 'sys/boot/i386')
-rw-r--r--sys/boot/i386/Makefile4
-rw-r--r--sys/boot/i386/gptboot/Makefile77
-rw-r--r--sys/boot/i386/gptboot/gptboot.c188
-rw-r--r--sys/boot/i386/gptboot/gptldr.S123
-rw-r--r--sys/boot/i386/libi386/biosdisk.c342
-rw-r--r--sys/boot/i386/libi386/devicename.c41
-rw-r--r--sys/boot/i386/pmbr/Makefile14
-rw-r--r--sys/boot/i386/pmbr/pmbr.s221
8 files changed, 838 insertions, 172 deletions
diff --git a/sys/boot/i386/Makefile b/sys/boot/i386/Makefile
index aaf94d2..b89222d 100644
--- a/sys/boot/i386/Makefile
+++ b/sys/boot/i386/Makefile
@@ -1,7 +1,7 @@
# $FreeBSD$
-SUBDIR= mbr boot0 boot0sio btx boot2 cdboot kgzldr libi386 libfirewire \
- loader
+SUBDIR= mbr pmbr boot0 boot0sio btx boot2 cdboot gptboot kgzldr \
+ libi386 libfirewire loader
# special boot programs, 'self-extracting boot2+loader'
SUBDIR+= pxeldr
diff --git a/sys/boot/i386/gptboot/Makefile b/sys/boot/i386/gptboot/Makefile
index 81e407d..b053c60 100644
--- a/sys/boot/i386/gptboot/Makefile
+++ b/sys/boot/i386/gptboot/Makefile
@@ -1,11 +1,10 @@
# $FreeBSD$
-FILES= boot boot1 boot2
+.PATH: ${.CURDIR}/../boot2
-NM?= nm
+FILES= gptboot
-# A value of 0x80 enables LBA support.
-BOOT_BOOT1_FLAGS?= 0x80
+NM?= nm
BOOT_COMCONSOLE_PORT?= 0x3f8
BOOT_COMCONSOLE_SPEED?= 9600
@@ -13,12 +12,12 @@ B2SIOFMT?= 0x3
REL1= 0x700
ORG1= 0x7c00
-ORG2= 0x2000
+ORG2= 0x0
# Decide level of UFS support.
-BOOT2_UFS?= UFS1_AND_UFS2
-#BOOT2_UFS?= UFS2_ONLY
-#BOOT2_UFS?= UFS1_ONLY
+GPTBOOT_UFS?= UFS1_AND_UFS2
+#GPTBOOT_UFS?= UFS2_ONLY
+#GPTBOOT_UFS?= UFS1_ONLY
CFLAGS= -Os \
-fno-guess-branch-probability \
@@ -27,13 +26,13 @@ CFLAGS= -Os \
-mno-align-long-strings \
-mrtd \
-mno-mmx -mno-3dnow -mno-sse -mno-sse2 -mno-sse3 \
- -D${BOOT2_UFS} \
- -DFLAGS=${BOOT_BOOT1_FLAGS} \
+ -D${GPTBOOT_UFS} \
-DSIOPRT=${BOOT_COMCONSOLE_PORT} \
-DSIOFMT=${B2SIOFMT} \
-DSIOSPD=${BOOT_COMCONSOLE_SPEED} \
-I${.CURDIR}/../../common \
- -I${.CURDIR}/../btx/lib -I. \
+ -I${.CURDIR}/../btx/lib \
+ -I${.CURDIR}/../boot2 \
-Wall -Waggregate-return -Wbad-function-cast -Wcast-align \
-Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
-Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \
@@ -44,58 +43,32 @@ LDFLAGS=-static -N --gc-sections
# Pick up ../Makefile.inc early.
.include <bsd.init.mk>
-CLEANFILES= boot
-
-boot: boot1 boot2
- cat boot1 boot2 > boot
-
-CLEANFILES+= boot1 boot1.out boot1.o
+CLEANFILES= gptboot
-boot1: boot1.out
- objcopy -S -O binary boot1.out ${.TARGET}
+gptboot: gptldr.bin gptboot.bin ${BTXKERN}
+ btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l gptldr.bin \
+ -o ${.TARGET} gptboot.bin
-boot1.out: boot1.o
- ${LD} ${LDFLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} boot1.o
+CLEANFILES+= gptldr.bin gptldr.out gptldr.o
-CLEANFILES+= boot2 boot2.ld boot2.ldr boot2.bin boot2.out boot2.o \
- boot2.s boot2.s.tmp boot2.h sio.o
+gptldr.bin: gptldr.out
+ objcopy -S -O binary gptldr.out ${.TARGET}
-boot2: boot2.ld
- @set -- `ls -l boot2.ld`; x=$$((7680-$$5)); \
- echo "$$x bytes available"; test $$x -ge 0
- dd if=boot2.ld of=${.TARGET} obs=7680 conv=osync
+gptldr.out: gptldr.o
+ ${LD} ${LDFLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o
-boot2.ld: boot2.ldr boot2.bin ${BTXKERN}
- btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l boot2.ldr \
- -o ${.TARGET} -P 1 boot2.bin
+CLEANFILES+= gptboot.bin gptboot.out gptboot.o sio.o
-boot2.ldr:
- dd if=/dev/zero of=${.TARGET} bs=276 count=1
+gptboot.bin: gptboot.out
+ objcopy -S -O binary gptboot.out ${.TARGET}
-boot2.bin: boot2.out
- objcopy -S -O binary boot2.out ${.TARGET}
-
-boot2.out: ${BTXCRT} boot2.o sio.o
+gptboot.out: ${BTXCRT} gptboot.o sio.o
${LD} ${LDFLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC}
-boot2.o: boot2.s
-
-SRCS= boot2.c boot2.h
-
-boot2.s: boot2.c boot2.h ${.CURDIR}/../../common/ufsread.c
- ${CC} ${CFLAGS} -S -o boot2.s.tmp ${.CURDIR}/boot2.c
- sed -e '/align/d' -e '/nop/d' < boot2.s.tmp > boot2.s
- rm -f boot2.s.tmp
-
-boot2.h: boot1.out
- ${NM} -t d ${.ALLSRC} | awk '/([0-9])+ T xread/ \
- { x = $$1 - ORG1; \
- printf("#define XREADORG %#x\n", REL1 + x) }' \
- ORG1=`printf "%d" ${ORG1}` \
- REL1=`printf "%d" ${REL1}` > ${.TARGET}
+gptboot.o: ${.CURDIR}/../../common/ufsread.c
.if ${MACHINE_ARCH} == "amd64"
-beforedepend boot2.s: machine
+beforedepend gptboot.o: machine
CLEANFILES+= machine
machine:
ln -sf ${.CURDIR}/../../../i386/include machine
diff --git a/sys/boot/i386/gptboot/gptboot.c b/sys/boot/i386/gptboot/gptboot.c
index f227233..401328b 100644
--- a/sys/boot/i386/gptboot/gptboot.c
+++ b/sys/boot/i386/gptboot/gptboot.c
@@ -17,8 +17,7 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
-#include <sys/disklabel.h>
-#include <sys/diskmbr.h>
+#include <sys/gpt.h>
#include <sys/dirent.h>
#include <sys/reboot.h>
@@ -31,7 +30,6 @@ __FBSDID("$FreeBSD$");
#include <btxv86.h>
-#include "boot2.h"
#include "lib.h"
#define IO_KEYBOARD 1
@@ -99,6 +97,7 @@ __FBSDID("$FreeBSD$");
extern uint32_t _end;
+static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS;
static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
static const unsigned char flags[NOPT] = {
RBX_DUAL,
@@ -124,9 +123,8 @@ static struct dsk {
unsigned drive;
unsigned type;
unsigned unit;
- unsigned slice;
- unsigned part;
- unsigned start;
+ int part;
+ daddr_t start;
int init;
} dsk;
static char cmd[512];
@@ -137,20 +135,21 @@ static struct bootinfo bootinfo;
static uint8_t ioctrl = IO_KEYBOARD;
void exit(int);
+static int bcmp(const void *, const void *, size_t);
static void load(void);
static int parse(void);
static int xfsread(ino_t, void *, size_t);
-static int dskread(void *, unsigned, unsigned);
+static int dskread(void *, daddr_t, unsigned);
static void printf(const char *,...);
static void putchar(int);
+static void memcpy(void *, const void *, int);
static uint32_t memsize(void);
-static int drvread(void *, unsigned, unsigned);
+static int drvread(void *, daddr_t, unsigned);
static int keyhit(unsigned);
static int xputc(int);
static int xgetc(int);
static int getc(int);
-static void memcpy(void *, const void *, int);
static void
memcpy(void *dst, const void *src, int len)
{
@@ -168,6 +167,7 @@ strcmp(const char *s1, const char *s2)
return (unsigned char)*s1 - (unsigned char)*s2;
}
+#define GPTBOOT
#include "ufsread.c"
static inline int
@@ -239,7 +239,7 @@ main(void)
dsk.drive = *(uint8_t *)PTOV(ARGS);
dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
dsk.unit = dsk.drive & DRV_MASK;
- dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
+ dsk.part = -1;
bootinfo.bi_version = BOOTINFO_VERSION;
bootinfo.bi_size = sizeof(bootinfo);
bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */
@@ -280,10 +280,10 @@ main(void)
for (;;) {
if (!autoboot || !OPT_CHECK(RBX_QUIET))
printf("\nFreeBSD/i386 boot\n"
- "Default: %u:%s(%u,%c)%s\n"
+ "Default: %u:%s(%up%u)%s\n"
"boot: ",
dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
- 'a' + dsk.part, kname);
+ dsk.part, kname);
if (ioctrl & IO_SERIAL)
sio_flush();
if (!autoboot || keyhit(5*SECOND))
@@ -395,12 +395,12 @@ load(void)
bootinfo.bi_kernelname = VTOP(kname);
bootinfo.bi_bios_dev = dsk.drive;
__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
- MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part),
+ MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff),
0, 0, 0, VTOP(&bootinfo));
}
static int
-parse()
+parse(void)
{
char *arg = cmd;
char *ep, *p, *q;
@@ -467,19 +467,16 @@ parse()
if (arg[1] != ',' || dsk.unit > 9)
return -1;
arg += 2;
- dsk.slice = WHOLE_DISK_SLICE;
+ dsk.part = -1;
if (arg[1] == ',') {
- dsk.slice = *arg - '0' + 1;
- if (dsk.slice > NDOSPART)
+ dsk.part = *arg - '0';
+ if (dsk.part < 1 || dsk.part > 9)
return -1;
arg += 2;
}
- if (arg[1] != ')')
+ if (arg[0] != ')')
return -1;
- dsk.part = *arg - 'a';
- if (dsk.part > 7)
- return (-1);
- arg += 2;
+ arg++;
if (drv == -1)
drv = dsk.unit;
dsk.drive = (dsk.type <= TYPE_MAXHARD
@@ -498,63 +495,87 @@ parse()
}
static int
-dskread(void *buf, unsigned lba, unsigned nblk)
+dskread(void *buf, daddr_t lba, unsigned nblk)
{
- struct dos_partition *dp;
- struct disklabel *d;
+ struct gpt_hdr hdr;
+ struct gpt_ent *ent;
char *sec;
- unsigned sl, i;
+ daddr_t slba, elba;
+ int part, entries_per_sec;
if (!dsk_meta) {
+ /* Read and verify GPT. */
sec = dmadat->secbuf;
dsk.start = 0;
- if (drvread(sec, DOSBBSECTOR, 1))
+ if (drvread(sec, 1, 1))
return -1;
- dp = (void *)(sec + DOSPARTOFF);
- sl = dsk.slice;
- if (sl < BASE_SLICE) {
- for (i = 0; i < NDOSPART; i++)
- if (dp[i].dp_typ == DOSPTYP_386BSD &&
- (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
- sl = BASE_SLICE + i;
- if (dp[i].dp_flag & 0x80 ||
- dsk.slice == COMPATIBILITY_SLICE)
+ memcpy(&hdr, sec, sizeof(hdr));
+ if (bcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) != 0 ||
+ hdr.hdr_lba_self != 1 || hdr.hdr_revision < 0x00010000 ||
+ hdr.hdr_entsz < sizeof(*ent) || DEV_BSIZE % hdr.hdr_entsz != 0) {
+ printf("Invalid GPT header\n");
+ return -1;
+ }
+
+ /* XXX: CRC check? */
+
+ /*
+ * If the partition isn't specified, then search for the first UFS
+ * partition and hope it is /. Perhaps we should be using an OS
+ * flag in the GPT entry to mark / partitions.
+ *
+ * If the partition is specified, then figure out the LBA for the
+ * sector containing that partition index and load it.
+ */
+ entries_per_sec = DEV_BSIZE / hdr.hdr_entsz;
+ if (dsk.part == -1) {
+ slba = hdr.hdr_lba_table;
+ elba = slba + hdr.hdr_entries / entries_per_sec;
+ while (slba < elba && dsk.part == -1) {
+ if (drvread(sec, slba, 1))
+ return -1;
+ for (part = 0; part < entries_per_sec; part++) {
+ ent = (struct gpt_ent *)(sec + part * hdr.hdr_entsz);
+ if (bcmp(&ent->ent_type, &freebsd_ufs_uuid,
+ sizeof(uuid_t)) == 0) {
+ dsk.part = (slba - hdr.hdr_lba_table) *
+ entries_per_sec + part + 1;
+ dsk.start = ent->ent_lba_start;
break;
+ }
}
- if (dsk.slice == WHOLE_DISK_SLICE)
- dsk.slice = sl;
- }
- if (sl != WHOLE_DISK_SLICE) {
- if (sl != COMPATIBILITY_SLICE)
- dp += sl - BASE_SLICE;
- if (dp->dp_typ != DOSPTYP_386BSD) {
- printf("Invalid %s\n", "slice");
- return -1;
+ slba++;
}
- dsk.start = dp->dp_start;
- }
- if (drvread(sec, dsk.start + LABELSECTOR, 1))
- return -1;
- d = (void *)(sec + LABELOFFSET);
- if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
- if (dsk.part != RAW_PART) {
- printf("Invalid %s\n", "label");
+ if (dsk.part == -1) {
+ printf("No UFS partition was found\n");
return -1;
}
} else {
- if (!dsk.init) {
- if (d->d_type == DTYPE_SCSI)
- dsk.type = TYPE_DA;
- dsk.init++;
+ if (dsk.part > hdr.hdr_entries) {
+ printf("Invalid partition index\n");
+ return -1;
}
- if (dsk.part >= d->d_npartitions ||
- !d->d_partitions[dsk.part].p_size) {
- printf("Invalid %s\n", "partition");
+ slba = hdr.hdr_lba_table + (dsk.part - 1) / entries_per_sec;
+ if (drvread(sec, slba, 1))
+ return -1;
+ part = (dsk.part - 1) % entries_per_sec;
+ ent = (struct gpt_ent *)(sec + part * hdr.hdr_entsz);
+ if (bcmp(&ent->ent_type, &freebsd_ufs_uuid, sizeof(uuid_t)) != 0) {
+ printf("Specified partition is not UFS\n");
return -1;
}
- dsk.start += d->d_partitions[dsk.part].p_offset;
- dsk.start -= d->d_partitions[RAW_PART].p_offset;
+ dsk.start = ent->ent_lba_start;
+ }
+ /*
+ * XXX: No way to detect SCSI vs. ATA currently.
+ */
+#if 0
+ if (!dsk.init) {
+ if (d->d_type == DTYPE_SCSI)
+ dsk.type = TYPE_DA;
+ dsk.init++;
}
+#endif
}
return drvread(buf, dsk.start + lba, nblk);
}
@@ -606,21 +627,46 @@ putchar(int c)
}
static int
-drvread(void *buf, unsigned lba, unsigned nblk)
+bcmp(const void *b1, const void *b2, size_t length)
+{
+ const char *p1 = b1, *p2 = b2;
+
+ if (length == 0)
+ return (0);
+ do {
+ if (*p1++ != *p2++)
+ break;
+ } while (--length);
+ return (length);
+}
+
+static struct {
+ uint16_t len;
+ uint16_t count;
+ uint16_t seg;
+ uint16_t off;
+ uint64_t lba;
+} packet;
+
+static int
+drvread(void *buf, daddr_t lba, unsigned nblk)
{
static unsigned c = 0x2d5c7c2f;
if (!OPT_CHECK(RBX_QUIET))
printf("%c\b", c = c << 8 | c >> 24);
- v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
- v86.addr = XREADORG; /* call to xread in boot1 */
- v86.es = VTOPSEG(buf);
- v86.eax = lba;
- v86.ebx = VTOPOFF(buf);
- v86.ecx = lba >> 16;
- v86.edx = nblk << 8 | dsk.drive;
- v86int();
+ packet.len = 0x10;
+ packet.count = nblk;
+ packet.seg = VTOPOFF(buf);
+ packet.off = VTOPSEG(buf);
+ packet.lba = lba;
v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4200;
+ v86.edx = dsk.drive;
+ v86.ds = VTOPSEG(&packet);
+ v86.esi = VTOPOFF(&packet);
+ v86int();
if (V86_CY(v86.efl)) {
printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
return -1;
diff --git a/sys/boot/i386/gptboot/gptldr.S b/sys/boot/i386/gptboot/gptldr.S
new file mode 100644
index 0000000..6dbe0ac
--- /dev/null
+++ b/sys/boot/i386/gptboot/gptldr.S
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (c) 2007 Yahoo!, Inc.
+ * All rights reserved.
+ * Written by: John Baldwin <jhb@FreeBSD.org>
+ *
+ * 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.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+/* Memory Locations */
+ .set MEM_REL,0x700 # Relocation address
+ .set MEM_ARG,0x900 # Arguments
+ .set MEM_ORG,0x7c00 # Origin
+ .set MEM_BUF,0x8cec # Load area
+ .set MEM_BTX,0x9000 # BTX start
+ .set MEM_JMP,0x9010 # BTX entry point
+ .set MEM_USR,0xa000 # Client start
+ .set BDA_BOOT,0x472 # Boot howto flag
+
+/* Misc. Constants */
+ .set SIZ_PAG,0x1000 # Page size
+ .set SIZ_SEC,0x200 # Sector size
+
+ .globl start
+ .code16
+
+/*
+ * Copy BTX and boot2 to the right locations and start it all up.
+ */
+
+/*
+ * Setup the segment registers to flat addressing (segment 0) and setup the
+ * stack to end just below the start of our code.
+ */
+start: xor %cx,%cx # Zero
+ mov %cx,%es # Address
+ mov %cx,%ds # data
+ mov %cx,%ss # Set up
+ mov $start,%sp # stack
+
+/*
+ * BTX is right after us at 'end'. We read the length of BTX out of
+ * its header to find boot2. We need to copy boot2 to MEM_USR and BTX
+ * to MEM_BTX. Since those might overlap, we have to copy boot2
+ * backwards first and then copy BTX. We aren't sure exactly how long
+ * boot2 is, but we assume it can't be longer than 64k, so we just always
+ * copy 64k.
+ */
+ mov $end,%bx # BTX
+ mov 0xa(%bx),%si # Get BTX length and set
+ add %bx,%si # %si to start of boot2
+ mov %si,%ax # Align %ds:%si on a
+ shr $4,%ax # paragraph boundary
+ and $0xf,%si # with the smallest
+ mov %ax,%ds # possible %si
+ add $(64 * 1024 - 16),%si
+ mov $MEM_USR/16,%ax # Point %es:%di at end of
+ mov $(64 * 1024 - 16),%di # largest boot2 range
+ mov %ax,%es
+ std
+ mov %di,%cx # Copy 64k - paragraph + 1
+ inc %cx # bytes
+ rep movsb
+ mov %cx,%ds # Reset %ds and %es
+ mov %cx,%es
+ mov 0xa(%bx),%cx # Get BTX length and set
+ mov %bx,%si # %si to end of BTX
+ mov $MEM_BTX,%di # %di -> end of BTX at
+ add %cx,%si # MEM_BTX
+ add %cx,%di
+ dec %si
+ dec %di
+ rep movsb # Move BTX
+ cld # String ops inc
+/*
+ * Enable A20 so we can access memory above 1 meg.
+ * Use the zero-valued %cx as a timeout for embedded hardware which do not
+ * have a keyboard controller.
+ */
+seta20: cli # Disable interrupts
+seta20.1: dec %cx # Timeout?
+ jz seta20.3 # Yes
+ inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.1 # Yes
+ movb $0xd1,%al # Command: Write
+ outb %al,$0x64 # output port
+seta20.2: inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.2 # Yes
+ movb $0xdf,%al # Enable
+ outb %al,$0x60 # A20
+seta20.3: sti # Enable interrupts
+
+/*
+ * Save drive number from BIOS so boot2 can see it and start BTX.
+ */
+ movb %dl,MEM_ARG
+ jmp MEM_JMP # Start BTX
+end:
diff --git a/sys/boot/i386/libi386/biosdisk.c b/sys/boot/i386/libi386/biosdisk.c
index 4bbc025..5e96af4 100644
--- a/sys/boot/i386/libi386/biosdisk.c
+++ b/sys/boot/i386/libi386/biosdisk.c
@@ -41,9 +41,11 @@ __FBSDID("$FreeBSD$");
#include <sys/disklabel.h>
#include <sys/diskmbr.h>
+#include <sys/gpt.h>
#include <machine/bootinfo.h>
#include <stdarg.h>
+#include <uuid.h>
#include <bootstrap.h>
#include <btxv86.h>
@@ -66,6 +68,13 @@ __FBSDID("$FreeBSD$");
# define DEBUG(fmt, args...)
#endif
+struct gpt_part {
+ int gp_index;
+ uuid_t gp_type;
+ uint64_t gp_start;
+ uint64_t gp_end;
+};
+
struct open_disk {
int od_dkunit; /* disk unit number */
int od_unit; /* BIOS unit number */
@@ -81,11 +90,26 @@ struct open_disk {
#define BD_FLOPPY 0x0004
#define BD_LABELOK 0x0008
#define BD_PARTTABOK 0x0010
- struct disklabel od_disklabel;
- int od_nslices; /* slice count */
- struct dos_partition od_slicetab[NEXTDOSPART];
+#define BD_GPTOK 0x0020
+ union {
+ struct {
+ struct disklabel mbr_disklabel;
+ int mbr_nslices; /* slice count */
+ struct dos_partition mbr_slicetab[NEXTDOSPART];
+ } _mbr;
+ struct {
+ int gpt_nparts;
+ struct gpt_part *gpt_partitions;
+ } _gpt;
+ } _data;
};
+#define od_disklabel _data._mbr.mbr_disklabel
+#define od_nslices _data._mbr.mbr_nslices
+#define od_slicetab _data._mbr.mbr_slicetab
+#define od_nparts _data._gpt.gpt_nparts
+#define od_partitions _data._gpt.gpt_partitions
+
/*
* List of BIOS devices, translation from disk unit number to
* BIOS unit number.
@@ -106,6 +130,8 @@ static int bd_write(struct open_disk *od, daddr_t dblk, int blks,
static int bd_int13probe(struct bdinfo *bd);
+static void bd_printgptpart(struct open_disk *od, struct gpt_part *gp,
+ char *prefix, int verbose);
static void bd_printslice(struct open_disk *od, struct dos_partition *dp,
char *prefix, int verbose);
static void bd_printbsdslice(struct open_disk *od, daddr_t offset,
@@ -134,8 +160,11 @@ struct devsw biosdisk = {
static int bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev);
static void bd_closedisk(struct open_disk *od);
+static int bd_open_mbr(struct open_disk *od, struct i386_devdesc *dev);
static int bd_bestslice(struct open_disk *od);
static void bd_checkextended(struct open_disk *od, int slicenum);
+static int bd_open_gpt(struct open_disk *od, struct i386_devdesc *dev);
+static struct gpt_part *bd_best_gptpart(struct open_disk *od);
/*
* Translate between BIOS device numbers and our private unit numbers.
@@ -257,8 +286,16 @@ bd_print(int verbose)
if (!bd_opendisk(&od, &dev)) {
+ /* Do we have a GPT table? */
+ if (od->od_flags & BD_GPTOK) {
+ for (j = 0; j < od->od_nparts; j++) {
+ sprintf(line, " disk%dp%d", i,
+ od->od_partitions[j].gp_index);
+ bd_printgptpart(od, &od->od_partitions[j], line, verbose);
+ }
+
/* Do we have a partition table? */
- if (od->od_flags & BD_PARTTABOK) {
+ } else if (od->od_flags & BD_PARTTABOK) {
dptr = &od->od_slicetab[0];
/* Check for a "dedicated" disk */
@@ -279,6 +316,59 @@ bd_print(int verbose)
}
}
+static uuid_t efi = GPT_ENT_TYPE_EFI;
+static uuid_t freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
+static uuid_t freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
+static uuid_t freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
+static uuid_t freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
+static uuid_t ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
+
+static void
+bd_printgptpart(struct open_disk *od, struct gpt_part *gp, char *prefix,
+ int verbose)
+{
+ char stats[80];
+ char line[96];
+ uint64_t size;
+ char unit;
+
+ if (verbose) {
+ size = (gp->gp_end + 1 - gp->gp_start) / 2048;
+ unit = 'M';
+ if (size >= 10240000) {
+ size /= 1048576;
+ unit = 'T';
+ } else if (size >= 10000) {
+ size /= 1024;
+ unit = 'G';
+ }
+ sprintf(stats, " %.6ld%cB", (long)size, unit);
+ } else
+ stats[0] = '\0';
+
+ if (uuid_equal(&gp->gp_type, &efi, NULL))
+ sprintf(line, "%s: EFI%s\n", prefix, stats);
+ else if (uuid_equal(&gp->gp_type, &ms_basic_data, NULL))
+ sprintf(line, "%s: FAT/NTFS%s\n", prefix, stats);
+ else if (uuid_equal(&gp->gp_type, &freebsd_boot, NULL))
+ sprintf(line, "%s: FreeBSD boot%s\n", prefix, stats);
+ else if (uuid_equal(&gp->gp_type, &freebsd_ufs, NULL))
+ sprintf(line, "%s: FreeBSD UFS%s\n", prefix, stats);
+ else if (uuid_equal(&gp->gp_type, &freebsd_zfs, NULL))
+ sprintf(line, "%s: FreeBSD ZFS%s\n", prefix, stats);
+ else if (uuid_equal(&gp->gp_type, &freebsd_swap, NULL))
+ sprintf(line, "%s: FreeBSD swap%s\n", prefix, stats);
+ else
+ sprintf(line, "%s: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x%s\n",
+ gp->gp_type.time_low, gp->gp_type.time_mid,
+ gp->gp_type.time_hi_and_version,
+ gp->gp_type.clock_seq_hi_and_reserved, gp->gp_type.clock_seq_low,
+ gp->gp_type.node[0], gp->gp_type.node[1], gp->gp_type.node[2],
+ gp->gp_type.node[3], gp->gp_type.node[4], gp->gp_type.node[5],
+ stats);
+ pager_output(line);
+}
+
/*
* Print information about slices on a disk. For the size calculations we
* assume a 512 byte sector.
@@ -447,12 +537,8 @@ bd_open(struct open_file *f, ...)
static int
bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
{
- struct dos_partition *dptr;
- struct disklabel *lp;
struct open_disk *od;
- int sector, slice, i;
int error;
- char buf[BUFSIZE];
if (dev->d_unit >= nbdinfo) {
DEBUG("attempt to open nonexistent disk");
@@ -470,11 +556,10 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
od->od_unit = bdinfo[od->od_dkunit].bd_unit;
od->od_flags = bdinfo[od->od_dkunit].bd_flags;
od->od_boff = 0;
- od->od_nslices = 0;
error = 0;
- DEBUG("open '%s', unit 0x%x slice %d partition %c",
+ DEBUG("open '%s', unit 0x%x slice %d partition %d",
i386_fmtdev(dev), dev->d_unit,
- dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition + 'a');
+ dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition);
/* Get geometry for this open (removable device may have changed) */
if (bd_getgeom(od)) {
@@ -483,6 +568,29 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
goto out;
}
+ /* Determine disk layout. */
+ error = bd_open_gpt(od, dev);
+ if (error)
+ error = bd_open_mbr(od, dev);
+
+ out:
+ if (error) {
+ free(od);
+ } else {
+ *odp = od; /* return the open disk */
+ }
+ return(error);
+}
+
+static int
+bd_open_mbr(struct open_disk *od, struct i386_devdesc *dev)
+{
+ struct dos_partition *dptr;
+ struct disklabel *lp;
+ int sector, slice, i;
+ int error;
+ char buf[BUFSIZE];
+
/*
* Following calculations attempt to determine the correct value
* for d->od_boff by looking for the slice and partition specified,
@@ -492,10 +600,10 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
/*
* Find the slice in the DOS slice table.
*/
+ od->od_nslices = 0;
if (bd_read(od, 0, 1, buf)) {
DEBUG("error reading MBR");
- error = EIO;
- goto out;
+ return (EIO);
}
/*
@@ -505,8 +613,7 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
/* If a slice number was explicitly supplied, this is an error */
if (dev->d_kind.biosdisk.slice > 0) {
DEBUG("no slice table/MBR (no magic)");
- error = ENOENT;
- goto out;
+ return (ENOENT);
}
sector = 0;
goto unsliced; /* may be a floppy */
@@ -536,8 +643,7 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
slice = dev->d_kind.biosdisk.slice - 1;
if (slice >= od->od_nslices) {
DEBUG("slice %d not found", slice);
- error = ENOENT;
- goto out;
+ return (ENOENT);
}
}
@@ -555,8 +661,7 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
if (dev->d_kind.biosdisk.slice == 0) {
slice = bd_bestslice(od);
if (slice == -1) {
- error = ENOENT;
- goto out;
+ return (ENOENT);
}
dev->d_kind.biosdisk.slice = slice;
}
@@ -590,8 +695,7 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
if (bd_read(od, sector + LABELSECTOR, 1, buf)) {
DEBUG("error reading disklabel");
- error = EIO;
- goto out;
+ return (EIO);
}
DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel);
bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel));
@@ -600,15 +704,12 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
if (lp->d_magic != DISKMAGIC) {
DEBUG("no disklabel");
- error = ENOENT;
- goto out;
+ return (ENOENT);
}
if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) {
DEBUG("partition '%c' exceeds partitions in table (a-'%c')",
'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions);
- error = EPART;
- goto out;
-
+ return (EPART);
}
#ifdef DISK_DEBUG
@@ -623,14 +724,7 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
lp->d_partitions[RAW_PART].p_offset +
sector;
}
-
- out:
- if (error) {
- free(od);
- } else {
- *odp = od; /* return the open disk */
- }
- return(error);
+ return (0);
}
static void
@@ -739,7 +833,183 @@ bd_bestslice(struct open_disk *od)
}
return (prefslice);
}
-
+
+static int
+bd_open_gpt(struct open_disk *od, struct i386_devdesc *dev)
+{
+ struct dos_partition *dp;
+ struct gpt_hdr *hdr;
+ struct gpt_ent *ent;
+ struct gpt_part *gp;
+ int entries_per_sec, error, i, part;
+ daddr_t lba, elba;
+ char gpt[BIOSDISK_SECSIZE], tbl[BIOSDISK_SECSIZE];
+
+ /*
+ * Following calculations attempt to determine the correct value
+ * for d->od_boff by looking for the slice and partition specified,
+ * or searching for reasonable defaults.
+ */
+ error = 0;
+
+ /* First, read the MBR and see if we have a PMBR. */
+ if (bd_read(od, 0, 1, tbl)) {
+ DEBUG("error reading MBR");
+ return (EIO);
+ }
+
+ /* Check the slice table magic. */
+ if (((u_char)tbl[0x1fe] != 0x55) || ((u_char)tbl[0x1ff] != 0xaa))
+ return (ENXIO);
+
+ /* Check for GPT slice. */
+ part = 0;
+ dp = (struct dos_partition *)(tbl + DOSPARTOFF);
+ for (i = 0; i < NDOSPART; i++) {
+ if (dp[i].dp_typ == 0xee)
+ part++;
+ else if (dp[i].dp_typ != 0x00)
+ return (EINVAL);
+ }
+ if (part != 1)
+ return (EINVAL);
+
+ /* Read primary GPT table header. */
+ if (bd_read(od, 1, 1, gpt)) {
+ DEBUG("error reading GPT header");
+ return (EIO);
+ }
+ hdr = (struct gpt_hdr *)gpt;
+ if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0 ||
+ hdr->hdr_lba_self != 1 || hdr->hdr_revision < 0x00010000 ||
+ hdr->hdr_entsz < sizeof(*ent) ||
+ BIOSDISK_SECSIZE % hdr->hdr_entsz != 0) {
+ DEBUG("Invalid GPT header\n");
+ return (EINVAL);
+ }
+
+ /* Now walk the partition table to count the number of valid partitions. */
+ part = 0;
+ entries_per_sec = BIOSDISK_SECSIZE / hdr->hdr_entsz;
+ elba = hdr->hdr_lba_table + hdr->hdr_entries / entries_per_sec;
+ for (lba = hdr->hdr_lba_table; lba < elba; lba++) {
+ if (bd_read(od, lba, 1, tbl)) {
+ DEBUG("error reading GPT table");
+ return (EIO);
+ }
+ for (i = 0; i < entries_per_sec; i++) {
+ ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz);
+ if (uuid_is_nil(&ent->ent_type, NULL) || ent->ent_lba_start == 0 ||
+ ent->ent_lba_end < ent->ent_lba_start)
+ continue;
+ part++;
+ }
+ }
+
+ /* Save the important information about all the valid partitions. */
+ od->od_nparts = part;
+ if (part != 0) {
+ od->od_partitions = malloc(part * sizeof(struct gpt_part));
+ part = 0;
+ for (lba = hdr->hdr_lba_table; lba < elba; lba++) {
+ if (bd_read(od, lba, 1, tbl)) {
+ DEBUG("error reading GPT table");
+ error = EIO;
+ goto out;
+ }
+ for (i = 0; i < entries_per_sec; i++) {
+ ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz);
+ if (uuid_is_nil(&ent->ent_type, NULL) ||
+ ent->ent_lba_start == 0 ||
+ ent->ent_lba_end < ent->ent_lba_start)
+ continue;
+ od->od_partitions[part].gp_index = (lba - hdr->hdr_lba_table) *
+ entries_per_sec + i + 1;
+ od->od_partitions[part].gp_type = ent->ent_type;
+ od->od_partitions[part].gp_start = ent->ent_lba_start;
+ od->od_partitions[part].gp_end = ent->ent_lba_end;
+ part++;
+ }
+ }
+ }
+ od->od_flags |= BD_GPTOK;
+
+ /* Is this a request for the whole disk? */
+ if (dev->d_kind.biosdisk.slice < 0) {
+ od->od_boff = 0;
+ return (0);
+ }
+
+ /*
+ * If a partition number was supplied, then the user is trying to use
+ * an MBR address rather than a GPT address, so fail.
+ */
+ if (dev->d_kind.biosdisk.partition != 0xff) {
+ error = ENOENT;
+ goto out;
+ }
+
+ /* If a slice number was supplied but not found, this is an error. */
+ gp = NULL;
+ if (dev->d_kind.biosdisk.slice > 0) {
+ for (i = 0; i < od->od_nparts; i++) {
+ if (od->od_partitions[i].gp_index == dev->d_kind.biosdisk.slice) {
+ gp = &od->od_partitions[i];
+ break;
+ }
+ }
+ if (gp == NULL) {
+ DEBUG("partition %d not found", dev->d_kind.biosdisk.slice);
+ error = ENOENT;
+ goto out;
+ }
+ }
+
+ /* Try to auto-detect the best partition. */
+ if (dev->d_kind.biosdisk.slice == 0) {
+ gp = bd_best_gptpart(od);
+ if (gp == NULL) {
+ error = ENOENT;
+ goto out;
+ }
+ dev->d_kind.biosdisk.slice = gp->gp_index;
+ }
+ od->od_boff = gp->gp_start;
+
+out:
+ if (error)
+ free(od->od_partitions);
+ return (error);
+}
+
+static struct gpt_part *
+bd_best_gptpart(struct open_disk *od)
+{
+ struct gpt_part *gp, *prefpart;
+ int i, pref, preflevel;
+
+ prefpart = NULL;
+ preflevel = PREF_NONE;
+
+ gp = od->od_partitions;
+ for (i = 0; i < od->od_nparts; i++, gp++) {
+ /* Windows. XXX: Also Linux. */
+ if (uuid_equal(&gp->gp_type, &ms_basic_data, NULL))
+ pref = PREF_DOS;
+ /* FreeBSD */
+ else if (uuid_equal(&gp->gp_type, &freebsd_ufs, NULL) ||
+ uuid_equal(&gp->gp_type, &freebsd_zfs, NULL))
+ pref = PREF_FBSD;
+ else
+ pref = PREF_NONE;
+ if (pref < preflevel) {
+ preflevel = pref;
+ prefpart = gp;
+ }
+ }
+ return (prefpart);
+}
+
static int
bd_close(struct open_file *f)
{
@@ -758,6 +1028,8 @@ bd_closedisk(struct open_disk *od)
if (od->od_flags & BD_FLOPPY)
delay(3000000);
#endif
+ if (od->od_flags & BD_GPTOK)
+ free(od->od_partitions);
free(od);
}
diff --git a/sys/boot/i386/libi386/devicename.c b/sys/boot/i386/libi386/devicename.c
index c906a52..520fdb5 100644
--- a/sys/boot/i386/libi386/devicename.c
+++ b/sys/boot/i386/libi386/devicename.c
@@ -120,21 +120,35 @@ i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path)
err = EUNIT;
goto fail;
}
- if (*cp == 's') { /* got a slice number */
+ if (*cp == 'p') { /* got a GPT partition */
np = cp + 1;
slice = strtol(np, &cp, 10);
if (cp == np) {
err = ESLICE;
goto fail;
}
- }
- if (*cp && (*cp != ':')) {
- partition = *cp - 'a'; /* get a partition number */
- if ((partition < 0) || (partition >= MAXPARTITIONS)) {
- err = EPART;
+ if (*cp && (*cp != ':')) {
+ err = EINVAL;
goto fail;
}
- cp++;
+ partition = 0xff;
+ } else {
+ if (*cp == 's') { /* got a slice number */
+ np = cp + 1;
+ slice = strtol(np, &cp, 10);
+ if (cp == np) {
+ err = ESLICE;
+ goto fail;
+ }
+ }
+ if (*cp && (*cp != ':')) {
+ partition = *cp - 'a'; /* got a partition number */
+ if ((partition < 0) || (partition >= MAXPARTITIONS)) {
+ err = EPART;
+ goto fail;
+ }
+ cp++;
+ }
}
}
if (*cp && (*cp != ':')) {
@@ -208,10 +222,14 @@ i386_fmtdev(void *vdev)
case DEVT_DISK:
cp = buf;
cp += sprintf(cp, "%s%d", dev->d_dev->dv_name, dev->d_unit);
- if (dev->d_kind.biosdisk.slice > 0)
- cp += sprintf(cp, "s%d", dev->d_kind.biosdisk.slice);
- if (dev->d_kind.biosdisk.partition >= 0)
- cp += sprintf(cp, "%c", dev->d_kind.biosdisk.partition + 'a');
+ if (dev->d_kind.biosdisk.partition == 0xff) {
+ cp += sprintf(cp, "p%d", dev->d_kind.biosdisk.slice);
+ } else {
+ if (dev->d_kind.biosdisk.slice > 0)
+ cp += sprintf(cp, "s%d", dev->d_kind.biosdisk.slice);
+ if (dev->d_kind.biosdisk.partition >= 0)
+ cp += sprintf(cp, "%c", dev->d_kind.biosdisk.partition + 'a');
+ }
strcat(cp, ":");
break;
@@ -238,4 +256,3 @@ i386_setcurrdev(struct env_var *ev, int flags, const void *value)
env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
return(0);
}
-
diff --git a/sys/boot/i386/pmbr/Makefile b/sys/boot/i386/pmbr/Makefile
new file mode 100644
index 0000000..4fbbbe5
--- /dev/null
+++ b/sys/boot/i386/pmbr/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG= pmbr
+STRIP=
+BINMODE=${NOBINMODE}
+NO_MAN=
+SRCS= ${PROG}.s
+
+ORG= 0x600
+
+AFLAGS+=--defsym FLAGS=${BOOT_MBR_FLAGS}
+LDFLAGS=-N -e start -Ttext ${ORG} -Wl,-S,--oformat,binary
+
+.include <bsd.prog.mk>
diff --git a/sys/boot/i386/pmbr/pmbr.s b/sys/boot/i386/pmbr/pmbr.s
new file mode 100644
index 0000000..217ccfb
--- /dev/null
+++ b/sys/boot/i386/pmbr/pmbr.s
@@ -0,0 +1,221 @@
+#-
+# Copyright (c) 2007 Yahoo!, Inc.
+# All rights reserved.
+# Written by: John Baldwin <jhb@FreeBSD.org>
+#
+# 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.
+# 3. Neither the name of the author nor the names of any co-contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 AUTHOR 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.
+#
+# $FreeBSD$
+
+# A 512 byte PMBR boot manager that looks for a FreeBSD boot GPT partition
+# and boots it.
+
+ .set LOAD,0x7c00 # Load address
+ .set EXEC,0x600 # Execution address
+ .set MAGIC,0xaa55 # Magic: bootable
+ .set SECSIZE,0x200 # Size of a single disk sector
+ .set DISKSIG,440 # Disk signature offset
+ .set STACK,EXEC+SECSIZE*4 # Stack address
+ .set GPT_ADDR,STACK # GPT header address
+ .set GPT_SIG,0
+ .set GPT_SIG_0,0x20494645
+ .set GPT_SIG_1,0x54524150
+ .set GPT_MYLBA,24
+ .set GPT_PART_LBA,72
+ .set GPT_NPART,80
+ .set GPT_PART_SIZE,84
+ .set PART_ADDR,GPT_ADDR+SECSIZE # GPT partition array address
+ .set PART_TYPE,0
+ .set PART_START_LBA,32
+ .set PART_END_LBA,40
+
+ .set NHRDRV,0x475 # Number of hard drives
+
+ .globl start # Entry point
+ .code16
+
+#
+# Setup the segment registers for flat addressing and setup the stack.
+#
+start: cld # String ops inc
+ xorw %ax,%ax # Zero
+ movw %ax,%es # Address
+ movw %ax,%ds # data
+ movw %ax,%ss # Set up
+ movw $STACK,%sp # stack
+#
+# Relocate ourself to a lower address so that we have more room to load
+# other sectors.
+#
+ movw $main-EXEC+LOAD,%si # Source
+ movw $main,%di # Destination
+ movw $SECSIZE-(main-start),%cx # Byte count
+ rep # Relocate
+ movsb # code
+#
+# Jump to the relocated code.
+#
+ jmp main-LOAD+EXEC # To relocated code
+#
+# Validate drive number in %dl.
+#
+main: cmpb $0x80,%dl # Drive valid?
+ jb main.1 # No
+ movb NHRDRV,%dh # Calculate the highest
+ addb $0x80,%dh # drive number available
+ cmpb %dh,%dl # Within range?
+ jb main.2 # Yes
+main.1: movb $0x80,%dl # Assume drive 0x80
+#
+# Load the primary GPT header from LBA 1 and verify signature.
+#
+main.2: movw $GPT_ADDR,%bx
+ movw $lba,%si
+ call read
+ cmpl $GPT_SIG_0,GPT_ADDR+GPT_SIG
+ jnz err_pt
+ cmpl $GPT_SIG_1,GPT_ADDR+GPT_SIG+4
+ jnz err_pt
+#
+# Load a partition table sector from disk and look for a FreeBSD boot
+# partition.
+#
+load_part: movw $GPT_ADDR+GPT_PART_LBA,%si
+ movw $PART_ADDR,%bx
+ call read
+scan: movw %bx,%si # Compare partition UUID
+ movw $boot_uuid,%di # with FreeBSD boot UUID
+ movb $0x10,%cl
+ repe cmpsb
+ jnz next_part # Didn't match, next partition
+#
+# We found a boot partition. Load it into RAM starting at 0x7c00.
+#
+ movw %bx,%di # Save partition pointer in %di
+ leaw PART_START_LBA(%di),%si
+ movw $LOAD/16,%bx
+ movw %bx,%es
+ xorw %bx,%bx
+load_boot: push %si # Save %si
+ call read
+ pop %si # Restore
+ movl PART_END_LBA(%di),%eax # See if this was the last LBA
+ cmpl (%si),%eax
+ jnz next_boot
+ movl PART_END_LBA+4(%di),%eax
+ cmpl 4(%si),%eax
+ jnz next_boot
+ mov %bx,%es # Reset %es to zero
+ jmp LOAD # Jump to boot code
+next_boot: incl (%si) # Next LBA
+ adcl $0,4(%si)
+ mov %es,%ax # Adjust segment for next
+ addw $SECSIZE/16,%ax # sector
+ cmp $0x9000,%ax # Don't load past 0x90000,
+ jae err_big # 545k should be enough for
+ mov %ax,%es # any boot code. :)
+ jmp load_boot
+#
+# Move to the next partition. If we walk off the end of the sector, load
+# the next sector. We assume that partition entries are smaller than 64k
+# and that they won't span a sector boundary.
+#
+# XXX: Should we int 0x18 instead of err_noboot if we hit the end of the table?
+#
+next_part: decl GPT_ADDR+GPT_NPART # Was this the last partition?
+ jz err_noboot
+ movw GPT_ADDR+GPT_PART_SIZE,%ax
+ addw %ax,%bx # Next partition
+ cmpw $PART_ADDR+0x200,%bx # Still in sector?
+ jb scan
+ incl GPT_ADDR+GPT_PART_LBA # Next sector
+ adcl $0,GPT_ADDR+GPT_PART_LBA+4
+ jmp load_part
+#
+# Load a sector (64-bit LBA at %si) from disk %dl into %es:%bx by creating
+# a EDD packet on the stack and passing it to the BIOS. Trashes %ax and %si.
+#
+read: pushl 0x4(%si) # Set the LBA
+ pushl 0x0(%si) # address
+ pushw %es # Set the address of
+ pushw %bx # the transfer buffer
+ pushw $0x1 # Read 1 sector
+ pushw $0x10 # Packet length
+ movw %sp,%si # Packer pointer
+ movw $0x4200,%ax # BIOS: LBA Read from disk
+ int $0x13 # Call the BIOS
+ add $0x10,%sp # Restore stack
+ jc err_rd # If error
+ ret
+#
+# Various error message entry points.
+#
+err_big: movw $msg_big,%si # "Boot loader too
+ jmp putstr # large"
+
+err_pt: movw $msg_pt,%si # "Invalid partition
+ jmp putstr # table"
+
+err_rd: movw $msg_rd,%si # "I/O error loading
+ jmp putstr # boot loader"
+
+err_noboot: movw $msg_noboot,%si # "Missing boot
+ jmp putstr # loader"
+#
+# Output an ASCIZ string to the console via the BIOS.
+#
+putstr.0: movw $0x7,%bx # Page:attribute
+ movb $0xe,%ah # BIOS: Display
+ int $0x10 # character
+putstr: lodsb # Get character
+ testb %al,%al # End of string?
+ jnz putstr.0 # No
+putstr.1: jmp putstr.1 # Await reset
+
+msg_big: .asciz "Boot loader too large"
+msg_pt: .asciz "Invalid partition table"
+msg_rd: .asciz "I/O error loading boot loader"
+msg_noboot: .asciz "Missing boot loader"
+
+lba: .quad 1 # LBA of GPT header
+
+boot_uuid: .long 0x83bd6b9d
+ .word 0x7f41
+ .word 0x11dc
+ .byte 0xbe
+ .byte 0x0b
+ .byte 0x00
+ .byte 0x15
+ .byte 0x60
+ .byte 0xb8
+ .byte 0x4f
+ .byte 0x0f
+
+ .org DISKSIG,0x90
+sig: .long 0 # OS Disk Signature
+ .word 0 # "Unknown" in PMBR
+
+partbl: .fill 0x10,0x4,0x0 # Partition table
+ .word MAGIC # Magic number
OpenPOWER on IntegriCloud