[PATCH 1/2] efi: Fix reservation of unaccepted memory table

Kiryl Shutsemau (Meta) posted 2 patches 1 month, 2 weeks ago
There is a newer version of this series
[PATCH 1/2] efi: Fix reservation of unaccepted memory table
Posted by Kiryl Shutsemau (Meta) 1 month, 2 weeks ago
The reserve_unaccepted() function incorrectly calculates the size of the
memblock reservation for the unaccepted memory table. It aligns the
size of the table, but fails to account for cases where the table's
starting physical address (efi.unaccepted) is not page-aligned.

If the table starts at an offset within a page and its end crosses into
a subsequent page that the aligned size does not cover, the end of the
table will not be reserved. This can lead to the table being overwritten
or inaccessible, causing a kernel panic in accept_memory().

This issue was observed when starting Intel TDX VMs with specific memory
sizes (e.g., > 64GB).

Fix this by calculating the end address first (including the unaligned
start) and then aligning it up, ensuring the entire range is covered
by the reservation.

Fixes: 8dbe33956d96 ("efi/unaccepted: Make sure unaccepted table is mapped")
Reported-by: Moritz Sanft <ms@edgeless.systems>
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
---
 drivers/firmware/efi/efi.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 111e87a618e5..56e9d73412fa 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -692,13 +692,13 @@ static __init int match_config_table(const efi_guid_t *guid,
 
 static __init void reserve_unaccepted(struct efi_unaccepted_memory *unaccepted)
 {
-	phys_addr_t start, size;
+	phys_addr_t start, end;
 
 	start = PAGE_ALIGN_DOWN(efi.unaccepted);
-	size = PAGE_ALIGN(sizeof(*unaccepted) + unaccepted->size);
+	end = PAGE_ALIGN(efi.unaccepted + sizeof(*unaccepted) + unaccepted->size);
 
-	memblock_add(start, size);
-	memblock_reserve(start, size);
+	memblock_add(start, end - start);
+	memblock_reserve(start, end - start);
 }
 
 int __init efi_config_parse_tables(const efi_config_table_t *config_tables,
-- 
2.51.2
Re: [PATCH 1/2] efi: Fix reservation of unaccepted memory table
Posted by Dave Hansen 1 month, 2 weeks ago
On 2/13/26 07:48, Kiryl Shutsemau (Meta) wrote:
>  static __init void reserve_unaccepted(struct efi_unaccepted_memory *unaccepted)
>  {
> -	phys_addr_t start, size;
> +	phys_addr_t start, end;
>  
>  	start = PAGE_ALIGN_DOWN(efi.unaccepted);

Why are we even aligning the start? Isn't *that* the bug?

The memblock code seems to be able to handle arbitrary alignment just fine.
Re: [PATCH 1/2] efi: Fix reservation of unaccepted memory table
Posted by Kiryl Shutsemau 1 month, 2 weeks ago
On Fri, Feb 13, 2026 at 08:01:55AM -0800, Dave Hansen wrote:
> On 2/13/26 07:48, Kiryl Shutsemau (Meta) wrote:
> >  static __init void reserve_unaccepted(struct efi_unaccepted_memory *unaccepted)
> >  {
> > -	phys_addr_t start, size;
> > +	phys_addr_t start, end;
> >  
> >  	start = PAGE_ALIGN_DOWN(efi.unaccepted);
> 
> Why are we even aligning the start? Isn't *that* the bug?

How so? It is up to EFI how the table is allocated. We need to be sure
that this memory is mapped and not overwritten.

> The memblock code seems to be able to handle arbitrary alignment just fine.

Memblock will track it, but, as the comment says, anything smaller than
page size will not be mapped, but we need the table to be accessible by
kernel.

-- 
  Kiryl Shutsemau / Kirill A. Shutemov
Re: [PATCH 1/2] efi: Fix reservation of unaccepted memory table
Posted by Dave Hansen 1 month, 2 weeks ago
On 2/13/26 08:14, Kiryl Shutsemau wrote:
>> The memblock code seems to be able to handle arbitrary alignment just fine.
> Memblock will track it, but, as the comment says, anything smaller than
> page size will not be mapped, but we need the table to be accessible by
> kernel.

That seems really, really fragile.

We should first make sure this is intentional memblock behavior and not
a bug before we go add more hacks on top of it.

Why would you even present a byte-level reservation interface if it is
free to just silently ignore some of the ranges by rounding them off later?
Re: [PATCH 1/2] efi: Fix reservation of unaccepted memory table
Posted by Kiryl Shutsemau 1 month, 2 weeks ago
On Fri, Feb 13, 2026 at 08:46:55AM -0800, Dave Hansen wrote:
> On 2/13/26 08:14, Kiryl Shutsemau wrote:
> >> The memblock code seems to be able to handle arbitrary alignment just fine.
> > Memblock will track it, but, as the comment says, anything smaller than
> > page size will not be mapped, but we need the table to be accessible by
> > kernel.
> 
> That seems really, really fragile.
> 
> We should first make sure this is intentional memblock behavior and not
> a bug before we go add more hacks on top of it.
> 
> Why would you even present a byte-level reservation interface if it is
> free to just silently ignore some of the ranges by rounding them off later?

+Mike.

My guess that multiple memblock_add() calls might add up to the full
page size.

-- 
  Kiryl Shutsemau / Kirill A. Shutemov
Re: [PATCH 1/2] efi: Fix reservation of unaccepted memory table
Posted by Mike Rapoport 1 month, 2 weeks ago
On Fri, Feb 13, 2026 at 05:20:14PM +0000, Kiryl Shutsemau wrote:
> On Fri, Feb 13, 2026 at 08:46:55AM -0800, Dave Hansen wrote:
> > On 2/13/26 08:14, Kiryl Shutsemau wrote:
> > >> The memblock code seems to be able to handle arbitrary alignment just fine.
> > > Memblock will track it, but, as the comment says, anything smaller than
> > > page size will not be mapped, but we need the table to be accessible by
> > > kernel.
> > 
> > That seems really, really fragile.
> > 
> > We should first make sure this is intentional memblock behavior and not
> > a bug before we go add more hacks on top of it.
> > 
> > Why would you even present a byte-level reservation interface if it is
> > free to just silently ignore some of the ranges by rounding them off later?

Heh, it's x86's choice of memblock iterator that's rounding the ranges ;)

Maybe I miss some context, but my understanding is that for crash kernels
the unaccepted table is E820_TYPE_RESERVED and those are never added to
memblock.memory by e820 code, hence the call to memblock_add() in
reserve_unaccepted().

When x86 creates page tables, init_range_memory_mapping() walks
memblock.memory with for_each_mem_pfn_range() that rounds ranges that are
not page-aligned, which is normally fine, because it would mean that we
miss some partial pages that are divided between E820_RAM and
E820_SOMETHING_ELSE.

And Kiryl's intention to round up unaccepted to page boundary seems correct
to me.

> My guess that multiple memblock_add() calls might add up to the full
> page size.

I'm not following here. Can you explain what do you mean?

Multiple memblock_add() calls to adjacent ranges will coalesce into one
larger range. But I don't see how is that related.

> 
> -- 
>   Kiryl Shutsemau / Kirill A. Shutemov

-- 
Sincerely yours,
Mike.
Re: [PATCH 1/2] efi: Fix reservation of unaccepted memory table
Posted by Dave Hansen 1 month, 2 weeks ago
On 2/14/26 07:51, Mike Rapoport wrote:
> Heh, it's x86's choice of memblock iterator that's rounding the ranges 😉

Ahh, good point. I was just assuming that the memblock iteration _had_
to be over PFNs. Silly me.

> Maybe I miss some context, but my understanding is that for crash kernels
> the unaccepted table is E820_TYPE_RESERVED and those are never added to
> memblock.memory by e820 code, hence the call to memblock_add() in
> reserve_unaccepted().
> 
> When x86 creates page tables, init_range_memory_mapping() walks
> memblock.memory with for_each_mem_pfn_range() that rounds ranges that are
> not page-aligned, which is normally fine, because it would mean that we
> miss some partial pages that are divided between E820_RAM and
> E820_SOMETHING_ELSE.
> 
> And Kiryl's intention to round up unaccepted to page boundary seems correct
> to me.

It fixes the bug for sure.

I'm more worried about the next feature, or the existing features that
also only working because memory is page-aligned somewhere (even though
it isn't guaranteed to remain that way).

There are two choices for fixing this: One, we do Kiryl's fix plus
checks to ensure that all the memblocks that generate direct mappings
(is it _just_ the "memory" type?) are padded out to page-aligned boundaries.

The other alternative is to do for_each_mem_range() and do the padding
universally when creating the mappings. Maybe _also_ with warnings or
maybe a pr_debug().

I do still think it's a little wonky for memblock_add()'s management of
the "memory" type to allow unaligned arguments when that type is also
used to create page-aligned mapping structures. Memblocks themselves
obviously need to be byte-level, but I'm not sure it's the right thing
for the "memory" type API.
Re: [PATCH 1/2] efi: Fix reservation of unaccepted memory table
Posted by Mike Rapoport 1 month, 2 weeks ago
On Mon, Feb 16, 2026 at 07:53:24AM -0800, Dave Hansen wrote:
> On 2/14/26 07:51, Mike Rapoport wrote:
> > Heh, it's x86's choice of memblock iterator that's rounding the ranges 😉
> 
> Ahh, good point. I was just assuming that the memblock iteration _had_
> to be over PFNs. Silly me.
> 
> > Maybe I miss some context, but my understanding is that for crash kernels
> > the unaccepted table is E820_TYPE_RESERVED and those are never added to
> > memblock.memory by e820 code, hence the call to memblock_add() in
> > reserve_unaccepted().
> > 
> > When x86 creates page tables, init_range_memory_mapping() walks
> > memblock.memory with for_each_mem_pfn_range() that rounds ranges that are
> > not page-aligned, which is normally fine, because it would mean that we
> > miss some partial pages that are divided between E820_RAM and
> > E820_SOMETHING_ELSE.
> > 
> > And Kiryl's intention to round up unaccepted to page boundary seems correct
> > to me.
> 
> It fixes the bug for sure.
> 
> I'm more worried about the next feature, or the existing features that
> also only working because memory is page-aligned somewhere (even though
> it isn't guaranteed to remain that way).
> 
> There are two choices for fixing this: One, we do Kiryl's fix plus
> checks to ensure that all the memblocks that generate direct mappings
> (is it _just_ the "memory" type?) are padded out to page-aligned boundaries.
> 
> The other alternative is to do for_each_mem_range() and do the padding
> universally when creating the mappings. Maybe _also_ with warnings or
> maybe a pr_debug().
> 
> I do still think it's a little wonky for memblock_add()'s management of
> the "memory" type to allow unaligned arguments when that type is also
> used to create page-aligned mapping structures. Memblocks themselves
> obviously need to be byte-level, but I'm not sure it's the right thing
> for the "memory" type API.

Well, we could make memblock_add() implicitly cut down the edges when it's
adding to memblock.memory and make everything there page aligned, but I
truly have no idea what will break and I'm sure something will :)

Another thing that's more on x86 side, is that translation from e820 to
memblock only adds E820_TYPE_RAM to memblock. And since in e820 these are
mutually exclusive with other e820 types, this could create non-aligned
chunks when firmware reservations are not page aligned. It also creates
unnecessary holes in memblock.memory that slow down memblock interation a
bit and more interestingly, everything that's not in E820_TYPE_RAM is
treated as IO and requires ioremap/memremap for access, even it is in DRAM.

If these reserved regions were added to memblock.memory along with being
memblock_reserve()ed we wouldn't hit the bug with unaccepted I believe some
others as well.

-- 
Sincerely yours,
Mike.
Re: [PATCH 1/2] efi: Fix reservation of unaccepted memory table
Posted by Kiryl Shutsemau 1 month, 2 weeks ago
On Sat, Feb 14, 2026 at 05:51:47PM +0200, Mike Rapoport wrote:
> > My guess that multiple memblock_add() calls might add up to the full
> > page size.
> 
> I'm not following here. Can you explain what do you mean?
> 
> Multiple memblock_add() calls to adjacent ranges will coalesce into one
> larger range. But I don't see how is that related.

I tried to find justification for the byte-level tracking by memblock.
Not in relation to this case, but in general.

-- 
  Kiryl Shutsemau / Kirill A. Shutemov
Re: [PATCH 1/2] efi: Fix reservation of unaccepted memory table
Posted by Mike Rapoport 1 month, 2 weeks ago
On Mon, Feb 16, 2026 at 02:22:49PM +0000, Kiryl Shutsemau wrote:
> On Sat, Feb 14, 2026 at 05:51:47PM +0200, Mike Rapoport wrote:
> > > My guess that multiple memblock_add() calls might add up to the full
> > > page size.
> > 
> > I'm not following here. Can you explain what do you mean?
> > 
> > Multiple memblock_add() calls to adjacent ranges will coalesce into one
> > larger range. But I don't see how is that related.
> 
> I tried to find justification for the byte-level tracking by memblock.
> Not in relation to this case, but in general.

Probably somewhere deep in git archaeology :)

I presume to not waste a page for every small allocation.
 
> -- 
>   Kiryl Shutsemau / Kirill A. Shutemov

-- 
Sincerely yours,
Mike.