Hello. A bug was filed that embedding code in grub-setup.c was
ignoring partmap metadata which could cause its overwriting. To avoid
usage of metadata and because of upcoming sunpc embedding support I
propose to move embedding to partmap/*.c. For this I devised an
interface which enables future reusage of embedding for other too if
necessary.
--
Regards
Vladimir 'phcoder' Serbinenko
Personal git repository: http://repo.or.cz/w/grub2/phcoder.git
diff --git a/include/grub/partition.h b/include/grub/partition.h
index 37c5f24..c162955 100644
--- a/include/grub/partition.h
+++ b/include/grub/partition.h
@@ -25,6 +25,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
{
@@ -43,6 +50,12 @@ struct grub_partition_map
/* Return the name of the partition PARTITION. */
char *(*get_name) (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
+
/* The next partition map type. */
struct grub_partition_map *next;
};
diff --git a/partmap/gpt.c b/partmap/gpt.c
index d646d41..0ac38a4 100644
--- a/partmap/gpt.c
+++ b/partmap/gpt.c
@@ -25,6 +25,13 @@
#include <grub/pc_partition.h>
#include <grub/gpt_partition.h>
+#ifdef GRUB_UTIL
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#endif
+
static grub_uint8_t grub_gpt_magic[8] =
{
0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54
@@ -32,6 +39,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
+
static struct grub_partition_map grub_gpt_partition_map;
@@ -99,7 +110,7 @@ gpt_partition_map_iterate (grub_disk_t disk,
(unsigned long long) part.len);
if (hook (disk, &part))
- return 1;
+ return grub_errno;
}
last_offset += grub_le_to_cpu32 (gpt.partentry_size);
@@ -172,6 +183,60 @@ gpt_partition_map_get_name (const grub_partition_t p)
return name;
}
+#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 = p->data;
+
+ /* If there's an embed region, it is in a dedicated partition. */
+ if (! 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 =
@@ -179,7 +244,10 @@ static struct grub_partition_map grub_gpt_partition_map =
.name = "gpt_partition_map",
.iterate = gpt_partition_map_iterate,
.probe = gpt_partition_map_probe,
- .get_name = gpt_partition_map_get_name
+ .get_name = gpt_partition_map_get_name,
+#ifdef GRUB_UTIL
+ .embed = gpt_partition_map_embed
+#endif
};
GRUB_MOD_INIT(gpt_partition_map)
diff --git a/partmap/pc.c b/partmap/pc.c
index 6a2efd2..466e790 100644
--- a/partmap/pc.c
+++ b/partmap/pc.c
@@ -153,7 +153,7 @@ pc_partition_map_iterate (grub_disk_t disk,
pcdata.dos_part++;
if (hook (disk, &p))
- return 1;
+ return grub_errno;
/* Check if this is a BSD partition. */
if (grub_pc_partition_is_bsd (e->type))
@@ -299,6 +299,107 @@ pc_partition_map_get_name (const grub_partition_t p)
return name;
}
+#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;
+ unsigned int i;
+ struct grub_pc_partition_mbr mbr;
+ struct grub_disk raw;
+ grub_uint32_t ext_offset, offset;
+ int index;
+ grub_err_t err;
+
+ if (embed_type != GRUB_EMBED_PCBIOS)
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "PC-style partitions curently support "
+ "only PC-BIOS embedding");
+
+ /* Enforce raw disk access. */
+ raw = *disk;
+ raw.partition = 0;
+
+ offset = 0;
+ ext_offset = 0;
+
+ while (1)
+ {
+ struct grub_pc_partition_entry *e;
+
+ /* Read the MBR. */
+ err = grub_disk_read (&raw, offset, 0, sizeof (mbr), &mbr);
+ if (err)
+ return err;
+
+ /* 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 (index = 0; index < 4; index++)
+ {
+ e = mbr.entries + index;
+
+ if (!grub_pc_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 && index == 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_pc_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)
+ {
+ 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.");
+ }
+ for (i = 0; i < nsectors; i++)
+ sectors[i] = 1 + i;
+
+ return GRUB_ERR_NONE;
+}
+#endif
+
/* Partition map type. */
static struct grub_partition_map grub_pc_partition_map =
@@ -306,7 +407,10 @@ static struct grub_partition_map grub_pc_partition_map =
.name = "pc_partition_map",
.iterate = pc_partition_map_iterate,
.probe = pc_partition_map_probe,
- .get_name = pc_partition_map_get_name
+ .get_name = pc_partition_map_get_name,
+#ifdef GRUB_UTIL
+ .embed = pc_partition_map_embed
+#endif
};
GRUB_MOD_INIT(pc_partition_map)
diff --git a/util/i386/pc/grub-setup.c b/util/i386/pc/grub-setup.c
index 92c69ef..8f903c1 100644
--- a/util/i386/pc/grub-setup.c
+++ b/util/i386/pc/grub-setup.c
@@ -26,7 +26,6 @@
#include <grub/fs.h>
#include <grub/partition.h>
#include <grub/pc_partition.h>
-#include <grub/gpt_partition.h>
#include <grub/env.h>
#include <grub/util/hostdisk.h>
#include <grub/machine/boot.h>
@@ -35,8 +34,6 @@
#include <grub/util/raid.h>
#include <grub/util/lvm.h>
-static const grub_gpt_part_type_t grub_gpt_partition_type_bios_boot =
GRUB_GPT_PARTITION_TYPE_BIOS_BOOT;
-
#include <grub_setup_init.h>
#include <stdio.h>
@@ -93,7 +90,7 @@ setup (const char *dir,
size_t boot_size, core_size;
grub_uint16_t core_sectors;
grub_device_t root_dev, dest_dev;
- const char *dest_partmap;
+ grub_partition_map_t dest_partmap;
grub_uint8_t *boot_drive;
grub_disk_addr_t *kernel_sector;
grub_uint16_t *boot_drive_check;
@@ -108,52 +105,13 @@ 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;
+ grub_err_t err;
auto void NESTED_FUNC_ATTR save_first_sector (grub_disk_addr_t sector,
unsigned offset,
unsigned length);
auto void NESTED_FUNC_ATTR save_blocklists (grub_disk_addr_t sector,
unsigned offset,
unsigned length);
- 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)
- {
- struct grub_pc_partition *pcdata = p->data;
-
- /* There's always an embed region, and it starts right after the MBR. */
- embed_region.start = 1;
-
- /* For its end offset, include as many dummy partitions as we can. */
- if (! grub_pc_partition_is_empty (pcdata->dos_type)
- && ! grub_pc_partition_is_bsd (pcdata->dos_type)
- && embed_region.end > p->start)
- embed_region.end = p->start;
-
- 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 = p->data;
-
- /* 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 = p->start;
- embed_region.end = p->start + p->len;
-
- return 1;
- }
-
- return 0;
- }
-
void NESTED_FUNC_ATTR save_first_sector (grub_disk_addr_t sector, unsigned
offset,
unsigned length)
{
@@ -307,6 +265,20 @@ setup (const char *dir,
grub_util_info ("dos partition is %d, bsd partition is %d",
dos_part, bsd_part);
+ /* Clean out the blocklists. */
+ block = first_block;
+ while (block->len)
+ {
+ block->start = 0;
+ block->len = 0;
+ block->segment = 0;
+
+ block--;
+
+ if ((char *) block <= core_img)
+ grub_util_error ("No terminator in the core image");
+ }
+
if (! dest_dev->disk->has_partitions)
{
grub_util_warn ("Attempting to install GRUB to a partitionless disk.
This is a BAD idea.");
@@ -326,7 +298,7 @@ setup (const char *dir,
int NESTED_FUNC_ATTR identify_partmap (grub_disk_t disk __attribute__
((unused)),
const grub_partition_t p)
{
- dest_partmap = p->partmap->name;
+ dest_partmap = p->partmap;
return 1;
}
dest_partmap = 0;
@@ -338,62 +310,52 @@ setup (const char *dir,
goto unable_to_embed;
}
- grub_partition_iterate (dest_dev->disk, (strcmp (dest_partmap,
"pc_partition_map") ?
- find_usable_region_gpt :
find_usable_region_msdos));
+ if (strcmp (dest_partmap->name, "pc_partition_map") != 0
+ && strcmp (dest_partmap->name, "gpt_partition_map") != 0)
+ grub_util_error ("Can't install on '%s'", dest_partmap);
- if (embed_region.end == embed_region.start)
+ if (!dest_partmap->embed)
{
- if (! strcmp (dest_partmap, "pc_partition_map"))
- 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)
- {
- if (core_sectors > 62)
- grub_util_warn ("Your core.img is unusually large. It won't fit in the
embedding area.");
- else if (embed_region.end - embed_region.start < 62)
- grub_util_warn ("Your embedding area is unusually small. core.img
won't fit in it.");
- else
- grub_util_warn ("Embedding area is too small for core.img.");
- goto unable_to_embed;
- }
-
-
- grub_util_info ("will embed the core image at sector 0x%llx",
embed_region.start);
-
- *install_dos_part = grub_cpu_to_le32 (dos_part);
- *install_bsd_part = grub_cpu_to_le32 (bsd_part);
-
- /* The first blocklist contains the whole sectors. */
- first_block->start = grub_cpu_to_le64 (embed_region.start + 1);
- first_block->len = grub_cpu_to_le16 (core_sectors - 1);
- first_block->segment
- = grub_cpu_to_le16 (GRUB_BOOT_MACHINE_KERNEL_SEG
- + (GRUB_DISK_SECTOR_SIZE >> 4));
-
- /* Make sure that the second blocklist is a terminator. */
- block = first_block - 1;
- block->start = 0;
- block->len = 0;
- 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);
-
- /* FIXME: can this be skipped? */
- *boot_drive = 0xFF;
-
- *kernel_sector = grub_cpu_to_le64 (embed_region.start);
-
- /* Write the boot image onto the disk. */
- if (grub_disk_write (dest_dev->disk, 0, 0, GRUB_DISK_SECTOR_SIZE,
- boot_img))
- grub_util_error ("%s", grub_errmsg);
-
+ {
+ grub_disk_addr_t sectors[core_sectors];
+ int i;
+
+ err = dest_partmap->embed (dest_dev->disk, core_sectors,
+ GRUB_EMBED_PCBIOS, sectors);
+
+ if (err)
+ {
+ grub_util_warn ("%s", grub_errmsg);
+ grub_errno = GRUB_ERR_NONE;
+ goto unable_to_embed;
+ }
+
+ save_first_sector (sectors[0], 0, GRUB_DISK_SECTOR_SIZE);
+
+ block = first_block;
+ for (i = 1; i < core_sectors; i++)
+ save_blocklists (sectors[i], 0, GRUB_DISK_SECTOR_SIZE);
+
+ /* FIXME: can this be skipped? */
+ *boot_drive = 0xFF;
+
+ *install_dos_part = grub_cpu_to_le32 (dos_part);
+ *install_bsd_part = grub_cpu_to_le32 (bsd_part);
+
+ /* Write the core image onto the disk. */
+ 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);
+ }
goto finish;
unable_to_embed:
@@ -480,20 +442,6 @@ unable_to_embed:
if (i == MAX_TRIES)
grub_util_error ("Cannot read `%s' correctly", core_path_dev);
- /* Clean out the blocklists. */
- block = first_block;
- while (block->len)
- {
- block->start = 0;
- block->len = 0;
- block->segment = 0;
-
- block--;
-
- if ((char *) block <= core_img)
- grub_util_error ("No terminator in the core image");
- }
-
/* Now read the core image to determine where the sectors are. */
file = grub_file_open (core_path_dev);
if (! file)
@@ -515,8 +463,6 @@ unable_to_embed:
free (core_path_dev);
free (tmp_img);
- *kernel_sector = grub_cpu_to_le64 (first_sector);
-
/* FIXME: can this be skipped? */
*boot_drive = 0xFF;
@@ -532,12 +478,14 @@ unable_to_embed:
grub_util_write_image (core_img, GRUB_DISK_SECTOR_SIZE * 2, fp);
fclose (fp);
+ finish:
+
+ *kernel_sector = grub_cpu_to_le64 (first_sector);
+
/* Write the boot image onto the disk. */
if (grub_disk_write (dest_dev->disk, 0, 0, GRUB_DISK_SECTOR_SIZE, boot_img))
grub_util_error ("%s", grub_errmsg);
- finish:
-
/* Sync is a Good Thing. */
sync ();
_______________________________________________
Grub-devel mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/grub-devel