diff options
author | John Baldwin <jhb@FreeBSD.org> | 2007-10-24 21:33:00 +0000 |
---|---|---|
committer | John Baldwin <jhb@FreeBSD.org> | 2007-10-24 21:33:00 +0000 |
commit | f352a0d45f6b081b5b551de6b9734f513a358fae (patch) | |
tree | 2e0ad7e3920a2c896b216cf32db1db922c1f0e29 /sbin | |
parent | e0f5da6d08207c2148e3fc47e301db60dd3e93a5 (diff) | |
download | src-f352a0d45f6b081b5b551de6b9734f513a358fae.tar.gz src-f352a0d45f6b081b5b551de6b9734f513a358fae.zip |
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)
Notes
Notes:
svn path=/head/; revision=172940
Diffstat (limited to 'sbin')
-rw-r--r-- | sbin/gpt/Makefile | 4 | ||||
-rw-r--r-- | sbin/gpt/add.c | 76 | ||||
-rw-r--r-- | sbin/gpt/boot.c | 266 | ||||
-rw-r--r-- | sbin/gpt/gpt.8 | 45 | ||||
-rw-r--r-- | sbin/gpt/gpt.c | 1 | ||||
-rw-r--r-- | sbin/gpt/gpt.h | 2 | ||||
-rw-r--r-- | sbin/gpt/show.c | 3 |
7 files changed, 361 insertions, 36 deletions
diff --git a/sbin/gpt/Makefile b/sbin/gpt/Makefile index cfae78d03cf2..b619b00f3002 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 348989fb7e2a..b2c20a0302b9 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 000000000000..ff9d4a07be42 --- /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 13f9223257f1..7e5413905698 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 b45cfd104cc5..7a76f6d4ab49 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 6b1b1a287440..857eaf0901d7 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 43fd6eae3ee3..5d0ee2e971bc 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)) |