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

[PATCH 2/2] x86: Implement crash kexec for EFI


  • To: xen-devel@xxxxxxxxxxxxxxxxxxxx
  • From: Kevin Lampis <kevin.lampis@xxxxxxxxxx>
  • Date: Tue, 2 Jun 2026 17:49:11 +0100
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=citrix.com; dmarc=pass action=none header.from=citrix.com; dkim=pass header.d=citrix.com; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=c5SMf9+M3eYkK+VCoT5N51c8wrzFph7I+7rr12mI5fg=; b=QT6Mdre2a2czDMZefxt+WNf0XT07R2mCOgOgLr0LzR/rVrVxkoDraYekAu9fuE7u26BAT6pto09l5tuss5Rh3FH7ewPt9jg3ytbFlt7yRyjC12vYSOJKzbmLJbNuW2oAi688EiGVcU/rzVRbCeMOWRicZnTT8wpL3ooVhIb5x+pV26P7IGymHos1MCpgv8XS9UtslR7SNA4QdrKsOnzlHoYxpyD8sOw1vY0dyYYJDzAiDB0O0gu+/est6P36TvUlVFfEmvLH22nKuiD1FGQ3KmRPrspx4e73v7s7GtkZXPMVLztrKw6LVS64wWiILlYALEso/p2xl+GepeHVxl69tg==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=knxJeJ8sw7neZDvuJ1WCRixLX5s8ztnI4QS/kz5QZsqPwBYEqGFhIpY9sYOna3rwW4czFlpr486WA5Qwr8tlA4I+Ig8OTyWFpV006oGZrfEP7ntRXJPw+mSKcTBiF/BiULZLDfKpGp3F08JA70ZZmkf+F0zLclxJ92gYT/pMLnfshMMrvkCA5BYBVeEmTov3CfBwkurjny6+jyw5j9m0ScAkG4BQunkrCWrCTrzz7mQCqYV+v6W01AKBJcWLIM8NLaF3A9MwdxJEiY8tQHZwbyqOaiPz11nupfg+E5jy2xL7M7a8yO1q8NwK85ZsuR0uxiKRk+utpiRbfHCSjepkuA==
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=selector1 header.d=citrix.com header.i="@citrix.com" header.h="From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck"
  • Authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=citrix.com;
  • Cc: jbeulich@xxxxxxxx, andrew.cooper3@xxxxxxxxxx, roger.pau@xxxxxxxxxx, Kevin Lampis <kevin.lampis@xxxxxxxxxx>
  • Delivery-date: Tue, 02 Jun 2026 16:48:49 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

This change adds a new KEXEC_TYPE_CRASH_EFI load type which is suitable
for use when Secure Boot is enabled.

When this load type is used, the caller should not pass purgatory as one
of the kexec segments. This is because in Secure Boot mode we cannot
accept and run arbitrary unsigned code from dom0 userspace. Instead,
Xen prepares any necessary intermediary glue code itself.

When Secure Boot/lockdown mode is enabled only the KEXEC_TYPE_CRASH_EFI
type will be accepted.

During load a digest of the kexec segments is calculated and stored. The
digest is calculated again during kexec_reboot, this removes the need to
re-verify the signatures.

Signed-off-by: Kevin Lampis <kevin.lampis@xxxxxxxxxx>
Signed-off-by: Ross Lagerwall <ross.lagerwall@xxxxxxxxxx>
Signed-off-by: Gerald Elder-Vass <gerald.elder-vass@xxxxxxxxx>
---
 xen/arch/x86/bzimage.c                   |  40 +---
 xen/arch/x86/include/asm/machine_kexec.h |   2 +-
 xen/arch/x86/machine_kexec.c             |  10 +-
 xen/arch/x86/x86_64/kexec_reloc.S        |  16 ++
 xen/common/kexec.c                       |  41 +++-
 xen/common/kimage.c                      | 264 +++++++++++++++++++----
 xen/include/public/kexec.h               |  23 +-
 xen/include/xen/kimage.h                 |  23 +-
 xen/include/xen/x86-linux.h              |  62 ++++++
 9 files changed, 381 insertions(+), 100 deletions(-)
 create mode 100644 xen/include/xen/x86-linux.h

diff --git a/xen/arch/x86/bzimage.c b/xen/arch/x86/bzimage.c
index 66f648f311..f4d5b584cb 100644
--- a/xen/arch/x86/bzimage.c
+++ b/xen/arch/x86/bzimage.c
@@ -6,6 +6,7 @@
 #include <xen/gunzip.h>
 #include <xen/decompress.h>
 #include <xen/libelf.h>
+#include <xen/x86-linux.h>
 #include <asm/bzimage.h>
 
 static __init unsigned long output_length(void *image, unsigned long image_len)
@@ -13,45 +14,6 @@ static __init unsigned long output_length(void *image, 
unsigned long image_len)
     return *(uint32_t *)(image + image_len - 4);
 }
 
-struct __packed setup_header {
-        uint8_t         _pad0[0x1f1];           /* skip uninteresting stuff */
-        uint8_t         setup_sects;
-        uint16_t        root_flags;
-        uint32_t        syssize;
-        uint16_t        ram_size;
-        uint16_t        vid_mode;
-        uint16_t        root_dev;
-        uint16_t        boot_flag;
-        uint16_t        jump;
-        uint32_t        header;
-#define HDR_MAGIC               "HdrS"
-#define HDR_MAGIC_SZ    4
-        uint16_t        version;
-#define VERSION(h,l)    (((h)<<8) | (l))
-        uint32_t        realmode_swtch;
-        uint16_t        start_sys;
-        uint16_t        kernel_version;
-        uint8_t         type_of_loader;
-        uint8_t         loadflags;
-        uint16_t        setup_move_size;
-        uint32_t        code32_start;
-        uint32_t        ramdisk_image;
-        uint32_t        ramdisk_size;
-        uint32_t        bootsect_kludge;
-        uint16_t        heap_end_ptr;
-        uint16_t        _pad1;
-        uint32_t        cmd_line_ptr;
-        uint32_t        initrd_addr_max;
-        uint32_t        kernel_alignment;
-        uint8_t         relocatable_kernel;
-        uint8_t         _pad2[3];
-        uint32_t        cmdline_size;
-        uint32_t        hardware_subarch;
-        uint64_t        hardware_subarch_data;
-        uint32_t        payload_offset;
-        uint32_t        payload_length;
-    };
-
 static __init int bzimage_check(struct setup_header *hdr, unsigned long len)
 {
     if ( len < sizeof(struct setup_header) )
diff --git a/xen/arch/x86/include/asm/machine_kexec.h 
b/xen/arch/x86/include/asm/machine_kexec.h
index 3e189acf24..e2d7b4854a 100644
--- a/xen/arch/x86/include/asm/machine_kexec.h
+++ b/xen/arch/x86/include/asm/machine_kexec.h
@@ -7,7 +7,7 @@
 
 extern void kexec_reloc(unsigned long reloc_code, unsigned long reloc_pt,
                         unsigned long ind_maddr, unsigned long entry_maddr,
-                        unsigned long flags);
+                        unsigned long flags, unsigned long rsi);
 
 extern const char kexec_reloc_end[];
 
diff --git a/xen/arch/x86/machine_kexec.c b/xen/arch/x86/machine_kexec.c
index f921eec5aa..e61b63ac53 100644
--- a/xen/arch/x86/machine_kexec.c
+++ b/xen/arch/x86/machine_kexec.c
@@ -154,6 +154,13 @@ void machine_kexec(struct kexec_image *image)
     int i;
     unsigned long reloc_flags = 0;
 
+    if ( image->type == KEXEC_TYPE_CRASH_EFI &&
+         kimage_verify_digest(image) != 0 )
+    {
+        printk(XENLOG_ERR "kexec digest failed, won't boot corrupted image\n");
+        for (;;);
+    }
+
     /* We are about to permenantly jump out of the Xen context into the kexec
      * purgatory code.  We really dont want to be still servicing interupts.
      */
@@ -198,7 +205,8 @@ void machine_kexec(struct kexec_image *image)
 
     kexec_reloc(page_to_maddr(image->control_code_page),
                 page_to_maddr(image->aux_page),
-                image->head, image->entry_maddr, reloc_flags);
+                image->head, image->entry_maddr, reloc_flags,
+                image->efi_boot_params);
 }
 
 int machine_kexec_get(xen_kexec_range_t *range)
diff --git a/xen/arch/x86/x86_64/kexec_reloc.S 
b/xen/arch/x86/x86_64/kexec_reloc.S
index b52d31a654..aa725266e2 100644
--- a/xen/arch/x86/x86_64/kexec_reloc.S
+++ b/xen/arch/x86/x86_64/kexec_reloc.S
@@ -33,6 +33,7 @@ FUNC(kexec_reloc, PAGE_SIZE)
         /* %rdx - indirection page maddr */
         /* %rcx - entry maddr (%rbp) */
         /* %r8 - flags */
+        /* %r9 - boot params (EFI only) */
 
         movq    %rcx, %rbp
 
@@ -75,6 +76,21 @@ FUNC(kexec_reloc, PAGE_SIZE)
         testq   $KEXEC_RELOC_FLAG_COMPAT, %r8
         jnz     .L_call_32_bit
 
+        movq %r9, %rsi /* boot params */
+        xor %rdi, %rdi
+        xor %rax, %rax
+        xor %rbx, %rbx
+        xor %rcx, %rcx
+        xor %rdx, %rdx
+        xor %r8,  %r8
+        xor %r9,  %r9
+        xor %r10, %r10
+        xor %r11, %r11
+        xor %r12, %r12
+        xor %r13, %r13
+        xor %r14, %r14
+        xor %r15, %r15
+
         /* Jump to the image entry point */
         jmp     *%rbp
 
diff --git a/xen/common/kexec.c b/xen/common/kexec.c
index 65776a95fd..14f15dd371 100644
--- a/xen/common/kexec.c
+++ b/xen/common/kexec.c
@@ -34,6 +34,7 @@
 #ifdef CONFIG_COMPAT
 #include <compat/kexec.h>
 #endif
+#include <xen/lockdown.h>
 
 bool __read_mostly kexecing;
 
@@ -910,7 +911,7 @@ static uint16_t kexec_load_v1_arch(void)
 }
 
 static int kexec_segments_add_segment(unsigned int *nr_segments,
-                                      xen_kexec_segment_t *segments,
+                                      struct kimage_segment *segments,
                                       mfn_t mfn)
 {
     paddr_t maddr = mfn_to_maddr(mfn);
@@ -936,7 +937,7 @@ static int kexec_segments_add_segment(unsigned int 
*nr_segments,
 
 static int kexec_segments_from_ind_page(mfn_t mfn,
                                         unsigned int *nr_segments,
-                                        xen_kexec_segment_t *segments,
+                                        struct kimage_segment *segments,
                                         bool compat)
 {
     void *page;
@@ -991,7 +992,7 @@ done:
 static int kexec_do_load_v1(xen_kexec_load_v1_t *load, int compat)
 {
     struct kexec_image *kimage = NULL;
-    xen_kexec_segment_t *segments;
+    struct kimage_segment *segments;
     uint16_t arch;
     unsigned int nr_segments = 0;
     mfn_t ind_mfn = maddr_to_mfn(load->image.indirection_page);
@@ -1001,7 +1002,7 @@ static int kexec_do_load_v1(xen_kexec_load_v1_t *load, 
int compat)
     if ( arch == EM_NONE )
         return -ENOSYS;
 
-    segments = xmalloc_array(xen_kexec_segment_t, KEXEC_SEGMENT_MAX);
+    segments = xmalloc_array(struct kimage_segment, KEXEC_SEGMENT_MAX);
     if ( segments == NULL )
         return -ENOMEM;
 
@@ -1103,24 +1104,35 @@ static int 
kexec_load_v1_compat(XEN_GUEST_HANDLE_PARAM(void) uarg)
 static int kexec_load(XEN_GUEST_HANDLE_PARAM(void) uarg)
 {
     xen_kexec_load_t load;
-    xen_kexec_segment_t *segments;
+    struct kimage_segment *segments;
     struct kexec_image *kimage = NULL;
     int ret;
+    unsigned int i;
 
     if ( copy_from_guest(&load, uarg, 1) )
         return -EFAULT;
 
+    if ( load.type == KEXEC_TYPE_DEFAULT_EFI )
+        return -EOPNOTSUPP;
+
+    if ( load.type != KEXEC_TYPE_CRASH_EFI && is_locked_down() )
+        return -EPERM;
+
     if ( load.nr_segments >= KEXEC_SEGMENT_MAX )
         return -EINVAL;
 
-    segments = xmalloc_array(xen_kexec_segment_t, load.nr_segments);
+    segments = xmalloc_array(struct kimage_segment, load.nr_segments);
     if ( segments == NULL )
         return -ENOMEM;
 
-    if ( copy_from_guest(segments, load.segments.h, load.nr_segments) )
+    for ( i = 0; i < load.nr_segments; i++ )
     {
-        ret = -EFAULT;
-        goto error;
+        if ( copy_from_guest_offset((xen_kexec_segment_t *)&segments[i],
+                                    load.segments.h, i, 1) )
+        {
+            ret = -EFAULT;
+            goto error;
+        }
     }
 
     ret = kimage_alloc(&kimage, load.type, load.arch, load.entry_maddr,
@@ -1132,6 +1144,13 @@ static int kexec_load(XEN_GUEST_HANDLE_PARAM(void) uarg)
     if ( ret < 0 )
         goto error;
 
+    if ( load.type == KEXEC_TYPE_CRASH_EFI )
+    {
+        ret = kimage_efi_setup(kimage, load.parameters);
+        if ( ret )
+            return ret;
+    }
+
     ret = kexec_load_slot(kimage);
     if ( ret < 0 )
         goto error;
@@ -1235,7 +1254,9 @@ static int do_kexec_op_internal(unsigned long op,
                 ret = kexec_get_range(uarg);
         break;
     case KEXEC_CMD_kexec_load_v1:
-        if ( compat )
+        if ( is_locked_down() )
+            ret = -EPERM;
+        else if ( compat )
             ret = kexec_load_v1_compat(uarg);
         else
             ret = kexec_load_v1(uarg);
diff --git a/xen/common/kimage.c b/xen/common/kimage.c
index 6202491f7e..69833d3ba6 100644
--- a/xen/common/kimage.c
+++ b/xen/common/kimage.c
@@ -19,10 +19,21 @@
 #include <xen/guest_access.h>
 #include <xen/mm.h>
 #include <xen/kexec.h>
+#include <xen/x86-linux.h>
 #include <xen/kimage.h>
+#include <xen/sha2.h>
 
 #include <asm/page.h>
 
+#define KIMAGE_SHA256_REGIONS 16
+
+typedef struct
+{
+    uint64_t start;
+    uint64_t len;
+}
+sha256_region_t;
+
 /*
  * When kexec transitions to the new kernel there is a one-to-one
  * mapping between physical and virtual addresses.  On processors
@@ -83,7 +94,7 @@ static struct page_info *kimage_alloc_zeroed_page(unsigned 
memflags)
 
 static int do_kimage_alloc(struct kexec_image **rimage, paddr_t entry,
                            unsigned long nr_segments,
-                           xen_kexec_segment_t *segments, uint8_t type)
+                           struct kimage_segment *segments, uint8_t type)
 {
     struct kexec_image *image;
     unsigned long i;
@@ -106,29 +117,6 @@ static int do_kimage_alloc(struct kexec_image **rimage, 
paddr_t entry,
     INIT_PAGE_LIST_HEAD(&image->dest_pages);
     INIT_PAGE_LIST_HEAD(&image->unusable_pages);
 
-    /*
-     * Verify we have good destination addresses.  The caller is
-     * responsible for making certain we don't attempt to load the new
-     * image into invalid or reserved areas of RAM.  This just
-     * verifies it is an address we can use.
-     *
-     * Since the kernel does everything in page size chunks ensure the
-     * destination addresses are page aligned.  Too many special cases
-     * crop of when we don't do this.  The most insidious is getting
-     * overlapping destination addresses simply because addresses are
-     * changed to page size granularity.
-     */
-    result = -EADDRNOTAVAIL;
-    for ( i = 0; i < nr_segments; i++ )
-    {
-        paddr_t mstart, mend;
-
-        mstart = image->segments[i].dest_maddr;
-        mend   = mstart + image->segments[i].dest_size;
-        if ( (mstart & ~PAGE_MASK) || (mend & ~PAGE_MASK) )
-            goto out;
-    }
-
     /*
      * Verify our destination addresses do not overlap.  If we allowed
      * overlapping destination addresses through very weird things can
@@ -208,23 +196,18 @@ out:
 
 static int kimage_normal_alloc(struct kexec_image **rimage, paddr_t entry,
                                unsigned long nr_segments,
-                               xen_kexec_segment_t *segments)
+                               struct kimage_segment *segments)
 {
     return do_kimage_alloc(rimage, entry, nr_segments, segments,
                            KEXEC_TYPE_DEFAULT);
 }
 
-static int kimage_crash_alloc(struct kexec_image **rimage, paddr_t entry,
-                              unsigned long nr_segments,
-                              xen_kexec_segment_t *segments)
+static int do_kimage_crash_alloc(struct kexec_image **rimage, paddr_t entry,
+                                 unsigned long nr_segments,
+                                 struct kimage_segment *segments)
 {
     unsigned long i;
 
-    /* Verify we have a valid entry point */
-    if ( (entry < kexec_crash_area.start)
-         || (entry > kexec_crash_area.start + kexec_crash_area.size))
-        return -EADDRNOTAVAIL;
-
     /*
      * Verify we have good destination addresses.  Normally
      * the caller is responsible for making certain we don't
@@ -254,6 +237,25 @@ static int kimage_crash_alloc(struct kexec_image **rimage, 
paddr_t entry,
                            KEXEC_TYPE_CRASH);
 }
 
+static int kimage_crash_alloc(struct kexec_image **rimage, paddr_t entry,
+                              unsigned long nr_segments,
+                              struct kimage_segment *segments)
+{
+    /* Verify we have a valid entry point */
+    if ( (entry < kexec_crash_area.start)
+         || (entry > kexec_crash_area.start + kexec_crash_area.size))
+        return -EADDRNOTAVAIL;
+
+    return do_kimage_crash_alloc(rimage, entry, nr_segments, segments);
+}
+
+static int kimage_crash_alloc_efi(struct kexec_image **rimage, paddr_t entry,
+                                  unsigned long nr_segments,
+                                  struct kimage_segment *segments)
+{
+    return do_kimage_crash_alloc(rimage, entry, nr_segments, segments);
+}
+
 static int kimage_is_destination_range(struct kexec_image *image,
                                        paddr_t start,
                                        paddr_t end)
@@ -666,7 +668,7 @@ found:
 }
 
 static int kimage_load_normal_segment(struct kexec_image *image,
-                                      xen_kexec_segment_t *segment)
+                                      struct kimage_segment *segment)
 {
     unsigned long to_copy;
     unsigned long src_offset;
@@ -719,7 +721,7 @@ static int kimage_load_normal_segment(struct kexec_image 
*image,
 }
 
 static int kimage_load_crash_segment(struct kexec_image *image,
-                                     xen_kexec_segment_t *segment)
+                                     struct kimage_segment *segment)
 {
     /*
      * For crash dumps kernels we simply copy the data from user space
@@ -727,12 +729,14 @@ static int kimage_load_crash_segment(struct kexec_image 
*image,
      */
     paddr_t dest;
     unsigned long sbytes, dbytes;
+    unsigned int dest_offset;
     int ret = 0;
     unsigned long src_offset = 0;
 
     sbytes = segment->buf_size;
     dbytes = segment->dest_size;
     dest = segment->dest_maddr;
+    dest_offset = segment->dest_offset;
 
     while ( dbytes )
     {
@@ -742,30 +746,35 @@ static int kimage_load_crash_segment(struct kexec_image 
*image,
 
         dest_mfn = dest >> PAGE_SHIFT;
 
-        dchunk = PAGE_SIZE;
+        dchunk = PAGE_SIZE - dest_offset;
         schunk = min(dchunk, sbytes);
 
         dest_va = map_domain_page(_mfn(dest_mfn));
         if ( !dest_va )
             return -EINVAL;
 
-        ret = copy_from_guest_offset(dest_va, segment->buf.h, src_offset, 
schunk);
+        if ( dest_offset )
+            memset(dest_va, 0, dest_offset);
+        ret = copy_from_guest_offset(dest_va + dest_offset, segment->buf.h,
+                                     src_offset, schunk);
         memset(dest_va + schunk, 0, dchunk - schunk);
 
         unmap_domain_page(dest_va);
         if ( ret )
             return -EFAULT;
 
-        dbytes -= dchunk;
+        dbytes -= dchunk + dest_offset;
         sbytes -= schunk;
-        dest += dchunk;
+        dest += dchunk + dest_offset;
         src_offset += schunk;
+        dest_offset = 0;
     }
 
     return 0;
 }
 
-static int kimage_load_segment(struct kexec_image *image, xen_kexec_segment_t 
*segment)
+static int kimage_load_segment(struct kexec_image *image,
+                               struct kimage_segment *segment)
 {
     int result = -ENOMEM;
     paddr_t addr;
@@ -795,10 +804,30 @@ static int kimage_load_segment(struct kexec_image *image, 
xen_kexec_segment_t *s
 }
 
 int kimage_alloc(struct kexec_image **rimage, uint8_t type, uint16_t arch,
-                 uint64_t entry_maddr,
-                 uint32_t nr_segments, xen_kexec_segment_t *segment)
+                 uint64_t entry_maddr, uint32_t nr_segments,
+                 struct kimage_segment *segment)
 {
     int result;
+    unsigned int i;
+
+    for ( i = 0; i < nr_segments; i++ )
+    {
+        paddr_t mend;
+
+        /*
+         * Stash the destination offset-in-page for use when copying the
+         * buffer later.
+         */
+        segment[i].dest_offset = PAGE_OFFSET(segment[i].dest_maddr);
+
+        /*
+         * Align down the start address to page size and align up the end
+         * address to page size.
+         */
+        mend = segment[i].dest_maddr + segment[i].dest_size;
+        segment[i].dest_maddr &= PAGE_MASK;
+        segment[i].dest_size = ROUNDUP(mend, PAGE_SIZE) - 
segment[i].dest_maddr;
+    }
 
     switch( type )
     {
@@ -808,6 +837,10 @@ int kimage_alloc(struct kexec_image **rimage, uint8_t 
type, uint16_t arch,
     case KEXEC_TYPE_CRASH:
         result = kimage_crash_alloc(rimage, entry_maddr, nr_segments, segment);
         break;
+    case KEXEC_TYPE_CRASH_EFI:
+        result = kimage_crash_alloc_efi(rimage, entry_maddr,
+                                        nr_segments, segment);
+        break;
     default:
         result = -EINVAL;
         break;
@@ -939,6 +972,153 @@ done:
     return ret;
 }
 
+static int kimage_calc_one_digest(struct sha2_256_state *ctx,
+                                  struct kimage_segment *segment)
+{
+    paddr_t dest;
+    unsigned long sbytes;
+    unsigned int dest_offset;
+    int ret = 0;
+
+    sbytes = segment->buf_size;
+    dest = segment->dest_maddr + segment->dest_offset;
+    dest_offset = segment->dest_offset;
+
+    while ( sbytes )
+    {
+        unsigned long dest_mfn;
+        void *dest_va;
+        size_t schunk, dchunk;
+
+        dest_mfn = dest >> PAGE_SHIFT;
+
+        dchunk = PAGE_SIZE - dest_offset;
+        schunk = min(dchunk, sbytes);
+
+        dest_va = map_domain_page(_mfn(dest_mfn));
+        if ( !dest_va )
+            return -EINVAL;
+
+        sha2_256_update(ctx, dest_va + dest_offset, schunk);
+
+        unmap_domain_page(dest_va);
+        if ( ret )
+            return -EFAULT;
+
+        sbytes -= schunk;
+        dest += dchunk;
+        dest_offset = 0;
+    }
+    return 0;
+}
+
+static int kimage_calc_digest(const struct kexec_image *image,
+                              uint8_t digest[SHA2_256_DIGEST_SIZE])
+{
+    int ret;
+    sha256_region_t regions[KIMAGE_SHA256_REGIONS] = {{0}};
+    struct sha2_256_state ctx;
+    unsigned int s;
+
+    if ( image->nr_segments > KIMAGE_SHA256_REGIONS )
+    {
+        dprintk(XENLOG_DEBUG, "More segments than allocated SHA256 regions\n");
+        return -E2BIG;
+    }
+
+
+    sha2_256_init(&ctx);
+
+    for ( s = 0; s < image->nr_segments; s++ ) {
+        ret = kimage_calc_one_digest(&ctx, &image->segments[s]);
+        if ( ret )
+            return ret;
+
+        regions[s].start = image->segments[s].dest_maddr +
+                           image->segments[s].dest_offset;
+        regions[s].len = image->segments[s].buf_size;
+    }
+
+    sha2_256_final(&ctx, digest);
+    return 0;
+}
+
+int kimage_verify_digest(const struct kexec_image *image)
+{
+    uint8_t digest[SHA2_256_DIGEST_SIZE];
+    int ret;
+
+    ret = kimage_calc_digest(image, digest);
+    if ( ret )
+        return ret;
+
+    if ( memcmp(digest, image->digest, sizeof(digest)) != 0 )
+        return 1;
+
+    return 0;
+}
+
+/*
+ * Find the entry point to the new kernel, we need to map the crash region into
+ * memory in order to read the kernel header.
+ */
+#define KERNEL_SEGMENT_IDX 0
+static uint64_t kimage_find_kernel_entry_maddr(struct kexec_image *image)
+{
+    uint64_t alignment_addr;
+    uint32_t alignment;
+    unsigned long dest_mfn;
+    void *dest_va;
+
+    alignment_addr = image->segments[KERNEL_SEGMENT_IDX].dest_maddr +
+                         image->segments[KERNEL_SEGMENT_IDX].dest_offset +
+                         offsetof(struct setup_header, kernel_alignment);
+
+    dest_mfn = alignment_addr >> PAGE_SHIFT;
+    dest_va = map_domain_page(_mfn(dest_mfn));
+    if ( !dest_va )
+        return -EINVAL;
+
+    alignment = *((uint32_t *) ((uint8_t *) dest_va +
+                                                PAGE_OFFSET(alignment_addr)));
+
+    unmap_domain_page(dest_va);
+
+    /*
+     * Ensure the kernel alignment is a valid LOAD_PHYSICAL_ADDR,
+     * which ranges from 0x200000 (2MiB) to 0x1000000 (16Mib) on 64-bit systems
+     * as defined in the kernel x86 Kconfig
+     */
+    if ( alignment % 0x200000 != 0 ||
+         alignment < 0x200000 ||
+         alignment > 0x1000000 )
+        return -EINVAL;
+
+    return ROUNDUP(image->segments[KERNEL_SEGMENT_IDX].dest_maddr +
+                       image->segments[KERNEL_SEGMENT_IDX].dest_offset,
+                   alignment) +
+                   0x200;
+}
+
+int kimage_efi_setup(struct kexec_image *image, uint64_t parameters)
+{
+    int ret;
+    int64_t rip;
+
+    ret = kimage_calc_digest(image, image->digest);
+    if ( ret )
+        return ret;
+
+    rip = kimage_find_kernel_entry_maddr(image);
+    if ( rip < 0 )
+        return -EINVAL;
+
+    image->efi_boot_params = parameters;
+    image->entry_maddr = rip;
+
+    return 0;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/include/public/kexec.h b/xen/include/public/kexec.h
index 40d79e936b..9bc94c6fd6 100644
--- a/xen/include/public/kexec.h
+++ b/xen/include/public/kexec.h
@@ -56,15 +56,24 @@
 /*
  * Kexec supports two types of operation:
  * - kexec into a regular kernel, very similar to a standard reboot
- *   - KEXEC_TYPE_DEFAULT is used to specify this type
+ *   - KEXEC_TYPE_DEFAULT or KEXEC_TYPE_DEFAULT_EFI are used to specify
+ *     this type
+ *   - in case of KEXEC_TYPE_DEFAULT_EFI the first segment will
+ *     point to full kernel to load and entry point will point to
+ *     parameters
  * - kexec into a special "crash kernel", aka kexec-on-panic
- *   - KEXEC_TYPE_CRASH is used to specify this type
+ *   - KEXEC_TYPE_CRASH or KEXEC_TYPE_CRASH_EFI are used to specify this
+ *     type
+ *   - see above for differences between KEXEC_TYPE_CRASH and
+ *     KEXEC_TYPE_CRASH_EFI
  *   - parts of our system may be broken at kexec-on-panic time
  *     - the code should be kept as simple and self-contained as possible
  */
 
-#define KEXEC_TYPE_DEFAULT 0
-#define KEXEC_TYPE_CRASH   1
+#define KEXEC_TYPE_DEFAULT     0
+#define KEXEC_TYPE_CRASH       1
+#define KEXEC_TYPE_DEFAULT_EFI 2
+#define KEXEC_TYPE_CRASH_EFI   3
 
 
 /* The kexec implementation for Xen allows the user to load two
@@ -195,7 +204,11 @@ typedef struct xen_kexec_load {
         XEN_GUEST_HANDLE(xen_kexec_segment_t) h;
         uint64_t _pad;
     } segments;
-    uint64_t entry_maddr; /* image entry point machine address. */
+    /* image entry point machine address or parameters in case of EFI. */
+    union {
+        uint64_t entry_maddr;
+        uint64_t parameters;
+    };
 } xen_kexec_load_t;
 DEFINE_XEN_GUEST_HANDLE(xen_kexec_load_t);
 
diff --git a/xen/include/xen/kimage.h b/xen/include/xen/kimage.h
index fccba1d88d..5bfb678897 100644
--- a/xen/include/xen/kimage.h
+++ b/xen/include/xen/kimage.h
@@ -11,18 +11,30 @@
 
 #include <xen/list.h>
 #include <xen/mm.h>
+#include <xen/sha2.h>
 #include <public/kexec.h>
 
 #define KEXEC_SEGMENT_MAX 16
 
 typedef paddr_t kimage_entry_t;
 
+struct kimage_segment {
+    union {
+        XEN_GUEST_HANDLE(const_void) h;
+        uint64_t _pad;
+    } buf;
+    uint64_t buf_size;
+    uint64_t dest_maddr;
+    uint64_t dest_size;
+    unsigned int dest_offset;
+};
+
 struct kexec_image {
     uint8_t type;
     uint16_t arch;
     uint64_t entry_maddr;
     uint32_t nr_segments;
-    xen_kexec_segment_t *segments;
+    struct kimage_segment *segments;
 
     kimage_entry_t head;
     struct page_info *entry_page;
@@ -37,11 +49,16 @@ struct kexec_image {
 
     /* Address of next control page to allocate for crash kernels. */
     paddr_t next_crash_page;
+
+    uint8_t digest[SHA2_256_DIGEST_SIZE];
+
+    /* Address of boot params. Will be loaded into %rsi. For EFI kexec only. */
+    uint64_t efi_boot_params;
 };
 
 int kimage_alloc(struct kexec_image **rimage, uint8_t type, uint16_t arch,
                  uint64_t entry_maddr,
-                 uint32_t nr_segments, xen_kexec_segment_t *segment);
+                 uint32_t nr_segments, struct kimage_segment *segment);
 void kimage_free(struct kexec_image *image);
 int kimage_load_segments(struct kexec_image *image);
 struct page_info *kimage_alloc_control_page(struct kexec_image *image,
@@ -52,6 +69,8 @@ mfn_t kimage_entry_mfn(kimage_entry_t *entry, bool compat);
 unsigned long kimage_entry_ind(kimage_entry_t *entry, bool compat);
 int kimage_build_ind(struct kexec_image *image, mfn_t ind_mfn,
                      bool compat);
+int kimage_efi_setup(struct kexec_image *image, uint64_t parameters);
+int kimage_verify_digest(const struct kexec_image *image);
 
 #endif /* __ASSEMBLER__ */
 
diff --git a/xen/include/xen/x86-linux.h b/xen/include/xen/x86-linux.h
new file mode 100644
index 0000000000..940d830323
--- /dev/null
+++ b/xen/include/xen/x86-linux.h
@@ -0,0 +1,62 @@
+/*
+ * This file was extracted from x86-linux.h in kexec-tools
+ *
+ * Copyright (C) 2003-2010  Eric Biederman (ebiederm@xxxxxxxxxxxx)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef X86_LINUX_H
+#define X86_LINUX_H
+
+struct __packed setup_header {
+    uint8_t         _pad0[0x1f1];           /* skip uninteresting stuff */
+    uint8_t         setup_sects;
+    uint16_t        root_flags;
+    uint32_t        syssize;
+    uint16_t        ram_size;
+    uint16_t        vid_mode;
+    uint16_t        root_dev;
+    uint16_t        boot_flag;
+    uint16_t        jump;
+    uint32_t        header;
+#define HDR_MAGIC               "HdrS"
+#define HDR_MAGIC_SZ    4
+    uint16_t        version;
+#define VERSION(h,l)    (((h)<<8) | (l))
+    uint32_t        realmode_swtch;
+    uint16_t        start_sys;
+    uint16_t        kernel_version;
+    uint8_t         type_of_loader;
+    uint8_t         loadflags;
+    uint16_t        setup_move_size;
+    uint32_t        code32_start;
+    uint32_t        ramdisk_image;
+    uint32_t        ramdisk_size;
+    uint32_t        bootsect_kludge;
+    uint16_t        heap_end_ptr;
+    uint16_t        _pad1;
+    uint32_t        cmd_line_ptr;
+    uint32_t        initrd_addr_max;
+    uint32_t        kernel_alignment;
+    uint8_t         relocatable_kernel;
+    uint8_t         _pad2[3];
+    uint32_t        cmdline_size;
+    uint32_t        hardware_subarch;
+    uint64_t        hardware_subarch_data;
+    uint32_t        payload_offset;
+    uint32_t        payload_length;
+};
+
+#endif /* X86_LINUX_H */
-- 
2.52.0




 


Rackspace

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