First cut at support for booting a GPT labeled disk via the BIOS bootstrap
on i386 and amd64 machines. The overall process is that /boot/pmbr lives in the PMBR (similar to /boot/mbr for MBR disks) and is responsible for locating and loading /boot/gptboot. /boot/gptboot is similar to /boot/boot except that it groks GPT rather than MBR + bsdlabel. Unlike /boot/boot, /boot/gptboot lives in its own dedicated GPT partition with a new "FreeBSD boot" type. This partition does not have a fixed size in that /boot/pmbr will load the entire partition into the lower 640k. However, it is limited in that it can only be 545k. That's still a lot better than the current 7.5k limit for boot2 on MBR. gptboot mostly acts just like boot2 in that it reads /boot.config and loads up /boot/loader. Some more details: - Include uuid_equal() and uuid_is_nil() in libstand. - Add a new 'boot' command to gpt(8) which makes a GPT disk bootable using /boot/pmbr and /boot/gptboot. Note that the disk must have some free space for the boot partition. - This required exposing the backend of the 'add' function as a gpt_add_part() function to the rest of gpt(8). 'boot' uses this to create a boot partition if needed. - Don't cripple cgbase() in the UFS boot code for /boot/gptboot so that it can handle a filesystem > 1.5 TB. - /boot/gptboot has a simple loader (gptldr) that doesn't do any I/O unlike boot1 since /boot/pmbr loads all of gptboot up front. The C portion of gptboot (gptboot.c) has been repocopied from boot2.c. The primary changes are to parse the GPT to find a root filesystem and to use 64-bit disk addresses. Currently gptboot assumes that the first UFS partition on the disk is the / filesystem, but this algorithm will likely be improved in the future. - Teach the biosdisk driver in /boot/loader to understand GPT tables. GPT partitions are identified as 'disk0pX:' (e.g. disk0p2:) which is similar to the /dev names the kernel uses (e.g. /dev/ad0p2). - Add a new "freebsd-boot" alias to g_part() for the new boot UUID. MFC after: 1 month Discussed with: marcel (some things might still change, but am committing what I have so far)
This commit is contained in:
parent
e0f5da6d08
commit
f352a0d45f
@ -72,6 +72,10 @@ SRCS+= ashldi3.c ashrdi3.c
|
||||
SRCS+= syncicache.c
|
||||
.endif
|
||||
|
||||
# uuid functions from libc
|
||||
.PATH: ${.CURDIR}/../libc/uuid
|
||||
SRCS+= uuid_equal.c uuid_is_nil.c
|
||||
|
||||
# _setjmp/_longjmp
|
||||
.if ${MACHINE_ARCH} == "amd64"
|
||||
.PATH: ${.CURDIR}/i386
|
||||
|
@ -1,8 +1,8 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= gpt
|
||||
SRCS= add.c create.c destroy.c gpt.c label.c map.c migrate.c recover.c \
|
||||
remove.c show.c
|
||||
SRCS= add.c boot.c create.c destroy.c gpt.c label.c map.c migrate.c \
|
||||
recover.c remove.c show.c
|
||||
WARNS?= 4
|
||||
MAN= gpt.8
|
||||
|
||||
|
@ -39,9 +39,9 @@ __FBSDID("$FreeBSD$");
|
||||
#include "map.h"
|
||||
#include "gpt.h"
|
||||
|
||||
static uuid_t type;
|
||||
static off_t block, size;
|
||||
static unsigned int entry;
|
||||
static uuid_t add_type;
|
||||
static off_t add_block, add_size;
|
||||
static unsigned int add_entry;
|
||||
|
||||
static void
|
||||
usage_add(void)
|
||||
@ -53,8 +53,8 @@ usage_add(void)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void
|
||||
add(int fd)
|
||||
map_t *
|
||||
gpt_add_part(int fd, uuid_t type, off_t start, off_t size, unsigned int *entry)
|
||||
{
|
||||
map_t *gpt, *tpg;
|
||||
map_t *tbl, *lbt;
|
||||
@ -67,38 +67,38 @@ add(int fd)
|
||||
if (gpt == NULL) {
|
||||
warnx("%s: error: no primary GPT header; run create or recover",
|
||||
device_name);
|
||||
return;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
tpg = map_find(MAP_TYPE_SEC_GPT_HDR);
|
||||
if (tpg == NULL) {
|
||||
warnx("%s: error: no secondary GPT header; run recover",
|
||||
device_name);
|
||||
return;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
tbl = map_find(MAP_TYPE_PRI_GPT_TBL);
|
||||
lbt = map_find(MAP_TYPE_SEC_GPT_TBL);
|
||||
if (tbl == NULL || lbt == NULL) {
|
||||
warnx("%s: error: run recover -- trust me", device_name);
|
||||
return;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
hdr = gpt->map_data;
|
||||
if (entry > le32toh(hdr->hdr_entries)) {
|
||||
if (*entry > le32toh(hdr->hdr_entries)) {
|
||||
warnx("%s: error: index %u out of range (%u max)", device_name,
|
||||
entry, le32toh(hdr->hdr_entries));
|
||||
return;
|
||||
*entry, le32toh(hdr->hdr_entries));
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (entry > 0) {
|
||||
i = entry - 1;
|
||||
if (*entry > 0) {
|
||||
i = *entry - 1;
|
||||
ent = (void*)((char*)tbl->map_data + i *
|
||||
le32toh(hdr->hdr_entsz));
|
||||
if (!uuid_is_nil(&ent->ent_type, NULL)) {
|
||||
warnx("%s: error: entry at index %u is not free",
|
||||
device_name, entry);
|
||||
return;
|
||||
device_name, *entry);
|
||||
return (NULL);
|
||||
}
|
||||
} else {
|
||||
/* Find empty slot in GPT table. */
|
||||
@ -111,14 +111,14 @@ add(int fd)
|
||||
if (i == le32toh(hdr->hdr_entries)) {
|
||||
warnx("%s: error: no available table entries",
|
||||
device_name);
|
||||
return;
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
map = map_alloc(block, size);
|
||||
map = map_alloc(start, size);
|
||||
if (map == NULL) {
|
||||
warnx("%s: error: no space available on device", device_name);
|
||||
return;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
le_uuid_enc(&ent->ent_type, &type);
|
||||
@ -148,7 +148,19 @@ add(int fd)
|
||||
gpt_write(fd, lbt);
|
||||
gpt_write(fd, tpg);
|
||||
|
||||
printf("%sp%u added\n", device_name, i + 1);
|
||||
*entry = i + 1;
|
||||
|
||||
return (map);
|
||||
}
|
||||
|
||||
static void
|
||||
add(int fd)
|
||||
{
|
||||
|
||||
if (gpt_add_part(fd, add_type, add_block, add_size, &add_entry) != 0)
|
||||
return;
|
||||
|
||||
printf("%sp%u added\n", device_name, add_entry);
|
||||
}
|
||||
|
||||
int
|
||||
@ -161,30 +173,30 @@ cmd_add(int argc, char *argv[])
|
||||
while ((ch = getopt(argc, argv, "b:i:s:t:")) != -1) {
|
||||
switch(ch) {
|
||||
case 'b':
|
||||
if (block > 0)
|
||||
if (add_block > 0)
|
||||
usage_add();
|
||||
block = strtoll(optarg, &p, 10);
|
||||
if (*p != 0 || block < 1)
|
||||
add_block = strtoll(optarg, &p, 10);
|
||||
if (*p != 0 || add_block < 1)
|
||||
usage_add();
|
||||
break;
|
||||
case 'i':
|
||||
if (entry > 0)
|
||||
if (add_entry > 0)
|
||||
usage_add();
|
||||
entry = strtol(optarg, &p, 10);
|
||||
if (*p != 0 || entry < 1)
|
||||
add_entry = strtol(optarg, &p, 10);
|
||||
if (*p != 0 || add_entry < 1)
|
||||
usage_add();
|
||||
break;
|
||||
case 's':
|
||||
if (size > 0)
|
||||
if (add_size > 0)
|
||||
usage_add();
|
||||
size = strtoll(optarg, &p, 10);
|
||||
if (*p != 0 || size < 1)
|
||||
add_size = strtoll(optarg, &p, 10);
|
||||
if (*p != 0 || add_size < 1)
|
||||
usage_add();
|
||||
break;
|
||||
case 't':
|
||||
if (!uuid_is_nil(&type, NULL))
|
||||
if (!uuid_is_nil(&add_type, NULL))
|
||||
usage_add();
|
||||
if (parse_uuid(optarg, &type) != 0)
|
||||
if (parse_uuid(optarg, &add_type) != 0)
|
||||
usage_add();
|
||||
break;
|
||||
default:
|
||||
@ -196,9 +208,9 @@ cmd_add(int argc, char *argv[])
|
||||
usage_add();
|
||||
|
||||
/* Create UFS partitions by default. */
|
||||
if (uuid_is_nil(&type, NULL)) {
|
||||
if (uuid_is_nil(&add_type, NULL)) {
|
||||
uuid_t ufs = GPT_ENT_TYPE_FREEBSD_UFS;
|
||||
type = ufs;
|
||||
add_type = ufs;
|
||||
}
|
||||
|
||||
while (optind < argc) {
|
||||
|
266
sbin/gpt/boot.c
Normal file
266
sbin/gpt/boot.c
Normal file
@ -0,0 +1,266 @@
|
||||
/*-
|
||||
* Copyright (c) 2007 Yahoo!, Inc.
|
||||
* All rights reserved.
|
||||
* Written by: John Baldwin <jhb@FreeBSD.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "map.h"
|
||||
#include "gpt.h"
|
||||
|
||||
static uuid_t boot_uuid = GPT_ENT_TYPE_FREEBSD_BOOT;
|
||||
static const char *pmbr_path = "/boot/pmbr";
|
||||
static const char *gptboot_path = "/boot/gptboot";
|
||||
static u_long boot_size;
|
||||
|
||||
static void
|
||||
usage_boot(void)
|
||||
{
|
||||
|
||||
fprintf(stderr,
|
||||
"usage: %s [-b pmbr] [-g gptboot] [-s count] device ...\n",
|
||||
getprogname());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int
|
||||
gpt_find(uuid_t *type, map_t **mapp)
|
||||
{
|
||||
map_t *gpt, *tbl, *map;
|
||||
struct gpt_hdr *hdr;
|
||||
struct gpt_ent *ent;
|
||||
unsigned int i;
|
||||
|
||||
/* Find a GPT partition with the requested UUID type. */
|
||||
gpt = map_find(MAP_TYPE_PRI_GPT_HDR);
|
||||
if (gpt == NULL) {
|
||||
warnx("%s: error: no primary GPT header", device_name);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
tbl = map_find(MAP_TYPE_PRI_GPT_TBL);
|
||||
if (tbl == NULL) {
|
||||
warnx("%s: error: no primary partition table", device_name);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
hdr = gpt->map_data;
|
||||
for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
|
||||
ent = (void *)((char *)tbl->map_data + i *
|
||||
le32toh(hdr->hdr_entsz));
|
||||
if (uuid_equal(&ent->ent_type, type, NULL))
|
||||
break;
|
||||
}
|
||||
if (i == le32toh(hdr->hdr_entries)) {
|
||||
*mapp = NULL;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Lookup the map corresponding to this partition. */
|
||||
for (map = map_find(MAP_TYPE_GPT_PART); map != NULL;
|
||||
map = map->map_next) {
|
||||
if (map->map_type != MAP_TYPE_GPT_PART)
|
||||
continue;
|
||||
if (map->map_start == (off_t)le64toh(ent->ent_lba_start)) {
|
||||
assert(map->map_start + map->map_size - 1LL ==
|
||||
(off_t)le64toh(ent->ent_lba_end));
|
||||
*mapp = map;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Hmm, the map list is not in sync with the GPT table. */
|
||||
errx(1, "internal map list is corrupted");
|
||||
}
|
||||
|
||||
static void
|
||||
boot(int fd)
|
||||
{
|
||||
struct stat sb;
|
||||
off_t bsize, ofs;
|
||||
map_t *pmbr, *gptboot;
|
||||
struct mbr *mbr;
|
||||
char *buf;
|
||||
ssize_t nbytes;
|
||||
unsigned int entry;
|
||||
int bfd;
|
||||
|
||||
/* First step: verify boot partition size. */
|
||||
if (boot_size == 0)
|
||||
/* Default to 64k. */
|
||||
bsize = 65536 / secsz;
|
||||
else {
|
||||
if (boot_size * secsz < 16384) {
|
||||
warnx("invalid boot partition size %lu", boot_size);
|
||||
return;
|
||||
}
|
||||
bsize = boot_size;
|
||||
}
|
||||
|
||||
/* Second step: write the PMBR boot loader into the PMBR. */
|
||||
pmbr = map_find(MAP_TYPE_PMBR);
|
||||
if (pmbr == NULL) {
|
||||
warnx("%s: error: PMBR not found", device_name);
|
||||
return;
|
||||
}
|
||||
bfd = open(pmbr_path, O_RDONLY);
|
||||
if (bfd < 0 || fstat(bfd, &sb) < 0) {
|
||||
warn("unable to open PMBR boot loader");
|
||||
return;
|
||||
}
|
||||
if (sb.st_size != secsz) {
|
||||
warnx("invalid PMBR boot loader");
|
||||
return;
|
||||
}
|
||||
mbr = pmbr->map_data;
|
||||
nbytes = read(bfd, mbr->mbr_code, sizeof(mbr->mbr_code));
|
||||
if (nbytes < 0) {
|
||||
warn("unable to read PMBR boot loader");
|
||||
return;
|
||||
}
|
||||
if (nbytes != sizeof(mbr->mbr_code)) {
|
||||
warnx("short read of PMBR boot loader");
|
||||
return;
|
||||
}
|
||||
close(bfd);
|
||||
gpt_write(fd, pmbr);
|
||||
|
||||
/* Third step: open gptboot and obtain its size. */
|
||||
bfd = open(gptboot_path, O_RDONLY);
|
||||
if (bfd < 0 || fstat(bfd, &sb) < 0) {
|
||||
warn("unable to open GPT boot loader");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Fourth step: find an existing boot partition or create one. */
|
||||
if (gpt_find(&boot_uuid, &gptboot) != 0)
|
||||
return;
|
||||
if (gptboot != NULL) {
|
||||
if (gptboot->map_size * secsz < sb.st_size) {
|
||||
warnx("%s: error: boot partition is too small",
|
||||
device_name);
|
||||
return;
|
||||
}
|
||||
} else if (bsize * secsz < sb.st_size) {
|
||||
warnx(
|
||||
"%s: error: proposed size for boot partition is too small",
|
||||
device_name);
|
||||
return;
|
||||
} else {
|
||||
entry = 0;
|
||||
gptboot = gpt_add_part(fd, boot_uuid, 0, bsize, &entry);
|
||||
if (gptboot == NULL)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fourth step, write out the gptboot binary to the boot partition. */
|
||||
buf = malloc(sb.st_size);
|
||||
nbytes = read(bfd, buf, sb.st_size);
|
||||
if (nbytes < 0) {
|
||||
warn("unable to read GPT boot loader");
|
||||
return;
|
||||
}
|
||||
if (nbytes != sb.st_size) {
|
||||
warnx("short read of GPT boot loader");
|
||||
return;
|
||||
}
|
||||
close(bfd);
|
||||
ofs = gptboot->map_start * secsz;
|
||||
if (lseek(fd, ofs, SEEK_SET) != ofs) {
|
||||
warn("%s: error: unable to seek to boot partition",
|
||||
device_name);
|
||||
return;
|
||||
}
|
||||
nbytes = write(fd, buf, sb.st_size);
|
||||
if (nbytes < 0) {
|
||||
warn("unable to write GPT boot loader");
|
||||
return;
|
||||
}
|
||||
if (nbytes != sb.st_size) {
|
||||
warnx("short write of GPT boot loader");
|
||||
return;
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
|
||||
int
|
||||
cmd_boot(int argc, char *argv[])
|
||||
{
|
||||
char *p;
|
||||
int ch, fd;
|
||||
|
||||
while ((ch = getopt(argc, argv, "b:g:s:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'b':
|
||||
pmbr_path = optarg;
|
||||
break;
|
||||
case 'g':
|
||||
gptboot_path = optarg;
|
||||
break;
|
||||
case 's':
|
||||
if (boot_size > 0)
|
||||
usage_boot();
|
||||
boot_size = strtol(optarg, &p, 10);
|
||||
if (*p != '\0' || boot_size < 1)
|
||||
usage_boot();
|
||||
break;
|
||||
default:
|
||||
usage_boot();
|
||||
}
|
||||
}
|
||||
|
||||
if (argc == optind)
|
||||
usage_boot();
|
||||
|
||||
while (optind < argc) {
|
||||
fd = gpt_open(argv[optind++]);
|
||||
if (fd < 0) {
|
||||
warn("unable to open device '%s'", device_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
boot(fd);
|
||||
|
||||
gpt_close(fd);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
@ -24,7 +24,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd June 22, 2006
|
||||
.Dd October 24, 2007
|
||||
.Os
|
||||
.Dt GPT 8
|
||||
.Sh NAME
|
||||
@ -130,10 +130,51 @@ option allows the user to specify the partition type.
|
||||
The type is given as an UUID, but
|
||||
.Nm
|
||||
accepts
|
||||
.Cm efi , swap , ufs , hfs , linux
|
||||
.Cm boot , efi , swap , ufs , hfs , linux
|
||||
and
|
||||
.Cm windows
|
||||
as aliases for the most commonly used partition types.
|
||||
.\" ==== boot ====
|
||||
.It Xo
|
||||
.Nm
|
||||
.Ic boot
|
||||
.Op Fl b Ar pmbr
|
||||
.Op Fl g Ar gptboot
|
||||
.Op Fl s Ar count
|
||||
.Ar device ...
|
||||
.Xc
|
||||
The
|
||||
.Ic boot
|
||||
command allows the user to make a GPT labeled disk bootable via the BIOS
|
||||
bootstrap on i386 and amd64 machines.
|
||||
By default,
|
||||
the
|
||||
.Pa /boot/pmbr
|
||||
boot loader is installed into the PMBR and the
|
||||
.Pa /boot/gptboot
|
||||
boot loader is installed into the first boot partition.
|
||||
If no boot partition exists and there is available space,
|
||||
a boot partition will be created.
|
||||
.Pp
|
||||
The
|
||||
.Fl b Ar pmbr
|
||||
option allows the user to specify an alternate path for the PMBR boot loader.
|
||||
.Pp
|
||||
The
|
||||
.Fl g Ar gptboot
|
||||
option allows the user to specify an alternate path for the GPT boot loader
|
||||
that is installed into the boot partition.
|
||||
.Pp
|
||||
The
|
||||
.Fl s Ar count
|
||||
option allows the user to specify the size in sectors of the boot partition
|
||||
if one does not already exist.
|
||||
A boot partition must be at least 16 kilobytes.
|
||||
By default,
|
||||
a size of 64 kilobytes is used.
|
||||
Note that the PMBR boot loader will load the entire boot partition into
|
||||
memory.
|
||||
As a result, the boot partition may not exceed 545 kilobytes.
|
||||
.\" ==== create ====
|
||||
.It Nm Ic create Oo Fl fp Oc Ar device ...
|
||||
The
|
||||
|
@ -609,6 +609,7 @@ static struct {
|
||||
const char *name;
|
||||
} cmdsw[] = {
|
||||
{ cmd_add, "add" },
|
||||
{ cmd_boot, "boot" },
|
||||
{ cmd_create, "create" },
|
||||
{ cmd_destroy, "destroy" },
|
||||
{ NULL, "help" },
|
||||
|
@ -67,6 +67,7 @@ extern u_int secsz;
|
||||
extern int readonly, verbose;
|
||||
|
||||
uint32_t crc32(const void *, size_t);
|
||||
map_t *gpt_add_part(int, uuid_t, off_t, off_t, unsigned int *);
|
||||
void gpt_close(int);
|
||||
int gpt_open(const char *);
|
||||
void* gpt_read(int, off_t, size_t);
|
||||
@ -76,6 +77,7 @@ uint8_t *utf16_to_utf8(uint16_t *);
|
||||
void utf8_to_utf16(const uint8_t *, uint16_t *, size_t);
|
||||
|
||||
int cmd_add(int, char *[]);
|
||||
int cmd_boot(int, char *[]);
|
||||
int cmd_create(int, char *[]);
|
||||
int cmd_destroy(int, char *[]);
|
||||
int cmd_label(int, char *[]);
|
||||
|
@ -54,6 +54,7 @@ usage_show(void)
|
||||
static const char *
|
||||
friendly(uuid_t *t)
|
||||
{
|
||||
static uuid_t boot = GPT_ENT_TYPE_FREEBSD_BOOT;
|
||||
static uuid_t efi_slice = GPT_ENT_TYPE_EFI;
|
||||
static uuid_t mslinux = GPT_ENT_TYPE_MS_BASIC_DATA;
|
||||
static uuid_t freebsd = GPT_ENT_TYPE_FREEBSD;
|
||||
@ -71,6 +72,8 @@ friendly(uuid_t *t)
|
||||
|
||||
if (uuid_equal(t, &efi_slice, NULL))
|
||||
return ("EFI System");
|
||||
if (uuid_equal(t, &boot, NULL))
|
||||
return ("FreeBSD boot");
|
||||
if (uuid_equal(t, &swap, NULL))
|
||||
return ("FreeBSD swap");
|
||||
if (uuid_equal(t, &ufs, NULL))
|
||||
|
@ -48,7 +48,7 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <ufs/ufs/dinode.h>
|
||||
#include <ufs/ffs/fs.h>
|
||||
#ifdef __i386__
|
||||
#if defined(__i386__) && !defined(GPTBOOT)
|
||||
/* XXX: Revert to old (broken for over 1.5Tb filesystems) version of cgbase
|
||||
(see sys/ufs/ffs/fs.h rev 1.39) so that i386 boot loader (boot2) can
|
||||
support both UFS1 and UFS2 again. */
|
||||
|
@ -1,7 +1,7 @@
|
||||
# $FreeBSD$
|
||||
|
||||
SUBDIR= mbr boot0 boot0sio btx boot2 cdboot kgzldr libi386 libfirewire \
|
||||
loader
|
||||
SUBDIR= mbr pmbr boot0 boot0sio btx boot2 cdboot gptboot kgzldr \
|
||||
libi386 libfirewire loader
|
||||
|
||||
# special boot programs, 'self-extracting boot2+loader'
|
||||
SUBDIR+= pxeldr
|
||||
|
@ -1,24 +1,23 @@
|
||||
# $FreeBSD$
|
||||
|
||||
FILES= boot boot1 boot2
|
||||
.PATH: ${.CURDIR}/../boot2
|
||||
|
||||
FILES= gptboot
|
||||
|
||||
NM?= nm
|
||||
|
||||
# A value of 0x80 enables LBA support.
|
||||
BOOT_BOOT1_FLAGS?= 0x80
|
||||
|
||||
BOOT_COMCONSOLE_PORT?= 0x3f8
|
||||
BOOT_COMCONSOLE_SPEED?= 9600
|
||||
B2SIOFMT?= 0x3
|
||||
|
||||
REL1= 0x700
|
||||
ORG1= 0x7c00
|
||||
ORG2= 0x2000
|
||||
ORG2= 0x0
|
||||
|
||||
# Decide level of UFS support.
|
||||
BOOT2_UFS?= UFS1_AND_UFS2
|
||||
#BOOT2_UFS?= UFS2_ONLY
|
||||
#BOOT2_UFS?= UFS1_ONLY
|
||||
GPTBOOT_UFS?= UFS1_AND_UFS2
|
||||
#GPTBOOT_UFS?= UFS2_ONLY
|
||||
#GPTBOOT_UFS?= UFS1_ONLY
|
||||
|
||||
CFLAGS= -Os \
|
||||
-fno-guess-branch-probability \
|
||||
@ -27,13 +26,13 @@ CFLAGS= -Os \
|
||||
-mno-align-long-strings \
|
||||
-mrtd \
|
||||
-mno-mmx -mno-3dnow -mno-sse -mno-sse2 -mno-sse3 \
|
||||
-D${BOOT2_UFS} \
|
||||
-DFLAGS=${BOOT_BOOT1_FLAGS} \
|
||||
-D${GPTBOOT_UFS} \
|
||||
-DSIOPRT=${BOOT_COMCONSOLE_PORT} \
|
||||
-DSIOFMT=${B2SIOFMT} \
|
||||
-DSIOSPD=${BOOT_COMCONSOLE_SPEED} \
|
||||
-I${.CURDIR}/../../common \
|
||||
-I${.CURDIR}/../btx/lib -I. \
|
||||
-I${.CURDIR}/../btx/lib \
|
||||
-I${.CURDIR}/../boot2 \
|
||||
-Wall -Waggregate-return -Wbad-function-cast -Wcast-align \
|
||||
-Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
|
||||
-Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \
|
||||
@ -44,58 +43,32 @@ LDFLAGS=-static -N --gc-sections
|
||||
# Pick up ../Makefile.inc early.
|
||||
.include <bsd.init.mk>
|
||||
|
||||
CLEANFILES= boot
|
||||
CLEANFILES= gptboot
|
||||
|
||||
boot: boot1 boot2
|
||||
cat boot1 boot2 > boot
|
||||
gptboot: gptldr.bin gptboot.bin ${BTXKERN}
|
||||
btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l gptldr.bin \
|
||||
-o ${.TARGET} gptboot.bin
|
||||
|
||||
CLEANFILES+= boot1 boot1.out boot1.o
|
||||
CLEANFILES+= gptldr.bin gptldr.out gptldr.o
|
||||
|
||||
boot1: boot1.out
|
||||
objcopy -S -O binary boot1.out ${.TARGET}
|
||||
gptldr.bin: gptldr.out
|
||||
objcopy -S -O binary gptldr.out ${.TARGET}
|
||||
|
||||
boot1.out: boot1.o
|
||||
${LD} ${LDFLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} boot1.o
|
||||
gptldr.out: gptldr.o
|
||||
${LD} ${LDFLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o
|
||||
|
||||
CLEANFILES+= boot2 boot2.ld boot2.ldr boot2.bin boot2.out boot2.o \
|
||||
boot2.s boot2.s.tmp boot2.h sio.o
|
||||
CLEANFILES+= gptboot.bin gptboot.out gptboot.o sio.o
|
||||
|
||||
boot2: boot2.ld
|
||||
@set -- `ls -l boot2.ld`; x=$$((7680-$$5)); \
|
||||
echo "$$x bytes available"; test $$x -ge 0
|
||||
dd if=boot2.ld of=${.TARGET} obs=7680 conv=osync
|
||||
gptboot.bin: gptboot.out
|
||||
objcopy -S -O binary gptboot.out ${.TARGET}
|
||||
|
||||
boot2.ld: boot2.ldr boot2.bin ${BTXKERN}
|
||||
btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l boot2.ldr \
|
||||
-o ${.TARGET} -P 1 boot2.bin
|
||||
|
||||
boot2.ldr:
|
||||
dd if=/dev/zero of=${.TARGET} bs=276 count=1
|
||||
|
||||
boot2.bin: boot2.out
|
||||
objcopy -S -O binary boot2.out ${.TARGET}
|
||||
|
||||
boot2.out: ${BTXCRT} boot2.o sio.o
|
||||
gptboot.out: ${BTXCRT} gptboot.o sio.o
|
||||
${LD} ${LDFLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC}
|
||||
|
||||
boot2.o: boot2.s
|
||||
|
||||
SRCS= boot2.c boot2.h
|
||||
|
||||
boot2.s: boot2.c boot2.h ${.CURDIR}/../../common/ufsread.c
|
||||
${CC} ${CFLAGS} -S -o boot2.s.tmp ${.CURDIR}/boot2.c
|
||||
sed -e '/align/d' -e '/nop/d' < boot2.s.tmp > boot2.s
|
||||
rm -f boot2.s.tmp
|
||||
|
||||
boot2.h: boot1.out
|
||||
${NM} -t d ${.ALLSRC} | awk '/([0-9])+ T xread/ \
|
||||
{ x = $$1 - ORG1; \
|
||||
printf("#define XREADORG %#x\n", REL1 + x) }' \
|
||||
ORG1=`printf "%d" ${ORG1}` \
|
||||
REL1=`printf "%d" ${REL1}` > ${.TARGET}
|
||||
gptboot.o: ${.CURDIR}/../../common/ufsread.c
|
||||
|
||||
.if ${MACHINE_ARCH} == "amd64"
|
||||
beforedepend boot2.s: machine
|
||||
beforedepend gptboot.o: machine
|
||||
CLEANFILES+= machine
|
||||
machine:
|
||||
ln -sf ${.CURDIR}/../../../i386/include machine
|
||||
|
@ -17,8 +17,7 @@
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/disklabel.h>
|
||||
#include <sys/diskmbr.h>
|
||||
#include <sys/gpt.h>
|
||||
#include <sys/dirent.h>
|
||||
#include <sys/reboot.h>
|
||||
|
||||
@ -31,7 +30,6 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <btxv86.h>
|
||||
|
||||
#include "boot2.h"
|
||||
#include "lib.h"
|
||||
|
||||
#define IO_KEYBOARD 1
|
||||
@ -99,6 +97,7 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
extern uint32_t _end;
|
||||
|
||||
static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS;
|
||||
static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
|
||||
static const unsigned char flags[NOPT] = {
|
||||
RBX_DUAL,
|
||||
@ -124,9 +123,8 @@ static struct dsk {
|
||||
unsigned drive;
|
||||
unsigned type;
|
||||
unsigned unit;
|
||||
unsigned slice;
|
||||
unsigned part;
|
||||
unsigned start;
|
||||
int part;
|
||||
daddr_t start;
|
||||
int init;
|
||||
} dsk;
|
||||
static char cmd[512];
|
||||
@ -137,20 +135,21 @@ static struct bootinfo bootinfo;
|
||||
static uint8_t ioctrl = IO_KEYBOARD;
|
||||
|
||||
void exit(int);
|
||||
static int bcmp(const void *, const void *, size_t);
|
||||
static void load(void);
|
||||
static int parse(void);
|
||||
static int xfsread(ino_t, void *, size_t);
|
||||
static int dskread(void *, unsigned, unsigned);
|
||||
static int dskread(void *, daddr_t, unsigned);
|
||||
static void printf(const char *,...);
|
||||
static void putchar(int);
|
||||
static void memcpy(void *, const void *, int);
|
||||
static uint32_t memsize(void);
|
||||
static int drvread(void *, unsigned, unsigned);
|
||||
static int drvread(void *, daddr_t, unsigned);
|
||||
static int keyhit(unsigned);
|
||||
static int xputc(int);
|
||||
static int xgetc(int);
|
||||
static int getc(int);
|
||||
|
||||
static void memcpy(void *, const void *, int);
|
||||
static void
|
||||
memcpy(void *dst, const void *src, int len)
|
||||
{
|
||||
@ -168,6 +167,7 @@ strcmp(const char *s1, const char *s2)
|
||||
return (unsigned char)*s1 - (unsigned char)*s2;
|
||||
}
|
||||
|
||||
#define GPTBOOT
|
||||
#include "ufsread.c"
|
||||
|
||||
static inline int
|
||||
@ -239,7 +239,7 @@ main(void)
|
||||
dsk.drive = *(uint8_t *)PTOV(ARGS);
|
||||
dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
|
||||
dsk.unit = dsk.drive & DRV_MASK;
|
||||
dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
|
||||
dsk.part = -1;
|
||||
bootinfo.bi_version = BOOTINFO_VERSION;
|
||||
bootinfo.bi_size = sizeof(bootinfo);
|
||||
bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */
|
||||
@ -280,10 +280,10 @@ main(void)
|
||||
for (;;) {
|
||||
if (!autoboot || !OPT_CHECK(RBX_QUIET))
|
||||
printf("\nFreeBSD/i386 boot\n"
|
||||
"Default: %u:%s(%u,%c)%s\n"
|
||||
"Default: %u:%s(%up%u)%s\n"
|
||||
"boot: ",
|
||||
dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
|
||||
'a' + dsk.part, kname);
|
||||
dsk.part, kname);
|
||||
if (ioctrl & IO_SERIAL)
|
||||
sio_flush();
|
||||
if (!autoboot || keyhit(5*SECOND))
|
||||
@ -395,12 +395,12 @@ load(void)
|
||||
bootinfo.bi_kernelname = VTOP(kname);
|
||||
bootinfo.bi_bios_dev = dsk.drive;
|
||||
__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
|
||||
MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part),
|
||||
MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff),
|
||||
0, 0, 0, VTOP(&bootinfo));
|
||||
}
|
||||
|
||||
static int
|
||||
parse()
|
||||
parse(void)
|
||||
{
|
||||
char *arg = cmd;
|
||||
char *ep, *p, *q;
|
||||
@ -467,19 +467,16 @@ parse()
|
||||
if (arg[1] != ',' || dsk.unit > 9)
|
||||
return -1;
|
||||
arg += 2;
|
||||
dsk.slice = WHOLE_DISK_SLICE;
|
||||
dsk.part = -1;
|
||||
if (arg[1] == ',') {
|
||||
dsk.slice = *arg - '0' + 1;
|
||||
if (dsk.slice > NDOSPART)
|
||||
dsk.part = *arg - '0';
|
||||
if (dsk.part < 1 || dsk.part > 9)
|
||||
return -1;
|
||||
arg += 2;
|
||||
}
|
||||
if (arg[1] != ')')
|
||||
if (arg[0] != ')')
|
||||
return -1;
|
||||
dsk.part = *arg - 'a';
|
||||
if (dsk.part > 7)
|
||||
return (-1);
|
||||
arg += 2;
|
||||
arg++;
|
||||
if (drv == -1)
|
||||
drv = dsk.unit;
|
||||
dsk.drive = (dsk.type <= TYPE_MAXHARD
|
||||
@ -498,63 +495,87 @@ parse()
|
||||
}
|
||||
|
||||
static int
|
||||
dskread(void *buf, unsigned lba, unsigned nblk)
|
||||
dskread(void *buf, daddr_t lba, unsigned nblk)
|
||||
{
|
||||
struct dos_partition *dp;
|
||||
struct disklabel *d;
|
||||
struct gpt_hdr hdr;
|
||||
struct gpt_ent *ent;
|
||||
char *sec;
|
||||
unsigned sl, i;
|
||||
daddr_t slba, elba;
|
||||
int part, entries_per_sec;
|
||||
|
||||
if (!dsk_meta) {
|
||||
/* Read and verify GPT. */
|
||||
sec = dmadat->secbuf;
|
||||
dsk.start = 0;
|
||||
if (drvread(sec, DOSBBSECTOR, 1))
|
||||
if (drvread(sec, 1, 1))
|
||||
return -1;
|
||||
dp = (void *)(sec + DOSPARTOFF);
|
||||
sl = dsk.slice;
|
||||
if (sl < BASE_SLICE) {
|
||||
for (i = 0; i < NDOSPART; i++)
|
||||
if (dp[i].dp_typ == DOSPTYP_386BSD &&
|
||||
(dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
|
||||
sl = BASE_SLICE + i;
|
||||
if (dp[i].dp_flag & 0x80 ||
|
||||
dsk.slice == COMPATIBILITY_SLICE)
|
||||
memcpy(&hdr, sec, sizeof(hdr));
|
||||
if (bcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) != 0 ||
|
||||
hdr.hdr_lba_self != 1 || hdr.hdr_revision < 0x00010000 ||
|
||||
hdr.hdr_entsz < sizeof(*ent) || DEV_BSIZE % hdr.hdr_entsz != 0) {
|
||||
printf("Invalid GPT header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* XXX: CRC check? */
|
||||
|
||||
/*
|
||||
* If the partition isn't specified, then search for the first UFS
|
||||
* partition and hope it is /. Perhaps we should be using an OS
|
||||
* flag in the GPT entry to mark / partitions.
|
||||
*
|
||||
* If the partition is specified, then figure out the LBA for the
|
||||
* sector containing that partition index and load it.
|
||||
*/
|
||||
entries_per_sec = DEV_BSIZE / hdr.hdr_entsz;
|
||||
if (dsk.part == -1) {
|
||||
slba = hdr.hdr_lba_table;
|
||||
elba = slba + hdr.hdr_entries / entries_per_sec;
|
||||
while (slba < elba && dsk.part == -1) {
|
||||
if (drvread(sec, slba, 1))
|
||||
return -1;
|
||||
for (part = 0; part < entries_per_sec; part++) {
|
||||
ent = (struct gpt_ent *)(sec + part * hdr.hdr_entsz);
|
||||
if (bcmp(&ent->ent_type, &freebsd_ufs_uuid,
|
||||
sizeof(uuid_t)) == 0) {
|
||||
dsk.part = (slba - hdr.hdr_lba_table) *
|
||||
entries_per_sec + part + 1;
|
||||
dsk.start = ent->ent_lba_start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dsk.slice == WHOLE_DISK_SLICE)
|
||||
dsk.slice = sl;
|
||||
}
|
||||
if (sl != WHOLE_DISK_SLICE) {
|
||||
if (sl != COMPATIBILITY_SLICE)
|
||||
dp += sl - BASE_SLICE;
|
||||
if (dp->dp_typ != DOSPTYP_386BSD) {
|
||||
printf("Invalid %s\n", "slice");
|
||||
return -1;
|
||||
slba++;
|
||||
}
|
||||
dsk.start = dp->dp_start;
|
||||
}
|
||||
if (drvread(sec, dsk.start + LABELSECTOR, 1))
|
||||
return -1;
|
||||
d = (void *)(sec + LABELOFFSET);
|
||||
if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
|
||||
if (dsk.part != RAW_PART) {
|
||||
printf("Invalid %s\n", "label");
|
||||
if (dsk.part == -1) {
|
||||
printf("No UFS partition was found\n");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (!dsk.init) {
|
||||
if (d->d_type == DTYPE_SCSI)
|
||||
dsk.type = TYPE_DA;
|
||||
dsk.init++;
|
||||
}
|
||||
if (dsk.part >= d->d_npartitions ||
|
||||
!d->d_partitions[dsk.part].p_size) {
|
||||
printf("Invalid %s\n", "partition");
|
||||
if (dsk.part > hdr.hdr_entries) {
|
||||
printf("Invalid partition index\n");
|
||||
return -1;
|
||||
}
|
||||
dsk.start += d->d_partitions[dsk.part].p_offset;
|
||||
dsk.start -= d->d_partitions[RAW_PART].p_offset;
|
||||
slba = hdr.hdr_lba_table + (dsk.part - 1) / entries_per_sec;
|
||||
if (drvread(sec, slba, 1))
|
||||
return -1;
|
||||
part = (dsk.part - 1) % entries_per_sec;
|
||||
ent = (struct gpt_ent *)(sec + part * hdr.hdr_entsz);
|
||||
if (bcmp(&ent->ent_type, &freebsd_ufs_uuid, sizeof(uuid_t)) != 0) {
|
||||
printf("Specified partition is not UFS\n");
|
||||
return -1;
|
||||
}
|
||||
dsk.start = ent->ent_lba_start;
|
||||
}
|
||||
/*
|
||||
* XXX: No way to detect SCSI vs. ATA currently.
|
||||
*/
|
||||
#if 0
|
||||
if (!dsk.init) {
|
||||
if (d->d_type == DTYPE_SCSI)
|
||||
dsk.type = TYPE_DA;
|
||||
dsk.init++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return drvread(buf, dsk.start + lba, nblk);
|
||||
}
|
||||
@ -606,21 +627,46 @@ putchar(int c)
|
||||
}
|
||||
|
||||
static int
|
||||
drvread(void *buf, unsigned lba, unsigned nblk)
|
||||
bcmp(const void *b1, const void *b2, size_t length)
|
||||
{
|
||||
const char *p1 = b1, *p2 = b2;
|
||||
|
||||
if (length == 0)
|
||||
return (0);
|
||||
do {
|
||||
if (*p1++ != *p2++)
|
||||
break;
|
||||
} while (--length);
|
||||
return (length);
|
||||
}
|
||||
|
||||
static struct {
|
||||
uint16_t len;
|
||||
uint16_t count;
|
||||
uint16_t seg;
|
||||
uint16_t off;
|
||||
uint64_t lba;
|
||||
} packet;
|
||||
|
||||
static int
|
||||
drvread(void *buf, daddr_t lba, unsigned nblk)
|
||||
{
|
||||
static unsigned c = 0x2d5c7c2f;
|
||||
|
||||
if (!OPT_CHECK(RBX_QUIET))
|
||||
printf("%c\b", c = c << 8 | c >> 24);
|
||||
v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
|
||||
v86.addr = XREADORG; /* call to xread in boot1 */
|
||||
v86.es = VTOPSEG(buf);
|
||||
v86.eax = lba;
|
||||
v86.ebx = VTOPOFF(buf);
|
||||
v86.ecx = lba >> 16;
|
||||
v86.edx = nblk << 8 | dsk.drive;
|
||||
v86int();
|
||||
packet.len = 0x10;
|
||||
packet.count = nblk;
|
||||
packet.seg = VTOPOFF(buf);
|
||||
packet.off = VTOPSEG(buf);
|
||||
packet.lba = lba;
|
||||
v86.ctl = V86_FLAGS;
|
||||
v86.addr = 0x13;
|
||||
v86.eax = 0x4200;
|
||||
v86.edx = dsk.drive;
|
||||
v86.ds = VTOPSEG(&packet);
|
||||
v86.esi = VTOPOFF(&packet);
|
||||
v86int();
|
||||
if (V86_CY(v86.efl)) {
|
||||
printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
|
||||
return -1;
|
||||
|
123
sys/boot/i386/gptboot/gptldr.S
Normal file
123
sys/boot/i386/gptboot/gptldr.S
Normal file
@ -0,0 +1,123 @@
|
||||
/*-
|
||||
* Copyright (c) 2007 Yahoo!, Inc.
|
||||
* All rights reserved.
|
||||
* Written by: John Baldwin <jhb@FreeBSD.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the author nor the names of any co-contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/* Memory Locations */
|
||||
.set MEM_REL,0x700 # Relocation address
|
||||
.set MEM_ARG,0x900 # Arguments
|
||||
.set MEM_ORG,0x7c00 # Origin
|
||||
.set MEM_BUF,0x8cec # Load area
|
||||
.set MEM_BTX,0x9000 # BTX start
|
||||
.set MEM_JMP,0x9010 # BTX entry point
|
||||
.set MEM_USR,0xa000 # Client start
|
||||
.set BDA_BOOT,0x472 # Boot howto flag
|
||||
|
||||
/* Misc. Constants */
|
||||
.set SIZ_PAG,0x1000 # Page size
|
||||
.set SIZ_SEC,0x200 # Sector size
|
||||
|
||||
.globl start
|
||||
.code16
|
||||
|
||||
/*
|
||||
* Copy BTX and boot2 to the right locations and start it all up.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Setup the segment registers to flat addressing (segment 0) and setup the
|
||||
* stack to end just below the start of our code.
|
||||
*/
|
||||
start: xor %cx,%cx # Zero
|
||||
mov %cx,%es # Address
|
||||
mov %cx,%ds # data
|
||||
mov %cx,%ss # Set up
|
||||
mov $start,%sp # stack
|
||||
|
||||
/*
|
||||
* BTX is right after us at 'end'. We read the length of BTX out of
|
||||
* its header to find boot2. We need to copy boot2 to MEM_USR and BTX
|
||||
* to MEM_BTX. Since those might overlap, we have to copy boot2
|
||||
* backwards first and then copy BTX. We aren't sure exactly how long
|
||||
* boot2 is, but we assume it can't be longer than 64k, so we just always
|
||||
* copy 64k.
|
||||
*/
|
||||
mov $end,%bx # BTX
|
||||
mov 0xa(%bx),%si # Get BTX length and set
|
||||
add %bx,%si # %si to start of boot2
|
||||
mov %si,%ax # Align %ds:%si on a
|
||||
shr $4,%ax # paragraph boundary
|
||||
and $0xf,%si # with the smallest
|
||||
mov %ax,%ds # possible %si
|
||||
add $(64 * 1024 - 16),%si
|
||||
mov $MEM_USR/16,%ax # Point %es:%di at end of
|
||||
mov $(64 * 1024 - 16),%di # largest boot2 range
|
||||
mov %ax,%es
|
||||
std
|
||||
mov %di,%cx # Copy 64k - paragraph + 1
|
||||
inc %cx # bytes
|
||||
rep movsb
|
||||
mov %cx,%ds # Reset %ds and %es
|
||||
mov %cx,%es
|
||||
mov 0xa(%bx),%cx # Get BTX length and set
|
||||
mov %bx,%si # %si to end of BTX
|
||||
mov $MEM_BTX,%di # %di -> end of BTX at
|
||||
add %cx,%si # MEM_BTX
|
||||
add %cx,%di
|
||||
dec %si
|
||||
dec %di
|
||||
rep movsb # Move BTX
|
||||
cld # String ops inc
|
||||
/*
|
||||
* Enable A20 so we can access memory above 1 meg.
|
||||
* Use the zero-valued %cx as a timeout for embedded hardware which do not
|
||||
* have a keyboard controller.
|
||||
*/
|
||||
seta20: cli # Disable interrupts
|
||||
seta20.1: dec %cx # Timeout?
|
||||
jz seta20.3 # Yes
|
||||
inb $0x64,%al # Get status
|
||||
testb $0x2,%al # Busy?
|
||||
jnz seta20.1 # Yes
|
||||
movb $0xd1,%al # Command: Write
|
||||
outb %al,$0x64 # output port
|
||||
seta20.2: inb $0x64,%al # Get status
|
||||
testb $0x2,%al # Busy?
|
||||
jnz seta20.2 # Yes
|
||||
movb $0xdf,%al # Enable
|
||||
outb %al,$0x60 # A20
|
||||
seta20.3: sti # Enable interrupts
|
||||
|
||||
/*
|
||||
* Save drive number from BIOS so boot2 can see it and start BTX.
|
||||
*/
|
||||
movb %dl,MEM_ARG
|
||||
jmp MEM_JMP # Start BTX
|
||||
end:
|
@ -41,9 +41,11 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/disklabel.h>
|
||||
#include <sys/diskmbr.h>
|
||||
#include <sys/gpt.h>
|
||||
#include <machine/bootinfo.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <uuid.h>
|
||||
|
||||
#include <bootstrap.h>
|
||||
#include <btxv86.h>
|
||||
@ -66,6 +68,13 @@ __FBSDID("$FreeBSD$");
|
||||
# define DEBUG(fmt, args...)
|
||||
#endif
|
||||
|
||||
struct gpt_part {
|
||||
int gp_index;
|
||||
uuid_t gp_type;
|
||||
uint64_t gp_start;
|
||||
uint64_t gp_end;
|
||||
};
|
||||
|
||||
struct open_disk {
|
||||
int od_dkunit; /* disk unit number */
|
||||
int od_unit; /* BIOS unit number */
|
||||
@ -81,11 +90,26 @@ struct open_disk {
|
||||
#define BD_FLOPPY 0x0004
|
||||
#define BD_LABELOK 0x0008
|
||||
#define BD_PARTTABOK 0x0010
|
||||
struct disklabel od_disklabel;
|
||||
int od_nslices; /* slice count */
|
||||
struct dos_partition od_slicetab[NEXTDOSPART];
|
||||
#define BD_GPTOK 0x0020
|
||||
union {
|
||||
struct {
|
||||
struct disklabel mbr_disklabel;
|
||||
int mbr_nslices; /* slice count */
|
||||
struct dos_partition mbr_slicetab[NEXTDOSPART];
|
||||
} _mbr;
|
||||
struct {
|
||||
int gpt_nparts;
|
||||
struct gpt_part *gpt_partitions;
|
||||
} _gpt;
|
||||
} _data;
|
||||
};
|
||||
|
||||
#define od_disklabel _data._mbr.mbr_disklabel
|
||||
#define od_nslices _data._mbr.mbr_nslices
|
||||
#define od_slicetab _data._mbr.mbr_slicetab
|
||||
#define od_nparts _data._gpt.gpt_nparts
|
||||
#define od_partitions _data._gpt.gpt_partitions
|
||||
|
||||
/*
|
||||
* List of BIOS devices, translation from disk unit number to
|
||||
* BIOS unit number.
|
||||
@ -106,6 +130,8 @@ static int bd_write(struct open_disk *od, daddr_t dblk, int blks,
|
||||
|
||||
static int bd_int13probe(struct bdinfo *bd);
|
||||
|
||||
static void bd_printgptpart(struct open_disk *od, struct gpt_part *gp,
|
||||
char *prefix, int verbose);
|
||||
static void bd_printslice(struct open_disk *od, struct dos_partition *dp,
|
||||
char *prefix, int verbose);
|
||||
static void bd_printbsdslice(struct open_disk *od, daddr_t offset,
|
||||
@ -134,8 +160,11 @@ struct devsw biosdisk = {
|
||||
|
||||
static int bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev);
|
||||
static void bd_closedisk(struct open_disk *od);
|
||||
static int bd_open_mbr(struct open_disk *od, struct i386_devdesc *dev);
|
||||
static int bd_bestslice(struct open_disk *od);
|
||||
static void bd_checkextended(struct open_disk *od, int slicenum);
|
||||
static int bd_open_gpt(struct open_disk *od, struct i386_devdesc *dev);
|
||||
static struct gpt_part *bd_best_gptpart(struct open_disk *od);
|
||||
|
||||
/*
|
||||
* Translate between BIOS device numbers and our private unit numbers.
|
||||
@ -257,8 +286,16 @@ bd_print(int verbose)
|
||||
|
||||
if (!bd_opendisk(&od, &dev)) {
|
||||
|
||||
/* Do we have a GPT table? */
|
||||
if (od->od_flags & BD_GPTOK) {
|
||||
for (j = 0; j < od->od_nparts; j++) {
|
||||
sprintf(line, " disk%dp%d", i,
|
||||
od->od_partitions[j].gp_index);
|
||||
bd_printgptpart(od, &od->od_partitions[j], line, verbose);
|
||||
}
|
||||
|
||||
/* Do we have a partition table? */
|
||||
if (od->od_flags & BD_PARTTABOK) {
|
||||
} else if (od->od_flags & BD_PARTTABOK) {
|
||||
dptr = &od->od_slicetab[0];
|
||||
|
||||
/* Check for a "dedicated" disk */
|
||||
@ -279,6 +316,59 @@ bd_print(int verbose)
|
||||
}
|
||||
}
|
||||
|
||||
static uuid_t efi = GPT_ENT_TYPE_EFI;
|
||||
static uuid_t freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
|
||||
static uuid_t freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
|
||||
static uuid_t freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
|
||||
static uuid_t freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
|
||||
static uuid_t ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
|
||||
|
||||
static void
|
||||
bd_printgptpart(struct open_disk *od, struct gpt_part *gp, char *prefix,
|
||||
int verbose)
|
||||
{
|
||||
char stats[80];
|
||||
char line[96];
|
||||
uint64_t size;
|
||||
char unit;
|
||||
|
||||
if (verbose) {
|
||||
size = (gp->gp_end + 1 - gp->gp_start) / 2048;
|
||||
unit = 'M';
|
||||
if (size >= 10240000) {
|
||||
size /= 1048576;
|
||||
unit = 'T';
|
||||
} else if (size >= 10000) {
|
||||
size /= 1024;
|
||||
unit = 'G';
|
||||
}
|
||||
sprintf(stats, " %.6ld%cB", (long)size, unit);
|
||||
} else
|
||||
stats[0] = '\0';
|
||||
|
||||
if (uuid_equal(&gp->gp_type, &efi, NULL))
|
||||
sprintf(line, "%s: EFI%s\n", prefix, stats);
|
||||
else if (uuid_equal(&gp->gp_type, &ms_basic_data, NULL))
|
||||
sprintf(line, "%s: FAT/NTFS%s\n", prefix, stats);
|
||||
else if (uuid_equal(&gp->gp_type, &freebsd_boot, NULL))
|
||||
sprintf(line, "%s: FreeBSD boot%s\n", prefix, stats);
|
||||
else if (uuid_equal(&gp->gp_type, &freebsd_ufs, NULL))
|
||||
sprintf(line, "%s: FreeBSD UFS%s\n", prefix, stats);
|
||||
else if (uuid_equal(&gp->gp_type, &freebsd_zfs, NULL))
|
||||
sprintf(line, "%s: FreeBSD ZFS%s\n", prefix, stats);
|
||||
else if (uuid_equal(&gp->gp_type, &freebsd_swap, NULL))
|
||||
sprintf(line, "%s: FreeBSD swap%s\n", prefix, stats);
|
||||
else
|
||||
sprintf(line, "%s: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x%s\n",
|
||||
gp->gp_type.time_low, gp->gp_type.time_mid,
|
||||
gp->gp_type.time_hi_and_version,
|
||||
gp->gp_type.clock_seq_hi_and_reserved, gp->gp_type.clock_seq_low,
|
||||
gp->gp_type.node[0], gp->gp_type.node[1], gp->gp_type.node[2],
|
||||
gp->gp_type.node[3], gp->gp_type.node[4], gp->gp_type.node[5],
|
||||
stats);
|
||||
pager_output(line);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print information about slices on a disk. For the size calculations we
|
||||
* assume a 512 byte sector.
|
||||
@ -447,12 +537,8 @@ bd_open(struct open_file *f, ...)
|
||||
static int
|
||||
bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
|
||||
{
|
||||
struct dos_partition *dptr;
|
||||
struct disklabel *lp;
|
||||
struct open_disk *od;
|
||||
int sector, slice, i;
|
||||
int error;
|
||||
char buf[BUFSIZE];
|
||||
|
||||
if (dev->d_unit >= nbdinfo) {
|
||||
DEBUG("attempt to open nonexistent disk");
|
||||
@ -470,11 +556,10 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
|
||||
od->od_unit = bdinfo[od->od_dkunit].bd_unit;
|
||||
od->od_flags = bdinfo[od->od_dkunit].bd_flags;
|
||||
od->od_boff = 0;
|
||||
od->od_nslices = 0;
|
||||
error = 0;
|
||||
DEBUG("open '%s', unit 0x%x slice %d partition %c",
|
||||
DEBUG("open '%s', unit 0x%x slice %d partition %d",
|
||||
i386_fmtdev(dev), dev->d_unit,
|
||||
dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition + 'a');
|
||||
dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition);
|
||||
|
||||
/* Get geometry for this open (removable device may have changed) */
|
||||
if (bd_getgeom(od)) {
|
||||
@ -483,6 +568,29 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Determine disk layout. */
|
||||
error = bd_open_gpt(od, dev);
|
||||
if (error)
|
||||
error = bd_open_mbr(od, dev);
|
||||
|
||||
out:
|
||||
if (error) {
|
||||
free(od);
|
||||
} else {
|
||||
*odp = od; /* return the open disk */
|
||||
}
|
||||
return(error);
|
||||
}
|
||||
|
||||
static int
|
||||
bd_open_mbr(struct open_disk *od, struct i386_devdesc *dev)
|
||||
{
|
||||
struct dos_partition *dptr;
|
||||
struct disklabel *lp;
|
||||
int sector, slice, i;
|
||||
int error;
|
||||
char buf[BUFSIZE];
|
||||
|
||||
/*
|
||||
* Following calculations attempt to determine the correct value
|
||||
* for d->od_boff by looking for the slice and partition specified,
|
||||
@ -492,10 +600,10 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
|
||||
/*
|
||||
* Find the slice in the DOS slice table.
|
||||
*/
|
||||
od->od_nslices = 0;
|
||||
if (bd_read(od, 0, 1, buf)) {
|
||||
DEBUG("error reading MBR");
|
||||
error = EIO;
|
||||
goto out;
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -505,8 +613,7 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
|
||||
/* If a slice number was explicitly supplied, this is an error */
|
||||
if (dev->d_kind.biosdisk.slice > 0) {
|
||||
DEBUG("no slice table/MBR (no magic)");
|
||||
error = ENOENT;
|
||||
goto out;
|
||||
return (ENOENT);
|
||||
}
|
||||
sector = 0;
|
||||
goto unsliced; /* may be a floppy */
|
||||
@ -536,8 +643,7 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
|
||||
slice = dev->d_kind.biosdisk.slice - 1;
|
||||
if (slice >= od->od_nslices) {
|
||||
DEBUG("slice %d not found", slice);
|
||||
error = ENOENT;
|
||||
goto out;
|
||||
return (ENOENT);
|
||||
}
|
||||
}
|
||||
|
||||
@ -555,8 +661,7 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
|
||||
if (dev->d_kind.biosdisk.slice == 0) {
|
||||
slice = bd_bestslice(od);
|
||||
if (slice == -1) {
|
||||
error = ENOENT;
|
||||
goto out;
|
||||
return (ENOENT);
|
||||
}
|
||||
dev->d_kind.biosdisk.slice = slice;
|
||||
}
|
||||
@ -590,8 +695,7 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
|
||||
|
||||
if (bd_read(od, sector + LABELSECTOR, 1, buf)) {
|
||||
DEBUG("error reading disklabel");
|
||||
error = EIO;
|
||||
goto out;
|
||||
return (EIO);
|
||||
}
|
||||
DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel);
|
||||
bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel));
|
||||
@ -600,15 +704,12 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
|
||||
|
||||
if (lp->d_magic != DISKMAGIC) {
|
||||
DEBUG("no disklabel");
|
||||
error = ENOENT;
|
||||
goto out;
|
||||
return (ENOENT);
|
||||
}
|
||||
if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) {
|
||||
DEBUG("partition '%c' exceeds partitions in table (a-'%c')",
|
||||
'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions);
|
||||
error = EPART;
|
||||
goto out;
|
||||
|
||||
return (EPART);
|
||||
}
|
||||
|
||||
#ifdef DISK_DEBUG
|
||||
@ -623,14 +724,7 @@ bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
|
||||
lp->d_partitions[RAW_PART].p_offset +
|
||||
sector;
|
||||
}
|
||||
|
||||
out:
|
||||
if (error) {
|
||||
free(od);
|
||||
} else {
|
||||
*odp = od; /* return the open disk */
|
||||
}
|
||||
return(error);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -739,7 +833,183 @@ bd_bestslice(struct open_disk *od)
|
||||
}
|
||||
return (prefslice);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
bd_open_gpt(struct open_disk *od, struct i386_devdesc *dev)
|
||||
{
|
||||
struct dos_partition *dp;
|
||||
struct gpt_hdr *hdr;
|
||||
struct gpt_ent *ent;
|
||||
struct gpt_part *gp;
|
||||
int entries_per_sec, error, i, part;
|
||||
daddr_t lba, elba;
|
||||
char gpt[BIOSDISK_SECSIZE], tbl[BIOSDISK_SECSIZE];
|
||||
|
||||
/*
|
||||
* Following calculations attempt to determine the correct value
|
||||
* for d->od_boff by looking for the slice and partition specified,
|
||||
* or searching for reasonable defaults.
|
||||
*/
|
||||
error = 0;
|
||||
|
||||
/* First, read the MBR and see if we have a PMBR. */
|
||||
if (bd_read(od, 0, 1, tbl)) {
|
||||
DEBUG("error reading MBR");
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
/* Check the slice table magic. */
|
||||
if (((u_char)tbl[0x1fe] != 0x55) || ((u_char)tbl[0x1ff] != 0xaa))
|
||||
return (ENXIO);
|
||||
|
||||
/* Check for GPT slice. */
|
||||
part = 0;
|
||||
dp = (struct dos_partition *)(tbl + DOSPARTOFF);
|
||||
for (i = 0; i < NDOSPART; i++) {
|
||||
if (dp[i].dp_typ == 0xee)
|
||||
part++;
|
||||
else if (dp[i].dp_typ != 0x00)
|
||||
return (EINVAL);
|
||||
}
|
||||
if (part != 1)
|
||||
return (EINVAL);
|
||||
|
||||
/* Read primary GPT table header. */
|
||||
if (bd_read(od, 1, 1, gpt)) {
|
||||
DEBUG("error reading GPT header");
|
||||
return (EIO);
|
||||
}
|
||||
hdr = (struct gpt_hdr *)gpt;
|
||||
if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0 ||
|
||||
hdr->hdr_lba_self != 1 || hdr->hdr_revision < 0x00010000 ||
|
||||
hdr->hdr_entsz < sizeof(*ent) ||
|
||||
BIOSDISK_SECSIZE % hdr->hdr_entsz != 0) {
|
||||
DEBUG("Invalid GPT header\n");
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
/* Now walk the partition table to count the number of valid partitions. */
|
||||
part = 0;
|
||||
entries_per_sec = BIOSDISK_SECSIZE / hdr->hdr_entsz;
|
||||
elba = hdr->hdr_lba_table + hdr->hdr_entries / entries_per_sec;
|
||||
for (lba = hdr->hdr_lba_table; lba < elba; lba++) {
|
||||
if (bd_read(od, lba, 1, tbl)) {
|
||||
DEBUG("error reading GPT table");
|
||||
return (EIO);
|
||||
}
|
||||
for (i = 0; i < entries_per_sec; i++) {
|
||||
ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz);
|
||||
if (uuid_is_nil(&ent->ent_type, NULL) || ent->ent_lba_start == 0 ||
|
||||
ent->ent_lba_end < ent->ent_lba_start)
|
||||
continue;
|
||||
part++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Save the important information about all the valid partitions. */
|
||||
od->od_nparts = part;
|
||||
if (part != 0) {
|
||||
od->od_partitions = malloc(part * sizeof(struct gpt_part));
|
||||
part = 0;
|
||||
for (lba = hdr->hdr_lba_table; lba < elba; lba++) {
|
||||
if (bd_read(od, lba, 1, tbl)) {
|
||||
DEBUG("error reading GPT table");
|
||||
error = EIO;
|
||||
goto out;
|
||||
}
|
||||
for (i = 0; i < entries_per_sec; i++) {
|
||||
ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz);
|
||||
if (uuid_is_nil(&ent->ent_type, NULL) ||
|
||||
ent->ent_lba_start == 0 ||
|
||||
ent->ent_lba_end < ent->ent_lba_start)
|
||||
continue;
|
||||
od->od_partitions[part].gp_index = (lba - hdr->hdr_lba_table) *
|
||||
entries_per_sec + i + 1;
|
||||
od->od_partitions[part].gp_type = ent->ent_type;
|
||||
od->od_partitions[part].gp_start = ent->ent_lba_start;
|
||||
od->od_partitions[part].gp_end = ent->ent_lba_end;
|
||||
part++;
|
||||
}
|
||||
}
|
||||
}
|
||||
od->od_flags |= BD_GPTOK;
|
||||
|
||||
/* Is this a request for the whole disk? */
|
||||
if (dev->d_kind.biosdisk.slice < 0) {
|
||||
od->od_boff = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* If a partition number was supplied, then the user is trying to use
|
||||
* an MBR address rather than a GPT address, so fail.
|
||||
*/
|
||||
if (dev->d_kind.biosdisk.partition != 0xff) {
|
||||
error = ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If a slice number was supplied but not found, this is an error. */
|
||||
gp = NULL;
|
||||
if (dev->d_kind.biosdisk.slice > 0) {
|
||||
for (i = 0; i < od->od_nparts; i++) {
|
||||
if (od->od_partitions[i].gp_index == dev->d_kind.biosdisk.slice) {
|
||||
gp = &od->od_partitions[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (gp == NULL) {
|
||||
DEBUG("partition %d not found", dev->d_kind.biosdisk.slice);
|
||||
error = ENOENT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to auto-detect the best partition. */
|
||||
if (dev->d_kind.biosdisk.slice == 0) {
|
||||
gp = bd_best_gptpart(od);
|
||||
if (gp == NULL) {
|
||||
error = ENOENT;
|
||||
goto out;
|
||||
}
|
||||
dev->d_kind.biosdisk.slice = gp->gp_index;
|
||||
}
|
||||
od->od_boff = gp->gp_start;
|
||||
|
||||
out:
|
||||
if (error)
|
||||
free(od->od_partitions);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static struct gpt_part *
|
||||
bd_best_gptpart(struct open_disk *od)
|
||||
{
|
||||
struct gpt_part *gp, *prefpart;
|
||||
int i, pref, preflevel;
|
||||
|
||||
prefpart = NULL;
|
||||
preflevel = PREF_NONE;
|
||||
|
||||
gp = od->od_partitions;
|
||||
for (i = 0; i < od->od_nparts; i++, gp++) {
|
||||
/* Windows. XXX: Also Linux. */
|
||||
if (uuid_equal(&gp->gp_type, &ms_basic_data, NULL))
|
||||
pref = PREF_DOS;
|
||||
/* FreeBSD */
|
||||
else if (uuid_equal(&gp->gp_type, &freebsd_ufs, NULL) ||
|
||||
uuid_equal(&gp->gp_type, &freebsd_zfs, NULL))
|
||||
pref = PREF_FBSD;
|
||||
else
|
||||
pref = PREF_NONE;
|
||||
if (pref < preflevel) {
|
||||
preflevel = pref;
|
||||
prefpart = gp;
|
||||
}
|
||||
}
|
||||
return (prefpart);
|
||||
}
|
||||
|
||||
static int
|
||||
bd_close(struct open_file *f)
|
||||
{
|
||||
@ -758,6 +1028,8 @@ bd_closedisk(struct open_disk *od)
|
||||
if (od->od_flags & BD_FLOPPY)
|
||||
delay(3000000);
|
||||
#endif
|
||||
if (od->od_flags & BD_GPTOK)
|
||||
free(od->od_partitions);
|
||||
free(od);
|
||||
}
|
||||
|
||||
|
@ -120,21 +120,35 @@ i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path)
|
||||
err = EUNIT;
|
||||
goto fail;
|
||||
}
|
||||
if (*cp == 's') { /* got a slice number */
|
||||
if (*cp == 'p') { /* got a GPT partition */
|
||||
np = cp + 1;
|
||||
slice = strtol(np, &cp, 10);
|
||||
if (cp == np) {
|
||||
err = ESLICE;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (*cp && (*cp != ':')) {
|
||||
partition = *cp - 'a'; /* get a partition number */
|
||||
if ((partition < 0) || (partition >= MAXPARTITIONS)) {
|
||||
err = EPART;
|
||||
if (*cp && (*cp != ':')) {
|
||||
err = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
cp++;
|
||||
partition = 0xff;
|
||||
} else {
|
||||
if (*cp == 's') { /* got a slice number */
|
||||
np = cp + 1;
|
||||
slice = strtol(np, &cp, 10);
|
||||
if (cp == np) {
|
||||
err = ESLICE;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (*cp && (*cp != ':')) {
|
||||
partition = *cp - 'a'; /* got a partition number */
|
||||
if ((partition < 0) || (partition >= MAXPARTITIONS)) {
|
||||
err = EPART;
|
||||
goto fail;
|
||||
}
|
||||
cp++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (*cp && (*cp != ':')) {
|
||||
@ -208,10 +222,14 @@ i386_fmtdev(void *vdev)
|
||||
case DEVT_DISK:
|
||||
cp = buf;
|
||||
cp += sprintf(cp, "%s%d", dev->d_dev->dv_name, dev->d_unit);
|
||||
if (dev->d_kind.biosdisk.slice > 0)
|
||||
cp += sprintf(cp, "s%d", dev->d_kind.biosdisk.slice);
|
||||
if (dev->d_kind.biosdisk.partition >= 0)
|
||||
cp += sprintf(cp, "%c", dev->d_kind.biosdisk.partition + 'a');
|
||||
if (dev->d_kind.biosdisk.partition == 0xff) {
|
||||
cp += sprintf(cp, "p%d", dev->d_kind.biosdisk.slice);
|
||||
} else {
|
||||
if (dev->d_kind.biosdisk.slice > 0)
|
||||
cp += sprintf(cp, "s%d", dev->d_kind.biosdisk.slice);
|
||||
if (dev->d_kind.biosdisk.partition >= 0)
|
||||
cp += sprintf(cp, "%c", dev->d_kind.biosdisk.partition + 'a');
|
||||
}
|
||||
strcat(cp, ":");
|
||||
break;
|
||||
|
||||
@ -238,4 +256,3 @@ i386_setcurrdev(struct env_var *ev, int flags, const void *value)
|
||||
env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
14
sys/boot/i386/pmbr/Makefile
Normal file
14
sys/boot/i386/pmbr/Makefile
Normal file
@ -0,0 +1,14 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= pmbr
|
||||
STRIP=
|
||||
BINMODE=${NOBINMODE}
|
||||
NO_MAN=
|
||||
SRCS= ${PROG}.s
|
||||
|
||||
ORG= 0x600
|
||||
|
||||
AFLAGS+=--defsym FLAGS=${BOOT_MBR_FLAGS}
|
||||
LDFLAGS=-N -e start -Ttext ${ORG} -Wl,-S,--oformat,binary
|
||||
|
||||
.include <bsd.prog.mk>
|
221
sys/boot/i386/pmbr/pmbr.s
Normal file
221
sys/boot/i386/pmbr/pmbr.s
Normal file
@ -0,0 +1,221 @@
|
||||
#-
|
||||
# Copyright (c) 2007 Yahoo!, Inc.
|
||||
# All rights reserved.
|
||||
# Written by: John Baldwin <jhb@FreeBSD.org>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the author nor the names of any co-contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# $FreeBSD$
|
||||
|
||||
# A 512 byte PMBR boot manager that looks for a FreeBSD boot GPT partition
|
||||
# and boots it.
|
||||
|
||||
.set LOAD,0x7c00 # Load address
|
||||
.set EXEC,0x600 # Execution address
|
||||
.set MAGIC,0xaa55 # Magic: bootable
|
||||
.set SECSIZE,0x200 # Size of a single disk sector
|
||||
.set DISKSIG,440 # Disk signature offset
|
||||
.set STACK,EXEC+SECSIZE*4 # Stack address
|
||||
.set GPT_ADDR,STACK # GPT header address
|
||||
.set GPT_SIG,0
|
||||
.set GPT_SIG_0,0x20494645
|
||||
.set GPT_SIG_1,0x54524150
|
||||
.set GPT_MYLBA,24
|
||||
.set GPT_PART_LBA,72
|
||||
.set GPT_NPART,80
|
||||
.set GPT_PART_SIZE,84
|
||||
.set PART_ADDR,GPT_ADDR+SECSIZE # GPT partition array address
|
||||
.set PART_TYPE,0
|
||||
.set PART_START_LBA,32
|
||||
.set PART_END_LBA,40
|
||||
|
||||
.set NHRDRV,0x475 # Number of hard drives
|
||||
|
||||
.globl start # Entry point
|
||||
.code16
|
||||
|
||||
#
|
||||
# Setup the segment registers for flat addressing and setup the stack.
|
||||
#
|
||||
start: cld # String ops inc
|
||||
xorw %ax,%ax # Zero
|
||||
movw %ax,%es # Address
|
||||
movw %ax,%ds # data
|
||||
movw %ax,%ss # Set up
|
||||
movw $STACK,%sp # stack
|
||||
#
|
||||
# Relocate ourself to a lower address so that we have more room to load
|
||||
# other sectors.
|
||||
#
|
||||
movw $main-EXEC+LOAD,%si # Source
|
||||
movw $main,%di # Destination
|
||||
movw $SECSIZE-(main-start),%cx # Byte count
|
||||
rep # Relocate
|
||||
movsb # code
|
||||
#
|
||||
# Jump to the relocated code.
|
||||
#
|
||||
jmp main-LOAD+EXEC # To relocated code
|
||||
#
|
||||
# Validate drive number in %dl.
|
||||
#
|
||||
main: cmpb $0x80,%dl # Drive valid?
|
||||
jb main.1 # No
|
||||
movb NHRDRV,%dh # Calculate the highest
|
||||
addb $0x80,%dh # drive number available
|
||||
cmpb %dh,%dl # Within range?
|
||||
jb main.2 # Yes
|
||||
main.1: movb $0x80,%dl # Assume drive 0x80
|
||||
#
|
||||
# Load the primary GPT header from LBA 1 and verify signature.
|
||||
#
|
||||
main.2: movw $GPT_ADDR,%bx
|
||||
movw $lba,%si
|
||||
call read
|
||||
cmpl $GPT_SIG_0,GPT_ADDR+GPT_SIG
|
||||
jnz err_pt
|
||||
cmpl $GPT_SIG_1,GPT_ADDR+GPT_SIG+4
|
||||
jnz err_pt
|
||||
#
|
||||
# Load a partition table sector from disk and look for a FreeBSD boot
|
||||
# partition.
|
||||
#
|
||||
load_part: movw $GPT_ADDR+GPT_PART_LBA,%si
|
||||
movw $PART_ADDR,%bx
|
||||
call read
|
||||
scan: movw %bx,%si # Compare partition UUID
|
||||
movw $boot_uuid,%di # with FreeBSD boot UUID
|
||||
movb $0x10,%cl
|
||||
repe cmpsb
|
||||
jnz next_part # Didn't match, next partition
|
||||
#
|
||||
# We found a boot partition. Load it into RAM starting at 0x7c00.
|
||||
#
|
||||
movw %bx,%di # Save partition pointer in %di
|
||||
leaw PART_START_LBA(%di),%si
|
||||
movw $LOAD/16,%bx
|
||||
movw %bx,%es
|
||||
xorw %bx,%bx
|
||||
load_boot: push %si # Save %si
|
||||
call read
|
||||
pop %si # Restore
|
||||
movl PART_END_LBA(%di),%eax # See if this was the last LBA
|
||||
cmpl (%si),%eax
|
||||
jnz next_boot
|
||||
movl PART_END_LBA+4(%di),%eax
|
||||
cmpl 4(%si),%eax
|
||||
jnz next_boot
|
||||
mov %bx,%es # Reset %es to zero
|
||||
jmp LOAD # Jump to boot code
|
||||
next_boot: incl (%si) # Next LBA
|
||||
adcl $0,4(%si)
|
||||
mov %es,%ax # Adjust segment for next
|
||||
addw $SECSIZE/16,%ax # sector
|
||||
cmp $0x9000,%ax # Don't load past 0x90000,
|
||||
jae err_big # 545k should be enough for
|
||||
mov %ax,%es # any boot code. :)
|
||||
jmp load_boot
|
||||
#
|
||||
# Move to the next partition. If we walk off the end of the sector, load
|
||||
# the next sector. We assume that partition entries are smaller than 64k
|
||||
# and that they won't span a sector boundary.
|
||||
#
|
||||
# XXX: Should we int 0x18 instead of err_noboot if we hit the end of the table?
|
||||
#
|
||||
next_part: decl GPT_ADDR+GPT_NPART # Was this the last partition?
|
||||
jz err_noboot
|
||||
movw GPT_ADDR+GPT_PART_SIZE,%ax
|
||||
addw %ax,%bx # Next partition
|
||||
cmpw $PART_ADDR+0x200,%bx # Still in sector?
|
||||
jb scan
|
||||
incl GPT_ADDR+GPT_PART_LBA # Next sector
|
||||
adcl $0,GPT_ADDR+GPT_PART_LBA+4
|
||||
jmp load_part
|
||||
#
|
||||
# Load a sector (64-bit LBA at %si) from disk %dl into %es:%bx by creating
|
||||
# a EDD packet on the stack and passing it to the BIOS. Trashes %ax and %si.
|
||||
#
|
||||
read: pushl 0x4(%si) # Set the LBA
|
||||
pushl 0x0(%si) # address
|
||||
pushw %es # Set the address of
|
||||
pushw %bx # the transfer buffer
|
||||
pushw $0x1 # Read 1 sector
|
||||
pushw $0x10 # Packet length
|
||||
movw %sp,%si # Packer pointer
|
||||
movw $0x4200,%ax # BIOS: LBA Read from disk
|
||||
int $0x13 # Call the BIOS
|
||||
add $0x10,%sp # Restore stack
|
||||
jc err_rd # If error
|
||||
ret
|
||||
#
|
||||
# Various error message entry points.
|
||||
#
|
||||
err_big: movw $msg_big,%si # "Boot loader too
|
||||
jmp putstr # large"
|
||||
|
||||
err_pt: movw $msg_pt,%si # "Invalid partition
|
||||
jmp putstr # table"
|
||||
|
||||
err_rd: movw $msg_rd,%si # "I/O error loading
|
||||
jmp putstr # boot loader"
|
||||
|
||||
err_noboot: movw $msg_noboot,%si # "Missing boot
|
||||
jmp putstr # loader"
|
||||
#
|
||||
# Output an ASCIZ string to the console via the BIOS.
|
||||
#
|
||||
putstr.0: movw $0x7,%bx # Page:attribute
|
||||
movb $0xe,%ah # BIOS: Display
|
||||
int $0x10 # character
|
||||
putstr: lodsb # Get character
|
||||
testb %al,%al # End of string?
|
||||
jnz putstr.0 # No
|
||||
putstr.1: jmp putstr.1 # Await reset
|
||||
|
||||
msg_big: .asciz "Boot loader too large"
|
||||
msg_pt: .asciz "Invalid partition table"
|
||||
msg_rd: .asciz "I/O error loading boot loader"
|
||||
msg_noboot: .asciz "Missing boot loader"
|
||||
|
||||
lba: .quad 1 # LBA of GPT header
|
||||
|
||||
boot_uuid: .long 0x83bd6b9d
|
||||
.word 0x7f41
|
||||
.word 0x11dc
|
||||
.byte 0xbe
|
||||
.byte 0x0b
|
||||
.byte 0x00
|
||||
.byte 0x15
|
||||
.byte 0x60
|
||||
.byte 0xb8
|
||||
.byte 0x4f
|
||||
.byte 0x0f
|
||||
|
||||
.org DISKSIG,0x90
|
||||
sig: .long 0 # OS Disk Signature
|
||||
.word 0 # "Unknown" in PMBR
|
||||
|
||||
partbl: .fill 0x10,0x4,0x0 # Partition table
|
||||
.word MAGIC # Magic number
|
@ -66,6 +66,7 @@ struct g_part_alias_list {
|
||||
} g_part_alias_list[G_PART_ALIAS_COUNT] = {
|
||||
{ "efi", G_PART_ALIAS_EFI },
|
||||
{ "freebsd", G_PART_ALIAS_FREEBSD },
|
||||
{ "freebsd-boot", G_PART_ALIAS_FREEBSD_BOOT },
|
||||
{ "freebsd-swap", G_PART_ALIAS_FREEBSD_SWAP },
|
||||
{ "freebsd-ufs", G_PART_ALIAS_FREEBSD_UFS },
|
||||
{ "freebsd-vinum", G_PART_ALIAS_FREEBSD_VINUM },
|
||||
|
@ -38,6 +38,7 @@
|
||||
enum g_part_alias {
|
||||
G_PART_ALIAS_EFI, /* A EFI system partition entry. */
|
||||
G_PART_ALIAS_FREEBSD, /* A BSD labeled partition entry. */
|
||||
G_PART_ALIAS_FREEBSD_BOOT, /* A FreeBSD boot partition entry. */
|
||||
G_PART_ALIAS_FREEBSD_SWAP, /* A swap partition entry. */
|
||||
G_PART_ALIAS_FREEBSD_UFS, /* A UFS/UFS2 file system entry. */
|
||||
G_PART_ALIAS_FREEBSD_VINUM, /* A Vinum partition entry. */
|
||||
|
@ -121,6 +121,7 @@ G_PART_SCHEME_DECLARE(g_part_gpt_scheme);
|
||||
|
||||
static struct uuid gpt_uuid_efi = GPT_ENT_TYPE_EFI;
|
||||
static struct uuid gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD;
|
||||
static struct uuid gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
|
||||
static struct uuid gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
|
||||
static struct uuid gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
|
||||
static struct uuid gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
|
||||
@ -295,6 +296,11 @@ gpt_parse_type(const char *type, struct uuid *uuid)
|
||||
*uuid = gpt_uuid_freebsd;
|
||||
return (0);
|
||||
}
|
||||
alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_BOOT);
|
||||
if (!strcasecmp(type, alias)) {
|
||||
*uuid = gpt_uuid_freebsd_boot;
|
||||
return (0);
|
||||
}
|
||||
alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP);
|
||||
if (!strcasecmp(type, alias)) {
|
||||
*uuid = gpt_uuid_freebsd_swap;
|
||||
@ -600,6 +606,8 @@ g_part_gpt_type(struct g_part_table *basetable, struct g_part_entry *baseentry,
|
||||
return (g_part_alias_name(G_PART_ALIAS_EFI));
|
||||
if (EQUUID(type, &gpt_uuid_freebsd))
|
||||
return (g_part_alias_name(G_PART_ALIAS_FREEBSD));
|
||||
if (EQUUID(type, &gpt_uuid_freebsd_boot))
|
||||
return (g_part_alias_name(G_PART_ALIAS_FREEBSD_BOOT));
|
||||
if (EQUUID(type, &gpt_uuid_freebsd_swap))
|
||||
return (g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP));
|
||||
if (EQUUID(type, &gpt_uuid_freebsd_ufs))
|
||||
|
@ -84,6 +84,8 @@ struct gpt_ent {
|
||||
{0x516e7cb8,0x6ecf,0x11d6,0x8f,0xf8,{0x00,0x02,0x2d,0x09,0x71,0x2b}}
|
||||
#define GPT_ENT_TYPE_FREEBSD_ZFS \
|
||||
{0x516e7cba,0x6ecf,0x11d6,0x8f,0xf8,{0x00,0x02,0x2d,0x09,0x71,0x2b}}
|
||||
#define GPT_ENT_TYPE_FREEBSD_BOOT \
|
||||
{0x83bd6b9d,0x7f41,0x11dc,0xbe,0x0b,{0x00,0x15,0x60,0xb8,0x4f,0x0f}}
|
||||
|
||||
/*
|
||||
* The following are unused but documented here to avoid reuse.
|
||||
@ -95,7 +97,7 @@ struct gpt_ent {
|
||||
/*
|
||||
* Foreign partition types that we're likely to encounter. Note that Linux
|
||||
* apparently choose to share data partitions with MS. I don't what the
|
||||
* advantage might be. I can see how sharing swap partitions is advantaous
|
||||
* advantage might be. I can see how sharing swap partitions is advantageous
|
||||
* though.
|
||||
*/
|
||||
#define GPT_ENT_TYPE_MS_RESERVED \
|
||||
|
Loading…
x
Reference in New Issue
Block a user