diff options
author | jhb <jhb@FreeBSD.org> | 2007-10-24 21:33:00 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2007-10-24 21:33:00 +0000 |
commit | 2f8a906c36edd256f39a8d2ef209fef1a548d79e (patch) | |
tree | 2e0ad7e3920a2c896b216cf32db1db922c1f0e29 | |
parent | 81c7dc737f4505dc976b462a40fdd5832e63a765 (diff) | |
download | FreeBSD-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)
-rw-r--r-- | lib/libstand/Makefile | 4 | ||||
-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 | ||||
-rw-r--r-- | sys/boot/common/ufsread.c | 2 | ||||
-rw-r--r-- | sys/boot/i386/Makefile | 4 | ||||
-rw-r--r-- | sys/boot/i386/gptboot/Makefile | 77 | ||||
-rw-r--r-- | sys/boot/i386/gptboot/gptboot.c | 188 | ||||
-rw-r--r-- | sys/boot/i386/gptboot/gptldr.S | 123 | ||||
-rw-r--r-- | sys/boot/i386/libi386/biosdisk.c | 342 | ||||
-rw-r--r-- | sys/boot/i386/libi386/devicename.c | 41 | ||||
-rw-r--r-- | sys/boot/i386/pmbr/Makefile | 14 | ||||
-rw-r--r-- | sys/boot/i386/pmbr/pmbr.s | 221 | ||||
-rw-r--r-- | sys/geom/part/g_part.c | 1 | ||||
-rw-r--r-- | sys/geom/part/g_part.h | 1 | ||||
-rw-r--r-- | sys/geom/part/g_part_gpt.c | 8 | ||||
-rw-r--r-- | sys/sys/gpt.h | 4 |
21 files changed, 1217 insertions, 210 deletions
diff --git a/lib/libstand/Makefile b/lib/libstand/Makefile index c2fc7a4..5464f9c 100644 --- a/lib/libstand/Makefile +++ b/lib/libstand/Makefile @@ -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 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)) diff --git a/sys/boot/common/ufsread.c b/sys/boot/common/ufsread.c index ae0b356..715387c 100644 --- a/sys/boot/common/ufsread.c +++ b/sys/boot/common/ufsread.c @@ -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. */ 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 diff --git a/sys/geom/part/g_part.c b/sys/geom/part/g_part.c index 0a35f6a..477584f 100644 --- a/sys/geom/part/g_part.c +++ b/sys/geom/part/g_part.c @@ -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 }, diff --git a/sys/geom/part/g_part.h b/sys/geom/part/g_part.h index 01bf65f..1410b7a 100644 --- a/sys/geom/part/g_part.h +++ b/sys/geom/part/g_part.h @@ -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. */ diff --git a/sys/geom/part/g_part_gpt.c b/sys/geom/part/g_part_gpt.c index 140c91a..12d28b5 100644 --- a/sys/geom/part/g_part_gpt.c +++ b/sys/geom/part/g_part_gpt.c @@ -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)) diff --git a/sys/sys/gpt.h b/sys/sys/gpt.h index 0c5fb06..2afd4f2 100644 --- a/sys/sys/gpt.h +++ b/sys/sys/gpt.h @@ -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 \ |