nalika / rpms / grub2

Forked from rpms/grub2 2 years ago
Clone

Blame SOURCES/0192-fs-btrfs-Use-full-btrfs-bootloader-area.patch

b35c50
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
b35c50
From: Michael Chang <mchang@suse.com>
b35c50
Date: Mon, 13 Dec 2021 14:25:49 +0800
b35c50
Subject: [PATCH] fs/btrfs: Use full btrfs bootloader area
b35c50
b35c50
Up to now GRUB can only embed to the first 64 KiB before primary
b35c50
superblock of btrfs, effectively limiting the GRUB core size. That
b35c50
could consequently pose restrictions to feature enablement like
b35c50
advanced zstd compression.
b35c50
b35c50
This patch attempts to utilize full unused area reserved by btrfs for
b35c50
the bootloader outlined in the document [1]:
b35c50
b35c50
  The first 1MiB on each device is unused with the exception of primary
b35c50
  superblock that is on the offset 64KiB and spans 4KiB.
b35c50
b35c50
Apart from that, adjacent sectors to superblock and first block group
b35c50
are not used for embedding in case of overflow and logged access to
b35c50
adjacent sectors could be useful for tracing it up.
b35c50
b35c50
This patch has been tested to provide out of the box support for btrfs
b35c50
zstd compression with which GRUB has been installed to the partition.
b35c50
b35c50
[1] https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#BOOTLOADER_SUPPORT
b35c50
b35c50
Signed-off-by: Michael Chang <mchang@suse.com>
b35c50
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
b35c50
(cherry picked from commit b0f06a81c6f31b6fa20be67a96b6683bba8210c9)
b35c50
---
b35c50
 grub-core/fs/btrfs.c | 90 ++++++++++++++++++++++++++++++++++++++++++++--------
b35c50
 include/grub/disk.h  |  2 ++
b35c50
 2 files changed, 79 insertions(+), 13 deletions(-)
b35c50
b35c50
diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
b35c50
index 4cc86e9b79..07c0ff874b 100644
b35c50
--- a/grub-core/fs/btrfs.c
b35c50
+++ b/grub-core/fs/btrfs.c
b35c50
@@ -2476,6 +2476,33 @@ grub_btrfs_label (grub_device_t device, char **label)
b35c50
 }
b35c50
 
b35c50
 #ifdef GRUB_UTIL
b35c50
+
b35c50
+struct embed_region {
b35c50
+  unsigned int start;
b35c50
+  unsigned int secs;
b35c50
+};
b35c50
+
b35c50
+/*
b35c50
+ * https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#BOOTLOADER_SUPPORT
b35c50
+ * The first 1 MiB on each device is unused with the exception of primary
b35c50
+ * superblock that is on the offset 64 KiB and spans 4 KiB.
b35c50
+ */
b35c50
+
b35c50
+static const struct {
b35c50
+  struct embed_region available;
b35c50
+  struct embed_region used[6];
b35c50
+} btrfs_head = {
b35c50
+  .available = {0, GRUB_DISK_KiB_TO_SECTORS (1024)}, /* The first 1 MiB. */
b35c50
+  .used = {
b35c50
+    {0, 1},                                                        /* boot.S. */
b35c50
+    {GRUB_DISK_KiB_TO_SECTORS (64) - 1, 1},                        /* Overflow guard. */
b35c50
+    {GRUB_DISK_KiB_TO_SECTORS (64), GRUB_DISK_KiB_TO_SECTORS (4)}, /* 4 KiB superblock. */
b35c50
+    {GRUB_DISK_KiB_TO_SECTORS (68), 1},                            /* Overflow guard. */
b35c50
+    {GRUB_DISK_KiB_TO_SECTORS (1024) - 1, 1},                      /* Overflow guard. */
b35c50
+    {0, 0}                                                         /* Array terminator. */
b35c50
+  }
b35c50
+};
b35c50
+
b35c50
 static grub_err_t
b35c50
 grub_btrfs_embed (grub_device_t device __attribute__ ((unused)),
b35c50
 		  unsigned int *nsectors,
b35c50
@@ -2483,25 +2510,62 @@ grub_btrfs_embed (grub_device_t device __attribute__ ((unused)),
b35c50
 		  grub_embed_type_t embed_type,
b35c50
 		  grub_disk_addr_t **sectors)
b35c50
 {
b35c50
-  unsigned i;
b35c50
+  unsigned int i, j, n = 0;
b35c50
+  const struct embed_region *u;
b35c50
+  grub_disk_addr_t *map;
b35c50
 
b35c50
   if (embed_type != GRUB_EMBED_PCBIOS)
b35c50
     return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
b35c50
 		       "BtrFS currently supports only PC-BIOS embedding");
b35c50
 
b35c50
-  if (64 * 2 - 1 < *nsectors)
b35c50
-    return grub_error (GRUB_ERR_OUT_OF_RANGE,
b35c50
-		       N_("your core.img is unusually large.  "
b35c50
-			  "It won't fit in the embedding area"));
b35c50
-
b35c50
-  *nsectors = 64 * 2 - 1;
b35c50
-  if (*nsectors > max_nsectors)
b35c50
-    *nsectors = max_nsectors;
b35c50
-  *sectors = grub_calloc (*nsectors, sizeof (**sectors));
b35c50
-  if (!*sectors)
b35c50
+  map = grub_calloc (btrfs_head.available.secs, sizeof (*map));
b35c50
+  if (map == NULL)
b35c50
     return grub_errno;
b35c50
-  for (i = 0; i < *nsectors; i++)
b35c50
-    (*sectors)[i] = i + 1;
b35c50
+
b35c50
+  /*
b35c50
+   * Populating the map array so that it can be used to index if a disk
b35c50
+   * address is available to embed:
b35c50
+   *   - 0: available,
b35c50
+   *   - 1: unavailable.
b35c50
+   */
b35c50
+  for (u = btrfs_head.used; u->secs; ++u)
b35c50
+    {
b35c50
+      unsigned int end = u->start + u->secs;
b35c50
+
b35c50
+      if (end > btrfs_head.available.secs)
b35c50
+        end = btrfs_head.available.secs;
b35c50
+      for (i = u->start; i < end; ++i)
b35c50
+        map[i] = 1;
b35c50
+    }
b35c50
+
b35c50
+  /* Adding up n until it matches total size of available embedding area. */
b35c50
+  for (i = 0; i < btrfs_head.available.secs; ++i)
b35c50
+    if (map[i] == 0)
b35c50
+      n++;
b35c50
+
b35c50
+  if (n < *nsectors)
b35c50
+    {
b35c50
+      grub_free (map);
b35c50
+      return grub_error (GRUB_ERR_OUT_OF_RANGE,
b35c50
+		         N_("your core.img is unusually large.  "
b35c50
+			    "It won't fit in the embedding area"));
b35c50
+    }
b35c50
+
b35c50
+  if (n > max_nsectors)
b35c50
+    n = max_nsectors;
b35c50
+
b35c50
+  /*
b35c50
+   * Populating the array so that it can used to index disk block address for
b35c50
+   * an image file's offset to be embedded on disk (the unit is in sectors):
b35c50
+   *   - i: The disk block address relative to btrfs_head.available.start,
b35c50
+   *   - j: The offset in image file.
b35c50
+   */
b35c50
+  for (i = 0, j = 0; i < btrfs_head.available.secs && j < n; ++i)
b35c50
+    if (map[i] == 0)
b35c50
+      map[j++] = btrfs_head.available.start + i;
b35c50
+
b35c50
+  *nsectors = n;
b35c50
+  *sectors = map;
b35c50
 
b35c50
   return GRUB_ERR_NONE;
b35c50
 }
b35c50
diff --git a/include/grub/disk.h b/include/grub/disk.h
b35c50
index f95aca929a..06210a7049 100644
b35c50
--- a/include/grub/disk.h
b35c50
+++ b/include/grub/disk.h
b35c50
@@ -182,6 +182,8 @@ typedef struct grub_disk_memberlist *grub_disk_memberlist_t;
b35c50
 /* Return value of grub_disk_native_sectors() in case disk size is unknown. */
b35c50
 #define GRUB_DISK_SIZE_UNKNOWN	 0xffffffffffffffffULL
b35c50
 
b35c50
+#define GRUB_DISK_KiB_TO_SECTORS(x) ((x) << (10 - GRUB_DISK_SECTOR_BITS))
b35c50
+
b35c50
 /* Convert sector number from one sector size to another. */
b35c50
 static inline grub_disk_addr_t
b35c50
 grub_convert_sector (grub_disk_addr_t sector,