[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v2 2/4] kexec: Include purgatory in Xen



From: Ross Lagerwall <ross.lagerwall@xxxxxxxxxx>

Purgatory is a binary that runs between two kernels during kexec. When Secure
Boot is enabled, it should be signed and verified before being loaded and
executed.

Currently, purgatory is built as part of kexec-tools and dynamically modified
before being loaded. This prevents signing it at build time. To fix this, build
purgatory as a separate object in Xen and modify it at load time in Xen itself.
This is the same approach used by Linux.

No behavioural change with this patch other than the puratory binary being
included in Xen.

Signed-off-by: Ross Lagerwall <ross.lagerwall@xxxxxxxxxx>
---
 xen/arch/x86/Makefile                 |   1 +
 xen/arch/x86/purgatory/.gitignore     |   3 +
 xen/arch/x86/purgatory/Makefile       |  64 +++++++++++++++
 xen/arch/x86/purgatory/config.h       |  37 +++++++++
 xen/arch/x86/purgatory/entry64.S      | 108 ++++++++++++++++++++++++++
 xen/arch/x86/purgatory/purgatory.c    |  59 ++++++++++++++
 xen/arch/x86/purgatory/setup-x86_64.S |  63 +++++++++++++++
 xen/arch/x86/purgatory/stack.S        |  21 +++++
 xen/lib/sha2-256.c                    |   2 +-
 9 files changed, 357 insertions(+), 1 deletion(-)
 create mode 100644 xen/arch/x86/purgatory/.gitignore
 create mode 100644 xen/arch/x86/purgatory/Makefile
 create mode 100644 xen/arch/x86/purgatory/config.h
 create mode 100644 xen/arch/x86/purgatory/entry64.S
 create mode 100644 xen/arch/x86/purgatory/purgatory.c
 create mode 100644 xen/arch/x86/purgatory/setup-x86_64.S
 create mode 100644 xen/arch/x86/purgatory/stack.S

diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index c2f1dcf301..b3ee871ba1 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -1,6 +1,7 @@
 obj-y += acpi/
 obj-y += boot/
 obj-y += cpu/
+obj-$(CONFIG_KEXEC) += purgatory/
 obj-y += efi/
 obj-y += genapic/
 obj-$(CONFIG_GUEST) += guest/
diff --git a/xen/arch/x86/purgatory/.gitignore 
b/xen/arch/x86/purgatory/.gitignore
new file mode 100644
index 0000000000..9353e3ff26
--- /dev/null
+++ b/xen/arch/x86/purgatory/.gitignore
@@ -0,0 +1,3 @@
+purgatory.chk
+kexec-purgatory.S
+purgatory.ro
diff --git a/xen/arch/x86/purgatory/Makefile b/xen/arch/x86/purgatory/Makefile
new file mode 100644
index 0000000000..211264e4da
--- /dev/null
+++ b/xen/arch/x86/purgatory/Makefile
@@ -0,0 +1,64 @@
+cc_common_flags = -Wall -Os -g0                                \
+                    -nostdinc                                  \
+                    -mcmodel=large                             \
+                    -fno-builtin                               \
+                    -ffreestanding                             \
+                    -fno-zero-initialized-in-bss               \
+                    -fno-stack-protector                       \
+                    -fno-PIC                                   \
+                    -fno-PIE                                   \
+                    -fno-tree-vectorize                        \
+                    -D__XEN__                                  \
+                    -D__PURGATORY__                            \
+                    -include $(srctree)/include/xen/config.h   \
+                    -I$(srctree)/$(src)                        \
+                    -I$(objtree)/include                       \
+                    -I$(srctree)/include                       \
+                    -I$(objtree)/arch/x86/include              \
+                    -I$(srctree)/arch/x86/include              \
+                    -I$(objtree)/arch/x86/include/generated
+
+cmd_cc_purg = $(CC) $(cc_common_flags)               \
+                  -c $< -o $@
+
+cmd_cc_as_purg = $(CC) $(cc_common_flags)            \
+                    -D__ASSEMBLY__                  \
+                     -c $< -o $@
+
+cmd_ld_purg = $(LD) --no-undefined               \
+                -z nodefaultlib                  \
+                -z noexecstack                   \
+                -e purgatory_start               \
+                -S $(real-prereqs) -o $@
+
+cmd_ldr_purg = $(cmd_ld_purg) -r
+
+purgatory-y := purgatory.o stack.o setup-x86_64.o sha2-256.o entry64.o memcmp.o
+PURGATORY_OBJS = $(addprefix $(obj)/,$(purgatory-y))
+
+$(obj)/%.o: $(src)/%.c $(src)/config.h
+       $(call if_changed,cc_purg)
+
+$(obj)/%.o: $(src)/%.S $(src)/config.h
+       $(call if_changed,cc_as_purg)
+
+$(obj)/sha2-256.o: $(srctree)/lib/sha2-256.c $(src)/config.h
+       $(call if_changed,cc_purg)
+
+$(obj)/memcmp.o: $(srctree)/lib/memcmp.c $(src)/config.h
+       $(call if_changed,cc_purg)
+
+$(obj)/purgatory.ro: $(PURGATORY_OBJS) FORCE
+               $(call if_changed,ldr_purg)
+
+$(obj)/purgatory.chk: $(obj)/purgatory.ro FORCE
+               $(call if_changed,ld_purg)
+
+$(obj)/kexec-purgatory.o: $(obj)/purgatory.ro $(obj)/purgatory.chk
+
+$(obj)/kexec-purgatory.S: $(srctree)/tools/binfile FORCE
+       $(call if_changed,binfile,$(obj)/purgatory.ro kexec_purgatory)
+
+targets += kexec-purgatory.S
+
+obj-y += kexec-purgatory.o
diff --git a/xen/arch/x86/purgatory/config.h b/xen/arch/x86/purgatory/config.h
new file mode 100644
index 0000000000..f1f9f41aad
--- /dev/null
+++ b/xen/arch/x86/purgatory/config.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2025  Cloud Software Group Inc.
+ */
+
+#ifndef __PURGATORY_CONFIG_H__
+#define __PURGATORY_CONFIG_H__
+
+#define BALIGN(size) .balign size
+
+#undef ENTRY
+#define ENTRY(name)         \
+    name:
+
+#define END(name)           \
+    name##_end:
+
+#undef GLOBAL
+#define GLOBAL(name)        \
+    .globl name;            \
+    ENTRY(name)
+
+#define GLOBAL_END(name)    \
+    .globl name##_end;      \
+    END(name)
+
+#define SYM_T_DATA 1
+
+#define ASM_INT(name, val)  \
+    .type name, SYM_T_DATA; \
+    BALIGN(4);              \
+    .hidden name;           \
+    GLOBAL(name)            \
+    .long (val);            \
+    .size name, . - name
+
+#endif // __PURGATORY_CONFIG_H__
diff --git a/xen/arch/x86/purgatory/entry64.S b/xen/arch/x86/purgatory/entry64.S
new file mode 100644
index 0000000000..7256cb6a91
--- /dev/null
+++ b/xen/arch/x86/purgatory/entry64.S
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2003,2004  Eric Biederman (ebiederm@xxxxxxxxxxxx)
+ * Copyright (C) 2014  Red Hat Inc.
+ * Copyright (C) 2025  Cloud Software Group Inc.
+
+ * Author(s): Vivek Goyal <vgoyal@xxxxxxxxxx>
+ *
+ * This code has been adapted from kexec-tools and Linux.
+ */
+
+#include "config.h"
+
+    .text
+    .code64
+
+BALIGN(16)
+GLOBAL(entry64)
+    /* Setup a gdt that should be preserved */
+    lgdt gdt(%rip)
+
+    /* load the data segments */
+    movl    $0x18, %eax     /* data segment */
+    movl    %eax, %ds
+    movl    %eax, %es
+    movl    %eax, %ss
+    movl    %eax, %fs
+    movl    %eax, %gs
+
+    /* Setup new stack */
+    leaq    stack_init(%rip), %rsp
+    pushq   $0x10 /* CS */
+    leaq    new_cs_exit(%rip), %rax
+    pushq   %rax
+    lretq
+ENTRY(new_cs_exit)
+
+    /* Load the registers */
+    movq    rax(%rip), %rax
+    movq    rbx(%rip), %rbx
+    movq    rcx(%rip), %rcx
+    movq    rdx(%rip), %rdx
+    movq    rsi(%rip), %rsi
+    movq    rdi(%rip), %rdi
+    movq    rsp(%rip), %rsp
+    movq    rbp(%rip), %rbp
+    movq    r8(%rip), %r8
+    movq    r9(%rip), %r9
+    movq    r10(%rip), %r10
+    movq    r11(%rip), %r11
+    movq    r12(%rip), %r12
+    movq    r13(%rip), %r13
+    movq    r14(%rip), %r14
+    movq    r15(%rip), %r15
+
+    /* Jump to the new code... */
+    jmpq    *rip(%rip)
+END(entry64)
+
+    .section ".rodata"
+
+BALIGN(4)
+GLOBAL(entry64_regs)
+rax:    .quad 0x0
+rbx:    .quad 0x0
+rcx:    .quad 0x0
+rdx:    .quad 0x0
+rsi:    .quad 0x0
+rdi:    .quad 0x0
+rsp:    .quad 0x0
+rbp:    .quad 0x0
+r8:     .quad 0x0
+r9:     .quad 0x0
+r10:    .quad 0x0
+r11:    .quad 0x0
+r12:    .quad 0x0
+r13:    .quad 0x0
+r14:    .quad 0x0
+r15:    .quad 0x0
+rip:    .quad 0x0
+END(entry64_regs)
+    .size entry64_regs, entry64_regs_end - entry64_regs
+
+    /* GDT */
+    .section ".rodata"
+
+BALIGN(16)
+ENTRY(gdt)
+    /*
+     * 0x00 unusable segment
+     * 0x08 unused
+     * so use them as gdt ptr
+     */
+    .word gdt_end - gdt - 1
+    .quad gdt
+    .word 0, 0, 0
+
+    /* 0x10 4GB flat code segment */
+    .word 0xFFFF, 0x0000, 0x9A00, 0x00AF
+
+    /* 0x18 4GB flat data segment */
+    .word 0xFFFF, 0x0000, 0x9200, 0x00CF
+END(gdt)
+
+ENTRY(stack)
+    .quad   0, 0
+END(stack)
+ENTRY(stack_init)
diff --git a/xen/arch/x86/purgatory/purgatory.c 
b/xen/arch/x86/purgatory/purgatory.c
new file mode 100644
index 0000000000..8b9a3cdb11
--- /dev/null
+++ b/xen/arch/x86/purgatory/purgatory.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * purgatory: Runs between two kernels
+ *
+ * Copyright (C) 2014 Red Hat Inc.
+ * Copyright (C) 2025  Cloud Software Group Inc.
+ *
+ * Author:
+ *       Vivek Goyal <vgoyal@xxxxxxxxxx>
+ *
+ * This code has been imported from kexec-tools.
+ */
+
+#include <xen/sha2.h>
+#include <xen/string.h>
+
+struct sha256_region {
+    uint64_t start;
+    uint64_t len;
+};
+
+typedef uint8_t sha256_digest_t[SHA2_256_DIGEST_SIZE];
+
+#define SHA256_REGIONS 16
+
+struct sha256_region sha256_regions[SHA256_REGIONS] = {};
+sha256_digest_t sha256_digest = { };
+
+int verify_sha256_digest(void)
+{
+    struct sha256_region *ptr, *end;
+    sha256_digest_t digest;
+    struct sha2_256_state ctx;
+    sha2_256_init(&ctx);
+    end = &sha256_regions[sizeof(sha256_regions) / sizeof(sha256_regions[0])];
+
+    for ( ptr = sha256_regions; ptr < end; ptr++ )
+        sha2_256_update(&ctx, (uint8_t *)((uintptr_t)ptr->start), ptr->len);
+
+    sha2_256_final(&ctx, digest);
+
+    if ( memcmp(digest, sha256_digest, sizeof(digest)) != 0 )
+        return 1;
+
+    return 0;
+}
+
+void purgatory(void)
+{
+    int ret;
+
+    ret = verify_sha256_digest();
+    if ( ret )
+    {
+        /* loop forever */
+        for ( ; ; )
+            ;
+    }
+}
diff --git a/xen/arch/x86/purgatory/setup-x86_64.S 
b/xen/arch/x86/purgatory/setup-x86_64.S
new file mode 100644
index 0000000000..c36be25656
--- /dev/null
+++ b/xen/arch/x86/purgatory/setup-x86_64.S
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * purgatory:  setup code
+ *
+ * Copyright (C) 2003,2004  Eric Biederman (ebiederm@xxxxxxxxxxxx)
+ * Copyright (C) 2014  Red Hat Inc.
+ * Copyright (C) 2025  Cloud Software Group Inc.
+ *
+ * This code has been adapted from kexec-tools and Linux.
+ */
+
+#include "config.h"
+
+    .text
+    .code64
+
+BALIGN(16)
+GLOBAL(purgatory_start)
+
+    /* Load a gdt so I know what the segment registers are */
+    lgdt    gdt(%rip)
+
+    /* load the data segments */
+    movl    $0x18, %eax /* data segment */
+    movl    %eax, %ds
+    movl    %eax, %es
+    movl    %eax, %ss
+    movl    %eax, %fs
+    movl    %eax, %gs
+
+    /* Setup a stack */
+    leaq    lstack_end(%rip), %rsp
+
+    /* Call the C code */
+    call purgatory
+    jmp entry64
+END(purgatory_start)
+
+    .section ".rodata"
+
+BALIGN(16)
+ENTRY(gdt)
+    /* 0x00 unusable segment
+     * 0x08 unused
+     * so use them as the gdt ptr
+     */
+    .word   gdt_end - gdt - 1
+    .quad   gdt
+    .word   0, 0, 0
+
+    /* 0x10 4GB flat code segment */
+    .word   0xFFFF, 0x0000, 0x9A00, 0x00AF
+
+    /* 0x18 4GB flat data segment */
+    .word   0xFFFF, 0x0000, 0x9200, 0x00CF
+END(gdt)
+
+    .bss
+
+BALIGN(4096)
+ENTRY(lstack)
+    .skip 4096
+END(lstack)
diff --git a/xen/arch/x86/purgatory/stack.S b/xen/arch/x86/purgatory/stack.S
new file mode 100644
index 0000000000..6ac7685cd5
--- /dev/null
+++ b/xen/arch/x86/purgatory/stack.S
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * purgatory:  stack
+ *
+ * Copyright (C) 2014 Red Hat Inc.
+ * Copyright (C) 2025 Cloud Software Group Inc.
+ *
+ *     This code has been adapted from kexec-tools and Linux.
+ */
+
+#include "config.h"
+
+       /* A stack for the loaded kernel.
+        * Separate and in the data section so it can be prepopulated.
+        */
+       .data
+
+BALIGN(4096)
+GLOBAL(stack)
+       .skip 4096
+GLOBAL_END(stack)
diff --git a/xen/lib/sha2-256.c b/xen/lib/sha2-256.c
index e55e297eff..09c033f97f 100644
--- a/xen/lib/sha2-256.c
+++ b/xen/lib/sha2-256.c
@@ -209,7 +209,7 @@ void sha2_256_digest(uint8_t digest[SHA2_256_DIGEST_SIZE],
     sha2_256_final(&s, digest);
 }
 
-#ifdef CONFIG_SELF_TESTS
+#if defined(CONFIG_SELF_TESTS) && !defined(__PURGATORY__)
 
 #include <xen/init.h>
 #include <xen/lib.h>
-- 
2.43.0




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.