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)
This commit is contained in:
John Baldwin 2007-10-24 21:33:00 +00:00
parent e0f5da6d08
commit f352a0d45f
21 changed files with 1218 additions and 211 deletions

@ -72,6 +72,10 @@ SRCS+= ashldi3.c ashrdi3.c
SRCS+= syncicache.c
.endif
# uuid functions from libc
.PATH: ${.CURDIR}/../libc/uuid
SRCS+= uuid_equal.c uuid_is_nil.c
# _setjmp/_longjmp
.if ${MACHINE_ARCH} == "amd64"
.PATH: ${.CURDIR}/i386

@ -1,8 +1,8 @@
# $FreeBSD$
PROG= gpt
SRCS= add.c create.c destroy.c gpt.c label.c map.c migrate.c recover.c \
remove.c show.c
SRCS= add.c boot.c create.c destroy.c gpt.c label.c map.c migrate.c \
recover.c remove.c show.c
WARNS?= 4
MAN= gpt.8

@ -39,9 +39,9 @@ __FBSDID("$FreeBSD$");
#include "map.h"
#include "gpt.h"
static uuid_t type;
static off_t block, size;
static unsigned int entry;
static uuid_t add_type;
static off_t add_block, add_size;
static unsigned int add_entry;
static void
usage_add(void)
@ -53,8 +53,8 @@ usage_add(void)
exit(1);
}
static void
add(int fd)
map_t *
gpt_add_part(int fd, uuid_t type, off_t start, off_t size, unsigned int *entry)
{
map_t *gpt, *tpg;
map_t *tbl, *lbt;
@ -67,38 +67,38 @@ add(int fd)
if (gpt == NULL) {
warnx("%s: error: no primary GPT header; run create or recover",
device_name);
return;
return (NULL);
}
tpg = map_find(MAP_TYPE_SEC_GPT_HDR);
if (tpg == NULL) {
warnx("%s: error: no secondary GPT header; run recover",
device_name);
return;
return (NULL);
}
tbl = map_find(MAP_TYPE_PRI_GPT_TBL);
lbt = map_find(MAP_TYPE_SEC_GPT_TBL);
if (tbl == NULL || lbt == NULL) {
warnx("%s: error: run recover -- trust me", device_name);
return;
return (NULL);
}
hdr = gpt->map_data;
if (entry > le32toh(hdr->hdr_entries)) {
if (*entry > le32toh(hdr->hdr_entries)) {
warnx("%s: error: index %u out of range (%u max)", device_name,
entry, le32toh(hdr->hdr_entries));
return;
*entry, le32toh(hdr->hdr_entries));
return (NULL);
}
if (entry > 0) {
i = entry - 1;
if (*entry > 0) {
i = *entry - 1;
ent = (void*)((char*)tbl->map_data + i *
le32toh(hdr->hdr_entsz));
if (!uuid_is_nil(&ent->ent_type, NULL)) {
warnx("%s: error: entry at index %u is not free",
device_name, entry);
return;
device_name, *entry);
return (NULL);
}
} else {
/* Find empty slot in GPT table. */
@ -111,14 +111,14 @@ add(int fd)
if (i == le32toh(hdr->hdr_entries)) {
warnx("%s: error: no available table entries",
device_name);
return;
return (NULL);
}
}
map = map_alloc(block, size);
map = map_alloc(start, size);
if (map == NULL) {
warnx("%s: error: no space available on device", device_name);
return;
return (NULL);
}
le_uuid_enc(&ent->ent_type, &type);
@ -148,7 +148,19 @@ add(int fd)
gpt_write(fd, lbt);
gpt_write(fd, tpg);
printf("%sp%u added\n", device_name, i + 1);
*entry = i + 1;
return (map);
}
static void
add(int fd)
{
if (gpt_add_part(fd, add_type, add_block, add_size, &add_entry) != 0)
return;
printf("%sp%u added\n", device_name, add_entry);
}
int
@ -161,30 +173,30 @@ cmd_add(int argc, char *argv[])
while ((ch = getopt(argc, argv, "b:i:s:t:")) != -1) {
switch(ch) {
case 'b':
if (block > 0)
if (add_block > 0)
usage_add();
block = strtoll(optarg, &p, 10);
if (*p != 0 || block < 1)
add_block = strtoll(optarg, &p, 10);
if (*p != 0 || add_block < 1)
usage_add();
break;
case 'i':
if (entry > 0)
if (add_entry > 0)
usage_add();
entry = strtol(optarg, &p, 10);
if (*p != 0 || entry < 1)
add_entry = strtol(optarg, &p, 10);
if (*p != 0 || add_entry < 1)
usage_add();
break;
case 's':
if (size > 0)
if (add_size > 0)
usage_add();
size = strtoll(optarg, &p, 10);
if (*p != 0 || size < 1)
add_size = strtoll(optarg, &p, 10);
if (*p != 0 || add_size < 1)
usage_add();
break;
case 't':
if (!uuid_is_nil(&type, NULL))
if (!uuid_is_nil(&add_type, NULL))
usage_add();
if (parse_uuid(optarg, &type) != 0)
if (parse_uuid(optarg, &add_type) != 0)
usage_add();
break;
default:
@ -196,9 +208,9 @@ cmd_add(int argc, char *argv[])
usage_add();
/* Create UFS partitions by default. */
if (uuid_is_nil(&type, NULL)) {
if (uuid_is_nil(&add_type, NULL)) {
uuid_t ufs = GPT_ENT_TYPE_FREEBSD_UFS;
type = ufs;
add_type = ufs;
}
while (optind < argc) {

266
sbin/gpt/boot.c Normal file

@ -0,0 +1,266 @@
/*-
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "map.h"
#include "gpt.h"
static uuid_t boot_uuid = GPT_ENT_TYPE_FREEBSD_BOOT;
static const char *pmbr_path = "/boot/pmbr";
static const char *gptboot_path = "/boot/gptboot";
static u_long boot_size;
static void
usage_boot(void)
{
fprintf(stderr,
"usage: %s [-b pmbr] [-g gptboot] [-s count] device ...\n",
getprogname());
exit(1);
}
static int
gpt_find(uuid_t *type, map_t **mapp)
{
map_t *gpt, *tbl, *map;
struct gpt_hdr *hdr;
struct gpt_ent *ent;
unsigned int i;
/* Find a GPT partition with the requested UUID type. */
gpt = map_find(MAP_TYPE_PRI_GPT_HDR);
if (gpt == NULL) {
warnx("%s: error: no primary GPT header", device_name);
return (ENXIO);
}
tbl = map_find(MAP_TYPE_PRI_GPT_TBL);
if (tbl == NULL) {
warnx("%s: error: no primary partition table", device_name);
return (ENXIO);
}
hdr = gpt->map_data;
for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
ent = (void *)((char *)tbl->map_data + i *
le32toh(hdr->hdr_entsz));
if (uuid_equal(&ent->ent_type, type, NULL))
break;
}
if (i == le32toh(hdr->hdr_entries)) {
*mapp = NULL;
return (0);
}
/* Lookup the map corresponding to this partition. */
for (map = map_find(MAP_TYPE_GPT_PART); map != NULL;
map = map->map_next) {
if (map->map_type != MAP_TYPE_GPT_PART)
continue;
if (map->map_start == (off_t)le64toh(ent->ent_lba_start)) {
assert(map->map_start + map->map_size - 1LL ==
(off_t)le64toh(ent->ent_lba_end));
*mapp = map;
return (0);
}
}
/* Hmm, the map list is not in sync with the GPT table. */
errx(1, "internal map list is corrupted");
}
static void
boot(int fd)
{
struct stat sb;
off_t bsize, ofs;
map_t *pmbr, *gptboot;
struct mbr *mbr;
char *buf;
ssize_t nbytes;
unsigned int entry;
int bfd;
/* First step: verify boot partition size. */
if (boot_size == 0)
/* Default to 64k. */
bsize = 65536 / secsz;
else {
if (boot_size * secsz < 16384) {
warnx("invalid boot partition size %lu", boot_size);
return;
}
bsize = boot_size;
}
/* Second step: write the PMBR boot loader into the PMBR. */
pmbr = map_find(MAP_TYPE_PMBR);
if (pmbr == NULL) {
warnx("%s: error: PMBR not found", device_name);
return;
}
bfd = open(pmbr_path, O_RDONLY);
if (bfd < 0 || fstat(bfd, &sb) < 0) {
warn("unable to open PMBR boot loader");
return;
}
if (sb.st_size != secsz) {
warnx("invalid PMBR boot loader");
return;
}
mbr = pmbr->map_data;
nbytes = read(bfd, mbr->mbr_code, sizeof(mbr->mbr_code));
if (nbytes < 0) {
warn("unable to read PMBR boot loader");
return;
}
if (nbytes != sizeof(mbr->mbr_code)) {
warnx("short read of PMBR boot loader");
return;
}
close(bfd);
gpt_write(fd, pmbr);
/* Third step: open gptboot and obtain its size. */
bfd = open(gptboot_path, O_RDONLY);
if (bfd < 0 || fstat(bfd, &sb) < 0) {
warn("unable to open GPT boot loader");
return;
}
/* Fourth step: find an existing boot partition or create one. */
if (gpt_find(&boot_uuid, &gptboot) != 0)
return;
if (gptboot != NULL) {
if (gptboot->map_size * secsz < sb.st_size) {
warnx("%s: error: boot partition is too small",
device_name);
return;
}
} else if (bsize * secsz < sb.st_size) {
warnx(
"%s: error: proposed size for boot partition is too small",
device_name);
return;
} else {
entry = 0;
gptboot = gpt_add_part(fd, boot_uuid, 0, bsize, &entry);
if (gptboot == NULL)
return;
}
/* Fourth step, write out the gptboot binary to the boot partition. */
buf = malloc(sb.st_size);
nbytes = read(bfd, buf, sb.st_size);
if (nbytes < 0) {
warn("unable to read GPT boot loader");
return;
}
if (nbytes != sb.st_size) {
warnx("short read of GPT boot loader");
return;
}
close(bfd);
ofs = gptboot->map_start * secsz;
if (lseek(fd, ofs, SEEK_SET) != ofs) {
warn("%s: error: unable to seek to boot partition",
device_name);
return;
}
nbytes = write(fd, buf, sb.st_size);
if (nbytes < 0) {
warn("unable to write GPT boot loader");
return;
}
if (nbytes != sb.st_size) {
warnx("short write of GPT boot loader");
return;
}
free(buf);
}
int
cmd_boot(int argc, char *argv[])
{
char *p;
int ch, fd;
while ((ch = getopt(argc, argv, "b:g:s:")) != -1) {
switch (ch) {
case 'b':
pmbr_path = optarg;
break;
case 'g':
gptboot_path = optarg;
break;
case 's':
if (boot_size > 0)
usage_boot();
boot_size = strtol(optarg, &p, 10);
if (*p != '\0' || boot_size < 1)
usage_boot();
break;
default:
usage_boot();
}
}
if (argc == optind)
usage_boot();
while (optind < argc) {
fd = gpt_open(argv[optind++]);
if (fd < 0) {
warn("unable to open device '%s'", device_name);
continue;
}
boot(fd);
gpt_close(fd);
}
return (0);
}

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd June 22, 2006
.Dd October 24, 2007
.Os
.Dt GPT 8
.Sh NAME
@ -130,10 +130,51 @@ option allows the user to specify the partition type.
The type is given as an UUID, but
.Nm
accepts
.Cm efi , swap , ufs , hfs , linux
.Cm boot , efi , swap , ufs , hfs , linux
and
.Cm windows
as aliases for the most commonly used partition types.
.\" ==== boot ====
.It Xo
.Nm
.Ic boot
.Op Fl b Ar pmbr
.Op Fl g Ar gptboot
.Op Fl s Ar count
.Ar device ...
.Xc
The
.Ic boot
command allows the user to make a GPT labeled disk bootable via the BIOS
bootstrap on i386 and amd64 machines.
By default,
the
.Pa /boot/pmbr
boot loader is installed into the PMBR and the
.Pa /boot/gptboot
boot loader is installed into the first boot partition.
If no boot partition exists and there is available space,
a boot partition will be created.
.Pp
The
.Fl b Ar pmbr
option allows the user to specify an alternate path for the PMBR boot loader.
.Pp
The
.Fl g Ar gptboot
option allows the user to specify an alternate path for the GPT boot loader
that is installed into the boot partition.
.Pp
The
.Fl s Ar count
option allows the user to specify the size in sectors of the boot partition
if one does not already exist.
A boot partition must be at least 16 kilobytes.
By default,
a size of 64 kilobytes is used.
Note that the PMBR boot loader will load the entire boot partition into
memory.
As a result, the boot partition may not exceed 545 kilobytes.
.\" ==== create ====
.It Nm Ic create Oo Fl fp Oc Ar device ...
The

@ -609,6 +609,7 @@ static struct {
const char *name;
} cmdsw[] = {
{ cmd_add, "add" },
{ cmd_boot, "boot" },
{ cmd_create, "create" },
{ cmd_destroy, "destroy" },
{ NULL, "help" },

@ -67,6 +67,7 @@ extern u_int secsz;
extern int readonly, verbose;
uint32_t crc32(const void *, size_t);
map_t *gpt_add_part(int, uuid_t, off_t, off_t, unsigned int *);
void gpt_close(int);
int gpt_open(const char *);
void* gpt_read(int, off_t, size_t);
@ -76,6 +77,7 @@ uint8_t *utf16_to_utf8(uint16_t *);
void utf8_to_utf16(const uint8_t *, uint16_t *, size_t);
int cmd_add(int, char *[]);
int cmd_boot(int, char *[]);
int cmd_create(int, char *[]);
int cmd_destroy(int, char *[]);
int cmd_label(int, char *[]);

@ -54,6 +54,7 @@ usage_show(void)
static const char *
friendly(uuid_t *t)
{
static uuid_t boot = GPT_ENT_TYPE_FREEBSD_BOOT;
static uuid_t efi_slice = GPT_ENT_TYPE_EFI;
static uuid_t mslinux = GPT_ENT_TYPE_MS_BASIC_DATA;
static uuid_t freebsd = GPT_ENT_TYPE_FREEBSD;
@ -71,6 +72,8 @@ friendly(uuid_t *t)
if (uuid_equal(t, &efi_slice, NULL))
return ("EFI System");
if (uuid_equal(t, &boot, NULL))
return ("FreeBSD boot");
if (uuid_equal(t, &swap, NULL))
return ("FreeBSD swap");
if (uuid_equal(t, &ufs, NULL))

@ -48,7 +48,7 @@ __FBSDID("$FreeBSD$");
#include <ufs/ufs/dinode.h>
#include <ufs/ffs/fs.h>
#ifdef __i386__
#if defined(__i386__) && !defined(GPTBOOT)
/* XXX: Revert to old (broken for over 1.5Tb filesystems) version of cgbase
(see sys/ufs/ffs/fs.h rev 1.39) so that i386 boot loader (boot2) can
support both UFS1 and UFS2 again. */

@ -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

@ -1,24 +1,23 @@
# $FreeBSD$
FILES= boot boot1 boot2
.PATH: ${.CURDIR}/../boot2
FILES= gptboot
NM?= nm
# A value of 0x80 enables LBA support.
BOOT_BOOT1_FLAGS?= 0x80
BOOT_COMCONSOLE_PORT?= 0x3f8
BOOT_COMCONSOLE_SPEED?= 9600
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
CLEANFILES= gptboot
boot: boot1 boot2
cat boot1 boot2 > boot
gptboot: gptldr.bin gptboot.bin ${BTXKERN}
btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l gptldr.bin \
-o ${.TARGET} gptboot.bin
CLEANFILES+= boot1 boot1.out boot1.o
CLEANFILES+= gptldr.bin gptldr.out gptldr.o
boot1: boot1.out
objcopy -S -O binary boot1.out ${.TARGET}
gptldr.bin: gptldr.out
objcopy -S -O binary gptldr.out ${.TARGET}
boot1.out: boot1.o
${LD} ${LDFLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} boot1.o
gptldr.out: gptldr.o
${LD} ${LDFLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o
CLEANFILES+= boot2 boot2.ld boot2.ldr boot2.bin boot2.out boot2.o \
boot2.s boot2.s.tmp boot2.h sio.o
CLEANFILES+= gptboot.bin gptboot.out gptboot.o sio.o
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
gptboot.bin: gptboot.out
objcopy -S -O binary gptboot.out ${.TARGET}
boot2.ld: boot2.ldr boot2.bin ${BTXKERN}
btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l boot2.ldr \
-o ${.TARGET} -P 1 boot2.bin
boot2.ldr:
dd if=/dev/zero of=${.TARGET} bs=276 count=1
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

@ -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 >= d->d_npartitions ||
!d->d_partitions[dsk.part].p_size) {
printf("Invalid %s\n", "partition");
if (dsk.part > hdr.hdr_entries) {
printf("Invalid partition index\n");
return -1;
}
dsk.start += d->d_partitions[dsk.part].p_offset;
dsk.start -= d->d_partitions[RAW_PART].p_offset;
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 = 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;

@ -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:

@ -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);
}

@ -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);
}

@ -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>

221
sys/boot/i386/pmbr/pmbr.s Normal file

@ -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

@ -66,6 +66,7 @@ struct g_part_alias_list {
} g_part_alias_list[G_PART_ALIAS_COUNT] = {
{ "efi", G_PART_ALIAS_EFI },
{ "freebsd", G_PART_ALIAS_FREEBSD },
{ "freebsd-boot", G_PART_ALIAS_FREEBSD_BOOT },
{ "freebsd-swap", G_PART_ALIAS_FREEBSD_SWAP },
{ "freebsd-ufs", G_PART_ALIAS_FREEBSD_UFS },
{ "freebsd-vinum", G_PART_ALIAS_FREEBSD_VINUM },

@ -38,6 +38,7 @@
enum g_part_alias {
G_PART_ALIAS_EFI, /* A EFI system partition entry. */
G_PART_ALIAS_FREEBSD, /* A BSD labeled partition entry. */
G_PART_ALIAS_FREEBSD_BOOT, /* A FreeBSD boot partition entry. */
G_PART_ALIAS_FREEBSD_SWAP, /* A swap partition entry. */
G_PART_ALIAS_FREEBSD_UFS, /* A UFS/UFS2 file system entry. */
G_PART_ALIAS_FREEBSD_VINUM, /* A Vinum partition entry. */

@ -121,6 +121,7 @@ G_PART_SCHEME_DECLARE(g_part_gpt_scheme);
static struct uuid gpt_uuid_efi = GPT_ENT_TYPE_EFI;
static struct uuid gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD;
static struct uuid gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
static struct uuid gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
static struct uuid gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
static struct uuid gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
@ -295,6 +296,11 @@ gpt_parse_type(const char *type, struct uuid *uuid)
*uuid = gpt_uuid_freebsd;
return (0);
}
alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_BOOT);
if (!strcasecmp(type, alias)) {
*uuid = gpt_uuid_freebsd_boot;
return (0);
}
alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP);
if (!strcasecmp(type, alias)) {
*uuid = gpt_uuid_freebsd_swap;
@ -600,6 +606,8 @@ g_part_gpt_type(struct g_part_table *basetable, struct g_part_entry *baseentry,
return (g_part_alias_name(G_PART_ALIAS_EFI));
if (EQUUID(type, &gpt_uuid_freebsd))
return (g_part_alias_name(G_PART_ALIAS_FREEBSD));
if (EQUUID(type, &gpt_uuid_freebsd_boot))
return (g_part_alias_name(G_PART_ALIAS_FREEBSD_BOOT));
if (EQUUID(type, &gpt_uuid_freebsd_swap))
return (g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP));
if (EQUUID(type, &gpt_uuid_freebsd_ufs))

@ -84,6 +84,8 @@ struct gpt_ent {
{0x516e7cb8,0x6ecf,0x11d6,0x8f,0xf8,{0x00,0x02,0x2d,0x09,0x71,0x2b}}
#define GPT_ENT_TYPE_FREEBSD_ZFS \
{0x516e7cba,0x6ecf,0x11d6,0x8f,0xf8,{0x00,0x02,0x2d,0x09,0x71,0x2b}}
#define GPT_ENT_TYPE_FREEBSD_BOOT \
{0x83bd6b9d,0x7f41,0x11dc,0xbe,0x0b,{0x00,0x15,0x60,0xb8,0x4f,0x0f}}
/*
* The following are unused but documented here to avoid reuse.
@ -95,7 +97,7 @@ struct gpt_ent {
/*
* Foreign partition types that we're likely to encounter. Note that Linux
* apparently choose to share data partitions with MS. I don't what the
* advantage might be. I can see how sharing swap partitions is advantaous
* advantage might be. I can see how sharing swap partitions is advantageous
* though.
*/
#define GPT_ENT_TYPE_MS_RESERVED \