* [PATCH] x86/boot/compressed: Reserve more memory for page tables
@ 2023-09-14 12:30 Kirill A. Shutemov
2023-09-14 15:51 ` Dave Hansen
0 siblings, 1 reply; 4+ messages in thread
From: Kirill A. Shutemov @ 2023-09-14 12:30 UTC (permalink / raw)
To: Thomas Gleixner, Dave Hansen, Borislav Petkov
Cc: Ard Biesheuvel, Kees Cook, Aaron Lu, Bagas Sanjaya, Tom Lendacky,
x86, kexec, linux-kernel, regressions, Kirill A. Shutemov
The decompressor has a hard limit on the number of page tables it can
allocate. This limit is defined at compile-time and will cause boot
failure if it is reached.
The kernel is very strict and calculates the limit precisely for the
worst-case scenario based on the current configuration. However, it is
easy to forget to adjust the limit when a new use-case arises. The
worst-case scenario is rarely encountered during sanity checks.
In the case of enabling 5-level paging, a use-case was overlooked. The
limit needs to be increased by one to accommodate the additional level.
This oversight went unnoticed until Aaron attempted to run the kernel
via kexec with 5-level paging and unaccepted memory enabled.
To address this issue, let's allocate some extra space for page tables.
128K should be sufficient for any use-case. The logic can be simplified
by using a single value for all kernel configurations.
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Reported-by: Aaron Lu <aaron.lu@intel.com>
Fixes: 34bbb0009f3b ("x86/boot/compressed: Enable 5-level paging during decompression stage")
---
arch/x86/include/asm/boot.h | 27 ++++++++++++---------------
1 file changed, 12 insertions(+), 15 deletions(-)
diff --git a/arch/x86/include/asm/boot.h b/arch/x86/include/asm/boot.h
index 9191280d9ea3..aaf1b2fc6ede 100644
--- a/arch/x86/include/asm/boot.h
+++ b/arch/x86/include/asm/boot.h
@@ -40,23 +40,20 @@
#ifdef CONFIG_X86_64
# define BOOT_STACK_SIZE 0x4000
-# define BOOT_INIT_PGT_SIZE (6*4096)
-# ifdef CONFIG_RANDOMIZE_BASE
/*
- * Assuming all cross the 512GB boundary:
- * 1 page for level4
- * (2+2)*4 pages for kernel, param, cmd_line, and randomized kernel
- * 2 pages for first 2M (video RAM: CONFIG_X86_VERBOSE_BOOTUP).
- * Total is 19 pages.
+ * Used by decompressor's startup_32() to allocate page tables for identity
+ * mapping of the 4G of RAM in 4-level paging mode.
+ *
+ * The additional page table needed for 5-level paging is allocated from
+ * trampoline_32bit memory.
*/
-# ifdef CONFIG_X86_VERBOSE_BOOTUP
-# define BOOT_PGT_SIZE (19*4096)
-# else /* !CONFIG_X86_VERBOSE_BOOTUP */
-# define BOOT_PGT_SIZE (17*4096)
-# endif
-# else /* !CONFIG_RANDOMIZE_BASE */
-# define BOOT_PGT_SIZE BOOT_INIT_PGT_SIZE
-# endif
+# define BOOT_INIT_PGT_SIZE (6*4096)
+
+/*
+ * Total number of page table kernel_add_identity_map() can allocate,
+ * including page tables consumed by startup_32().
+ */
+# define BOOT_PGT_SIZE (32*4096)
#else /* !CONFIG_X86_64 */
# define BOOT_STACK_SIZE 0x1000
--
2.41.0
_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] x86/boot/compressed: Reserve more memory for page tables
2023-09-14 12:30 [PATCH] x86/boot/compressed: Reserve more memory for page tables Kirill A. Shutemov
@ 2023-09-14 15:51 ` Dave Hansen
2023-09-14 17:07 ` Kirill A. Shutemov
0 siblings, 1 reply; 4+ messages in thread
From: Dave Hansen @ 2023-09-14 15:51 UTC (permalink / raw)
To: Kirill A. Shutemov, Thomas Gleixner, Borislav Petkov
Cc: Ard Biesheuvel, Kees Cook, Aaron Lu, Bagas Sanjaya, Tom Lendacky,
x86, kexec, linux-kernel, regressions
[-- Attachment #1: Type: text/plain, Size: 898 bytes --]
On 9/14/23 05:30, Kirill A. Shutemov wrote:
> +/*
> + * Total number of page table kernel_add_identity_map() can allocate,
> + * including page tables consumed by startup_32().
> + */
> +# define BOOT_PGT_SIZE (32*4096)
I agree that needing to know this in advance *exactly* is troublesome.
But I do think that we should preserve the comment about the worst-case
scenario. Also, I thought this was triggered by unaccepted memory. Am
I remembering it wrong? How was it in play?
Either way, I think your general approach here is sound. But let's add
one little tweak to at least warn when we're getting close to the limit.
Now that nobody has to worry about the limit for the immediate future
it's a guarantee that in the long term someone will plow through it
accidentally.
Let's add a soft warning when we're nearing the limit so that there's a
chance to catch these things in the future.
[-- Attachment #2: pgtbuf.patch --]
[-- Type: text/x-patch, Size: 659 bytes --]
diff --git a/arch/x86/boot/compressed/ident_map_64.c b/arch/x86/boot/compressed/ident_map_64.c
index bcc956c17872..5dfb6a31bbb1 100644
--- a/arch/x86/boot/compressed/ident_map_64.c
+++ b/arch/x86/boot/compressed/ident_map_64.c
@@ -59,6 +59,13 @@ static void *alloc_pgt_page(void *context)
return NULL;
}
+ if (pages->pgt_buf_offset >= pages->pgt_buf_size * 3 / 4) {
+ debug_putstr("pgt_buf running low in " __FILE__ "\n");
+ debug_putstr("Need to raise BOOT_PGT_SIZE??\n");
+ debug_putaddr(pages->pgt_buf_offset);
+ debug_putaddr(pages->pgt_buf_size);
+ }
+
entry = pages->pgt_buf + pages->pgt_buf_offset;
pages->pgt_buf_offset += PAGE_SIZE;
[-- Attachment #3: Type: text/plain, Size: 143 bytes --]
_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] x86/boot/compressed: Reserve more memory for page tables
2023-09-14 15:51 ` Dave Hansen
@ 2023-09-14 17:07 ` Kirill A. Shutemov
2023-09-15 7:02 ` [PATCHv2] " Kirill A. Shutemov
0 siblings, 1 reply; 4+ messages in thread
From: Kirill A. Shutemov @ 2023-09-14 17:07 UTC (permalink / raw)
To: Dave Hansen
Cc: Thomas Gleixner, Borislav Petkov, Ard Biesheuvel, Kees Cook,
Aaron Lu, Bagas Sanjaya, Tom Lendacky, x86, kexec, linux-kernel,
regressions
On Thu, Sep 14, 2023 at 08:51:50AM -0700, Dave Hansen wrote:
> On 9/14/23 05:30, Kirill A. Shutemov wrote:
> > +/*
> > + * Total number of page table kernel_add_identity_map() can allocate,
> > + * including page tables consumed by startup_32().
> > + */
> > +# define BOOT_PGT_SIZE (32*4096)
>
> I agree that needing to know this in advance *exactly* is troublesome.
>
> But I do think that we should preserve the comment about the worst-case
> scenario.
Want me to send v2 for that?
> Also, I thought this was triggered by unaccepted memory. Am
> I remembering it wrong? How was it in play?
Unaccepted memory touched EFI system table. I was able to reproduce
without unaccepted memory enabled: if get_rsdp_addr() takes
efi_get_rsdp_addr() path. So it is not the root cause, just a trigger.
So we need several things to run into the problem:
- System supports 5-level paging and it is enabled;
- Decompressor takes control in 64-bit mode, so it uses page tables
inherited from bootloader until initialize_identity_maps().
In initialize_identity_maps() kernel resets page tables, rebuilding them
from scratch. Here we only map what is definitely required: kernel,
cmdline, boot_patams, setup_data.
Entering in 32-bit mode would make startup_32() map the first 4G
unconditionally, but in this setup we rely more on #PF to fill page
table. It masks problem as we rarely need all four PMD tables.
- Make kernel touch at least one page per-gigabyte in the first 4G.
In our case, unaccepted memory path was the last straw: it triggered
allocation of the fourth PMD table which failed.
We can increase the constant by one and it will work as long as nobody
need anything beyond the first 4G (or any 1G-aligned 4G region where we've
got loaded, I guess). I am not sure we can guarantee this with
(potentially buggy) ACPI and EFI in the picture.
> Either way, I think your general approach here is sound. But let's add
> one little tweak to at least warn when we're getting close to the limit.
Yeah, makes sense.
--
Kiryl Shutsemau / Kirill A. Shutemov
_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCHv2] x86/boot/compressed: Reserve more memory for page tables
2023-09-14 17:07 ` Kirill A. Shutemov
@ 2023-09-15 7:02 ` Kirill A. Shutemov
0 siblings, 0 replies; 4+ messages in thread
From: Kirill A. Shutemov @ 2023-09-15 7:02 UTC (permalink / raw)
To: dave.hansen
Cc: kirill.shutemov, aaron.lu, ardb, bagasdotme, bp, keescook, kexec,
linux-kernel, regressions, tglx, thomas.lendacky, x86
The decompressor has a hard limit on the number of page tables it can
allocate. This limit is defined at compile-time and will cause boot
failure if it is reached.
The kernel is very strict and calculates the limit precisely for the
worst-case scenario based on the current configuration. However, it is
easy to forget to adjust the limit when a new use-case arises. The
worst-case scenario is rarely encountered during sanity checks.
In the case of enabling 5-level paging, a use-case was overlooked. The
limit needs to be increased by one to accommodate the additional level.
This oversight went unnoticed until Aaron attempted to run the kernel
via kexec with 5-level paging and unaccepted memory enabled.
Update wost-case calculations to include 5-level paging.
To address this issue, let's allocate some extra space for page tables.
128K should be sufficient for any use-case. The logic can be simplified
by using a single value for all kernel configurations.
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Reported-by: Aaron Lu <aaron.lu@intel.com>
Fixes: 34bbb0009f3b ("x86/boot/compressed: Enable 5-level paging during decompression stage")
---
arch/x86/boot/compressed/ident_map_64.c | 8 +++++
arch/x86/include/asm/boot.h | 47 +++++++++++++++++--------
2 files changed, 40 insertions(+), 15 deletions(-)
diff --git a/arch/x86/boot/compressed/ident_map_64.c b/arch/x86/boot/compressed/ident_map_64.c
index bcc956c17872..08f93b0401bb 100644
--- a/arch/x86/boot/compressed/ident_map_64.c
+++ b/arch/x86/boot/compressed/ident_map_64.c
@@ -59,6 +59,14 @@ static void *alloc_pgt_page(void *context)
return NULL;
}
+ /* Consumed more tables than expected? */
+ if (pages->pgt_buf_offset == BOOT_PGT_SIZE_WARN) {
+ debug_putstr("pgt_buf running low in " __FILE__ "\n");
+ debug_putstr("Need to raise BOOT_PGT_SIZE?\n");
+ debug_putaddr(pages->pgt_buf_offset);
+ debug_putaddr(pages->pgt_buf_size);
+ }
+
entry = pages->pgt_buf + pages->pgt_buf_offset;
pages->pgt_buf_offset += PAGE_SIZE;
diff --git a/arch/x86/include/asm/boot.h b/arch/x86/include/asm/boot.h
index 9191280d9ea3..215d37f7dde8 100644
--- a/arch/x86/include/asm/boot.h
+++ b/arch/x86/include/asm/boot.h
@@ -40,23 +40,40 @@
#ifdef CONFIG_X86_64
# define BOOT_STACK_SIZE 0x4000
-# define BOOT_INIT_PGT_SIZE (6*4096)
-# ifdef CONFIG_RANDOMIZE_BASE
/*
- * Assuming all cross the 512GB boundary:
- * 1 page for level4
- * (2+2)*4 pages for kernel, param, cmd_line, and randomized kernel
- * 2 pages for first 2M (video RAM: CONFIG_X86_VERBOSE_BOOTUP).
- * Total is 19 pages.
+ * Used by decompressor's startup_32() to allocate page tables for identity
+ * mapping of the 4G of RAM in 4-level paging mode:
+ * - 1 level4 table;
+ * - 1 level3 table;
+ * - 4 level2 table that maps everything with 2M pages;
+ *
+ * The additional level5 table needed for 5-level paging is allocated from
+ * trampoline_32bit memory.
*/
-# ifdef CONFIG_X86_VERBOSE_BOOTUP
-# define BOOT_PGT_SIZE (19*4096)
-# else /* !CONFIG_X86_VERBOSE_BOOTUP */
-# define BOOT_PGT_SIZE (17*4096)
-# endif
-# else /* !CONFIG_RANDOMIZE_BASE */
-# define BOOT_PGT_SIZE BOOT_INIT_PGT_SIZE
-# endif
+# define BOOT_INIT_PGT_SIZE (6*4096)
+
+/*
+ * Total number of page tables kernel_add_identity_map() can allocate,
+ * including page tables consumed by startup_32().
+ *
+ * Worst-case scenario:
+ * - 5-level paging needs 1 level5 table;
+ * - KASLR needs to map kernel, boot_params, cmdline and randomized kernel,
+ * assuming all of them cross 256T boundary:
+ * + 4*2 level4 table;
+ * + 4*2 level3 table;
+ * + 4*2 level2 table;
+ * - X86_VERBOSE_BOOTUP needs to map the first 2M (video RAM):
+ * + 1 level4 table;
+ * + 1 level3 table;
+ * + 1 level2 table;
+ * Total: 28 tables
+ *
+ * Add 4 spare table in case decompressor touches anything beyond what is
+ * accounted above. Warn if it happens.
+ */
+# define BOOT_PGT_SIZE_WARN (28*4096)
+# define BOOT_PGT_SIZE (32*4096)
#else /* !CONFIG_X86_64 */
# define BOOT_STACK_SIZE 0x1000
--
2.41.0
_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2023-09-15 7:02 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-09-14 12:30 [PATCH] x86/boot/compressed: Reserve more memory for page tables Kirill A. Shutemov
2023-09-14 15:51 ` Dave Hansen
2023-09-14 17:07 ` Kirill A. Shutemov
2023-09-15 7:02 ` [PATCHv2] " Kirill A. Shutemov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox