MFC: Add support for booting from GPT-labeled disks from the BIOS. This
includes /boot/pmbr, /boot/gptboot, and 'gpt boot'.
This commit is contained in:
parent
681ef96d1c
commit
2f930c2edb
@ -123,6 +123,10 @@ SRCS+= ashldi3.c ashrdi3.c
|
||||
SRCS+= syncicache.c
|
||||
.endif
|
||||
|
||||
# uuid functions from libc
|
||||
.PATH: ${.CURDIR}/../libc/uuid
|
||||
SRCS+= uuid_equal.c uuid_is_nil.c
|
||||
|
||||
# _setjmp/_longjmp
|
||||
.if ${MACHINE_ARCH} == "amd64"
|
||||
.PATH: ${.CURDIR}/i386
|
||||
|
@ -1,8 +1,8 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= gpt
|
||||
SRCS= add.c create.c destroy.c gpt.c label.c map.c migrate.c recover.c \
|
||||
remove.c show.c
|
||||
SRCS= add.c boot.c create.c destroy.c gpt.c label.c map.c migrate.c \
|
||||
recover.c remove.c show.c
|
||||
WARNS?= 4
|
||||
MAN= gpt.8
|
||||
|
||||
|
@ -39,9 +39,9 @@ __FBSDID("$FreeBSD$");
|
||||
#include "map.h"
|
||||
#include "gpt.h"
|
||||
|
||||
static uuid_t type;
|
||||
static off_t block, size;
|
||||
static unsigned int entry;
|
||||
static uuid_t add_type;
|
||||
static off_t add_block, add_size;
|
||||
static unsigned int add_entry;
|
||||
|
||||
static void
|
||||
usage_add(void)
|
||||
@ -53,8 +53,8 @@ usage_add(void)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void
|
||||
add(int fd)
|
||||
map_t *
|
||||
gpt_add_part(int fd, uuid_t type, off_t start, off_t size, unsigned int *entry)
|
||||
{
|
||||
map_t *gpt, *tpg;
|
||||
map_t *tbl, *lbt;
|
||||
@ -67,38 +67,38 @@ add(int fd)
|
||||
if (gpt == NULL) {
|
||||
warnx("%s: error: no primary GPT header; run create or recover",
|
||||
device_name);
|
||||
return;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
tpg = map_find(MAP_TYPE_SEC_GPT_HDR);
|
||||
if (tpg == NULL) {
|
||||
warnx("%s: error: no secondary GPT header; run recover",
|
||||
device_name);
|
||||
return;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
tbl = map_find(MAP_TYPE_PRI_GPT_TBL);
|
||||
lbt = map_find(MAP_TYPE_SEC_GPT_TBL);
|
||||
if (tbl == NULL || lbt == NULL) {
|
||||
warnx("%s: error: run recover -- trust me", device_name);
|
||||
return;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
hdr = gpt->map_data;
|
||||
if (entry > le32toh(hdr->hdr_entries)) {
|
||||
if (*entry > le32toh(hdr->hdr_entries)) {
|
||||
warnx("%s: error: index %u out of range (%u max)", device_name,
|
||||
entry, le32toh(hdr->hdr_entries));
|
||||
return;
|
||||
*entry, le32toh(hdr->hdr_entries));
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (entry > 0) {
|
||||
i = entry - 1;
|
||||
if (*entry > 0) {
|
||||
i = *entry - 1;
|
||||
ent = (void*)((char*)tbl->map_data + i *
|
||||
le32toh(hdr->hdr_entsz));
|
||||
if (!uuid_is_nil(&ent->ent_type, NULL)) {
|
||||
warnx("%s: error: entry at index %u is not free",
|
||||
device_name, entry);
|
||||
return;
|
||||
device_name, *entry);
|
||||
return (NULL);
|
||||
}
|
||||
} else {
|
||||
/* Find empty slot in GPT table. */
|
||||
@ -111,14 +111,14 @@ add(int fd)
|
||||
if (i == le32toh(hdr->hdr_entries)) {
|
||||
warnx("%s: error: no available table entries",
|
||||
device_name);
|
||||
return;
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
map = map_alloc(block, size);
|
||||
map = map_alloc(start, size);
|
||||
if (map == NULL) {
|
||||
warnx("%s: error: no space available on device", device_name);
|
||||
return;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
le_uuid_enc(&ent->ent_type, &type);
|
||||
@ -148,7 +148,19 @@ add(int fd)
|
||||
gpt_write(fd, lbt);
|
||||
gpt_write(fd, tpg);
|
||||
|
||||
printf("%sp%u added\n", device_name, i + 1);
|
||||
*entry = i + 1;
|
||||
|
||||
return (map);
|
||||
}
|
||||
|
||||
static void
|
||||
add(int fd)
|
||||
{
|
||||
|
||||
if (gpt_add_part(fd, add_type, add_block, add_size, &add_entry) != 0)
|
||||
return;
|
||||
|
||||
printf("%sp%u added\n", device_name, add_entry);
|
||||
}
|
||||
|
||||
int
|
||||
@ -161,30 +173,30 @@ cmd_add(int argc, char *argv[])
|
||||
while ((ch = getopt(argc, argv, "b:i:s:t:")) != -1) {
|
||||
switch(ch) {
|
||||
case 'b':
|
||||
if (block > 0)
|
||||
if (add_block > 0)
|
||||
usage_add();
|
||||
block = strtoll(optarg, &p, 10);
|
||||
if (*p != 0 || block < 1)
|
||||
add_block = strtoll(optarg, &p, 10);
|
||||
if (*p != 0 || add_block < 1)
|
||||
usage_add();
|
||||
break;
|
||||
case 'i':
|
||||
if (entry > 0)
|
||||
if (add_entry > 0)
|
||||
usage_add();
|
||||
entry = strtol(optarg, &p, 10);
|
||||
if (*p != 0 || entry < 1)
|
||||
add_entry = strtol(optarg, &p, 10);
|
||||
if (*p != 0 || add_entry < 1)
|
||||
usage_add();
|
||||
break;
|
||||
case 's':
|
||||
if (size > 0)
|
||||
if (add_size > 0)
|
||||
usage_add();
|
||||
size = strtoll(optarg, &p, 10);
|
||||
if (*p != 0 || size < 1)
|
||||
add_size = strtoll(optarg, &p, 10);
|
||||
if (*p != 0 || add_size < 1)
|
||||
usage_add();
|
||||
break;
|
||||
case 't':
|
||||
if (!uuid_is_nil(&type, NULL))
|
||||
if (!uuid_is_nil(&add_type, NULL))
|
||||
usage_add();
|
||||
if (parse_uuid(optarg, &type) != 0)
|
||||
if (parse_uuid(optarg, &add_type) != 0)
|
||||
usage_add();
|
||||
break;
|
||||
default:
|
||||
@ -196,9 +208,9 @@ cmd_add(int argc, char *argv[])
|
||||
usage_add();
|
||||
|
||||
/* Create UFS partitions by default. */
|
||||
if (uuid_is_nil(&type, NULL)) {
|
||||
if (uuid_is_nil(&add_type, NULL)) {
|
||||
uuid_t ufs = GPT_ENT_TYPE_FREEBSD_UFS;
|
||||
type = ufs;
|
||||
add_type = ufs;
|
||||
}
|
||||
|
||||
while (optind < argc) {
|
||||
|
@ -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
|
||||
|
@ -615,6 +615,7 @@ static struct {
|
||||
const char *name;
|
||||
} cmdsw[] = {
|
||||
{ cmd_add, "add" },
|
||||
{ cmd_boot, "boot" },
|
||||
{ cmd_create, "create" },
|
||||
{ cmd_destroy, "destroy" },
|
||||
{ NULL, "help" },
|
||||
|
@ -67,6 +67,7 @@ extern u_int secsz;
|
||||
extern int readonly, verbose;
|
||||
|
||||
uint32_t crc32(const void *, size_t);
|
||||
map_t *gpt_add_part(int, uuid_t, off_t, off_t, unsigned int *);
|
||||
void gpt_close(int);
|
||||
int gpt_open(const char *);
|
||||
void* gpt_read(int, off_t, size_t);
|
||||
@ -76,6 +77,7 @@ uint8_t *utf16_to_utf8(uint16_t *);
|
||||
void utf8_to_utf16(const uint8_t *, uint16_t *, size_t);
|
||||
|
||||
int cmd_add(int, char *[]);
|
||||
int cmd_boot(int, char *[]);
|
||||
int cmd_create(int, char *[]);
|
||||
int cmd_destroy(int, char *[]);
|
||||
int cmd_label(int, char *[]);
|
||||
|
@ -54,6 +54,7 @@ usage_show(void)
|
||||
static const char *
|
||||
friendly(uuid_t *t)
|
||||
{
|
||||
static uuid_t boot = GPT_ENT_TYPE_FREEBSD_BOOT;
|
||||
static uuid_t efi_slice = GPT_ENT_TYPE_EFI;
|
||||
static uuid_t mslinux = GPT_ENT_TYPE_MS_BASIC_DATA;
|
||||
static uuid_t freebsd = GPT_ENT_TYPE_FREEBSD;
|
||||
@ -71,6 +72,8 @@ friendly(uuid_t *t)
|
||||
|
||||
if (uuid_equal(t, &efi_slice, NULL))
|
||||
return ("EFI System");
|
||||
if (uuid_equal(t, &boot, NULL))
|
||||
return ("FreeBSD boot");
|
||||
if (uuid_equal(t, &swap, NULL))
|
||||
return ("FreeBSD swap");
|
||||
if (uuid_equal(t, &ufs, NULL))
|
||||
|
@ -1,6 +1,7 @@
|
||||
# $FreeBSD$
|
||||
|
||||
SUBDIR= mbr boot0 boot0sio btx boot2 cdboot kgzldr libi386 loader
|
||||
SUBDIR= mbr pmbr boot0 boot0sio btx boot2 cdboot gptboot kgzldr \
|
||||
libi386 loader
|
||||
|
||||
# special boot programs, 'self-extracting boot2+loader'
|
||||
SUBDIR+= pxeldr
|
||||
|
@ -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,56 @@ 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 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_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 +534,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_kind.biosdisk.unit >= nbdinfo) {
|
||||
DEBUG("attempt to open nonexistent disk");
|
||||
@ -470,11 +553,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_kind.biosdisk.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 +565,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 +597,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 +610,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 +640,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 +658,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 +692,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 +701,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 +721,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 +830,182 @@ 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))
|
||||
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 +1024,8 @@ bd_closedisk(struct open_disk *od)
|
||||
if (od->od_flags & BD_FLOPPY)
|
||||
delay(3000000);
|
||||
#endif
|
||||
if (od->od_flags & BD_GPTOK)
|
||||
free(od->od_partitions);
|
||||
free(od);
|
||||
}
|
||||
|
||||
|
@ -120,21 +120,35 @@ i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path)
|
||||
err = EUNIT;
|
||||
goto fail;
|
||||
}
|
||||
if (*cp == 's') { /* got a slice number */
|
||||
if (*cp == 'p') { /* got a GPT partition */
|
||||
np = cp + 1;
|
||||
slice = strtol(np, &cp, 10);
|
||||
if (cp == np) {
|
||||
err = ESLICE;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (*cp && (*cp != ':')) {
|
||||
partition = *cp - 'a'; /* get a partition number */
|
||||
if ((partition < 0) || (partition >= MAXPARTITIONS)) {
|
||||
err = EPART;
|
||||
if (*cp && (*cp != ':')) {
|
||||
err = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
cp++;
|
||||
partition = 0xff;
|
||||
} else {
|
||||
if (*cp == 's') { /* got a slice number */
|
||||
np = cp + 1;
|
||||
slice = strtol(np, &cp, 10);
|
||||
if (cp == np) {
|
||||
err = ESLICE;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (*cp && (*cp != ':')) {
|
||||
partition = *cp - 'a'; /* got a partition number */
|
||||
if ((partition < 0) || (partition >= MAXPARTITIONS)) {
|
||||
err = EPART;
|
||||
goto fail;
|
||||
}
|
||||
cp++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (*cp && (*cp != ':')) {
|
||||
@ -211,10 +225,14 @@ i386_fmtdev(void *vdev)
|
||||
case DEVT_DISK:
|
||||
cp = buf;
|
||||
cp += sprintf(cp, "%s%d", dev->d_dev->dv_name, dev->d_kind.biosdisk.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;
|
||||
|
||||
@ -241,4 +259,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);
|
||||
}
|
||||
|
||||
|
@ -136,6 +136,7 @@ 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_pc98(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);
|
||||
|
||||
@ -378,12 +379,8 @@ bd_open(struct open_file *f, ...)
|
||||
static int
|
||||
bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
|
||||
{
|
||||
struct pc98_partition *dptr;
|
||||
struct disklabel *lp;
|
||||
struct open_disk *od;
|
||||
int sector, slice, i;
|
||||
int error;
|
||||
char buf[BUFSIZE];
|
||||
|
||||
if (dev->d_kind.biosdisk.unit >= nbdinfo) {
|
||||
DEBUG("attempt to open nonexistent disk");
|
||||
@ -401,11 +398,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_kind.biosdisk.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)) {
|
||||
@ -414,6 +410,26 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Determine disk layout. */
|
||||
error = bd_open_pc98(od, dev);
|
||||
|
||||
out:
|
||||
if (error) {
|
||||
free(od);
|
||||
} else {
|
||||
*odp = od; /* return the open disk */
|
||||
}
|
||||
return(error);
|
||||
}
|
||||
|
||||
static int
|
||||
bd_open_pc98(struct open_disk *od, struct i386_devdesc *dev)
|
||||
{
|
||||
struct pc98_partition *dptr;
|
||||
struct disklabel *lp;
|
||||
int sector, slice, i;
|
||||
char buf[BUFSIZE];
|
||||
|
||||
/*
|
||||
* Following calculations attempt to determine the correct value
|
||||
* for d->od_boff by looking for the slice and partition specified,
|
||||
@ -423,14 +439,14 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
|
||||
/*
|
||||
* Find the slice in the DOS slice table.
|
||||
*/
|
||||
od->od_nslices = 0;
|
||||
if (od->od_flags & BD_FLOPPY) {
|
||||
sector = 0;
|
||||
goto unsliced;
|
||||
}
|
||||
if (bd_read(od, 0, 1, buf)) {
|
||||
DEBUG("error reading MBR");
|
||||
error = EIO;
|
||||
goto out;
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -440,16 +456,14 @@ 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 */
|
||||
}
|
||||
if (bd_read(od, 1, 1, buf)) {
|
||||
DEBUG("error reading MBR");
|
||||
error = EIO;
|
||||
goto out;
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -474,8 +488,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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,8 +496,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;
|
||||
}
|
||||
@ -524,8 +536,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));
|
||||
@ -534,15 +545,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
|
||||
@ -557,14 +565,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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -82,6 +82,8 @@ struct gpt_ent {
|
||||
{0x516e7cb6,0x6ecf,0x11d6,0x8f,0xf8,{0x00,0x02,0x2d,0x09,0x71,0x2b}}
|
||||
#define GPT_ENT_TYPE_FREEBSD_VINUM \
|
||||
{0x516e7cb8,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.
|
||||
@ -93,7 +95,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 \
|
||||
|
Loading…
Reference in New Issue
Block a user