grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] Install to LVM PVs


From: Gabriel de Perthuis
Subject: [PATCH] Install to LVM PVs
Date: Wed, 25 Sep 2013 12:39:39 +0000 (UTC)
User-agent: Pan/0.139 (Sexual Chocolate; GIT bf56508 git://git.gnome.org/pan2)

Hello,
This patch lets grub install to a reserved area in LVM physical volumes.
These bootloader areas can be created with LVM 2.02.99 and the
--bootloaderareasize argument to pvcreate and vgconvert.
I tested it in QEMU, installing to and booting a disk that contains a PV
and no partition table.


=== modified file 'grub-core/disk/lvm.c'
--- grub-core/disk/lvm.c        2012-06-25 15:52:20 +0000
+++ grub-core/disk/lvm.c        2013-09-25 11:03:21 +0000
@@ -95,6 +95,38 @@
     }
 }
 
+static struct grub_lvm_pv_header *
+grub_lvm_get_pvh(grub_disk_t disk, char buf[static GRUB_LVM_LABEL_SIZE])
+{
+  struct grub_lvm_label_header *lh = (struct grub_lvm_label_header *) buf;
+  unsigned int i;
+
+  /* Search for label. */
+  for (i = 0; i < GRUB_LVM_LABEL_SCAN_SECTORS; i++)
+    {
+      if (grub_disk_read (disk, i, 0, GRUB_LVM_LABEL_SIZE, buf))
+       return NULL;
+
+      if ((! grub_strncmp ((char *)lh->id, GRUB_LVM_LABEL_ID,
+                          sizeof (lh->id)))
+         && (! grub_strncmp ((char *)lh->type, GRUB_LVM_LVM2_LABEL,
+                             sizeof (lh->type))))
+       break;
+    }
+
+  /* Return if we didn't find a label. */
+  if (i == GRUB_LVM_LABEL_SCAN_SECTORS)
+    {
+#ifdef GRUB_UTIL
+      grub_util_info ("no LVM signature found");
+#endif
+      return NULL;
+    }
+
+  return (struct grub_lvm_pv_header *) (buf + grub_le_to_cpu32(lh->offset_xl));
+}
+
+
 static struct grub_diskfilter_vg * 
 grub_lvm_detect (grub_disk_t disk,
                 struct grub_diskfilter_pv_id *id,
@@ -102,11 +134,10 @@
 {
   grub_err_t err;
   grub_uint64_t mda_offset, mda_size;
-  char buf[GRUB_LVM_LABEL_SIZE];
   char vg_id[GRUB_LVM_ID_STRLEN+1];
   char pv_id[GRUB_LVM_ID_STRLEN+1];
+  char buf[GRUB_LVM_LABEL_SIZE];
   char *metadatabuf, *p, *q, *vgname;
-  struct grub_lvm_label_header *lh = (struct grub_lvm_label_header *) buf;
   struct grub_lvm_pv_header *pvh;
   struct grub_lvm_disk_locn *dlocn;
   struct grub_lvm_mda_header *mdah;
@@ -115,30 +146,9 @@
   struct grub_diskfilter_vg *vg;
   struct grub_diskfilter_pv *pv;
 
-  /* Search for label. */
-  for (i = 0; i < GRUB_LVM_LABEL_SCAN_SECTORS; i++)
-    {
-      err = grub_disk_read (disk, i, 0, sizeof(buf), buf);
-      if (err)
-       goto fail;
-
-      if ((! grub_strncmp ((char *)lh->id, GRUB_LVM_LABEL_ID,
-                          sizeof (lh->id)))
-         && (! grub_strncmp ((char *)lh->type, GRUB_LVM_LVM2_LABEL,
-                             sizeof (lh->type))))
-       break;
-    }
-
-  /* Return if we didn't find a label. */
-  if (i == GRUB_LVM_LABEL_SCAN_SECTORS)
-    {
-#ifdef GRUB_UTIL
-      grub_util_info ("no LVM signature found");
-#endif
-      goto fail;
-    }
-
-  pvh = (struct grub_lvm_pv_header *) (buf + grub_le_to_cpu32(lh->offset_xl));
+  pvh = grub_lvm_get_pvh(disk, buf);
+  if (! pvh)
+    goto fail;
 
   for (i = 0, j = 0; i < GRUB_LVM_ID_LEN; i++)
     {
@@ -151,7 +161,7 @@
   dlocn = pvh->disk_areas_xl;
 
   dlocn++;
-  /* Is it possible to have multiple data/metadata areas? I haven't
+  /* Is it possible to have multiple data areas? I haven't
      seen devices that have it. */
   if (dlocn->offset)
     {
@@ -746,6 +756,88 @@
   return NULL;
 }
 
+#ifdef GRUB_UTIL
+int
+grub_util_is_lvm(grub_disk_t disk)
+{
+  struct grub_diskfilter_pv_id id;
+  struct grub_diskfilter_vg *vg;
+  grub_disk_addr_t start_sector;
+  vg = grub_lvm_detect(disk, &id, &start_sector);
+  if (! vg)
+    return 0;
+  /* don't free the vg, it's held by grub_diskfilter_vg_register */
+  grub_free(id.uuid);
+  return 1;
+}
+
+grub_err_t
+grub_util_lvm_embed (struct grub_disk *disk, unsigned int *nsectors,
+                    unsigned int max_nsectors,
+                    grub_embed_type_t embed_type,
+                    grub_disk_addr_t **sectors)
+{
+  char buf[GRUB_LVM_LABEL_SIZE];
+  struct grub_lvm_pv_header *pvh;
+  struct grub_lvm_pv_header_ext *pvh_ext;
+  struct grub_diskfilter_pv *pv = NULL;
+  struct grub_diskfilter_vg *vg = NULL;
+  struct grub_lvm_disk_locn *dlocn;
+  grub_uint64_t ba_offset, ba_size, ba_start_sector;
+  unsigned int i;
+
+  if (embed_type != GRUB_EMBED_PCBIOS)
+    return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+                      "LVM curently supports only PC-BIOS embedding");
+  if (disk->partition)
+    return grub_error (GRUB_ERR_BUG, "disk isn't LVM");
+  pv = grub_diskfilter_get_pv_from_disk (disk, &vg);
+  if (! pv)
+    return grub_error (GRUB_ERR_BUG, "disk isn't LVM");
+
+  pvh = grub_lvm_get_pvh(disk, buf);
+  if (! pvh)
+    return grub_error (GRUB_ERR_BUG, "disk isn't LVM");
+
+  dlocn = pvh->disk_areas_xl;
+
+  /* skip past the data area list */
+  while (dlocn->offset)
+    dlocn++;
+  dlocn++;
+  /* and the metadata area list */
+  while (dlocn->offset)
+    dlocn++;
+  dlocn++;
+
+  pvh_ext = (struct grub_lvm_pv_header_ext*)dlocn;
+  if (! pvh_ext->version_xl)
+    return grub_error (GRUB_ERR_BUG, "LVM PV doesn't have a bootloader area");
+
+  dlocn = pvh_ext->disk_areas_xl;
+  ba_offset = grub_le_to_cpu64 (dlocn->offset);
+  ba_size = grub_le_to_cpu64 (dlocn->size);
+  if (! (ba_offset && ba_size))
+    return grub_error (GRUB_ERR_BUG, "LVM PV doesn't have a bootloader area");
+  /* could be worked around with extra arithmetic if this actually happens */
+  if (ba_offset % GRUB_DISK_SECTOR_SIZE)
+    return grub_error (
+      GRUB_ERR_BUG, "LVM bootloader area is improperly aligned");
+  ba_start_sector = ba_offset / GRUB_DISK_SECTOR_SIZE;
+
+  *nsectors = ba_size / GRUB_DISK_SECTOR_SIZE;
+  if (*nsectors > max_nsectors)
+    *nsectors = max_nsectors;
+
+  *sectors = grub_malloc (*nsectors * sizeof (**sectors));
+  if (!*sectors)
+    return grub_errno;
+  for (i = 0; i < *nsectors; i++)
+    (*sectors)[i] = ba_start_sector + i;
+
+  return GRUB_ERR_NONE;
+}
+#endif
 
 
 static struct grub_diskfilter grub_lvm_dev = {

=== modified file 'include/grub/diskfilter.h'
--- include/grub/diskfilter.h   2012-06-25 15:36:50 +0000
+++ include/grub/diskfilter.h   2013-09-25 08:59:05 +0000
@@ -75,6 +75,9 @@
 #ifdef GRUB_UTIL
   char **partmaps;
 #endif
+  /* Optional bootloader embedding area */
+  grub_uint64_t ba_offset;
+  grub_uint64_t ba_size;
 };
 
 struct grub_diskfilter_lv {

=== modified file 'include/grub/emu/hostdisk.h'
--- include/grub/emu/hostdisk.h 2013-08-22 14:50:12 +0000
+++ include/grub/emu/hostdisk.h 2013-09-25 07:59:06 +0000
@@ -44,9 +44,16 @@
 char *
 grub_util_get_ldm (grub_disk_t disk, grub_disk_addr_t start);
 int
+grub_util_is_lvm (grub_disk_t disk);
+int
 grub_util_is_ldm (grub_disk_t disk);
 #ifdef GRUB_UTIL
 grub_err_t
+grub_util_lvm_embed (struct grub_disk *disk, unsigned int *nsectors,
+                    unsigned int max_nsectors,
+                    grub_embed_type_t embed_type,
+                    grub_disk_addr_t **sectors);
+grub_err_t
 grub_util_ldm_embed (struct grub_disk *disk, unsigned int *nsectors,
                     unsigned int max_nsectors,
                     grub_embed_type_t embed_type,

=== modified file 'include/grub/lvm.h'
--- include/grub/lvm.h  2012-01-29 13:28:01 +0000
+++ include/grub/lvm.h  2013-09-25 08:32:31 +0000
@@ -62,6 +62,13 @@
   struct grub_lvm_disk_locn disk_areas_xl[0];  /* Two lists */
 } __attribute__ ((packed));
 
+struct grub_lvm_pv_header_ext {
+  grub_uint32_t version_xl;
+  grub_uint32_t flags_xl;
+  /* NULL-terminated list of bootloader embedding areas */
+  struct grub_lvm_disk_locn disk_areas_xl[0];
+} __attribute__ ((packed));
+
 #define GRUB_LVM_FMTT_MAGIC 
"\040\114\126\115\062\040\170\133\065\101\045\162\060\116\052\076"
 #define GRUB_LVM_FMTT_VERSION 1
 #define GRUB_LVM_MDA_HEADER_SIZE 512

=== modified file 'util/grub-setup.c'
--- util/grub-setup.c   2013-04-04 06:55:06 +0000
+++ util/grub-setup.c   2013-09-25 11:31:47 +0000
@@ -387,7 +387,7 @@
       .container = dest_dev->disk->partition,
       .multiple_partmaps = 0
     };
-    int is_ldm;
+    int is_ldm, is_lvm;
     grub_err_t err;
     grub_disk_addr_t *sectors;
     int i;
@@ -411,8 +411,9 @@
       grub_errno = GRUB_ERR_NONE;
 
     is_ldm = grub_util_is_ldm (dest_dev->disk);
+    is_lvm = grub_util_is_lvm (dest_dev->disk);
 
-    if (fs_probe)
+    if (!is_lvm && fs_probe)
       {
        if (!fs && !ctx.dest_partmap)
          grub_util_error (_("unable to identify a filesystem in %s; safety 
check can't be performed"),
@@ -459,7 +460,7 @@
 
     free (tmp_img);
     
-    if (! ctx.dest_partmap && ! fs && !is_ldm)
+    if (! ctx.dest_partmap && ! fs && !is_ldm && !is_lvm)
       {
        grub_util_warn ("%s", _("Attempting to install GRUB to a partitionless 
disk or to a partition.  This is a BAD idea."));
        goto unable_to_embed;
@@ -492,7 +493,10 @@
       maxsec = ((0x78000 - GRUB_KERNEL_I386_PC_LINK_ADDR)
                >> GRUB_DISK_SECTOR_BITS);
 
-    if (is_ldm)
+    if (is_lvm)
+      err = grub_util_lvm_embed (dest_dev->disk, &nsec, maxsec,
+                                GRUB_EMBED_PCBIOS, &sectors);
+    else if (is_ldm)
       err = grub_util_ldm_embed (dest_dev->disk, &nsec, maxsec,
                                 GRUB_EMBED_PCBIOS, &sectors);
     else if (ctx.dest_partmap)
@@ -588,7 +592,7 @@
 
   if (dest_dev->disk->dev->id != root_dev->disk->dev->id)
     grub_util_error ("%s", _("embedding is not possible, but this is required 
for "
-                            "RAID and LVM install"));
+                            "RAID install"));
 
   {
     grub_fs_t fs;





reply via email to

[Prev in Thread] Current Thread [Next in Thread]