Memory hotplug in secure environments requires the unaccepted memory
bitmap to grow as new memory is added. Currently, the bitmap is
implemented as a flexible array member at the end of struct
efi_unaccepted_memory, which is reserved by memblock at boot and cannot
be resized without reallocating the entire structure.
Replace the flexible array member with a pointer. This allows the bitmap
to be allocated and managed independently from the unaccepted memory
table, enabling dynamic growth to support memory hotplug.
Signed-off-by: Pratik R. Sampat <prsampat@amd.com>
---
arch/x86/boot/compressed/efi.h | 2 +-
arch/x86/include/asm/unaccepted_memory.h | 9 +++++++++
.../firmware/efi/libstub/unaccepted_memory.c | 11 ++++++++++-
drivers/firmware/efi/unaccepted_memory.c | 19 ++++++++++++++-----
include/linux/efi.h | 2 +-
5 files changed, 35 insertions(+), 8 deletions(-)
diff --git a/arch/x86/boot/compressed/efi.h b/arch/x86/boot/compressed/efi.h
index b22300970f97..4f7027f33def 100644
--- a/arch/x86/boot/compressed/efi.h
+++ b/arch/x86/boot/compressed/efi.h
@@ -102,7 +102,7 @@ struct efi_unaccepted_memory {
u32 unit_size;
u64 phys_base;
u64 size;
- unsigned long bitmap[];
+ unsigned long *bitmap;
};
static inline int efi_guidcmp (efi_guid_t left, efi_guid_t right)
diff --git a/arch/x86/include/asm/unaccepted_memory.h b/arch/x86/include/asm/unaccepted_memory.h
index f5937e9866ac..5da80e68d718 100644
--- a/arch/x86/include/asm/unaccepted_memory.h
+++ b/arch/x86/include/asm/unaccepted_memory.h
@@ -24,4 +24,13 @@ static inline struct efi_unaccepted_memory *efi_get_unaccepted_table(void)
return NULL;
return __va(efi.unaccepted);
}
+
+static inline unsigned long *efi_get_unaccepted_bitmap(void)
+{
+ struct efi_unaccepted_memory *unaccepted = efi_get_unaccepted_table();
+
+ if (!unaccepted)
+ return NULL;
+ return __va(unaccepted->bitmap);
+}
#endif
diff --git a/drivers/firmware/efi/libstub/unaccepted_memory.c b/drivers/firmware/efi/libstub/unaccepted_memory.c
index 757dbe734a47..c1370fc14555 100644
--- a/drivers/firmware/efi/libstub/unaccepted_memory.c
+++ b/drivers/firmware/efi/libstub/unaccepted_memory.c
@@ -63,13 +63,22 @@ efi_status_t allocate_unaccepted_bitmap(__u32 nr_desc,
EFI_UNACCEPTED_UNIT_SIZE * BITS_PER_BYTE);
status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY,
- sizeof(*unaccepted_table) + bitmap_size,
+ sizeof(*unaccepted_table),
(void **)&unaccepted_table);
if (status != EFI_SUCCESS) {
efi_err("Failed to allocate unaccepted memory config table\n");
return status;
}
+ status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY,
+ bitmap_size,
+ (void **)&unaccepted_table->bitmap);
+ if (status != EFI_SUCCESS) {
+ efi_bs_call(free_pool, unaccepted_table);
+ efi_err("Failed to allocate unaccepted memory bitmap\n");
+ return status;
+ }
+
unaccepted_table->version = 1;
unaccepted_table->unit_size = EFI_UNACCEPTED_UNIT_SIZE;
unaccepted_table->phys_base = unaccepted_start;
diff --git a/drivers/firmware/efi/unaccepted_memory.c b/drivers/firmware/efi/unaccepted_memory.c
index c2c067eff634..4479aad258f8 100644
--- a/drivers/firmware/efi/unaccepted_memory.c
+++ b/drivers/firmware/efi/unaccepted_memory.c
@@ -36,7 +36,7 @@ void accept_memory(phys_addr_t start, unsigned long size)
unsigned long range_start, range_end;
struct accept_range range, *entry;
phys_addr_t end = start + size;
- unsigned long flags;
+ unsigned long flags, *bitmap;
u64 unit_size;
unaccepted = efi_get_unaccepted_table();
@@ -124,8 +124,12 @@ void accept_memory(phys_addr_t start, unsigned long size)
list_add(&range.list, &accepting_list);
range_start = range.start;
- for_each_set_bitrange_from(range_start, range_end, unaccepted->bitmap,
- range.end) {
+
+ bitmap = efi_get_unaccepted_bitmap();
+ if (!bitmap)
+ return;
+
+ for_each_set_bitrange_from(range_start, range_end, bitmap, range.end) {
unsigned long phys_start, phys_end;
unsigned long len = range_end - range_start;
@@ -147,7 +151,7 @@ void accept_memory(phys_addr_t start, unsigned long size)
arch_accept_memory(phys_start, phys_end);
spin_lock(&unaccepted_memory_lock);
- bitmap_clear(unaccepted->bitmap, range_start, len);
+ bitmap_clear(bitmap, range_start, len);
}
list_del(&range.list);
@@ -197,7 +201,12 @@ bool range_contains_unaccepted_memory(phys_addr_t start, unsigned long size)
spin_lock_irqsave(&unaccepted_memory_lock, flags);
while (start < end) {
- if (test_bit(start / unit_size, unaccepted->bitmap)) {
+ unsigned long *bitmap = efi_get_unaccepted_bitmap();
+
+ if (!bitmap)
+ break;
+
+ if (test_bit(start / unit_size, bitmap)) {
ret = true;
break;
}
diff --git a/include/linux/efi.h b/include/linux/efi.h
index a98cc39e7aaa..a74b393c54d8 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -545,7 +545,7 @@ struct efi_unaccepted_memory {
u32 unit_size;
u64 phys_base;
u64 size;
- unsigned long bitmap[];
+ unsigned long *bitmap;
};
/*
--
2.51.1
On Tue, Nov 25, 2025 at 11:57:50AM -0600, Pratik R. Sampat wrote: > Memory hotplug in secure environments requires the unaccepted memory > bitmap to grow as new memory is added. Currently, the bitmap is > implemented as a flexible array member at the end of struct > efi_unaccepted_memory, which is reserved by memblock at boot and cannot > be resized without reallocating the entire structure. > > Replace the flexible array member with a pointer. Well, it break interoperability between kernel before and after the patch. Consider kexec from kernel without the patch to the kernel with the patch and then back to older kernel. It is ABI break. Is re-allocating the entire structure such a big pain? -- Kiryl Shutsemau / Kirill A. Shutemov
Hi Kiryl, Thanks for you comments. On 11/26/25 5:08 AM, Kiryl Shutsemau wrote: > On Tue, Nov 25, 2025 at 11:57:50AM -0600, Pratik R. Sampat wrote: >> Memory hotplug in secure environments requires the unaccepted memory >> bitmap to grow as new memory is added. Currently, the bitmap is >> implemented as a flexible array member at the end of struct >> efi_unaccepted_memory, which is reserved by memblock at boot and cannot >> be resized without reallocating the entire structure. >> >> Replace the flexible array member with a pointer. > > Well, it break interoperability between kernel before and after the > patch. Consider kexec from kernel without the patch to the kernel with > the patch and then back to older kernel. It is ABI break. > > Is re-allocating the entire structure such a big pain? > We could do that. My concern is that we would then need to protect the entire table instead of just the bitmap, which may add an additional overhead? -- Pratik
On Wed, Nov 26, 2025 at 04:27:19PM -0600, Pratik R. Sampat wrote: > Hi Kiryl, > > Thanks for you comments. > > On 11/26/25 5:08 AM, Kiryl Shutsemau wrote: > > On Tue, Nov 25, 2025 at 11:57:50AM -0600, Pratik R. Sampat wrote: > >> Memory hotplug in secure environments requires the unaccepted memory > >> bitmap to grow as new memory is added. Currently, the bitmap is > >> implemented as a flexible array member at the end of struct > >> efi_unaccepted_memory, which is reserved by memblock at boot and cannot > >> be resized without reallocating the entire structure. > >> > >> Replace the flexible array member with a pointer. > > > > Well, it break interoperability between kernel before and after the > > patch. Consider kexec from kernel without the patch to the kernel with > > the patch and then back to older kernel. It is ABI break. > > > > Is re-allocating the entire structure such a big pain? > > > > We could do that. My concern is that we would then need to protect the > entire table instead of just the bitmap, which may add an additional > overhead? What additional overhead? The main contention is going to be on binmap anyway. -- Kiryl Shutsemau / Kirill A. Shutemov
© 2016 - 2025 Red Hat, Inc.