]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
x86/kexec: add support for passing kexec handover (KHO) data
authorAlexander Graf <graf@amazon.com>
Fri, 9 May 2025 07:46:29 +0000 (00:46 -0700)
committerAndrew Morton <akpm@linux-foundation.org>
Tue, 13 May 2025 06:50:41 +0000 (23:50 -0700)
kexec handover (KHO) creates a metadata that the kernels pass between each
other during kexec.  This metadata is stored in memory and kexec image
contains a (physical) pointer to that memory.

In addition, KHO keeps "scratch regions" available for kexec: physically
contiguous memory regions that are guaranteed to not have any memory that
KHO would preserve.  The new kernel bootstraps itself using the scratch
regions and sets all handed over memory as in use.  When subsystems that
support KHO initialize, they introspect the KHO metadata, restore
preserved memory regions, and retrieve their state stored in the preserved
memory.

Enlighten x86 kexec-file and boot path about the KHO metadata and make
sure it gets passed along to the next kernel.

Link: https://lkml.kernel.org/r/20250509074635.3187114-12-changyuanl@google.com
Signed-off-by: Alexander Graf <graf@amazon.com>
Co-developed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Co-developed-by: Changyuan Lyu <changyuanl@google.com>
Signed-off-by: Changyuan Lyu <changyuanl@google.com>
Acked-by: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Anthony Yznaga <anthony.yznaga@oracle.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Ashish Kalra <ashish.kalra@amd.com>
Cc: Ben Herrenschmidt <benh@kernel.crashing.org>
Cc: Borislav Betkov <bp@alien8.de>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Gowans <jgowans@amazon.com>
Cc: Jason Gunthorpe <jgg@nvidia.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Krzysztof Kozlowski <krzk@kernel.org>
Cc: Marc Rutland <mark.rutland@arm.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Pasha Tatashin <pasha.tatashin@soleen.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Pratyush Yadav <ptyadav@amazon.de>
Cc: Rob Herring <robh@kernel.org>
Cc: Saravana Kannan <saravanak@google.com>
Cc: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Thomas Gleinxer <tglx@linutronix.de>
Cc: Thomas Lendacky <thomas.lendacky@amd.com>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
arch/x86/include/asm/setup.h
arch/x86/include/uapi/asm/setup_data.h
arch/x86/kernel/kexec-bzimage64.c
arch/x86/kernel/setup.c

index ad9212df0ec0cba13ceed1b91a061b7339245464..3b37571911f4cd3d56cf9ecaed41f960e7026ff3 100644 (file)
@@ -67,6 +67,8 @@ extern void x86_ce4100_early_setup(void);
 static inline void x86_ce4100_early_setup(void) { }
 #endif
 
+#include <linux/kexec_handover.h>
+
 #ifndef _SETUP
 
 #include <asm/espfix.h>
index 50c45ead4e7c97818ba30c86816af70fd3895202..2671c4e1b3a0b7212a195f5678e0ad633d562562 100644 (file)
@@ -13,7 +13,8 @@
 #define SETUP_CC_BLOB                  7
 #define SETUP_IMA                      8
 #define SETUP_RNG_SEED                 9
-#define SETUP_ENUM_MAX                 SETUP_RNG_SEED
+#define SETUP_KEXEC_KHO                        10
+#define SETUP_ENUM_MAX                 SETUP_KEXEC_KHO
 
 #define SETUP_INDIRECT                 (1<<31)
 #define SETUP_TYPE_MAX                 (SETUP_ENUM_MAX | SETUP_INDIRECT)
@@ -78,6 +79,16 @@ struct ima_setup_data {
        __u64 size;
 } __attribute__((packed));
 
+/*
+ * Locations of kexec handover metadata
+ */
+struct kho_data {
+       __u64 fdt_addr;
+       __u64 fdt_size;
+       __u64 scratch_addr;
+       __u64 scratch_size;
+} __attribute__((packed));
+
 #endif /* __ASSEMBLER__ */
 
 #endif /* _UAPI_ASM_X86_SETUP_DATA_H */
index 68530fad05f7475fa03462175fd8c16dc09fc698..dad174e3bed0d2c5a88e3614183ce83c566a6e33 100644 (file)
@@ -233,6 +233,32 @@ setup_ima_state(const struct kimage *image, struct boot_params *params,
 #endif /* CONFIG_IMA_KEXEC */
 }
 
+static void setup_kho(const struct kimage *image, struct boot_params *params,
+                     unsigned long params_load_addr,
+                     unsigned int setup_data_offset)
+{
+       struct setup_data *sd = (void *)params + setup_data_offset;
+       struct kho_data *kho = (void *)sd + sizeof(*sd);
+
+       if (!IS_ENABLED(CONFIG_KEXEC_HANDOVER))
+               return;
+
+       sd->type = SETUP_KEXEC_KHO;
+       sd->len = sizeof(struct kho_data);
+
+       /* Only add if we have all KHO images in place */
+       if (!image->kho.fdt || !image->kho.scratch)
+               return;
+
+       /* Add setup data */
+       kho->fdt_addr = image->kho.fdt;
+       kho->fdt_size = PAGE_SIZE;
+       kho->scratch_addr = image->kho.scratch->mem;
+       kho->scratch_size = image->kho.scratch->bufsz;
+       sd->next = params->hdr.setup_data;
+       params->hdr.setup_data = params_load_addr + setup_data_offset;
+}
+
 static int
 setup_boot_parameters(struct kimage *image, struct boot_params *params,
                      unsigned long params_load_addr,
@@ -312,6 +338,13 @@ setup_boot_parameters(struct kimage *image, struct boot_params *params,
                                     sizeof(struct ima_setup_data);
        }
 
+       if (IS_ENABLED(CONFIG_KEXEC_HANDOVER)) {
+               /* Setup space to store preservation metadata */
+               setup_kho(image, params, params_load_addr, setup_data_offset);
+               setup_data_offset += sizeof(struct setup_data) +
+                                    sizeof(struct kho_data);
+       }
+
        /* Setup RNG seed */
        setup_rng_seed(params, params_load_addr, setup_data_offset);
 
@@ -479,6 +512,10 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
                kbuf.bufsz += sizeof(struct setup_data) +
                              sizeof(struct ima_setup_data);
 
+       if (IS_ENABLED(CONFIG_KEXEC_HANDOVER))
+               kbuf.bufsz += sizeof(struct setup_data) +
+                             sizeof(struct kho_data);
+
        params = kzalloc(kbuf.bufsz, GFP_KERNEL);
        if (!params)
                return ERR_PTR(-ENOMEM);
index 766176c4f5ee83c145c6b01f67d4e0ef7208b79f..664cd21b853296c517cc72bd7321882aa6ae996f 100644 (file)
@@ -451,6 +451,29 @@ int __init ima_get_kexec_buffer(void **addr, size_t *size)
 }
 #endif
 
+static void __init add_kho(u64 phys_addr, u32 data_len)
+{
+       struct kho_data *kho;
+       u64 addr = phys_addr + sizeof(struct setup_data);
+       u64 size = data_len - sizeof(struct setup_data);
+
+       if (!IS_ENABLED(CONFIG_KEXEC_HANDOVER)) {
+               pr_warn("Passed KHO data, but CONFIG_KEXEC_HANDOVER not set. Ignoring.\n");
+               return;
+       }
+
+       kho = early_memremap(addr, size);
+       if (!kho) {
+               pr_warn("setup: failed to memremap kho data (0x%llx, 0x%llx)\n",
+                       addr, size);
+               return;
+       }
+
+       kho_populate(kho->fdt_addr, kho->fdt_size, kho->scratch_addr, kho->scratch_size);
+
+       early_memunmap(kho, size);
+}
+
 static void __init parse_setup_data(void)
 {
        struct setup_data *data;
@@ -479,6 +502,9 @@ static void __init parse_setup_data(void)
                case SETUP_IMA:
                        add_early_ima_buffer(pa_data);
                        break;
+               case SETUP_KEXEC_KHO:
+                       add_kho(pa_data, data_len);
+                       break;
                case SETUP_RNG_SEED:
                        data = early_memremap(pa_data, data_len);
                        add_bootloader_randomness(data->data, data->len);