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.
160 lines
5.6 KiB
160 lines
5.6 KiB
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,
|
|
|