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
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,
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
+
\f
/* 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)
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
+
\f
/* 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)
#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>
* 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"
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,
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. */
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;
}
#endif
-#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)
+ /* Clean out the blocklists. */
+ block = first_block;
+ while (block->len)
{
- struct grub_gpt_partentry gptdata;
+ grub_memset (block, 0, sizeof (block));
- 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);
+ block--;
- return 1;
- }
- return 0;
+ if ((char *) block <= core_img)
+ grub_util_error ("No terminator in the core image");
}
- 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;
- }
+#ifdef GRUB_MACHINE_PCBIOS
+ {
+ 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. */
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;
}
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;
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,