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