* [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving
@ 2024-11-25 9:54 David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 01/20] x86/kexec: Ensure control_code_page is mapped in kexec page tables David Woodhouse
` (19 more replies)
0 siblings, 20 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
Debugging kexec failures is painful, as anything going wrong in execution
of the critical relocate_kernel() function tends to just lead to a triple
fault. Thus leading to *weeks* of my life that I won't get back. Having
hacked something up for my own use, I figured I should share it...
Add a CONFIG_KEXEC_DEBUG option which sets up a trivial exception handler
in that environment, and outputs to the early_printk serial console if
configured. Currently only I/O-based 8250 serial ports are supported, but
that could be extended.
While we're here, clean the code up a little and fix some other problems.
Most notably, load a suitable GDT on the way back into the kernel after a
KEXEC_PRESERVE_CONTEXT invocation instead of trusting the called code to
do so. And explicitly map the control_code_page into the identmap used by
relocate_kernel() instead of depending on it being in the same superpage
as something else that gets mapped.
I should probably bring the i386 version into line with this, although
the lack of rip-based addressing makes all the PIC code a bit harder.
David Woodhouse (20):
x86/kexec: Ensure control_code_page is mapped in kexec page tables
x86/kexec: Restore GDT on return from preserve_context kexec
x86/kexec: Clean up and document register use in relocate_kernel_64.S
x86/kexec: Use named labels in swap_pages in relocate_kernel_64.S
x86/kexec: Only swap pages for preserve_context mode
x86/kexec: Allocate PGD for x86_64 transition page tables separately
x86/kexec: Copy control page into place in machine_kexec_prepare()
x86/kexec: Invoke copy of relocate_kernel() instead of the original
x86/kexec: Move relocate_kernel to kernel .data section
x86/kexec: Add data section to relocate_kernel
x86/kexec: Drop page_list argument from relocate_kernel()
x86/kexec: Eliminate writes through kernel mapping of relocate_kernel page
x86/kexec: Clean up register usage in relocate_kernel()
x86/kexec: Mark relocate_kernel page as ROX instead of RWX
x86/kexec: Add CONFIG_KEXEC_DEBUG option
x86/kexec: Debugging support: load a GDT
x86/kexec: Debugging support: Load an IDT and basic exception entry points
x86/kexec: Debugging support: Dump registers on exception
x86/kexec: Add 8250 serial port output
[DO NOT MERGE] x86/kexec: Add int3 in kexec path for testing
arch/x86/Kconfig.debug | 8 +
arch/x86/include/asm/kexec.h | 34 +++-
arch/x86/include/asm/sections.h | 1 +
arch/x86/kernel/callthunks.c | 6 +
arch/x86/kernel/early_printk.c | 6 +
arch/x86/kernel/machine_kexec_64.c | 127 ++++++++----
arch/x86/kernel/relocate_kernel_64.S | 385 +++++++++++++++++++++++++++--------
arch/x86/kernel/vmlinux.lds.S | 16 +-
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 01/20] x86/kexec: Ensure control_code_page is mapped in kexec page tables
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 10:29 ` [EXTERNAL] " David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 02/20] x86/kexec: Restore GDT on return from preserve_context kexec David Woodhouse
` (18 subsequent siblings)
19 siblings, 1 reply; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
The control_code_page should be explicitly mapped into the identity
mapped page tables for the relocate_kernel environment. This only seems
to have worked by luck before, because it tended to be within the same
2MiB or 1GiB large page already mapped for another reason.
A subsequent commit will reduce the control_code_page to a single 4KiB
page instead of a higher-order allocation, and seems to make it much
*less* likely that we get lucky with its placement. This leads to a
fault when relocate_kernel() first tries to access the page through its
identity-mapped virtual address.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Cc: stable@vger.kernel.org
---
arch/x86/kernel/machine_kexec_64.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index 9c9ac606893e..b9b6243ee223 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -240,6 +240,12 @@ static int init_pgtable(struct kimage *image, unsigned long start_pgtable)
if (direct_gbpages)
info.direct_gbpages = true;
+ /* Ensure the control code page itself is in the direct map */
+ result = kernel_ident_mapping_init(&info, level4p, start_pgtable + PAGE_SIZE,
+ start_pgtable + KEXEC_CONTROL_CODE_MAX_SIZE);
+ if (result)
+ return result;
+
for (i = 0; i < nr_pfn_mapped; i++) {
mstart = pfn_mapped[i].start << PAGE_SHIFT;
mend = pfn_mapped[i].end << PAGE_SHIFT;
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 02/20] x86/kexec: Restore GDT on return from preserve_context kexec
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 01/20] x86/kexec: Ensure control_code_page is mapped in kexec page tables David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 03/20] x86/kexec: Clean up and document register use in relocate_kernel_64.S David Woodhouse
` (17 subsequent siblings)
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
The restore_processor_state() function explicitly states that "the asm code
that gets us here will have restored a usable GDT". That wasn't true in the
case of returning from a preserve_context kexec. Make it so.
Without this, the kernel was depending on the called function to reload a
GDT which is appropriate for the kernel before returning.
Test program:
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/kexec.h>
#include <linux/reboot.h>
#include <sys/reboot.h>
#include <sys/syscall.h>
int main (void)
{
struct kexec_segment segment = {};
unsigned char purgatory[] = {
0x66, 0xba, 0xf8, 0x03, // mov $0x3f8, %dx
0xb0, 0x42, // mov $0x42, %al
0xee, // outb %al, (%dx)
0xc3, // ret
};
int ret;
segment.buf = &purgatory;
segment.bufsz = sizeof(purgatory);
segment.mem = (void *)0x400000;
segment.memsz = 0x1000;
ret = syscall(__NR_kexec_load, 0x400000, 1, &segment, KEXEC_PRESERVE_CONTEXT);
if (ret) {
perror("kexec_load");
exit(1);
}
ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_KEXEC);
if (ret) {
perror("kexec reboot");
exit(1);
}
printf("Success\n");
return 0;
}
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Cc: stable@vger.kernel.org
---
arch/x86/kernel/relocate_kernel_64.S | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index e9e88c342f75..1236f25fc8d1 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -242,6 +242,13 @@ SYM_CODE_START_LOCAL_NOALIGN(virtual_mapped)
movq CR0(%r8), %r8
movq %rax, %cr3
movq %r8, %cr0
+
+#ifdef CONFIG_KEXEC_JUMP
+ /* Saved in save_processor_state. */
+ movq $saved_context, %rax
+ lgdt saved_context_gdt_desc(%rax)
+#endif
+
movq %rbp, %rax
popf
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 03/20] x86/kexec: Clean up and document register use in relocate_kernel_64.S
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 01/20] x86/kexec: Ensure control_code_page is mapped in kexec page tables David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 02/20] x86/kexec: Restore GDT on return from preserve_context kexec David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 04/20] x86/kexec: Use named labels in swap_pages " David Woodhouse
` (16 subsequent siblings)
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
Add more comments explaining what each register contains, and save the
preserve_context flag to a non-clobbered register sooner, to keep things
simpler.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Acked-by: Kai Huang <kai.huang@intel.com>
---
arch/x86/kernel/relocate_kernel_64.S | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index 1236f25fc8d1..92478e2e254f 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -100,6 +100,9 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
movq %r10, CP_PA_SWAP_PAGE(%r11)
movq %rdi, CP_PA_BACKUP_PAGES_MAP(%r11)
+ /* Save the preserve_context to %r11 as swap_pages clobbers %rcx. */
+ movq %rcx, %r11
+
/* Switch to the identity mapped page tables */
movq %r9, %cr3
@@ -116,6 +119,14 @@ SYM_CODE_END(relocate_kernel)
SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
UNWIND_HINT_END_OF_STACK
+ /*
+ * %rdi indirection page
+ * %rdx start address
+ * %r11 preserve_context
+ * %r12 host_mem_enc_active
+ * %r13 original CR4 when relocate_kernel() was invoked
+ */
+
/* set return address to 0 if not preserving context */
pushq $0
/* store the start address on the stack */
@@ -170,8 +181,6 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
wbinvd
.Lsme_off:
- /* Save the preserve_context to %r11 as swap_pages clobbers %rcx. */
- movq %rcx, %r11
call swap_pages
/*
@@ -183,13 +192,14 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
movq %cr3, %rax
movq %rax, %cr3
+ testq %r11, %r11 /* preserve_context */
+ jnz .Lrelocate
+
/*
* set all of the registers to known values
* leave %rsp alone
*/
- testq %r11, %r11
- jnz .Lrelocate
xorl %eax, %eax
xorl %ebx, %ebx
xorl %ecx, %ecx
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 04/20] x86/kexec: Use named labels in swap_pages in relocate_kernel_64.S
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
` (2 preceding siblings ...)
2024-11-25 9:54 ` [RFC PATCH v3 03/20] x86/kexec: Clean up and document register use in relocate_kernel_64.S David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 05/20] x86/kexec: Only swap pages for preserve_context mode David Woodhouse
` (15 subsequent siblings)
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
Make the code a little more readable.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Acked-by: Kai Huang <kai.huang@intel.com>
---
arch/x86/kernel/relocate_kernel_64.S | 30 ++++++++++++++--------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index 92478e2e254f..fea650f92606 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -279,31 +279,31 @@ SYM_CODE_START_LOCAL_NOALIGN(swap_pages)
movq %rdi, %rcx /* Put the indirection_page in %rcx */
xorl %edi, %edi
xorl %esi, %esi
- jmp 1f
+ jmp .Lstart /* Should start with an indirection record */
-0: /* top, read another word for the indirection page */
+.Lloop: /* top, read another word for the indirection page */
movq (%rbx), %rcx
addq $8, %rbx
-1:
+.Lstart:
testb $0x1, %cl /* is it a destination page? */
- jz 2f
+ jz .Lnotdest
movq %rcx, %rdi
andq $0xfffffffffffff000, %rdi
- jmp 0b
-2:
+ jmp .Lloop
+.Lnotdest:
testb $0x2, %cl /* is it an indirection page? */
- jz 2f
+ jz .Lnotind
movq %rcx, %rbx
andq $0xfffffffffffff000, %rbx
- jmp 0b
-2:
+ jmp .Lloop
+.Lnotind:
testb $0x4, %cl /* is it the done indicator? */
- jz 2f
- jmp 3f
-2:
+ jz .Lnotdone
+ jmp .Ldone
+.Lnotdone:
testb $0x8, %cl /* is it the source indicator? */
- jz 0b /* Ignore it otherwise */
+ jz .Lloop /* Ignore it otherwise */
movq %rcx, %rsi /* For ever source page do a copy */
andq $0xfffffffffffff000, %rsi
@@ -328,8 +328,8 @@ SYM_CODE_START_LOCAL_NOALIGN(swap_pages)
rep ; movsq
lea PAGE_SIZE(%rax), %rsi
- jmp 0b
-3:
+ jmp .Lloop
+.Ldone:
ANNOTATE_UNRET_SAFE
ret
int3
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 05/20] x86/kexec: Only swap pages for preserve_context mode
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
` (3 preceding siblings ...)
2024-11-25 9:54 ` [RFC PATCH v3 04/20] x86/kexec: Use named labels in swap_pages " David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 06/20] x86/kexec: Allocate PGD for x86_64 transition page tables separately David Woodhouse
` (14 subsequent siblings)
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
There's no need to swap pages (which involves three memcopies for each
page) in the plain kexec case. Just do a single copy from source to
destination page.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/kernel/relocate_kernel_64.S | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index fea650f92606..50cc33f2ecb7 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -310,6 +310,9 @@ SYM_CODE_START_LOCAL_NOALIGN(swap_pages)
movq %rdi, %rdx /* Save destination page to %rdx */
movq %rsi, %rax /* Save source page to %rax */
+ testq %r11, %r11 /* Only actually swap for preserve_context */
+ jz .Lnoswap
+
/* copy source page to swap page */
movq %r10, %rdi
movl $512, %ecx
@@ -324,6 +327,7 @@ SYM_CODE_START_LOCAL_NOALIGN(swap_pages)
/* copy swap page to destination page */
movq %rdx, %rdi
movq %r10, %rsi
+.Lnoswap:
movl $512, %ecx
rep ; movsq
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 06/20] x86/kexec: Allocate PGD for x86_64 transition page tables separately
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
` (4 preceding siblings ...)
2024-11-25 9:54 ` [RFC PATCH v3 05/20] x86/kexec: Only swap pages for preserve_context mode David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 07/20] x86/kexec: Copy control page into place in machine_kexec_prepare() David Woodhouse
` (13 subsequent siblings)
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
There's no good reason for this to be part of the control_code_page; just
allocate it separately on x86_64 like i386 does.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/include/asm/kexec.h | 18 ++++++++---
arch/x86/kernel/machine_kexec_64.c | 49 ++++++++++++++++--------------
2 files changed, 40 insertions(+), 27 deletions(-)
diff --git a/arch/x86/include/asm/kexec.h b/arch/x86/include/asm/kexec.h
index ae5482a2f0ca..ccb8ff37fa9d 100644
--- a/arch/x86/include/asm/kexec.h
+++ b/arch/x86/include/asm/kexec.h
@@ -16,6 +16,7 @@
# define PAGES_NR 4
#endif
+# define KEXEC_CONTROL_PAGE_SIZE 4096
# define KEXEC_CONTROL_CODE_MAX_SIZE 2048
#ifndef __ASSEMBLY__
@@ -43,7 +44,6 @@ struct kimage;
/* Maximum address we can use for the control code buffer */
# define KEXEC_CONTROL_MEMORY_LIMIT TASK_SIZE
-# define KEXEC_CONTROL_PAGE_SIZE 4096
/* The native architecture */
# define KEXEC_ARCH KEXEC_ARCH_386
@@ -58,9 +58,6 @@ struct kimage;
/* Maximum address we can use for the control pages */
# define KEXEC_CONTROL_MEMORY_LIMIT (MAXMEM-1)
-/* Allocate one page for the pdp and the second for the code */
-# define KEXEC_CONTROL_PAGE_SIZE (4096UL + 4096UL)
-
/* The native architecture */
# define KEXEC_ARCH KEXEC_ARCH_X86_64
#endif
@@ -145,6 +142,19 @@ struct kimage_arch {
};
#else
struct kimage_arch {
+ /*
+ * This is a kimage control page, as it must not overlap with either
+ * source or destination address ranges.
+ */
+ pgd_t *pgd;
+ /*
+ * The virtual mapping of the control code page itself is used only
+ * during the transition, while the current kernel's pages are all
+ * in place. Thus the intermediate page table pages used to map it
+ * are not control pages, but instead just normal pages obtained
+ * with get_zeroed_page(). And have to be tracked (below) so that
+ * they can be freed.
+ */
p4d_t *p4d;
pud_t *pud;
pmd_t *pmd;
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index b9b6243ee223..c9ae65c9a27c 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -146,7 +146,8 @@ static void free_transition_pgtable(struct kimage *image)
image->arch.pte = NULL;
}
-static int init_transition_pgtable(struct kimage *image, pgd_t *pgd)
+static int init_transition_pgtable(struct kimage *image, pgd_t *pgd,
+ unsigned long control_page)
{
pgprot_t prot = PAGE_KERNEL_EXEC_NOENC;
unsigned long vaddr, paddr;
@@ -157,7 +158,7 @@ static int init_transition_pgtable(struct kimage *image, pgd_t *pgd)
pte_t *pte;
vaddr = (unsigned long)relocate_kernel;
- paddr = __pa(page_address(image->control_code_page)+PAGE_SIZE);
+ paddr = control_page;
pgd += pgd_index(vaddr);
if (!pgd_present(*pgd)) {
p4d = (p4d_t *)get_zeroed_page(GFP_KERNEL);
@@ -216,7 +217,7 @@ static void *alloc_pgt_page(void *data)
return p;
}
-static int init_pgtable(struct kimage *image, unsigned long start_pgtable)
+static int init_pgtable(struct kimage *image, unsigned long control_page)
{
struct x86_mapping_info info = {
.alloc_pgt_page = alloc_pgt_page,
@@ -225,12 +226,12 @@ static int init_pgtable(struct kimage *image, unsigned long start_pgtable)
.kernpg_flag = _KERNPG_TABLE_NOENC,
};
unsigned long mstart, mend;
- pgd_t *level4p;
int result;
int i;
- level4p = (pgd_t *)__va(start_pgtable);
- clear_page(level4p);
+ image->arch.pgd = alloc_pgt_page(image);
+ if (!image->arch.pgd)
+ return -ENOMEM;
if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
info.page_flag |= _PAGE_ENC;
@@ -241,8 +242,8 @@ static int init_pgtable(struct kimage *image, unsigned long start_pgtable)
info.direct_gbpages = true;
/* Ensure the control code page itself is in the direct map */
- result = kernel_ident_mapping_init(&info, level4p, start_pgtable + PAGE_SIZE,
- start_pgtable + KEXEC_CONTROL_CODE_MAX_SIZE);
+ result = kernel_ident_mapping_init(&info, image->arch.pgd, control_page,
+ control_page + KEXEC_CONTROL_CODE_MAX_SIZE);
if (result)
return result;
@@ -250,8 +251,8 @@ static int init_pgtable(struct kimage *image, unsigned long start_pgtable)
mstart = pfn_mapped[i].start << PAGE_SHIFT;
mend = pfn_mapped[i].end << PAGE_SHIFT;
- result = kernel_ident_mapping_init(&info,
- level4p, mstart, mend);
+ result = kernel_ident_mapping_init(&info, image->arch.pgd,
+ mstart, mend);
if (result)
return result;
}
@@ -266,8 +267,8 @@ static int init_pgtable(struct kimage *image, unsigned long start_pgtable)
mstart = image->segment[i].mem;
mend = mstart + image->segment[i].memsz;
- result = kernel_ident_mapping_init(&info,
- level4p, mstart, mend);
+ result = kernel_ident_mapping_init(&info, image->arch.pgd,
+ mstart, mend);
if (result)
return result;
@@ -277,15 +278,19 @@ static int init_pgtable(struct kimage *image, unsigned long start_pgtable)
* Prepare EFI systab and ACPI tables for kexec kernel since they are
* not covered by pfn_mapped.
*/
- result = map_efi_systab(&info, level4p);
+ result = map_efi_systab(&info, image->arch.pgd);
if (result)
return result;
- result = map_acpi_tables(&info, level4p);
+ result = map_acpi_tables(&info, image->arch.pgd);
if (result)
return result;
- return init_transition_pgtable(image, level4p);
+ /*
+ * This must be last because the intermediate page table pages it
+ * allocates will not be control pages and may overlap the image.
+ */
+ return init_transition_pgtable(image, image->arch.pgd, control_page);
}
static void load_segments(void)
@@ -302,14 +307,14 @@ static void load_segments(void)
int machine_kexec_prepare(struct kimage *image)
{
- unsigned long start_pgtable;
+ unsigned long control_page;
int result;
/* Calculate the offsets */
- start_pgtable = page_to_pfn(image->control_code_page) << PAGE_SHIFT;
+ control_page = page_to_pfn(image->control_code_page) << PAGE_SHIFT;
/* Setup the identity mapped 64bit page table */
- result = init_pgtable(image, start_pgtable);
+ result = init_pgtable(image, control_page);
if (result)
return result;
@@ -363,13 +368,12 @@ void machine_kexec(struct kimage *image)
#endif
}
- control_page = page_address(image->control_code_page) + PAGE_SIZE;
+ control_page = page_address(image->control_code_page);
__memcpy(control_page, relocate_kernel, KEXEC_CONTROL_CODE_MAX_SIZE);
page_list[PA_CONTROL_PAGE] = virt_to_phys(control_page);
page_list[VA_CONTROL_PAGE] = (unsigned long)control_page;
- page_list[PA_TABLE_PAGE] =
- (unsigned long)__pa(page_address(image->control_code_page));
+ page_list[PA_TABLE_PAGE] = (unsigned long)__pa(image->arch.pgd);
if (image->type == KEXEC_TYPE_DEFAULT)
page_list[PA_SWAP_PAGE] = (page_to_pfn(image->swap_page)
@@ -579,8 +583,7 @@ static void kexec_mark_crashkres(bool protect)
/* Don't touch the control code page used in crash_kexec().*/
control = PFN_PHYS(page_to_pfn(kexec_crash_image->control_code_page));
- /* Control code page is located in the 2nd page. */
- kexec_mark_range(crashk_res.start, control + PAGE_SIZE - 1, protect);
+ kexec_mark_range(crashk_res.start, control - 1, protect);
control += KEXEC_CONTROL_PAGE_SIZE;
kexec_mark_range(control, crashk_res.end, protect);
}
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 07/20] x86/kexec: Copy control page into place in machine_kexec_prepare()
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
` (5 preceding siblings ...)
2024-11-25 9:54 ` [RFC PATCH v3 06/20] x86/kexec: Allocate PGD for x86_64 transition page tables separately David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 08/20] x86/kexec: Invoke copy of relocate_kernel() instead of the original David Woodhouse
` (12 subsequent siblings)
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
There's no need for this to wait until the actual machine_kexec() invocation;
future changes will need to make the control page read-only and executable,
so all writes should be completed before machine_kexec_prepare() returns.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/kernel/machine_kexec_64.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index c9ae65c9a27c..431a117f3fb3 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -307,17 +307,16 @@ static void load_segments(void)
int machine_kexec_prepare(struct kimage *image)
{
- unsigned long control_page;
+ void *control_page = page_address(image->control_code_page);
int result;
- /* Calculate the offsets */
- control_page = page_to_pfn(image->control_code_page) << PAGE_SHIFT;
-
/* Setup the identity mapped 64bit page table */
- result = init_pgtable(image, control_page);
+ result = init_pgtable(image, __pa(control_page));
if (result)
return result;
+ __memcpy(control_page, relocate_kernel, KEXEC_CONTROL_CODE_MAX_SIZE);
+
return 0;
}
@@ -369,7 +368,6 @@ void machine_kexec(struct kimage *image)
}
control_page = page_address(image->control_code_page);
- __memcpy(control_page, relocate_kernel, KEXEC_CONTROL_CODE_MAX_SIZE);
page_list[PA_CONTROL_PAGE] = virt_to_phys(control_page);
page_list[VA_CONTROL_PAGE] = (unsigned long)control_page;
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 08/20] x86/kexec: Invoke copy of relocate_kernel() instead of the original
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
` (6 preceding siblings ...)
2024-11-25 9:54 ` [RFC PATCH v3 07/20] x86/kexec: Copy control page into place in machine_kexec_prepare() David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 09/20] x86/kexec: Move relocate_kernel to kernel .data section David Woodhouse
` (11 subsequent siblings)
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
This currently calls set_memory_x() from machine_kexec_prepare() just
like the 32-bit version does. That's actually a bit earlier than I'd
like, as it leaves the page RWX all the time the image is even *loaded*.
Subsequent commits will eliminate all the writes to the page between the
point it's marked executable in machine_kexec_prepare() the time that
relocate_kernel() is running and has switched to the identmap %cr3, so
that it can be ROX. But that can't happen until it's moved to the .data
section of the kernel, and *that* can't happen until we start executing
the copy instead of executing it in place in the kernel .text. So break
the circular dependency in those commits by letting it be RWX for now.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/kernel/machine_kexec_64.c | 30 ++++++++++++++++++++++------
arch/x86/kernel/relocate_kernel_64.S | 5 ++++-
2 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index 431a117f3fb3..6fcf54e87d44 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -157,7 +157,12 @@ static int init_transition_pgtable(struct kimage *image, pgd_t *pgd,
pmd_t *pmd;
pte_t *pte;
- vaddr = (unsigned long)relocate_kernel;
+ /*
+ * For the transition to the identity mapped page tables, the control
+ * code page also needs to be mapped at the virtual address it starts
+ * off running from.
+ */
+ vaddr = (unsigned long)__va(control_page);
paddr = control_page;
pgd += pgd_index(vaddr);
if (!pgd_present(*pgd)) {
@@ -317,11 +322,17 @@ int machine_kexec_prepare(struct kimage *image)
__memcpy(control_page, relocate_kernel, KEXEC_CONTROL_CODE_MAX_SIZE);
+ set_memory_x((unsigned long)control_page, 1);
+
return 0;
}
void machine_kexec_cleanup(struct kimage *image)
{
+ void *control_page = page_address(image->control_code_page);
+
+ set_memory_nx((unsigned long)control_page, 1);
+
free_transition_pgtable(image);
}
@@ -331,6 +342,11 @@ void machine_kexec_cleanup(struct kimage *image)
*/
void machine_kexec(struct kimage *image)
{
+ unsigned long (*relocate_kernel_ptr)(unsigned long indirection_page,
+ unsigned long page_list,
+ unsigned long start_address,
+ unsigned int preserve_context,
+ unsigned int host_mem_enc_active);
unsigned long page_list[PAGES_NR];
unsigned int host_mem_enc_active;
int save_ftrace_enabled;
@@ -377,6 +393,8 @@ void machine_kexec(struct kimage *image)
page_list[PA_SWAP_PAGE] = (page_to_pfn(image->swap_page)
<< PAGE_SHIFT);
+ relocate_kernel_ptr = control_page;
+
/*
* The segment registers are funny things, they have both a
* visible and an invisible part. Whenever the visible part is
@@ -396,11 +414,11 @@ void machine_kexec(struct kimage *image)
native_gdt_invalidate();
/* now call it */
- image->start = relocate_kernel((unsigned long)image->head,
- (unsigned long)page_list,
- image->start,
- image->preserve_context,
- host_mem_enc_active);
+ image->start = relocate_kernel_ptr((unsigned long)image->head,
+ (unsigned long)page_list,
+ image->start,
+ image->preserve_context,
+ host_mem_enc_active);
#ifdef CONFIG_KEXEC_JUMP
if (image->preserve_context)
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index 50cc33f2ecb7..b48bd82843fd 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -39,6 +39,7 @@
#define CP_PA_TABLE_PAGE DATA(0x20)
#define CP_PA_SWAP_PAGE DATA(0x28)
#define CP_PA_BACKUP_PAGES_MAP DATA(0x30)
+#define CP_VA_CONTROL_PAGE DATA(0x38)
.text
.align PAGE_SIZE
@@ -99,6 +100,7 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
movq %r9, CP_PA_TABLE_PAGE(%r11)
movq %r10, CP_PA_SWAP_PAGE(%r11)
movq %rdi, CP_PA_BACKUP_PAGES_MAP(%r11)
+ movq %r11, CP_VA_CONTROL_PAGE(%r11)
/* Save the preserve_context to %r11 as swap_pages clobbers %rcx. */
movq %rcx, %r11
@@ -235,7 +237,8 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
movq %rax, %cr3
lea PAGE_SIZE(%r8), %rsp
call swap_pages
- movq $virtual_mapped, %rax
+ movq CP_VA_CONTROL_PAGE(%r8), %rax
+ addq $(virtual_mapped - relocate_kernel), %rax
pushq %rax
ANNOTATE_UNRET_SAFE
ret
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 09/20] x86/kexec: Move relocate_kernel to kernel .data section
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
` (7 preceding siblings ...)
2024-11-25 9:54 ` [RFC PATCH v3 08/20] x86/kexec: Invoke copy of relocate_kernel() instead of the original David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 10/20] x86/kexec: Add data section to relocate_kernel David Woodhouse
` (10 subsequent siblings)
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
Now that the copy is executed instead of the original, the relocate_kernel
page can live in the kernel's .text section. This will allow subsequent
commits to actually add real data to it and clean up the code somewhat as
well as making the control page ROX.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/include/asm/sections.h | 1 +
arch/x86/kernel/callthunks.c | 6 ++++++
arch/x86/kernel/machine_kexec_64.c | 4 +++-
arch/x86/kernel/relocate_kernel_64.S | 7 +------
arch/x86/kernel/vmlinux.lds.S | 15 ++++++++++++++-
5 files changed, 25 insertions(+), 8 deletions(-)
diff --git a/arch/x86/include/asm/sections.h b/arch/x86/include/asm/sections.h
index 3fa87e5e11ab..30e8ee7006f9 100644
--- a/arch/x86/include/asm/sections.h
+++ b/arch/x86/include/asm/sections.h
@@ -5,6 +5,7 @@
#include <asm-generic/sections.h>
#include <asm/extable.h>
+extern char __relocate_kernel_start[], __relocate_kernel_end[];
extern char __brk_base[], __brk_limit[];
extern char __end_rodata_aligned[];
diff --git a/arch/x86/kernel/callthunks.c b/arch/x86/kernel/callthunks.c
index 465647456753..51c3e0049152 100644
--- a/arch/x86/kernel/callthunks.c
+++ b/arch/x86/kernel/callthunks.c
@@ -139,9 +139,15 @@ static bool skip_addr(void *dest)
return true;
#endif
#ifdef CONFIG_KEXEC_CORE
+# ifdef CONFIG_X86_64
+ if (dest >= (void *)__relocate_kernel_start &&
+ dest < (void *)__relocate_kernel_end)
+ return true;
+# else
if (dest >= (void *)relocate_kernel &&
dest < (void*)relocate_kernel + KEXEC_CONTROL_CODE_MAX_SIZE)
return true;
+# endif
#endif
#ifdef CONFIG_XEN
if (dest >= (void *)hypercall_page &&
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index 6fcf54e87d44..76a45a76e0fe 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -313,6 +313,8 @@ static void load_segments(void)
int machine_kexec_prepare(struct kimage *image)
{
void *control_page = page_address(image->control_code_page);
+ unsigned long reloc_start = (unsigned long)__relocate_kernel_start;
+ unsigned long reloc_end = (unsigned long)__relocate_kernel_end;
int result;
/* Setup the identity mapped 64bit page table */
@@ -320,7 +322,7 @@ int machine_kexec_prepare(struct kimage *image)
if (result)
return result;
- __memcpy(control_page, relocate_kernel, KEXEC_CONTROL_CODE_MAX_SIZE);
+ __memcpy(control_page, __relocate_kernel_start, reloc_end - reloc_start);
set_memory_x((unsigned long)control_page, 1);
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index b48bd82843fd..01138f862c59 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -41,10 +41,8 @@
#define CP_PA_BACKUP_PAGES_MAP DATA(0x30)
#define CP_VA_CONTROL_PAGE DATA(0x38)
- .text
- .align PAGE_SIZE
+ .section .text.relocate_kernel,"ax";
.code64
-SYM_CODE_START_NOALIGN(relocate_range)
SYM_CODE_START_NOALIGN(relocate_kernel)
UNWIND_HINT_END_OF_STACK
ANNOTATE_NOENDBR
@@ -341,6 +339,3 @@ SYM_CODE_START_LOCAL_NOALIGN(swap_pages)
ret
int3
SYM_CODE_END(swap_pages)
-
- .skip KEXEC_CONTROL_CODE_MAX_SIZE - (. - relocate_kernel), 0xcc
-SYM_CODE_END(relocate_range);
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index b8c5741d2fb4..1ff23a4bbf03 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -28,6 +28,7 @@
#include <asm/orc_lookup.h>
#include <asm/cache.h>
#include <asm/boot.h>
+#include <asm/kexec.h>
#undef i386 /* in case the preprocessor is a 32bit one */
@@ -95,7 +96,18 @@ const_pcpu_hot = pcpu_hot;
#define BSS_DECRYPTED
#endif
-
+#if defined(CONFIG_X86_64) && defined(CONFIG_KEXEC_CORE)
+#define KEXEC_RELOCATE_KERNEL \
+ . = ALIGN(0x100); \
+ __relocate_kernel_start = .; \
+ *(.text.relocate_kernel); \
+ __relocate_kernel_end = .;
+
+ASSERT(__relocate_kernel_end - __relocate_kernel_start <= KEXEC_CONTROL_CODE_MAX_SIZE,
+ "relocate_kernel code too large!")
+#else
+#define KEXEC_RELOCATE_KERNEL
+#endif
PHDRS {
text PT_LOAD FLAGS(5); /* R_E */
data PT_LOAD FLAGS(6); /* RW_ */
@@ -181,6 +193,7 @@ SECTIONS
DATA_DATA
CONSTRUCTORS
+ KEXEC_RELOCATE_KERNEL
/* rarely changed data like cpu maps */
READ_MOSTLY_DATA(INTERNODE_CACHE_BYTES)
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 10/20] x86/kexec: Add data section to relocate_kernel
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
` (8 preceding siblings ...)
2024-11-25 9:54 ` [RFC PATCH v3 09/20] x86/kexec: Move relocate_kernel to kernel .data section David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 11/20] x86/kexec: Drop page_list argument from relocate_kernel() David Woodhouse
` (9 subsequent siblings)
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
Now that the relocate_kernel page is handled sanely by a linker script
we can have actual data, and just use %rip-relative addressing to access
it.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/kernel/machine_kexec_64.c | 8 +++-
arch/x86/kernel/relocate_kernel_64.S | 62 ++++++++++++++--------------
arch/x86/kernel/vmlinux.lds.S | 1 +
3 files changed, 38 insertions(+), 33 deletions(-)
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index 76a45a76e0fe..60232517f368 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -349,6 +349,7 @@ void machine_kexec(struct kimage *image)
unsigned long start_address,
unsigned int preserve_context,
unsigned int host_mem_enc_active);
+ unsigned long reloc_start = (unsigned long)__relocate_kernel_start;
unsigned long page_list[PAGES_NR];
unsigned int host_mem_enc_active;
int save_ftrace_enabled;
@@ -395,7 +396,12 @@ void machine_kexec(struct kimage *image)
page_list[PA_SWAP_PAGE] = (page_to_pfn(image->swap_page)
<< PAGE_SHIFT);
- relocate_kernel_ptr = control_page;
+ /*
+ * Allow for the possibility that relocate_kernel might not be at
+ * the very start of the page.
+ */
+ relocate_kernel_ptr = control_page + (unsigned long)relocate_kernel -
+ reloc_start;
/*
* The segment registers are funny things, they have both a
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index 01138f862c59..469af51589ee 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -23,23 +23,21 @@
#define PAGE_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY)
/*
- * control_page + KEXEC_CONTROL_CODE_MAX_SIZE
- * ~ control_page + PAGE_SIZE are used as data storage and stack for
- * jumping back
+ * The .text.relocate_kernel and .data.relocate_kernel sections are copied
+ * into the control page, and the remainder of the page is used as the stack.
*/
-#define DATA(offset) (KEXEC_CONTROL_CODE_MAX_SIZE+(offset))
+ .section .data.relocate_kernel,"a";
/* Minimal CPU state */
-#define RSP DATA(0x0)
-#define CR0 DATA(0x8)
-#define CR3 DATA(0x10)
-#define CR4 DATA(0x18)
-
-/* other data */
-#define CP_PA_TABLE_PAGE DATA(0x20)
-#define CP_PA_SWAP_PAGE DATA(0x28)
-#define CP_PA_BACKUP_PAGES_MAP DATA(0x30)
-#define CP_VA_CONTROL_PAGE DATA(0x38)
+SYM_DATA_LOCAL(saved_rsp, .quad 0)
+SYM_DATA_LOCAL(saved_cr0, .quad 0)
+SYM_DATA_LOCAL(saved_cr3, .quad 0)
+SYM_DATA_LOCAL(saved_cr4, .quad 0)
+ /* other data */
+SYM_DATA_LOCAL(va_control_page, .quad 0)
+SYM_DATA_LOCAL(pa_table_page, .quad 0)
+SYM_DATA_LOCAL(pa_swap_page, .quad 0)
+SYM_DATA_LOCAL(pa_backup_pages_map, .quad 0)
.section .text.relocate_kernel,"ax";
.code64
@@ -63,14 +61,13 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
pushq %r15
pushf
- movq PTR(VA_CONTROL_PAGE)(%rsi), %r11
- movq %rsp, RSP(%r11)
+ movq %rsp, saved_rsp(%rip)
movq %cr0, %rax
- movq %rax, CR0(%r11)
+ movq %rax, saved_cr0(%rip)
movq %cr3, %rax
- movq %rax, CR3(%r11)
+ movq %rax, saved_cr3(%rip)
movq %cr4, %rax
- movq %rax, CR4(%r11)
+ movq %rax, saved_cr4(%rip)
/* Save CR4. Required to enable the right paging mode later. */
movq %rax, %r13
@@ -83,10 +80,11 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
movq %r8, %r12
/*
- * get physical address of control page now
+ * get physical and virtual address of control page now
* this is impossible after page table switch
*/
movq PTR(PA_CONTROL_PAGE)(%rsi), %r8
+ movq PTR(VA_CONTROL_PAGE)(%rsi), %r11
/* get physical address of page table now too */
movq PTR(PA_TABLE_PAGE)(%rsi), %r9
@@ -95,10 +93,10 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
movq PTR(PA_SWAP_PAGE)(%rsi), %r10
/* save some information for jumping back */
- movq %r9, CP_PA_TABLE_PAGE(%r11)
- movq %r10, CP_PA_SWAP_PAGE(%r11)
- movq %rdi, CP_PA_BACKUP_PAGES_MAP(%r11)
- movq %r11, CP_VA_CONTROL_PAGE(%r11)
+ movq %r9, pa_table_page(%rip)
+ movq %r10, pa_swap_page(%rip)
+ movq %rdi, pa_backup_pages_map(%rip)
+ movq %r11, va_control_page(%rip)
/* Save the preserve_context to %r11 as swap_pages clobbers %rcx. */
movq %rcx, %r11
@@ -229,13 +227,13 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
/* get the re-entry point of the peer system */
movq 0(%rsp), %rbp
leaq relocate_kernel(%rip), %r8
- movq CP_PA_SWAP_PAGE(%r8), %r10
- movq CP_PA_BACKUP_PAGES_MAP(%r8), %rdi
- movq CP_PA_TABLE_PAGE(%r8), %rax
+ movq pa_swap_page(%rip), %r10
+ movq pa_backup_pages_map(%rip), %rdi
+ movq pa_table_page(%rip), %rax
movq %rax, %cr3
lea PAGE_SIZE(%r8), %rsp
call swap_pages
- movq CP_VA_CONTROL_PAGE(%r8), %rax
+ movq va_control_page(%rip), %rax
addq $(virtual_mapped - relocate_kernel), %rax
pushq %rax
ANNOTATE_UNRET_SAFE
@@ -246,11 +244,11 @@ SYM_CODE_END(identity_mapped)
SYM_CODE_START_LOCAL_NOALIGN(virtual_mapped)
UNWIND_HINT_END_OF_STACK
ANNOTATE_NOENDBR // RET target, above
- movq RSP(%r8), %rsp
- movq CR4(%r8), %rax
+ movq saved_rsp(%rip), %rsp
+ movq saved_cr4(%rip), %rax
movq %rax, %cr4
- movq CR3(%r8), %rax
- movq CR0(%r8), %r8
+ movq saved_cr3(%rip), %rax
+ movq saved_cr0(%rip), %r8
movq %rax, %cr3
movq %r8, %cr0
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index 1ff23a4bbf03..5d036fab1251 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -101,6 +101,7 @@ const_pcpu_hot = pcpu_hot;
. = ALIGN(0x100); \
__relocate_kernel_start = .; \
*(.text.relocate_kernel); \
+ *(.data.relocate_kernel); \
__relocate_kernel_end = .;
ASSERT(__relocate_kernel_end - __relocate_kernel_start <= KEXEC_CONTROL_CODE_MAX_SIZE,
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 11/20] x86/kexec: Drop page_list argument from relocate_kernel()
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
` (9 preceding siblings ...)
2024-11-25 9:54 ` [RFC PATCH v3 10/20] x86/kexec: Add data section to relocate_kernel David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 12/20] x86/kexec: Eliminate writes through kernel mapping of relocate_kernel page David Woodhouse
` (8 subsequent siblings)
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
The kernel's virtual mapping of the relocate_kernel page currently needs
to be RWX because it is written to before the %cr3 switch.
Now that the relocate_kernel page has its own .data section and local
variables, it can also have *global* variables. So eliminate the separate
page_list argument, and write the same information directly to variables
in the relocate_kernel page instead. This way, the relocate_kernel code
itself doesn't need to copy it.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/include/asm/kexec.h | 12 ++++------
arch/x86/kernel/machine_kexec_64.c | 18 ++++++--------
arch/x86/kernel/relocate_kernel_64.S | 36 ++++++++++------------------
3 files changed, 24 insertions(+), 42 deletions(-)
diff --git a/arch/x86/include/asm/kexec.h b/arch/x86/include/asm/kexec.h
index ccb8ff37fa9d..48e4f44f794f 100644
--- a/arch/x86/include/asm/kexec.h
+++ b/arch/x86/include/asm/kexec.h
@@ -8,12 +8,6 @@
# define PA_PGD 2
# define PA_SWAP_PAGE 3
# define PAGES_NR 4
-#else
-# define PA_CONTROL_PAGE 0
-# define VA_CONTROL_PAGE 1
-# define PA_TABLE_PAGE 2
-# define PA_SWAP_PAGE 3
-# define PAGES_NR 4
#endif
# define KEXEC_CONTROL_PAGE_SIZE 4096
@@ -60,6 +54,10 @@ struct kimage;
/* The native architecture */
# define KEXEC_ARCH KEXEC_ARCH_X86_64
+
+extern unsigned long kexec_va_control_page;
+extern unsigned long kexec_pa_table_page;
+extern unsigned long kexec_pa_swap_page;
#endif
/*
@@ -122,7 +120,7 @@ relocate_kernel(unsigned long indirection_page,
#else
unsigned long
relocate_kernel(unsigned long indirection_page,
- unsigned long page_list,
+ unsigned long pa_control_page,
unsigned long start_address,
unsigned int preserve_context,
unsigned int host_mem_enc_active);
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index 60232517f368..43db1739fd7a 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -321,6 +321,11 @@ int machine_kexec_prepare(struct kimage *image)
result = init_pgtable(image, __pa(control_page));
if (result)
return result;
+ kexec_va_control_page = (unsigned long)control_page;
+ kexec_pa_table_page = (unsigned long)__pa(image->arch.pgd);
+
+ if (image->type == KEXEC_TYPE_DEFAULT)
+ kexec_pa_swap_page = page_to_pfn(image->swap_page) << PAGE_SHIFT;
__memcpy(control_page, __relocate_kernel_start, reloc_end - reloc_start);
@@ -345,12 +350,11 @@ void machine_kexec_cleanup(struct kimage *image)
void machine_kexec(struct kimage *image)
{
unsigned long (*relocate_kernel_ptr)(unsigned long indirection_page,
- unsigned long page_list,
+ unsigned long pa_control_page,
unsigned long start_address,
unsigned int preserve_context,
unsigned int host_mem_enc_active);
unsigned long reloc_start = (unsigned long)__relocate_kernel_start;
- unsigned long page_list[PAGES_NR];
unsigned int host_mem_enc_active;
int save_ftrace_enabled;
void *control_page;
@@ -388,14 +392,6 @@ void machine_kexec(struct kimage *image)
control_page = page_address(image->control_code_page);
- page_list[PA_CONTROL_PAGE] = virt_to_phys(control_page);
- page_list[VA_CONTROL_PAGE] = (unsigned long)control_page;
- page_list[PA_TABLE_PAGE] = (unsigned long)__pa(image->arch.pgd);
-
- if (image->type == KEXEC_TYPE_DEFAULT)
- page_list[PA_SWAP_PAGE] = (page_to_pfn(image->swap_page)
- << PAGE_SHIFT);
-
/*
* Allow for the possibility that relocate_kernel might not be at
* the very start of the page.
@@ -423,7 +419,7 @@ void machine_kexec(struct kimage *image)
/* now call it */
image->start = relocate_kernel_ptr((unsigned long)image->head,
- (unsigned long)page_list,
+ virt_to_phys(control_page),
image->start,
image->preserve_context,
host_mem_enc_active);
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index 469af51589ee..16f123527406 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -34,9 +34,9 @@ SYM_DATA_LOCAL(saved_cr0, .quad 0)
SYM_DATA_LOCAL(saved_cr3, .quad 0)
SYM_DATA_LOCAL(saved_cr4, .quad 0)
/* other data */
-SYM_DATA_LOCAL(va_control_page, .quad 0)
-SYM_DATA_LOCAL(pa_table_page, .quad 0)
-SYM_DATA_LOCAL(pa_swap_page, .quad 0)
+SYM_DATA(kexec_va_control_page, .quad 0)
+SYM_DATA(kexec_pa_table_page, .quad 0)
+SYM_DATA(kexec_pa_swap_page, .quad 0)
SYM_DATA_LOCAL(pa_backup_pages_map, .quad 0)
.section .text.relocate_kernel,"ax";
@@ -46,7 +46,7 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
ANNOTATE_NOENDBR
/*
* %rdi indirection_page
- * %rsi page_list
+ * %rsi pa_control_page
* %rdx start address
* %rcx preserve_context
* %r8 host_mem_enc_active
@@ -79,31 +79,19 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
/* Save SME active flag */
movq %r8, %r12
- /*
- * get physical and virtual address of control page now
- * this is impossible after page table switch
- */
- movq PTR(PA_CONTROL_PAGE)(%rsi), %r8
- movq PTR(VA_CONTROL_PAGE)(%rsi), %r11
-
- /* get physical address of page table now too */
- movq PTR(PA_TABLE_PAGE)(%rsi), %r9
-
- /* get physical address of swap page now */
- movq PTR(PA_SWAP_PAGE)(%rsi), %r10
-
- /* save some information for jumping back */
- movq %r9, pa_table_page(%rip)
- movq %r10, pa_swap_page(%rip)
+ /* save indirection list for jumping back */
movq %rdi, pa_backup_pages_map(%rip)
- movq %r11, va_control_page(%rip)
/* Save the preserve_context to %r11 as swap_pages clobbers %rcx. */
movq %rcx, %r11
/* Switch to the identity mapped page tables */
+ movq kexec_pa_table_page(%rip), %r9
movq %r9, %cr3
+ /* Physical address of control page */
+ movq %rsi, %r8
+
/* setup a new stack at the end of the physical control page */
lea PAGE_SIZE(%r8), %rsp
@@ -227,13 +215,13 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
/* get the re-entry point of the peer system */
movq 0(%rsp), %rbp
leaq relocate_kernel(%rip), %r8
- movq pa_swap_page(%rip), %r10
+ movq kexec_pa_swap_page(%rip), %r10
movq pa_backup_pages_map(%rip), %rdi
- movq pa_table_page(%rip), %rax
+ movq kexec_pa_table_page(%rip), %rax
movq %rax, %cr3
lea PAGE_SIZE(%r8), %rsp
call swap_pages
- movq va_control_page(%rip), %rax
+ movq kexec_va_control_page(%rip), %rax
addq $(virtual_mapped - relocate_kernel), %rax
pushq %rax
ANNOTATE_UNRET_SAFE
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 12/20] x86/kexec: Eliminate writes through kernel mapping of relocate_kernel page
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
` (10 preceding siblings ...)
2024-11-25 9:54 ` [RFC PATCH v3 11/20] x86/kexec: Drop page_list argument from relocate_kernel() David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 13/20] x86/kexec: Clean up register usage in relocate_kernel() David Woodhouse
` (7 subsequent siblings)
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
All writes to the relocate_kernel control page are now done *after* the
%cr3 switch via simple %rip-relative addressing, which means the DATA()
macro with its pointer arithmetic can also now be removed.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/kernel/relocate_kernel_64.S | 29 ++++++++++++++--------------
1 file changed, 14 insertions(+), 15 deletions(-)
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index 16f123527406..288dfc08c63d 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -61,21 +61,24 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
pushq %r15
pushf
- movq %rsp, saved_rsp(%rip)
- movq %cr0, %rax
- movq %rax, saved_cr0(%rip)
- movq %cr3, %rax
- movq %rax, saved_cr3(%rip)
- movq %cr4, %rax
- movq %rax, saved_cr4(%rip)
-
- /* Save CR4. Required to enable the right paging mode later. */
- movq %rax, %r13
-
/* zero out flags, and disable interrupts */
pushq $0
popfq
+ /* Switch to the identity mapped page tables */
+ movq %cr3, %rax
+ movq kexec_pa_table_page(%rip), %r9
+ movq %r9, %cr3
+
+ /* Save %rsp and CRs. */
+ movq %rsp, saved_rsp(%rip)
+ movq %rax, saved_cr3(%rip)
+ movq %cr0, %rax
+ movq %rax, saved_cr0(%rip)
+ /* Leave CR4 in %r13 to enable the right paging mode later. */
+ movq %cr4, %r13
+ movq %r13, saved_cr4(%rip)
+
/* Save SME active flag */
movq %r8, %r12
@@ -85,10 +88,6 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
/* Save the preserve_context to %r11 as swap_pages clobbers %rcx. */
movq %rcx, %r11
- /* Switch to the identity mapped page tables */
- movq kexec_pa_table_page(%rip), %r9
- movq %r9, %cr3
-
/* Physical address of control page */
movq %rsi, %r8
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 13/20] x86/kexec: Clean up register usage in relocate_kernel()
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
` (11 preceding siblings ...)
2024-11-25 9:54 ` [RFC PATCH v3 12/20] x86/kexec: Eliminate writes through kernel mapping of relocate_kernel page David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 14/20] x86/kexec: Mark relocate_kernel page as ROX instead of RWX David Woodhouse
` (6 subsequent siblings)
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
The memory encryption flag is passed in %r8 because that's where the
calling convention puts it. Instead of moving it to %r12 and then using
%r8 for other things, just leave it in %r8 and use other registers
instead.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/kernel/relocate_kernel_64.S | 17 ++++++-----------
1 file changed, 6 insertions(+), 11 deletions(-)
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index 288dfc08c63d..b24198eb1fe9 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -79,24 +79,18 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
movq %cr4, %r13
movq %r13, saved_cr4(%rip)
- /* Save SME active flag */
- movq %r8, %r12
-
/* save indirection list for jumping back */
movq %rdi, pa_backup_pages_map(%rip)
/* Save the preserve_context to %r11 as swap_pages clobbers %rcx. */
movq %rcx, %r11
- /* Physical address of control page */
- movq %rsi, %r8
-
/* setup a new stack at the end of the physical control page */
- lea PAGE_SIZE(%r8), %rsp
+ lea PAGE_SIZE(%rsi), %rsp
/* jump to identity mapped page */
- addq $(identity_mapped - relocate_kernel), %r8
- pushq %r8
+ addq $(identity_mapped - relocate_kernel), %rsi
+ pushq %rsi
ANNOTATE_UNRET_SAFE
ret
int3
@@ -107,8 +101,9 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
/*
* %rdi indirection page
* %rdx start address
+ * %r8 host_mem_enc_active
+ * %r9 page table page
* %r11 preserve_context
- * %r12 host_mem_enc_active
* %r13 original CR4 when relocate_kernel() was invoked
*/
@@ -161,7 +156,7 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
* entries that will conflict with the now unencrypted memory
* used by kexec. Flush the caches before copying the kernel.
*/
- testq %r12, %r12
+ testq %r8, %r8
jz .Lsme_off
wbinvd
.Lsme_off:
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 14/20] x86/kexec: Mark relocate_kernel page as ROX instead of RWX
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
` (12 preceding siblings ...)
2024-11-25 9:54 ` [RFC PATCH v3 13/20] x86/kexec: Clean up register usage in relocate_kernel() David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 15/20] x86/kexec: Add CONFIG_KEXEC_DEBUG option David Woodhouse
` (5 subsequent siblings)
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
All writes to the page now happen before it gets marked as executable
(or after it's already switched to the identmap page tables where it's
OK to be RWX).
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/kernel/machine_kexec_64.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index 43db1739fd7a..c51e688c1f91 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -329,7 +329,7 @@ int machine_kexec_prepare(struct kimage *image)
__memcpy(control_page, __relocate_kernel_start, reloc_end - reloc_start);
- set_memory_x((unsigned long)control_page, 1);
+ set_memory_rox((unsigned long)control_page, 1);
return 0;
}
@@ -339,6 +339,7 @@ void machine_kexec_cleanup(struct kimage *image)
void *control_page = page_address(image->control_code_page);
set_memory_nx((unsigned long)control_page, 1);
+ set_memory_rw((unsigned long)control_page, 1);
free_transition_pgtable(image);
}
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 15/20] x86/kexec: Add CONFIG_KEXEC_DEBUG option
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
` (13 preceding siblings ...)
2024-11-25 9:54 ` [RFC PATCH v3 14/20] x86/kexec: Mark relocate_kernel page as ROX instead of RWX David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 16/20] x86/kexec: Debugging support: load a GDT David Woodhouse
` (4 subsequent siblings)
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
This does nothing yet.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/Kconfig.debug | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index 74777a97e394..9dde32865a9b 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -56,6 +56,14 @@ config EARLY_PRINTK_USB_XDBC
You should normally say N here, unless you want to debug early
crashes or need a very simple printk logging facility.
+config KEXEC_DEBUG
+ bool "Debug kexec transition"
+ depends on KEXEC_CORE && EARLY_PRINTK
+ help
+ Faults during kexec can be difficult to debug. This installs exception
+ handlers and attempts to report faults through the same serial port as
+ used for early_printk.
+
config EFI_PGT_DUMP
bool "Dump the EFI pagetable"
depends on EFI
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 16/20] x86/kexec: Debugging support: load a GDT
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
` (14 preceding siblings ...)
2024-11-25 9:54 ` [RFC PATCH v3 15/20] x86/kexec: Add CONFIG_KEXEC_DEBUG option David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 17/20] x86/kexec: Debugging support: Load an IDT and basic exception entry points David Woodhouse
` (3 subsequent siblings)
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
There are some failure modes which lead to triple-faults in the
relocate_kernel function, which is fairly much undebuggable for normal
mortals.
Adding a GDT in the relocate_kernel environment is step 1 towards being
able to catch faults and do something more useful.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/kernel/relocate_kernel_64.S | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index b24198eb1fe9..6c6bfdccfe6a 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -39,6 +39,18 @@ SYM_DATA(kexec_pa_table_page, .quad 0)
SYM_DATA(kexec_pa_swap_page, .quad 0)
SYM_DATA_LOCAL(pa_backup_pages_map, .quad 0)
+#ifdef CONFIG_KEXEC_DEBUG
+ .balign 16
+SYM_DATA_START_LOCAL(kexec_debug_gdt)
+ .word kexec_debug_gdt_end - kexec_debug_gdt - 1
+ .long 0
+ .word 0
+ .quad 0x00cf9a000000ffff /* __KERNEL32_CS */
+ .quad 0x00af9a000000ffff /* __KERNEL_CS */
+ .quad 0x00cf92000000ffff /* __KERNEL_DS */
+SYM_DATA_END_LABEL(kexec_debug_gdt, SYM_L_LOCAL, kexec_debug_gdt_end)
+#endif /* CONFIG_KEXEC_DEBUG */
+
.section .text.relocate_kernel,"ax";
.code64
SYM_CODE_START_NOALIGN(relocate_kernel)
@@ -112,6 +124,21 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
/* store the start address on the stack */
pushq %rdx
+#ifdef CONFIG_KEXEC_DEBUG
+ /* Create a GDTR (16 bits limit, 64 bits addr) on stack */
+ leaq kexec_debug_gdt(%rip), %rax
+ pushq %rax
+ pushw (%rax)
+
+ /* Load the GDT, put the stack back */
+ lgdt (%rsp)
+ addq $10, %rsp
+
+ /* Test that we can load segments */
+ movq %ds, %rax
+ movq %rax, %ds
+#endif /* CONFIG_KEXEC_DEBUG */
+
/*
* Clear X86_CR4_CET (if it was set) such that we can clear CR0_WP
* below.
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 17/20] x86/kexec: Debugging support: Load an IDT and basic exception entry points
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
` (15 preceding siblings ...)
2024-11-25 9:54 ` [RFC PATCH v3 16/20] x86/kexec: Debugging support: load a GDT David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 18/20] x86/kexec: Debugging support: Dump registers on exception David Woodhouse
` (2 subsequent siblings)
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/include/asm/kexec.h | 5 ++
arch/x86/kernel/machine_kexec_64.c | 23 ++++++++
arch/x86/kernel/relocate_kernel_64.S | 82 ++++++++++++++++++++++++++++
3 files changed, 110 insertions(+)
diff --git a/arch/x86/include/asm/kexec.h b/arch/x86/include/asm/kexec.h
index 48e4f44f794f..c14b0a2704c0 100644
--- a/arch/x86/include/asm/kexec.h
+++ b/arch/x86/include/asm/kexec.h
@@ -8,6 +8,9 @@
# define PA_PGD 2
# define PA_SWAP_PAGE 3
# define PAGES_NR 4
+#else
+/* Size of each exception handler referenced by the IDT */
+# define KEXEC_DEBUG_EXC_HANDLER_SIZE 6 /* pushi, pushi, 2-byte jmp */
#endif
# define KEXEC_CONTROL_PAGE_SIZE 4096
@@ -58,6 +61,8 @@ struct kimage;
extern unsigned long kexec_va_control_page;
extern unsigned long kexec_pa_table_page;
extern unsigned long kexec_pa_swap_page;
+extern gate_desc kexec_debug_idt[];
+extern unsigned char kexec_debug_exc_vectors[];
#endif
/*
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index c51e688c1f91..dfb1722622bd 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -310,6 +310,26 @@ static void load_segments(void)
);
}
+static void prepare_debug_idt(unsigned long control_page, unsigned long vec_ofs)
+{
+#ifdef CONFIG_KEXEC_DEBUG
+ gate_desc idtentry = { 0 };
+ int i;
+
+ idtentry.bits.p = 1;
+ idtentry.bits.type = GATE_TRAP;
+ idtentry.segment = __KERNEL_CS;
+ idtentry.offset_low = (control_page & 0xFFFF) + vec_ofs;
+ idtentry.offset_middle = (control_page >> 16) & 0xFFFF;
+ idtentry.offset_high = control_page >> 32;
+
+ for (i = 0; i < 16; i++) {
+ kexec_debug_idt[i] = idtentry;
+ idtentry.offset_low += KEXEC_DEBUG_EXC_HANDLER_SIZE;
+ }
+#endif
+}
+
int machine_kexec_prepare(struct kimage *image)
{
void *control_page = page_address(image->control_code_page);
@@ -327,6 +347,9 @@ int machine_kexec_prepare(struct kimage *image)
if (image->type == KEXEC_TYPE_DEFAULT)
kexec_pa_swap_page = page_to_pfn(image->swap_page) << PAGE_SHIFT;
+ prepare_debug_idt((unsigned long)__pa(control_page),
+ (unsigned long)kexec_debug_exc_vectors - reloc_start);
+
__memcpy(control_page, __relocate_kernel_start, reloc_end - reloc_start);
set_memory_rox((unsigned long)control_page, 1);
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index 6c6bfdccfe6a..2179f691a45b 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -49,6 +49,12 @@ SYM_DATA_START_LOCAL(kexec_debug_gdt)
.quad 0x00af9a000000ffff /* __KERNEL_CS */
.quad 0x00cf92000000ffff /* __KERNEL_DS */
SYM_DATA_END_LABEL(kexec_debug_gdt, SYM_L_LOCAL, kexec_debug_gdt_end)
+
+ .balign 8
+SYM_DATA_START(kexec_debug_idt)
+ .skip 0x100, 0x00
+SYM_DATA_END(kexec_debug_idt)
+
#endif /* CONFIG_KEXEC_DEBUG */
.section .text.relocate_kernel,"ax";
@@ -108,6 +114,11 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
int3
SYM_CODE_END(relocate_kernel)
+#ifdef DEBUG
+ UNWIND_HINT_UNDEFINED
+ .balign 0x100 /* relocate_kernel will be overwritten with an IDT */
+#endif
+
SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
UNWIND_HINT_END_OF_STACK
/*
@@ -137,6 +148,15 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
/* Test that we can load segments */
movq %ds, %rax
movq %rax, %ds
+
+ /* Now an IDTR on the stack to load the IDT the kernel created */
+ leaq kexec_debug_idt(%rip), %rsi
+ pushq %rsi
+ pushw $0xff
+ lidt (%rsp)
+ addq $10, %rsp
+
+ //int3
#endif /* CONFIG_KEXEC_DEBUG */
/*
@@ -346,3 +366,65 @@ SYM_CODE_START_LOCAL_NOALIGN(swap_pages)
ret
int3
SYM_CODE_END(swap_pages)
+
+#ifdef CONFIG_KEXEC_DEBUG
+SYM_CODE_START_NOALIGN(kexec_debug_exc_vectors)
+ /* Each of these is 6 bytes. */
+.macro vec_err exc
+ UNWIND_HINT_ENTRY
+ . = kexec_debug_exc_vectors + (\exc * KEXEC_DEBUG_EXC_HANDLER_SIZE)
+ nop
+ nop
+ pushq $\exc
+ jmp exc_handler
+.endm
+
+.macro vec_noerr exc
+ UNWIND_HINT_ENTRY
+ . = kexec_debug_exc_vectors + (\exc * KEXEC_DEBUG_EXC_HANDLER_SIZE)
+ pushq $0
+ pushq $\exc
+ jmp exc_handler
+.endm
+
+ ANNOTATE_NOENDBR
+ vec_noerr 0 // #DE
+ vec_noerr 1 // #DB
+ vec_noerr 2 // #NMI
+ vec_noerr 3 // #BP
+ vec_noerr 4 // #OF
+ vec_noerr 5 // #BR
+ vec_noerr 6 // #UD
+ vec_noerr 7 // #NM
+ vec_err 8 // #DF
+ vec_noerr 9
+ vec_err 10 // #TS
+ vec_err 11 // #NP
+ vec_err 12 // #SS
+ vec_err 13 // #GP
+ vec_err 14 // #PF
+ vec_noerr 15
+SYM_CODE_END(kexec_debug_exc_vectors)
+
+SYM_CODE_START_LOCAL_NOALIGN(exc_handler)
+ pushq %rax
+ pushq %rdx
+ movw $0x3f8, %dx
+ movb $'A', %al
+ outb %al, %dx
+ popq %rdx
+ popq %rax
+
+ /* Only return from int3 */
+ cmpq $3, (%rsp)
+ jne .Ldie
+
+ addq $16, %rsp
+ iretq
+
+.Ldie:
+ hlt
+ jmp .Ldie
+
+SYM_CODE_END(exc_handler)
+#endif /* CONFIG_KEXEC_DEBUG */
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 18/20] x86/kexec: Debugging support: Dump registers on exception
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
` (16 preceding siblings ...)
2024-11-25 9:54 ` [RFC PATCH v3 17/20] x86/kexec: Debugging support: Load an IDT and basic exception entry points David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 19/20] x86/kexec: Add 8250 serial port output David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 20/20] [DO NOT MERGE] x86/kexec: Add int3 in kexec path for testing David Woodhouse
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
The actual serial output function is a no-op for now.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/kernel/relocate_kernel_64.S | 104 ++++++++++++++++++++++++---
1 file changed, 96 insertions(+), 8 deletions(-)
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index 2179f691a45b..cd349b6d34a7 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -114,11 +114,6 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
int3
SYM_CODE_END(relocate_kernel)
-#ifdef DEBUG
- UNWIND_HINT_UNDEFINED
- .balign 0x100 /* relocate_kernel will be overwritten with an IDT */
-#endif
-
SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
UNWIND_HINT_END_OF_STACK
/*
@@ -368,6 +363,68 @@ SYM_CODE_START_LOCAL_NOALIGN(swap_pages)
SYM_CODE_END(swap_pages)
#ifdef CONFIG_KEXEC_DEBUG
+/*
+ * Generic 'print character' routine (as yet unimplemented)
+ * - %al: Character to be printed (may clobber %rax)
+ * - %rdx: MMIO address or port.
+ */
+SYM_CODE_START_LOCAL_NOALIGN(pr_char)
+ UNWIND_HINT_FUNC
+ ANNOTATE_UNRET_SAFE
+ ret
+SYM_CODE_END(pr_char)
+
+/*
+ * Load pr_char function pointer into %rsi and load %rdx with whatever
+ * that function wants to see there (typically port/MMIO address).
+ */
+.macro pr_setup
+ /* No output; pr_char just returns */
+ leaq pr_char(%rip), %rsi
+.endm
+
+/* Print the nybble in %bl, clobber %rax */
+SYM_CODE_START_LOCAL_NOALIGN(pr_nybble)
+ UNWIND_HINT_FUNC
+ movb %bl, %al
+ nop
+ andb $0x0f, %al
+ addb $0x30, %al
+ cmpb $0x3a, %al
+ jb 1f
+ addb $('a' - '0' - 10), %al
+ ANNOTATE_RETPOLINE_SAFE
+1: jmp *%rsi
+SYM_CODE_END(pr_nybble)
+
+SYM_CODE_START_LOCAL_NOALIGN(pr_qword)
+ UNWIND_HINT_FUNC
+ movq $16, %rcx
+1: rolq $4, %rbx
+ call pr_nybble
+ loop 1b
+ movb $'\n', %al
+ ANNOTATE_RETPOLINE_SAFE
+ jmp *%rsi
+SYM_CODE_END(pr_qword)
+
+.macro print_reg a, b, c, d, r
+ movb $\a, %al
+ ANNOTATE_RETPOLINE_SAFE
+ call *%rsi
+ movb $\b, %al
+ ANNOTATE_RETPOLINE_SAFE
+ call *%rsi
+ movb $\c, %al
+ ANNOTATE_RETPOLINE_SAFE
+ call *%rsi
+ movb $\d, %al
+ ANNOTATE_RETPOLINE_SAFE
+ call *%rsi
+ movq \r, %rbx
+ call pr_qword
+.endm
+
SYM_CODE_START_NOALIGN(kexec_debug_exc_vectors)
/* Each of these is 6 bytes. */
.macro vec_err exc
@@ -408,11 +465,42 @@ SYM_CODE_END(kexec_debug_exc_vectors)
SYM_CODE_START_LOCAL_NOALIGN(exc_handler)
pushq %rax
+ pushq %rbx
+ pushq %rcx
pushq %rdx
- movw $0x3f8, %dx
- movb $'A', %al
- outb %al, %dx
+ pushq %rsi
+
+ /* Set up %rdx/%rsi for debug output */
+ pr_setup
+
+ /* rip and exception info */
+ print_reg 'E', 'x', 'c', ':', 40(%rsp)
+ print_reg 'E', 'r', 'r', ':', 48(%rsp)
+ print_reg 'r', 'i', 'p', ':', 54(%rsp)
+
+ /* We spilled these to the stack */
+ print_reg 'r', 'a', 'x', ':', 32(%rsp)
+ print_reg 'r', 'b', 'x', ':', 24(%rsp)
+ print_reg 'r', 'c', 'x', ':', 16(%rsp)
+ print_reg 'r', 'd', 'x', ':', 8(%rsp)
+
+ /* Other registers */
+ print_reg 'r', 's', 'i', ':', (%rsp)
+ print_reg 'r', 'd', 'i', ':', %rdi
+ print_reg 'r', '8', ' ', ':', %r8
+ print_reg 'r', '9', ' ', ':', %r9
+ print_reg 'r', '1', '0', ':', %r10
+ print_reg 'r', '1', '1', ':', %r11
+ print_reg 'r', '1', '2', ':', %r12
+ print_reg 'r', '1', '3', ':', %r13
+ print_reg 'r', '1', '4', ':', %r14
+ print_reg 'r', '1', '5', ':', %r15
+ print_reg 'c', 'r', '2', ':', %cr2
+
+ popq %rsi
popq %rdx
+ popq %rcx
+ popq %rbx
popq %rax
/* Only return from int3 */
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 19/20] x86/kexec: Add 8250 serial port output
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
` (17 preceding siblings ...)
2024-11-25 9:54 ` [RFC PATCH v3 18/20] x86/kexec: Debugging support: Dump registers on exception David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 20/20] [DO NOT MERGE] x86/kexec: Add int3 in kexec path for testing David Woodhouse
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
If a serial port was configured for early_printk, use it for debug output
from the relocate_kernel exception handler too.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/include/asm/kexec.h | 1 +
arch/x86/kernel/early_printk.c | 6 +++++
arch/x86/kernel/relocate_kernel_64.S | 37 +++++++++++++++++++++++-----
3 files changed, 38 insertions(+), 6 deletions(-)
diff --git a/arch/x86/include/asm/kexec.h b/arch/x86/include/asm/kexec.h
index c14b0a2704c0..f37d2d9fda3f 100644
--- a/arch/x86/include/asm/kexec.h
+++ b/arch/x86/include/asm/kexec.h
@@ -63,6 +63,7 @@ extern unsigned long kexec_pa_table_page;
extern unsigned long kexec_pa_swap_page;
extern gate_desc kexec_debug_idt[];
extern unsigned char kexec_debug_exc_vectors[];
+extern uint16_t kexec_debug_8250_port;
#endif
/*
diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c
index 44f937015e1e..bf06866ee90a 100644
--- a/arch/x86/kernel/early_printk.c
+++ b/arch/x86/kernel/early_printk.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/console.h>
#include <linux/kernel.h>
+#include <linux/kexec.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/screen_info.h>
@@ -141,6 +142,11 @@ static __init void early_serial_hw_init(unsigned divisor)
serial_out(early_serial_base, DLL, divisor & 0xff);
serial_out(early_serial_base, DLH, (divisor >> 8) & 0xff);
serial_out(early_serial_base, LCR, c & ~DLAB);
+
+#ifdef CONFIG_KEXEC_DEBUG
+ if (serial_in == io_serial_in)
+ kexec_debug_8250_port = early_serial_base;
+#endif
}
#define DEFAULT_BAUD 9600
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index cd349b6d34a7..01a31e4a0664 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -38,6 +38,7 @@ SYM_DATA(kexec_va_control_page, .quad 0)
SYM_DATA(kexec_pa_table_page, .quad 0)
SYM_DATA(kexec_pa_swap_page, .quad 0)
SYM_DATA_LOCAL(pa_backup_pages_map, .quad 0)
+SYM_DATA(kexec_debug_8250_port, .word 0)
#ifdef CONFIG_KEXEC_DEBUG
.balign 16
@@ -364,23 +365,47 @@ SYM_CODE_END(swap_pages)
#ifdef CONFIG_KEXEC_DEBUG
/*
- * Generic 'print character' routine (as yet unimplemented)
+ * Generic 'print character' routine
* - %al: Character to be printed (may clobber %rax)
* - %rdx: MMIO address or port.
*/
-SYM_CODE_START_LOCAL_NOALIGN(pr_char)
+#define XMTRDY 0x20
+
+#define TXR 0 /* Transmit register (WRITE) */
+#define LSR 5 /* Line Status */
+
+SYM_CODE_START_LOCAL_NOALIGN(pr_char_8250)
UNWIND_HINT_FUNC
+ addw $LSR, %dx
+ xchg %al, %ah
+.Lxmtrdy_loop:
+ inb %dx, %al
+ testb $XMTRDY, %al
+ jnz .Lready
+ rep nop
+ jmp .Lxmtrdy_loop
+
+.Lready:
+ subw $LSR, %dx
+ xchg %al, %ah
+ outb %al, %dx
+pr_char_null:
ANNOTATE_UNRET_SAFE
ret
-SYM_CODE_END(pr_char)
+SYM_CODE_END(pr_char_8250)
/*
* Load pr_char function pointer into %rsi and load %rdx with whatever
* that function wants to see there (typically port/MMIO address).
*/
-.macro pr_setup
- /* No output; pr_char just returns */
- leaq pr_char(%rip), %rsi
+.macro pr_setup
+ leaq pr_char_8250(%rip), %rsi
+ movw kexec_debug_8250_port(%rip), %dx
+ testw %dx, %dx
+ jnz 1f
+
+ leaq pr_char_null(%rip), %rsi
+1:
.endm
/* Print the nybble in %bl, clobber %rax */
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH v3 20/20] [DO NOT MERGE] x86/kexec: Add int3 in kexec path for testing
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
` (18 preceding siblings ...)
2024-11-25 9:54 ` [RFC PATCH v3 19/20] x86/kexec: Add 8250 serial port output David Woodhouse
@ 2024-11-25 9:54 ` David Woodhouse
19 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 9:54 UTC (permalink / raw)
To: kexec
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, David Woodhouse, Kirill A. Shutemov, Kai Huang,
Nikolay Borisov, linux-kernel, Simon Horman, Dave Young,
Peter Zijlstra, jpoimboe, bsz
From: David Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/kernel/relocate_kernel_64.S | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index 01a31e4a0664..ff8a813a9f9b 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -152,7 +152,7 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
lidt (%rsp)
addq $10, %rsp
- //int3
+ int3
#endif /* CONFIG_KEXEC_DEBUG */
/*
--
2.47.0
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [EXTERNAL] [RFC PATCH v3 01/20] x86/kexec: Ensure control_code_page is mapped in kexec page tables
2024-11-25 9:54 ` [RFC PATCH v3 01/20] x86/kexec: Ensure control_code_page is mapped in kexec page tables David Woodhouse
@ 2024-11-25 10:29 ` David Woodhouse
2024-11-25 13:19 ` David Woodhouse
0 siblings, 1 reply; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 10:29 UTC (permalink / raw)
To: kexec, Schönherr, Jan H.
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kirill A. Shutemov, Kai Huang, Nikolay Borisov,
linux-kernel, Simon Horman, Dave Young, Peter Zijlstra, jpoimboe,
bsz
[-- Attachment #1: Type: text/plain, Size: 4672 bytes --]
On Mon, 2024-11-25 at 09:54 +0000, David Woodhouse wrote:
> From: David Woodhouse <dwmw@amazon.co.uk>
>
> The control_code_page should be explicitly mapped into the identity
> mapped page tables for the relocate_kernel environment. This only seems
> to have worked by luck before, because it tended to be within the same
> 2MiB or 1GiB large page already mapped for another reason.
>
> A subsequent commit will reduce the control_code_page to a single 4KiB
> page instead of a higher-order allocation, and seems to make it much
> *less* likely that we get lucky with its placement. This leads to a
> fault when relocate_kernel() first tries to access the page through its
> identity-mapped virtual address.
This one is confusing me. Jan points out that it shouldn't be needed,
because the control page should come from kernel memory and thus should
be mapped anyway because the loop immediately below my added code adds
*all* of the pfn_mapped[] ranges.
And from code inspection he appears to be right, but if I disable the
new mapping and add some printks...
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -247,15 +247,18 @@ static int init_pgtable(struct kimage *image, unsigned long control_page)
info.direct_gbpages = true;
/* Ensure the control code page itself is in the direct map */
+ pr_info("No Map control page at %lx", control_page);
+#if 0
result = kernel_ident_mapping_init(&info, image->arch.pgd, control_page,
control_page + KEXEC_CONTROL_CODE_MAX_SIZE);
if (result)
return result;
-
+#endif
for (i = 0; i < nr_pfn_mapped; i++) {
mstart = pfn_mapped[i].start << PAGE_SHIFT;
mend = pfn_mapped[i].end << PAGE_SHIFT;
+ pr_info("Map pfn_mapped[%d] %lx - %lx\n", i, mstart, mend);
result = kernel_ident_mapping_init(&info, image->arch.pgd,
mstart, mend);
if (result)
... and run in a version of qemu which dumps the CPU state on triple-
fault...
+ ./loadret
[ 0.948097] kexec: No Map control page at 2b32000
[ 0.948103] kexec: Map pfn_mapped[0] 0 - 7ffdd000
[ 0.960192] Freezing user space processes
[ 0.961685] Freezing user space processes completed (elapsed 0.001 seconds)
[ 0.962372] OOM killer disabled.
[ 1.088668] ata2: found unknown device (class 0)
[ 1.095810] Disabling non-boot CPUs ...
[ 1.117990] smpboot: CPU 1 is now offline
[ 1.118595] crash hp: kexec_trylock() failed, kdump image may be inaccurate
RAX=0000000080050033 RBX=0000000000000000 RCX=0000000000000001 RDX=0000000000400000
RSI=0000000002b3205a RDI=0000000003a44002 RBP=ffff9709c2109400 RSP=0000000002b33000
R8 =0000000000000000 R9 =00000000038a0000 R10=0000000000000000 R11=0000000000000001
R12=0000000000000000 R13=0000000000170ef0 R14=00000000fee1dead R15=0000000000000000
RIP=ffff9709c2b32057 RFL=00010006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0018 0000000000000000 ffffffff 00c09300 DPL=0 DS [-WA]
CS =0010 0000000000000000 ffffffff 00a09b00 DPL=0 CS64 [-RA]
SS =0018 0000000000000000 ffffffff 00c09300 DPL=0 DS [-WA]
DS =0018 0000000000000000 ffffffff 00c09300 DPL=0 DS [-WA]
FS =0018 0000000000000000 ffffffff 00c09300 DPL=0 DS [-WA]
GS =0018 0000000000000000 ffffffff 00c09300 DPL=0 DS [-WA]
LDT=0000 0000000000000000 00000000 00000000
TR =0040 fffffe2fb91b2000 00004087 00008b00 DPL=0 TSS64-busy
GDT= 0000000000000000 00000000
IDT= 0000000000000000 00000000
CR0=80050033 CR2=0000000002b32ff8 CR3=00000000038a0000 CR4=00170ef0
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
EFER=0000000000000d01
Code=04 00 00 49 89 cb 48 8d a6 00 10 00 00 48 81 c6 5a 00 00 00 <56> c3 cc 6a 00 52 48 8d 05 8c 04 00 00 50 66 ff 30 0f 01 14 24 48 83 c4 0a 8c d8 8e d8 48
RIP xxx057 is here, where relocate_kernel first touches the 1:1 mapping of the control page:
/* setup a new stack at the end of the physical control page */
lea PAGE_SIZE(%rsi), %rsp
49: 48 8d a6 00 10 00 00 lea 0x1000(%rsi),%rsp
/* jump to identity mapped page */
addq $(identity_mapped - relocate_kernel), %rsi
50: 48 81 c6 5a 00 00 00 add $0x5a,%rsi
pushq %rsi
57: 56 push %rsi
The control page at 2b32xxx *really* ought to be mapped, as it's
clearly within the 0 - 7ffdd000 range. What's going on?
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5965 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [EXTERNAL] [RFC PATCH v3 01/20] x86/kexec: Ensure control_code_page is mapped in kexec page tables
2024-11-25 10:29 ` [EXTERNAL] " David Woodhouse
@ 2024-11-25 13:19 ` David Woodhouse
2024-11-25 17:05 ` [RFC PATCH] x86/mm: Disable PTI for kernel_ident_mapping_init() David Woodhouse
0 siblings, 1 reply; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 13:19 UTC (permalink / raw)
To: kexec, Schönherr, Jan H.
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kirill A. Shutemov, Kai Huang, Nikolay Borisov,
linux-kernel, Simon Horman, Dave Young, Peter Zijlstra, jpoimboe,
bsz
[-- Attachment #1: Type: text/plain, Size: 1950 bytes --]
On Mon, 2024-11-25 at 10:29 +0000, David Woodhouse wrote:
> On Mon, 2024-11-25 at 09:54 +0000, David Woodhouse wrote:
> > From: David Woodhouse <dwmw@amazon.co.uk>
> >
> > The control_code_page should be explicitly mapped into the identity
> > mapped page tables for the relocate_kernel environment. This only seems
> > to have worked by luck before, because it tended to be within the same
> > 2MiB or 1GiB large page already mapped for another reason.
> >
> > A subsequent commit will reduce the control_code_page to a single 4KiB
> > page instead of a higher-order allocation, and seems to make it much
> > *less* likely that we get lucky with its placement. This leads to a
> > fault when relocate_kernel() first tries to access the page through its
> > identity-mapped virtual address.
>
> This one is confusing me. Jan points out that it shouldn't be needed,
> because the control page should come from kernel memory and thus should
> be mapped anyway because the loop immediately below my added code adds
> *all* of the pfn_mapped[] ranges.
I think we understand this one now; it's because of PTI. So where the
identmap code in e.g. ident_p4d_init() calls set_pte(), set_pte() is
actually trying to write *both* the kernel and userspace copies of the
page table, which it expects to be in adjacent pages. But in this case
it's just scribbling over the end of the single 4KiB page that was
allocated for it.
This should suffice to mask the problem (testing now) but obviously it
isn't a great solution:
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -213,7 +213,7 @@ static void *alloc_pgt_page(void *data)
struct page *page;
void *p = NULL;
- page = kimage_alloc_control_pages(image, 0);
+ page = kimage_alloc_control_pages(image, 1);
if (page) {
p = page_address(page);
clear_page(p);
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5965 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* [RFC PATCH] x86/mm: Disable PTI for kernel_ident_mapping_init()
2024-11-25 13:19 ` David Woodhouse
@ 2024-11-25 17:05 ` David Woodhouse
2024-11-25 18:31 ` Dave Hansen
0 siblings, 1 reply; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 17:05 UTC (permalink / raw)
To: kexec, Schönherr, Jan H., Rik van Riel
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kirill A. Shutemov, Kai Huang, Nikolay Borisov,
linux-kernel, Simon Horman, Dave Young, Peter Zijlstra, jpoimboe,
bsz
[-- Attachment #1: Type: text/plain, Size: 3049 bytes --]
From: David Woodhouse <dwmw@amazon.co.uk>
With PTI enabled, set_p4d() and set_pgd() will scribble over the end of
the 4KiB page allocated by the ->alloc_pgt_page() callback, expecting it
to have been an 8KiB allocation with the userspace version immediately
after the kernel's version.
So build *just* this code without PTI support. And without the PV MMU
ops too, since that would redirect to the standard build of those
functions which would have PTI enabled.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
Not sure I like this very much, but it works, and mirrors what
arch/x86/boot/compressed/ident_map_64.c already does.
We can't build the rest of the code in init_64.c with those config
options turned off, or Xen PV doesn't boot any more. So just build it
separately instead of #including it.
Now kexec is a little more reliable and doesn't scribble over adjacent
memory when building the page tables.
arch/x86/mm/Makefile | 1 +
arch/x86/mm/ident_map.c | 15 +++++++++++++++
arch/x86/mm/init_64.c | 2 --
3 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile
index 690fbf48e853..134302863233 100644
--- a/arch/x86/mm/Makefile
+++ b/arch/x86/mm/Makefile
@@ -37,6 +37,7 @@ CFLAGS_mem_encrypt_identity.o := -fno-stack-protector
CFLAGS_fault.o := -I $(src)/../include/asm/trace
obj-$(CONFIG_X86_32) += pgtable_32.o iomap_32.o
+obj-$(CONFIG_X86_64) += ident_map.o
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
obj-$(CONFIG_PTDUMP_CORE) += dump_pagetables.o
diff --git a/arch/x86/mm/ident_map.c b/arch/x86/mm/ident_map.c
index 437e96fb4977..090240f98141 100644
--- a/arch/x86/mm/ident_map.c
+++ b/arch/x86/mm/ident_map.c
@@ -4,6 +4,21 @@
* included by both the compressed kernel and the regular kernel.
*/
+/*
+ * If PTI is enabled, the standard set_p4d() et al functions will assume
+ * that each allocation is 8KiB, with the userspace page table 4KiB above
+ * the kernel one. Since users of the kernel_ident_mapping_init() code all
+ * allocate only a 4KiB page in their ->alloc_pgt_page() callback, this
+ * leads to scribbling over the end of the allocation. So *just* for this
+ * identmap code, disable PTI and disable the paravirt MMU ops which would
+ * redirect to the normally-compiled version that will use PTI.
+ */
+#undef CONFIG_MITIGATION_PAGE_TABLE_ISOLATION
+#undef CONFIG_PARAVIRT_XXL
+
+#include <linux/pgtable.h>
+#include <asm/init.h>
+
static void free_pte(struct x86_mapping_info *info, pmd_t *pmd)
{
pte_t *pte = pte_offset_kernel(pmd, 0);
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index ff253648706f..784f8d1c9140 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -58,8 +58,6 @@
#include "mm_internal.h"
-#include "ident_map.c"
-
#define DEFINE_POPULATE(fname, type1, type2, init) \
static inline void fname##_init(struct mm_struct *mm, \
type1##_t *arg1, type2##_t *arg2, bool init) \
--
2.43.0
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5965 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC PATCH] x86/mm: Disable PTI for kernel_ident_mapping_init()
2024-11-25 17:05 ` [RFC PATCH] x86/mm: Disable PTI for kernel_ident_mapping_init() David Woodhouse
@ 2024-11-25 18:31 ` Dave Hansen
2024-11-25 18:53 ` David Woodhouse
0 siblings, 1 reply; 31+ messages in thread
From: Dave Hansen @ 2024-11-25 18:31 UTC (permalink / raw)
To: David Woodhouse, kexec, Schönherr, Jan H., Rik van Riel
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kirill A. Shutemov, Kai Huang, Nikolay Borisov,
linux-kernel, Simon Horman, Dave Young, Peter Zijlstra, jpoimboe,
bsz
On 11/25/24 09:05, David Woodhouse wrote:
> Not sure I like this very much, but it works, and mirrors what
> arch/x86/boot/compressed/ident_map_64.c already does.
I don't like it much, either.
arch/x86/boot/compressed/ is already on the road to sharing no code with
the core kernel and it's full of horrors. It should be an example of
what *not* to do. ;)
I think we have a lot of software-available space in the page table
pointer entries. What would folks think if we set a special bit in those
p4d entries that said:
"I don't need to be propagated to
the user portion of the page tables."
It would obviously get set in this code that you're trying to fix. It
might _also_ be able to be set in in "_USR", like here:
#define _KERNPG_TABLE_NOENC (__PP|__RW| 0|___A| 0|___D| 0| 0)
#define _PAGE_TABLE_NOENC (__PP|__RW|_USR|___A| 0|___D| 0| 0)
like:
#define _USR _PAGE_USER|_PAGE_SW_WHATEVER
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC PATCH] x86/mm: Disable PTI for kernel_ident_mapping_init()
2024-11-25 18:31 ` Dave Hansen
@ 2024-11-25 18:53 ` David Woodhouse
2024-11-25 19:13 ` Dave Hansen
0 siblings, 1 reply; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 18:53 UTC (permalink / raw)
To: Dave Hansen, kexec, Schönherr, Jan H., Rik van Riel
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kirill A. Shutemov, Kai Huang, Nikolay Borisov,
linux-kernel, Simon Horman, Dave Young, Peter Zijlstra, jpoimboe,
bsz
[-- Attachment #1: Type: text/plain, Size: 1220 bytes --]
On Mon, 2024-11-25 at 10:31 -0800, Dave Hansen wrote:
> On 11/25/24 09:05, David Woodhouse wrote:
> > Not sure I like this very much, but it works, and mirrors what
> > arch/x86/boot/compressed/ident_map_64.c already does.
>
> I don't like it much, either.
>
> arch/x86/boot/compressed/ is already on the road to sharing no code with
> the core kernel and it's full of horrors. It should be an example of
> what *not* to do. ;)
>
> I think we have a lot of software-available space in the page table
> pointer entries. What would folks think if we set a special bit in those
> p4d entries that said:
>
> "I don't need to be propagated to
> the user portion of the page tables."
>
> It would obviously get set in this code that you're trying to fix. It
> might _also_ be able to be set in in "_USR", like here:
>
> #define _KERNPG_TABLE_NOENC (__PP|__RW| 0|___A| 0|___D| 0| 0)
> #define _PAGE_TABLE_NOENC (__PP|__RW|_USR|___A| 0|___D| 0| 0)
>
> like:
>
> #define _USR _PAGE_USER|_PAGE_SW_WHATEVER
In fact, do we even need a separate bit? Any PTE without the _PAGE_USER
bit set clearly doesn't need to be mirrored into the user page
tables...?
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5965 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC PATCH] x86/mm: Disable PTI for kernel_ident_mapping_init()
2024-11-25 18:53 ` David Woodhouse
@ 2024-11-25 19:13 ` Dave Hansen
2024-11-25 19:36 ` David Woodhouse
2024-11-26 11:42 ` David Woodhouse
0 siblings, 2 replies; 31+ messages in thread
From: Dave Hansen @ 2024-11-25 19:13 UTC (permalink / raw)
To: David Woodhouse, kexec, Schönherr, Jan H., Rik van Riel
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kirill A. Shutemov, Kai Huang, Nikolay Borisov,
linux-kernel, Simon Horman, Dave Young, Peter Zijlstra, jpoimboe,
bsz
On 11/25/24 10:53, David Woodhouse wrote:
>> I think we have a lot of software-available space in the page table
>> pointer entries. What would folks think if we set a special bit in those
>> p4d entries that said:
>>
>> "I don't need to be propagated to
>> the user portion of the page tables."
>>
>> It would obviously get set in this code that you're trying to fix. It
>> might _also_ be able to be set in in "_USR", like here:
>>
>> #define _KERNPG_TABLE_NOENC (__PP|__RW| 0|___A| 0|___D| 0| 0)
>> #define _PAGE_TABLE_NOENC (__PP|__RW|_USR|___A| 0|___D| 0| 0)
>>
>> like:
>>
>> #define _USR _PAGE_USER|_PAGE_SW_WHATEVER
> In fact, do we even need a separate bit? Any PTE without the _PAGE_USER
> bit set clearly doesn't need to be mirrored into the user page
> tables...?
I can't think of any exceptions where this would break off the top of my
head. It seems too simple to work. ;)
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC PATCH] x86/mm: Disable PTI for kernel_ident_mapping_init()
2024-11-25 19:13 ` Dave Hansen
@ 2024-11-25 19:36 ` David Woodhouse
2024-11-26 11:42 ` David Woodhouse
1 sibling, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-25 19:36 UTC (permalink / raw)
To: Dave Hansen, kexec, Schönherr, Jan H., Rik van Riel
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kirill A. Shutemov, Kai Huang, Nikolay Borisov,
linux-kernel, Simon Horman, Dave Young, Peter Zijlstra, jpoimboe,
bsz
On 25 November 2024 19:13:02 GMT, Dave Hansen <dave.hansen@intel.com> wrote:
>On 11/25/24 10:53, David Woodhouse wrote:
>>> I think we have a lot of software-available space in the page table
>>> pointer entries. What would folks think if we set a special bit in those
>>> p4d entries that said:
>>>
>>> "I don't need to be propagated to
>>> the user portion of the page tables."
>>>
>>> It would obviously get set in this code that you're trying to fix. It
>>> might _also_ be able to be set in in "_USR", like here:
>>>
>>> #define _KERNPG_TABLE_NOENC (__PP|__RW| 0|___A| 0|___D| 0| 0)
>>> #define _PAGE_TABLE_NOENC (__PP|__RW|_USR|___A| 0|___D| 0| 0)
>>>
>>> like:
>>>
>>> #define _USR _PAGE_USER|_PAGE_SW_WHATEVER
>> In fact, do we even need a separate bit? Any PTE without the _PAGE_USER
>> bit set clearly doesn't need to be mirrored into the user page
>> tables...?
>
>I can't think of any exceptions where this would break off the top of my
>head. It seems too simple to work. ;)
I'll throw something together and see if it explodes.
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC PATCH] x86/mm: Disable PTI for kernel_ident_mapping_init()
2024-11-25 19:13 ` Dave Hansen
2024-11-25 19:36 ` David Woodhouse
@ 2024-11-26 11:42 ` David Woodhouse
2024-11-26 15:49 ` Dave Hansen
1 sibling, 1 reply; 31+ messages in thread
From: David Woodhouse @ 2024-11-26 11:42 UTC (permalink / raw)
To: Dave Hansen, kexec, Schönherr, Jan H., Rik van Riel
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kirill A. Shutemov, Kai Huang, Nikolay Borisov,
linux-kernel, Simon Horman, Dave Young, Peter Zijlstra, jpoimboe,
bsz
[-- Attachment #1: Type: text/plain, Size: 4061 bytes --]
On Mon, 2024-11-25 at 11:13 -0800, Dave Hansen wrote:
> On 11/25/24 10:53, David Woodhouse wrote:
> > > I think we have a lot of software-available space in the page table
> > > pointer entries. What would folks think if we set a special bit in those
> > > p4d entries that said:
> > >
> > > "I don't need to be propagated to
> > > the user portion of the page tables."
> > >
> > > It would obviously get set in this code that you're trying to fix. It
> > > might _also_ be able to be set in in "_USR", like here:
> > >
> > > #define _KERNPG_TABLE_NOENC (__PP|__RW| 0|___A| 0|___D| 0| 0)
> > > #define _PAGE_TABLE_NOENC (__PP|__RW|_USR|___A| 0|___D| 0| 0)
> > >
> > > like:
> > >
> > > #define _USR _PAGE_USER|_PAGE_SW_WHATEVER
> > In fact, do we even need a separate bit? Any PTE without the _PAGE_USER
> > bit set clearly doesn't need to be mirrored into the user page
> > tables...?
>
> I can't think of any exceptions where this would break off the top of my
> head. It seems too simple to work. ;)
On IRC we discussed the fact that it's slightly non-trivial to use that
as the trigger for pgd_clear().
I threw this version together and it didn't immediately explode...
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
diff --git a/arch/x86/include/asm/pgtable_types.h b/arch/x86/include/asm/pgtable_types.h
index 6f82e75b6149..4b804531b03c 100644
--- a/arch/x86/include/asm/pgtable_types.h
+++ b/arch/x86/include/asm/pgtable_types.h
@@ -36,10 +36,12 @@
#define _PAGE_BIT_DEVMAP _PAGE_BIT_SOFTW4
#ifdef CONFIG_X86_64
-#define _PAGE_BIT_SAVED_DIRTY _PAGE_BIT_SOFTW5 /* Saved Dirty bit */
+#define _PAGE_BIT_SAVED_DIRTY _PAGE_BIT_SOFTW5 /* Saved Dirty bit (leaf) */
+#define _PAGE_BIT_NOPTISHADOW _PAGE_BIT_SOFTW5 /* No PTI shadow (root PGD) */
#else
/* Shared with _PAGE_BIT_UFFD_WP which is not supported on 32 bit */
-#define _PAGE_BIT_SAVED_DIRTY _PAGE_BIT_SOFTW2 /* Saved Dirty bit */
+#define _PAGE_BIT_SAVED_DIRTY _PAGE_BIT_SOFTW2 /* Saved Dirty bit (leaf) */
+#define _PAGE_BIT_NOPTISHADOW _PAGE_BIT_SOFTW2 /* No PTI shadow (root PGD) */
#endif
/* If _PAGE_BIT_PRESENT is clear, we use these: */
@@ -139,6 +141,8 @@
#define _PAGE_PROTNONE (_AT(pteval_t, 1) << _PAGE_BIT_PROTNONE)
+#define _PAGE_NOPTISHADOW (_AT(pteval_t, 1) << _PAGE_BIT_NOPTISHADOW)
+
/*
* Set of bits not changed in pte_modify. The pte's
* protection key is treated like _PAGE_RW, for
diff --git a/arch/x86/mm/ident_map.c b/arch/x86/mm/ident_map.c
index 437e96fb4977..5ab7bd2f1983 100644
--- a/arch/x86/mm/ident_map.c
+++ b/arch/x86/mm/ident_map.c
@@ -174,7 +174,7 @@ static int ident_p4d_init(struct x86_mapping_info *info, p4d_t *p4d_page,
if (result)
return result;
- set_p4d(p4d, __p4d(__pa(pud) | info->kernpg_flag));
+ set_p4d(p4d, __p4d(__pa(pud) | info->kernpg_flag | _PAGE_NOPTISHADOW));
}
return 0;
@@ -218,14 +218,14 @@ int kernel_ident_mapping_init(struct x86_mapping_info *info, pgd_t *pgd_page,
if (result)
return result;
if (pgtable_l5_enabled()) {
- set_pgd(pgd, __pgd(__pa(p4d) | info->kernpg_flag));
+ set_pgd(pgd, __pgd(__pa(p4d) | info->kernpg_flag | _PAGE_NOPTISHADOW));
} else {
/*
* With p4d folded, pgd is equal to p4d.
* The pgd entry has to point to the pud page table in this case.
*/
pud_t *pud = pud_offset(p4d, 0);
- set_pgd(pgd, __pgd(__pa(pud) | info->kernpg_flag));
+ set_pgd(pgd, __pgd(__pa(pud) | info->kernpg_flag | _PAGE_NOPTISHADOW));
}
}
diff --git a/arch/x86/mm/pti.c b/arch/x86/mm/pti.c
index 851ec8f1363a..5f0d579932c6 100644
--- a/arch/x86/mm/pti.c
+++ b/arch/x86/mm/pti.c
@@ -132,7 +132,7 @@ pgd_t __pti_set_user_pgtbl(pgd_t *pgdp, pgd_t pgd)
* Top-level entries added to init_mm's usermode pgd after boot
* will not be automatically propagated to other mms.
*/
- if (!pgdp_maps_userspace(pgdp))
+ if (!pgdp_maps_userspace(pgdp) || (pgd.pgd & _PAGE_NOPTISHADOW))
return pgd;
/*
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5965 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC PATCH] x86/mm: Disable PTI for kernel_ident_mapping_init()
2024-11-26 11:42 ` David Woodhouse
@ 2024-11-26 15:49 ` Dave Hansen
2024-11-26 16:09 ` David Woodhouse
0 siblings, 1 reply; 31+ messages in thread
From: Dave Hansen @ 2024-11-26 15:49 UTC (permalink / raw)
To: David Woodhouse, kexec, Schönherr, Jan H., Rik van Riel
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kirill A. Shutemov, Kai Huang, Nikolay Borisov,
linux-kernel, Simon Horman, Dave Young, Peter Zijlstra, jpoimboe,
bsz
On 11/26/24 03:42, David Woodhouse wrote:
> I threw this version together and it didn't immediately explode...
It's better than playing #define games. The damage is also pretty
limited and it helps us avoid plumbing a bit through the page table
handling function arguments.
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [RFC PATCH] x86/mm: Disable PTI for kernel_ident_mapping_init()
2024-11-26 15:49 ` Dave Hansen
@ 2024-11-26 16:09 ` David Woodhouse
0 siblings, 0 replies; 31+ messages in thread
From: David Woodhouse @ 2024-11-26 16:09 UTC (permalink / raw)
To: Dave Hansen, kexec, Schönherr, Jan H., Rik van Riel
Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Kirill A. Shutemov, Kai Huang, Nikolay Borisov,
linux-kernel, Simon Horman, Dave Young, Peter Zijlstra, jpoimboe,
bsz
[-- Attachment #1: Type: text/plain, Size: 5157 bytes --]
On Tue, 2024-11-26 at 07:49 -0800, Dave Hansen wrote:
> On 11/26/24 03:42, David Woodhouse wrote:
> > I threw this version together and it didn't immediately explode...
>
> It's better than playing #define games. The damage is also pretty
> limited and it helps us avoid plumbing a bit through the page table
> handling function arguments.
Thanks. I guess I should write it a commit message then... and add
Cc:stable since AFAICT the other users of this *are* already broken.
From b474ea925a2ea566061552d8a6ca9e4a2945f45d Mon Sep 17 00:00:00 2001
From: David Woodhouse <dwmw@amazon.co.uk>
Date: Tue, 26 Nov 2024 15:55:13 +0000
Subject: [PATCH 05/20] x86/mm: Add _PAGE_NOPTISHADOW bit to avoid updating
userspace page tables
The set_p4d() and set_pgd() functions (in 4-level or 5-level page table setups
respectively) assume that the root page table is actually a 8KiB allocation,
with the userspace root immediately after the kernel root page table (so that
the former can enforce NX on on all the subordinate pages, which are actually
shared).
However, users of the kernel_ident_mapping_init() code do not give it an 8KiB
allocation for its PGD. Both swsusp_arch_resume() and acpi_mp_setup_reset()
allocate only a single 4KiB page. The kexec code on x86_64 currently gets
away with it purely by chance, because it allocates 8KiB for its "control
code page" and then actually uses the first half for the PGD, then copies the
actual trampoline code into the second half only after the identmap code has
finished scribbling over it.
Fix this by defining a _PAGE_NOPTISHADOW bit (which can use the same bit as
_PAGE_SAVED_DIRTY since one is only for the PGD/P4D root and the other is
exclusively for leaf PTEs.). This instructs __pti_set_user_pgtbl() not to
write to the userspace 'shadow' PGD.
Strictly, the _PAGE_NOPTISHADOW bit doesn't need to be written out to the
actual page tables; since __pti_set_user_pgtbl() returns the value to be
written to the kernel page table, it could be filtered out. But there seems
to be no benefit to actually doing so.
Cc: stable@kernel.org
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/include/asm/pgtable_types.h | 8 ++++++--
arch/x86/mm/ident_map.c | 6 +++---
arch/x86/mm/pti.c | 2 +-
3 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/arch/x86/include/asm/pgtable_types.h b/arch/x86/include/asm/pgtable_types.h
index 6f82e75b6149..4b804531b03c 100644
--- a/arch/x86/include/asm/pgtable_types.h
+++ b/arch/x86/include/asm/pgtable_types.h
@@ -36,10 +36,12 @@
#define _PAGE_BIT_DEVMAP _PAGE_BIT_SOFTW4
#ifdef CONFIG_X86_64
-#define _PAGE_BIT_SAVED_DIRTY _PAGE_BIT_SOFTW5 /* Saved Dirty bit */
+#define _PAGE_BIT_SAVED_DIRTY _PAGE_BIT_SOFTW5 /* Saved Dirty bit (leaf) */
+#define _PAGE_BIT_NOPTISHADOW _PAGE_BIT_SOFTW5 /* No PTI shadow (root PGD) */
#else
/* Shared with _PAGE_BIT_UFFD_WP which is not supported on 32 bit */
-#define _PAGE_BIT_SAVED_DIRTY _PAGE_BIT_SOFTW2 /* Saved Dirty bit */
+#define _PAGE_BIT_SAVED_DIRTY _PAGE_BIT_SOFTW2 /* Saved Dirty bit (leaf) */
+#define _PAGE_BIT_NOPTISHADOW _PAGE_BIT_SOFTW2 /* No PTI shadow (root PGD) */
#endif
/* If _PAGE_BIT_PRESENT is clear, we use these: */
@@ -139,6 +141,8 @@
#define _PAGE_PROTNONE (_AT(pteval_t, 1) << _PAGE_BIT_PROTNONE)
+#define _PAGE_NOPTISHADOW (_AT(pteval_t, 1) << _PAGE_BIT_NOPTISHADOW)
+
/*
* Set of bits not changed in pte_modify. The pte's
* protection key is treated like _PAGE_RW, for
diff --git a/arch/x86/mm/ident_map.c b/arch/x86/mm/ident_map.c
index 437e96fb4977..5ab7bd2f1983 100644
--- a/arch/x86/mm/ident_map.c
+++ b/arch/x86/mm/ident_map.c
@@ -174,7 +174,7 @@ static int ident_p4d_init(struct x86_mapping_info *info, p4d_t *p4d_page,
if (result)
return result;
- set_p4d(p4d, __p4d(__pa(pud) | info->kernpg_flag));
+ set_p4d(p4d, __p4d(__pa(pud) | info->kernpg_flag | _PAGE_NOPTISHADOW));
}
return 0;
@@ -218,14 +218,14 @@ int kernel_ident_mapping_init(struct x86_mapping_info *info, pgd_t *pgd_page,
if (result)
return result;
if (pgtable_l5_enabled()) {
- set_pgd(pgd, __pgd(__pa(p4d) | info->kernpg_flag));
+ set_pgd(pgd, __pgd(__pa(p4d) | info->kernpg_flag | _PAGE_NOPTISHADOW));
} else {
/*
* With p4d folded, pgd is equal to p4d.
* The pgd entry has to point to the pud page table in this case.
*/
pud_t *pud = pud_offset(p4d, 0);
- set_pgd(pgd, __pgd(__pa(pud) | info->kernpg_flag));
+ set_pgd(pgd, __pgd(__pa(pud) | info->kernpg_flag | _PAGE_NOPTISHADOW));
}
}
diff --git a/arch/x86/mm/pti.c b/arch/x86/mm/pti.c
index 851ec8f1363a..5f0d579932c6 100644
--- a/arch/x86/mm/pti.c
+++ b/arch/x86/mm/pti.c
@@ -132,7 +132,7 @@ pgd_t __pti_set_user_pgtbl(pgd_t *pgdp, pgd_t pgd)
* Top-level entries added to init_mm's usermode pgd after boot
* will not be automatically propagated to other mms.
*/
- if (!pgdp_maps_userspace(pgdp))
+ if (!pgdp_maps_userspace(pgdp) || (pgd.pgd & _PAGE_NOPTISHADOW))
return pgd;
/*
--
2.43.0
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5965 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
end of thread, other threads:[~2024-11-26 16:10 UTC | newest]
Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-11-25 9:54 [RFC PATCH v3 00/20] x86/kexec: Add exception handling for relocate_kernel and further yak-shaving David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 01/20] x86/kexec: Ensure control_code_page is mapped in kexec page tables David Woodhouse
2024-11-25 10:29 ` [EXTERNAL] " David Woodhouse
2024-11-25 13:19 ` David Woodhouse
2024-11-25 17:05 ` [RFC PATCH] x86/mm: Disable PTI for kernel_ident_mapping_init() David Woodhouse
2024-11-25 18:31 ` Dave Hansen
2024-11-25 18:53 ` David Woodhouse
2024-11-25 19:13 ` Dave Hansen
2024-11-25 19:36 ` David Woodhouse
2024-11-26 11:42 ` David Woodhouse
2024-11-26 15:49 ` Dave Hansen
2024-11-26 16:09 ` David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 02/20] x86/kexec: Restore GDT on return from preserve_context kexec David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 03/20] x86/kexec: Clean up and document register use in relocate_kernel_64.S David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 04/20] x86/kexec: Use named labels in swap_pages " David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 05/20] x86/kexec: Only swap pages for preserve_context mode David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 06/20] x86/kexec: Allocate PGD for x86_64 transition page tables separately David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 07/20] x86/kexec: Copy control page into place in machine_kexec_prepare() David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 08/20] x86/kexec: Invoke copy of relocate_kernel() instead of the original David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 09/20] x86/kexec: Move relocate_kernel to kernel .data section David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 10/20] x86/kexec: Add data section to relocate_kernel David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 11/20] x86/kexec: Drop page_list argument from relocate_kernel() David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 12/20] x86/kexec: Eliminate writes through kernel mapping of relocate_kernel page David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 13/20] x86/kexec: Clean up register usage in relocate_kernel() David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 14/20] x86/kexec: Mark relocate_kernel page as ROX instead of RWX David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 15/20] x86/kexec: Add CONFIG_KEXEC_DEBUG option David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 16/20] x86/kexec: Debugging support: load a GDT David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 17/20] x86/kexec: Debugging support: Load an IDT and basic exception entry points David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 18/20] x86/kexec: Debugging support: Dump registers on exception David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 19/20] x86/kexec: Add 8250 serial port output David Woodhouse
2024-11-25 9:54 ` [RFC PATCH v3 20/20] [DO NOT MERGE] x86/kexec: Add int3 in kexec path for testing David Woodhouse
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®