]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Move embedding routines to partmap sources files.
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Wed, 15 Sep 2010 19:36:57 +0000 (21:36 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Wed, 15 Sep 2010 19:36:57 +0000 (21:36 +0200)
* 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.

ChangeLog
grub-core/partmap/gpt.c
grub-core/partmap/msdos.c
include/grub/partition.h
util/grub-setup.c

index b3f3c0e51a3032023ff59f3986d244298858f747..0c092d0b9a70df4837eb4abf352ec95ce9d0f1de 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
index 0dd670ccef1867441788780812fa185d9bfc851f..c9393d93250e863bf693aa9bfaed59f381953609 100644 (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
+
 \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)
index 3898d09fac2812ae3913638fa81897678b97fb68..7dab4aa0f084c3024189d1b61802d0144ed7fa26 100644 (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
+
 \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)
index 20705c5276d0331508d33251111a7dd3492d7e96..a29a3440dc8477608974f8c0b21586d84e03eec4 100644 (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;
 
index a95f9b9d59ca744207c540128c2f37abd6d46772..c9ab5cfdbe66e6925d26c927d1af51f9a0c88e6f 100644 (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>
  * 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
 
-#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.  */
@@ -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,