Move embedding routines to partmap sources files.

* grub-core/partmap/gpt.c (grub_gpt_partition_type_bios_boot)
	[GRUB_UTIL]: New variable.
	(gpt_partition_map_iterate): Set part.parent.
	(gpt_partition_map_embed) [GRUB_UTIL]: New function.
	(grub_gpt_partition_map) [GRUB_UTIL]: Set .embed.
	* grub-core/partmap/msdos.c (pc_partition_map_embed) [GRUB_UTIL]:
	New function.
	(grub_msdos_partition_map) [GRUB_UTIL]: Set .embed.
	* include/grub/partition.h (grub_embed_type_t) [GRUB_UTIL]: New type.
	(grub_partition_map) [GRUB_UTIL]: New field embed.
	* util/grub-setup.c (grub_gpt_partition_type_bios_boot): Removed.
	(setup): Use ->embed.
This commit is contained in:
Vladimir 'phcoder' Serbinenko 2010-09-15 21:36:57 +02:00
parent e9fc4da271
commit e50fca4a4c
5 changed files with 295 additions and 112 deletions

View File

@ -1,3 +1,20 @@
2010-09-15 Vladimir Serbinenko <phcoder@gmail.com>
Move embedding routines to partmap sources files.
* grub-core/partmap/gpt.c (grub_gpt_partition_type_bios_boot)
[GRUB_UTIL]: New variable.
(gpt_partition_map_iterate): Set part.parent.
(gpt_partition_map_embed) [GRUB_UTIL]: New function.
(grub_gpt_partition_map) [GRUB_UTIL]: Set .embed.
* grub-core/partmap/msdos.c (pc_partition_map_embed) [GRUB_UTIL]:
New function.
(grub_msdos_partition_map) [GRUB_UTIL]: Set .embed.
* include/grub/partition.h (grub_embed_type_t) [GRUB_UTIL]: New type.
(grub_partition_map) [GRUB_UTIL]: New field embed.
* util/grub-setup.c (grub_gpt_partition_type_bios_boot): Removed.
(setup): Use ->embed.
2010-09-15 Vladimir Serbinenko <phcoder@gmail.com>
* grub-core/kern/emu/hostdisk.c (grub_util_biosdisk_is_floppy): New

View File

@ -32,6 +32,10 @@ static grub_uint8_t grub_gpt_magic[8] =
static const grub_gpt_part_type_t grub_gpt_partition_type_empty = GRUB_GPT_PARTITION_TYPE_EMPTY;
#ifdef GRUB_UTIL
static const grub_gpt_part_type_t grub_gpt_partition_type_bios_boot = GRUB_GPT_PARTITION_TYPE_BIOS_BOOT;
#endif
/* 512 << 7 = 65536 byte sectors. */
#define MAX_SECTOR_LOG 7
@ -97,6 +101,7 @@ gpt_partition_map_iterate (grub_disk_t disk,
part.number = i;
part.index = last_offset;
part.partmap = &grub_gpt_partition_map;
part.parent = disk->partition;
grub_dprintf ("gpt", "GPT entry %d: start=%lld, length=%lld\n", i,
(unsigned long long) part.start,
@ -117,12 +122,73 @@ gpt_partition_map_iterate (grub_disk_t disk,
return GRUB_ERR_NONE;
}
#ifdef GRUB_UTIL
static grub_err_t
gpt_partition_map_embed (struct grub_disk *disk, unsigned int nsectors,
grub_embed_type_t embed_type,
grub_disk_addr_t *sectors)
{
grub_disk_addr_t start = 0, len = 0;
unsigned i;
grub_err_t err;
auto int NESTED_FUNC_ATTR find_usable_region (grub_disk_t disk,
const grub_partition_t p);
int NESTED_FUNC_ATTR find_usable_region (grub_disk_t disk __attribute__ ((unused)),
const grub_partition_t p)
{
struct grub_gpt_partentry gptdata;
disk->partition = p->parent;
if (grub_disk_read (disk, p->offset, p->index,
sizeof (gptdata), &gptdata))
return 0;
/* If there's an embed region, it is in a dedicated partition. */
if (! grub_memcmp (&gptdata.type, &grub_gpt_partition_type_bios_boot, 16))
{
start = p->start;
len = p->len;
return 1;
}
return 0;
}
if (embed_type != GRUB_EMBED_PCBIOS)
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"GPT curently supports only PC-BIOS embedding");
err = gpt_partition_map_iterate (disk, find_usable_region);
if (err)
return err;
if (len == 0)
return grub_error (GRUB_ERR_FILE_NOT_FOUND,
"This GPT partition label has no BIOS Boot Partition;"
" embedding won't be possible!");
if (len < nsectors)
return grub_error (GRUB_ERR_OUT_OF_RANGE,
"Your BIOS Boot Partition is too small;"
" embedding won't be possible!");
for (i = 0; i < nsectors; i++)
sectors[i] = start + i;
return GRUB_ERR_NONE;
}
#endif
/* Partition map type. */
static struct grub_partition_map grub_gpt_partition_map =
{
.name = "gpt",
.iterate = gpt_partition_map_iterate,
#ifdef GRUB_UTIL
.embed = gpt_partition_map_embed
#endif
};
GRUB_MOD_INIT(part_gpt)

View File

@ -133,12 +133,128 @@ pc_partition_map_iterate (grub_disk_t disk,
return grub_errno;
}
#ifdef GRUB_UTIL
static grub_err_t
pc_partition_map_embed (struct grub_disk *disk, unsigned int nsectors,
grub_embed_type_t embed_type,
grub_disk_addr_t *sectors)
{
grub_disk_addr_t end = ~0ULL;
struct grub_msdos_partition_mbr mbr;
int labeln = 0;
/* Any value different than `p.offset' will satisfy the check during
first loop. */
grub_disk_addr_t lastaddr = 1;
grub_disk_addr_t ext_offset = 0;
grub_disk_addr_t offset = 0;
if (embed_type != GRUB_EMBED_PCBIOS)
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
"PC-style partitions curently support "
"only PC-BIOS embedding");
if (disk->partition)
return grub_error (GRUB_ERR_OUT_OF_RANGE,
"Embedding on MSDOS subpartition isn't supported");
while (1)
{
int i;
struct grub_msdos_partition_entry *e;
grub_err_t err;
/* Read the MBR. */
err = grub_disk_read (disk, offset, 0, sizeof (mbr), &mbr);
if (err)
return err;
/* This is our loop-detection algorithm. It works the following way:
It saves last position which was a power of two. Then it compares the
saved value with a current one. This way it's guaranteed that the loop
will be broken by at most third walk.
*/
if (labeln && lastaddr == offset)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "loop detected");
labeln++;
if ((labeln & (labeln - 1)) == 0)
lastaddr = offset;
/* Check if it is valid. */
if (mbr.signature != grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE))
return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature");
for (i = 0; i < 4; i++)
if (mbr.entries[i].flag & 0x7f)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "bad boot flag");
/* Analyze DOS partitions. */
for (i = 0; i < 4; i++)
{
e = mbr.entries + i;
if (!grub_msdos_partition_is_empty (e->type)
&& end > offset + grub_le_to_cpu32 (e->start))
end = offset + grub_le_to_cpu32 (e->start);
/* If this is a GPT partition, this MBR is just a dummy. */
if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK && i == 0)
return grub_error (GRUB_ERR_BAD_PART_TABLE, "dummy mbr");
}
/* Find an extended partition. */
for (i = 0; i < 4; i++)
{
e = mbr.entries + i;
if (grub_msdos_partition_is_extended (e->type))
{
offset = ext_offset + grub_le_to_cpu32 (e->start);
if (! ext_offset)
ext_offset = offset;
break;
}
}
/* If no extended partition, the end. */
if (i == 4)
break;
}
if (end >= nsectors + 1)
{
int i;
for (i = 0; i < nsectors; i++)
sectors[i] = 1 + i;
return GRUB_ERR_NONE;
}
if (end <= 1)
return grub_error (GRUB_ERR_FILE_NOT_FOUND,
"This msdos-style partition label has no "
"post-MBR gap; embedding won't be possible!");
if (nsectors > 62)
return grub_error (GRUB_ERR_OUT_OF_RANGE,
"Your core.img is unusually large. "
"It won't fit in the embedding area.");
return grub_error (GRUB_ERR_OUT_OF_RANGE,
"Your embedding area is unusually small. "
"core.img won't fit in it.");
}
#endif
/* Partition map type. */
static struct grub_partition_map grub_msdos_partition_map =
{
.name = "msdos",
.iterate = pc_partition_map_iterate,
#ifdef GRUB_UTIL
.embed = pc_partition_map_embed
#endif
};
GRUB_MOD_INIT(part_msdos)

View File

@ -26,6 +26,13 @@ struct grub_disk;
typedef struct grub_partition *grub_partition_t;
#ifdef GRUB_UTIL
typedef enum
{
GRUB_EMBED_PCBIOS
} grub_embed_type_t;
#endif
/* Partition map type. */
struct grub_partition_map
{
@ -39,6 +46,11 @@ struct grub_partition_map
grub_err_t (*iterate) (struct grub_disk *disk,
int (*hook) (struct grub_disk *disk,
const grub_partition_t partition));
#ifdef GRUB_UTIL
/* Determine sectors available for embedding. */
grub_err_t (*embed) (struct grub_disk *disk, unsigned int nsectors,
grub_embed_type_t embed_type, grub_disk_addr_t *sectors);
#endif
};
typedef struct grub_partition_map *grub_partition_map_t;

View File

@ -26,8 +26,6 @@
#include <grub/file.h>
#include <grub/fs.h>
#include <grub/partition.h>
#include <grub/msdos_partition.h>
#include <grub/gpt_partition.h>
#include <grub/env.h>
#include <grub/emu/hostdisk.h>
#include <grub/machine/boot.h>
@ -74,10 +72,6 @@
* result.
*/
#ifdef GRUB_MACHINE_PCBIOS
static const grub_gpt_part_type_t grub_gpt_partition_type_bios_boot = GRUB_GPT_PARTITION_TYPE_BIOS_BOOT;
#endif
#define DEFAULT_BOOT_FILE "boot.img"
#define DEFAULT_CORE_FILE "core.img"
@ -200,8 +194,6 @@ setup (const char *dir,
grub_uint16_t last_length = GRUB_DISK_SECTOR_SIZE;
grub_file_t file;
FILE *fp;
struct { grub_uint64_t start; grub_uint64_t end; } embed_region;
embed_region.start = embed_region.end = ~0UL;
auto void NESTED_FUNC_ATTR save_first_sector (grub_disk_addr_t sector,
unsigned offset,
@ -283,7 +275,6 @@ setup (const char *dir,
first_block = (struct grub_boot_blocklist *) (core_img
+ GRUB_DISK_SECTOR_SIZE
- sizeof (*block));
grub_util_info ("root is `%s', dest is `%s'", root, dest);
/* Open the root device and the destination device. */
@ -308,24 +299,6 @@ setup (const char *dir,
grub_util_error ("%s", grub_errmsg);
#endif
#ifdef GRUB_MACHINE_PCBIOS
if (dest_dev->disk->partition && fs_probe)
{
grub_fs_t fs;
fs = grub_fs_probe (dest_dev);
if (! fs)
grub_util_error (_("unable to identify a filesystem in %s; safety check can't be performed"),
dest_dev->disk->name);
if (! fs->reserved_first_sector)
grub_util_error (_("%s appears to contain a %s filesystem which isn't known to "
"reserve space for DOS-style boot. Installing GRUB there could "
"result in FILESYSTEM DESTRUCTION if valuable data is overwritten "
"by grub-setup (--skip-fs-probe disables this "
"check, use at your own risk)"), dest_dev->disk->name, fs->name);
}
#endif
#ifdef GRUB_MACHINE_PCBIOS
{
grub_uint16_t *boot_drive_check;
@ -345,53 +318,27 @@ setup (const char *dir,
}
#endif
/* Clean out the blocklists. */
block = first_block;
while (block->len)
{
grub_memset (block, 0, sizeof (block));
block--;
if ((char *) block <= core_img)
grub_util_error ("No terminator in the core image");
}
#ifdef GRUB_MACHINE_PCBIOS
{
const char *dest_partmap;
int multiple_partmaps;
auto int NESTED_FUNC_ATTR find_usable_region_msdos (grub_disk_t disk,
const grub_partition_t p);
int NESTED_FUNC_ATTR find_usable_region_msdos (grub_disk_t disk __attribute__ ((unused)),
const grub_partition_t p)
{
/* There's always an embed region, and it starts right after the MBR. */
embed_region.start = 1;
if (embed_region.end > grub_partition_get_start (p))
embed_region.end = grub_partition_get_start (p);
return 0;
}
auto int NESTED_FUNC_ATTR find_usable_region_gpt (grub_disk_t disk,
const grub_partition_t p);
int NESTED_FUNC_ATTR find_usable_region_gpt (grub_disk_t disk __attribute__ ((unused)),
const grub_partition_t p)
{
struct grub_gpt_partentry gptdata;
disk->partition = p->parent;
if (grub_disk_read (disk, p->offset, p->index,
sizeof (gptdata), &gptdata))
return 0;
/* If there's an embed region, it is in a dedicated partition. */
if (! memcmp (&gptdata.type, &grub_gpt_partition_type_bios_boot, 16))
{
embed_region.start = grub_partition_get_start (p);
embed_region.end = grub_partition_get_start (p) + grub_partition_get_len (p);
return 1;
}
return 0;
}
if (dest_dev->disk->partition)
{
grub_util_warn (_("Attempting to install GRUB to a partition instead of the MBR. This is a BAD idea."));
goto unable_to_embed;
}
grub_partition_map_t dest_partmap = NULL;
grub_partition_map_t container = dest_dev->disk->partition;
int multiple_partmaps = 0;
grub_err_t err;
grub_disk_addr_t sectors[core_sectors];
int i;
grub_fs_t fs;
/* Unlike root_dev, with dest_dev we're interested in the partition map even
if dest_dev itself is a whole disk. */
@ -400,29 +347,60 @@ setup (const char *dir,
int NESTED_FUNC_ATTR identify_partmap (grub_disk_t disk __attribute__ ((unused)),
const grub_partition_t p)
{
if (p->parent)
if (p->parent != container)
return 0;
if (dest_partmap == NULL)
dest_partmap = p->partmap->name;
else if (strcmp (dest_partmap, p->partmap->name) != 0)
{
multiple_partmaps = 1;
return 1;
dest_partmap = p->partmap;
return 0;
}
return 0;
if (dest_partmap == p->partmap)
return 0;
multiple_partmaps = 1;
return 1;
}
dest_partmap = 0;
multiple_partmaps = 0;
grub_partition_iterate (dest_dev->disk, identify_partmap);
fs = grub_fs_probe (dest_dev);
if (!fs)
grub_errno = GRUB_ERR_NONE;
#ifdef GRUB_MACHINE_PCBIOS
if (fs_probe)
{
if (!fs && !dest_partmap)
grub_util_error (_("unable to identify a filesystem in %s; safety check can't be performed"),
dest_dev->disk->name);
if (fs && !fs->reserved_first_sector)
grub_util_error (_("%s appears to contain a %s filesystem which isn't known to "
"reserve space for DOS-style boot. Installing GRUB there could "
"result in FILESYSTEM DESTRUCTION if valuable data is overwritten "
"by grub-setup (--skip-fs-probe disables this "
"check, use at your own risk)"), dest_dev->disk->name, fs->name);
if (dest_partmap && strcmp (dest_partmap->name, "msdos") != 0
&& strcmp (dest_partmap->name, "gpt") != 0
&& strcmp (dest_partmap->name, "bsd") != 0
&& strcmp (dest_partmap->name, "netbsd") != 0
&& strcmp (dest_partmap->name, "openbsd") != 0
&& strcmp (dest_partmap->name, "sunpc") != 0)
grub_util_error (_("%s appears to contain a %s partition map which isn't known to "
"reserve space for DOS-style boot. Installing GRUB there could "
"result in FILESYSTEM DESTRUCTION if valuable data is overwritten "
"by grub-setup (--skip-fs-probe disables this "
"check, use at your own risk)"), dest_dev->disk->name, dest_partmap->name);
}
#endif
if (! dest_partmap)
{
grub_util_warn (_("Attempting to install GRUB to a partitionless disk. This is a BAD idea."));
goto unable_to_embed;
}
if (multiple_partmaps)
if (multiple_partmaps || fs)
{
grub_util_warn (_("Attempting to install GRUB to a disk with multiple partition labels. This is not supported yet."));
grub_util_warn (_("Attempting to install GRUB to a disk with multiple partition labels or both partition label and filesystem. This is not supported yet."));
goto unable_to_embed;
}
@ -433,44 +411,33 @@ setup (const char *dir,
GRUB_BOOT_MACHINE_PART_END - GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC);
free (tmp_img);
if (strcmp (dest_partmap, "msdos") == 0)
grub_partition_iterate (dest_dev->disk, find_usable_region_msdos);
else if (strcmp (dest_partmap, "gpt") == 0)
grub_partition_iterate (dest_dev->disk, find_usable_region_gpt);
else
grub_util_error (_("No DOS-style partitions found"));
if (embed_region.end <= embed_region.start)
if (!dest_partmap->embed)
{
if (! strcmp (dest_partmap, "msdos"))
grub_util_warn (_("This msdos-style partition label has no post-MBR gap; embedding won't be possible!"));
else
grub_util_warn (_("This GPT partition label has no BIOS Boot Partition; embedding won't be possible!"));
grub_util_warn ("Partition style '%s' doesn't support embeding",
dest_partmap->name);
goto unable_to_embed;
}
if ((unsigned long) core_sectors > embed_region.end - embed_region.start)
err = dest_partmap->embed (dest_dev->disk, core_sectors,
GRUB_EMBED_PCBIOS, sectors);
if (err)
{
if (core_sectors > 62)
grub_util_warn (_("Your core.img is unusually large. It won't fit in the embedding area."));
else /* embed_region.end - embed_region.start < 62 */
grub_util_warn (_("Your embedding area is unusually small. core.img won't fit in it."));
grub_util_warn ("%s", grub_errmsg);
grub_errno = GRUB_ERR_NONE;
goto unable_to_embed;
}
write_rootdev (core_img, root_dev,
boot_img, embed_region.start);
save_first_sector (sectors[0] + grub_partition_get_start (container),
0, GRUB_DISK_SECTOR_SIZE);
grub_util_info ("the core image will be embedded at sector 0x%llx", embed_region.start);
block = first_block;
for (i = 1; i < core_sectors; i++)
save_blocklists (sectors[i] + grub_partition_get_start (container),
0, GRUB_DISK_SECTOR_SIZE);
/* The first blocklist contains the whole sectors. */
first_block->start = grub_cpu_to_le64 (embed_region.start + 1);
/* These are filled elsewhere. Verify them just in case. */
assert (first_block->len == grub_host_to_target16 (core_sectors - 1));
assert (first_block->segment == grub_host_to_target16 (GRUB_BOOT_MACHINE_KERNEL_SEG
+ (GRUB_DISK_SECTOR_SIZE >> 4)));
write_rootdev (core_img, root_dev, boot_img, first_sector);
/* Make sure that the second blocklist is a terminator. */
block = first_block - 1;
@ -479,8 +446,13 @@ setup (const char *dir,
block->segment = 0;
/* Write the core image onto the disk. */
if (grub_disk_write (dest_dev->disk, embed_region.start, 0, core_size, core_img))
grub_util_error ("%s", grub_errmsg);
for (i = 0; i < core_sectors; i++)
grub_disk_write (dest_dev->disk, sectors[i], 0,
(core_size - i * GRUB_DISK_SECTOR_SIZE
< GRUB_DISK_SECTOR_SIZE) ? core_size
- i * GRUB_DISK_SECTOR_SIZE
: GRUB_DISK_SECTOR_SIZE,
core_img + i * GRUB_DISK_SECTOR_SIZE);
/* Write the boot image onto the disk. */
if (grub_disk_write (dest_dev->disk, 0, 0, GRUB_DISK_SECTOR_SIZE,