summaryrefslogtreecommitdiffstats
path: root/sbin
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 /sbin
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 'sbin')
-rw-r--r--sbin/gpt/Makefile4
-rw-r--r--sbin/gpt/add.c76
-rw-r--r--sbin/gpt/boot.c266
-rw-r--r--sbin/gpt/gpt.845
-rw-r--r--sbin/gpt/gpt.c1
-rw-r--r--sbin/gpt/gpt.h2
-rw-r--r--sbin/gpt/show.c3
7 files changed, 361 insertions, 36 deletions
diff --git a/sbin/gpt/Makefile b/sbin/gpt/Makefile
index cfae78d..b619b00 100644
--- a/sbin/gpt/Makefile
+++ b/sbin/gpt/Makefile
@@ -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
diff --git a/sbin/gpt/add.c b/sbin/gpt/add.c
index 348989f..b2c20a0 100644
--- a/sbin/gpt/add.c
+++ b/sbin/gpt/add.c
@@ -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) {
diff --git a/sbin/gpt/boot.c b/sbin/gpt/boot.c
new file mode 100644
index 0000000..ff9d4a0
--- /dev/null
+++ b/sbin/gpt/boot.c
@@ -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);
+}
diff --git a/sbin/gpt/gpt.8 b/sbin/gpt/gpt.8
index 13f9223..7e54139 100644
--- a/sbin/gpt/gpt.8
+++ b/sbin/gpt/gpt.8
@@ -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
diff --git a/sbin/gpt/gpt.c b/sbin/gpt/gpt.c
index b45cfd1..7a76f6d 100644
--- a/sbin/gpt/gpt.c
+++ b/sbin/gpt/gpt.c
@@ -609,6 +609,7 @@ static struct {
const char *name;
} cmdsw[] = {
{ cmd_add, "add" },
+ { cmd_boot, "boot" },
{ cmd_create, "create" },
{ cmd_destroy, "destroy" },
{ NULL, "help" },
diff --git a/sbin/gpt/gpt.h b/sbin/gpt/gpt.h
index 6b1b1a2..857eaf0 100644
--- a/sbin/gpt/gpt.h
+++ b/sbin/gpt/gpt.h
@@ -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 *[]);
diff --git a/sbin/gpt/show.c b/sbin/gpt/show.c
index 43fd6ea..5d0ee2e 100644
--- a/sbin/gpt/show.c
+++ b/sbin/gpt/show.c
@@ -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))
OpenPOWER on IntegriCloud