diff mbox series

[MINI-OS,07/12] kexec: build parameters for new kernel

Message ID 20250321092451.17309-8-jgross@suse.com (mailing list archive)
State New
Headers show
Series kexec: add kexec support to Mini-OS | expand

Commit Message

Jürgen Groß March 21, 2025, 9:24 a.m. UTC
Build the parameters for the new kernel, consisting of the
hvm_start_info struct, the memory map and the command line.

Signed-off-by: Juergen Gross <jgross@suse.com>
---
 arch/x86/kexec.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/kexec.h  |  4 ++++
 kexec.c          | 13 ++++++++++-
 3 files changed, 74 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/arch/x86/kexec.c b/arch/x86/kexec.c
index 98a478d3..6fc7d02d 100644
--- a/arch/x86/kexec.c
+++ b/arch/x86/kexec.c
@@ -200,6 +200,7 @@  bool kexec_arch_need_analyze_shdrs(void)
 
 static unsigned long kexec_param_loc;
 static unsigned int kexec_param_size;
+static unsigned long kexec_param_mem;
 
 void kexec_set_param_loc(const char *cmdline)
 {
@@ -212,4 +213,61 @@  void kexec_set_param_loc(const char *cmdline)
     kexec_last_addr += kexec_param_size;
     kexec_last_addr = round_pgup(kexec_last_addr);
 }
+
+int kexec_get_entry(const char *cmdline)
+{
+    struct hvm_start_info *info;
+    struct hvm_memmap_table_entry *mmap;
+    unsigned int order;
+    unsigned int i;
+
+    if ( kernel_entry == ~0UL )
+        return ENOEXEC;
+
+    order = get_order(kexec_param_size);
+
+    kexec_param_mem = alloc_pages(order);
+    if ( !kexec_param_mem )
+        return ENOMEM;
+
+    info = (struct hvm_start_info *)kexec_param_mem;
+    memset(info, 0, sizeof(*info));
+    info->magic = XEN_HVM_START_MAGIC_VALUE;
+    info->version = 1;
+    info->cmdline_paddr = kexec_param_mem + sizeof(*info) +
+                          e820_entries * sizeof(struct hvm_memmap_table_entry);
+    info->memmap_paddr = kexec_param_mem + sizeof(*info);
+    info->memmap_entries = e820_entries;
+
+    mmap = (struct hvm_memmap_table_entry *)(info + 1);
+    for ( i = 0; i < e820_entries; i++ )
+    {
+        mmap->addr = e820_map[i].addr;
+        mmap->size = e820_map[i].size;
+        mmap->type = e820_map[i].type;
+        mmap++;
+    }
+
+    strcpy((char *)mmap, cmdline);
+
+    if ( kexec_add_action(KEXEC_COPY, to_virt(kexec_param_loc), info,
+                          kexec_param_size) )
+        return ENOSPC;
+
+    /* The call of the new kernel happens via the physical address! */
+    if ( kexec_add_action(KEXEC_CALL, (void *)kernel_entry,
+                          (void *)kexec_param_loc, 0) )
+        return ENOSPC;
+
+    return 0;
+}
+
+void kexec_get_entry_undo(void)
+{
+    if ( kexec_param_mem )
+    {
+        free_pages((void *)kexec_param_mem, get_order(kexec_param_size));
+        kexec_param_mem = 0;
+    }
+}
 #endif /* CONFIG_KEXEC */
diff --git a/include/kexec.h b/include/kexec.h
index 8a2b552f..7b103dea 100644
--- a/include/kexec.h
+++ b/include/kexec.h
@@ -45,4 +45,8 @@  bool kexec_arch_need_analyze_shdrs(void);
 /* Finalize parameter location and size. */
 void kexec_set_param_loc(const char *cmdline);
 
+/* Get entry point and parameter of new kernel. */
+int kexec_get_entry(const char *cmdline);
+void kexec_get_entry_undo(void);
+
 #endif /* _KEXEC_H */
diff --git a/kexec.c b/kexec.c
index 68457711..0ef8eb35 100644
--- a/kexec.c
+++ b/kexec.c
@@ -177,10 +177,21 @@  int kexec(void *kernel, unsigned long kernel_size, const char *cmdline)
 
     reserve_memory_below(kexec_last_addr);
 
+    ret = kexec_get_entry(cmdline);
+    if ( ret )
+    {
+        printk("kexec: ELF file of new kernel has no valid entry point\n");
+        goto err;
+    }
+
     /* Error exit. */
+    ret = ENOSYS;
+
+ err:
     unreserve_memory_below();
+    kexec_get_entry_undo();
 
-    return ENOSYS;
+    return ret;
 }
 EXPORT_SYMBOL(kexec);