|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [mini-os master] mini-os: analyze new kernel for kexec
commit 15ed1cc0c1b24f94adc89bb906a651a3623430bb
Author: Juergen Gross <jgross@xxxxxxxx>
AuthorDate: Mon Jun 23 10:40:31 2025 +0200
Commit: Jan Beulich <jbeulich@xxxxxxxx>
CommitDate: Mon Jun 23 10:40:31 2025 +0200
mini-os: analyze new kernel for kexec
Analyze the properties of the new kernel to be loaded by kexec. The
data needed is:
- upper boundary in final location
- copy and memory clear operations
- entry point and entry parameter
Signed-off-by: Juergen Gross <jgross@xxxxxxxx>
Reviewed-by: Jason Andryuk <jason.andryuk@xxxxxxx>
---
arch/x86/kexec.c | 92 +++++++++++++++++++++++++++++++++++++++++++
include/kexec.h | 11 ++++++
kexec.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 220 insertions(+)
diff --git a/arch/x86/kexec.c b/arch/x86/kexec.c
index a8a2fa9..736ca1b 100644
--- a/arch/x86/kexec.c
+++ b/arch/x86/kexec.c
@@ -28,8 +28,15 @@
#include <mini-os/os.h>
#include <mini-os/lib.h>
+#include <mini-os/e820.h>
+#include <mini-os/err.h>
#include <mini-os/kexec.h>
+#include <xen/elfnote.h>
+#include <xen/arch-x86/hvm/start_info.h>
+
+static unsigned long kernel_phys_entry = ~0UL;
+
/*
* Final stage of kexec. Copies all data to the final destinations, zeroes
* .bss and activates new kernel.
@@ -104,4 +111,89 @@ void do_kexec(void *kexec_page)
: :"m" (stack), "m" (actions), "m" (phys), "m" (final));
}
+bool kexec_chk_arch(elf_ehdr *ehdr)
+{
+ return ehdr->e32.e_machine == EM_386 || ehdr->e32.e_machine == EM_X86_64;
+}
+
+static unsigned int note_data_sz(unsigned int sz)
+{
+ return (sz + 3) & ~3;
+}
+
+static void read_note_entry(elf_ehdr *ehdr, void *start, unsigned int len)
+{
+ elf_note *note = start;
+ unsigned int off, note_len, namesz, descsz;
+ char *val;
+
+ for ( off = 0; off < len; off += note_len )
+ {
+ namesz = note_data_sz(note_val(ehdr, note, namesz));
+ descsz = note_data_sz(note_val(ehdr, note, descsz));
+ val = note_val(ehdr, note, data);
+ note_len = val - (char *)note + namesz + descsz;
+
+ if ( !strncmp(val, "Xen", namesz) &&
+ note_val(ehdr, note, type) == XEN_ELFNOTE_PHYS32_ENTRY )
+ {
+ val += namesz;
+ switch ( note_val(ehdr, note, descsz) )
+ {
+ case 1:
+ kernel_phys_entry = *(uint8_t *)val;
+ return;
+ case 2:
+ kernel_phys_entry = *(uint16_t *)val;
+ return;
+ case 4:
+ kernel_phys_entry = *(uint32_t *)val;
+ return;
+ case 8:
+ kernel_phys_entry = *(uint64_t *)val;
+ return;
+ default:
+ break;
+ }
+ }
+
+ note = elf_ptr_add(note, note_len);
+ }
+}
+
+int kexec_arch_analyze_phdr(elf_ehdr *ehdr, elf_phdr *phdr)
+{
+ void *notes_start;
+ unsigned int notes_len;
+
+ if ( phdr_val(ehdr, phdr, p_type) != PT_NOTE || kernel_phys_entry != ~0UL )
+ return 0;
+
+ notes_start = elf_ptr_add(ehdr, phdr_val(ehdr, phdr, p_offset));
+ notes_len = phdr_val(ehdr, phdr, p_filesz);
+ read_note_entry(ehdr, notes_start, notes_len);
+
+ return 0;
+}
+
+int kexec_arch_analyze_shdr(elf_ehdr *ehdr, elf_shdr *shdr)
+{
+ void *notes_start;
+ unsigned int notes_len;
+
+ if ( shdr_val(ehdr, shdr, sh_type) != SHT_NOTE ||
+ kernel_phys_entry != ~0UL )
+ return 0;
+
+ notes_start = elf_ptr_add(ehdr, shdr_val(ehdr, shdr, sh_offset));
+ notes_len = shdr_val(ehdr, shdr, sh_size);
+ read_note_entry(ehdr, notes_start, notes_len);
+
+ return 0;
+}
+
+bool kexec_arch_need_analyze_shdrs(void)
+{
+ return kernel_phys_entry == ~0UL;
+}
#endif /* CONFIG_KEXEC */
diff --git a/include/kexec.h b/include/kexec.h
index 722be45..f54cbb9 100644
--- a/include/kexec.h
+++ b/include/kexec.h
@@ -1,5 +1,6 @@
#ifndef _KEXEC_H
#define _KEXEC_H
+#include <mini-os/elf.h>
/* One element of kexec actions (last element must have action KEXEC_CALL): */
struct kexec_action {
@@ -18,6 +19,8 @@ struct kexec_action {
extern char _kexec_start[], _kexec_end[];
extern struct kexec_action kexec_actions[KEXEC_MAX_ACTIONS];
+extern unsigned long kexec_last_addr;
+
int kexec_add_action(int action, void *dest, void *src, unsigned int len);
#define KEXEC_SECSIZE ((unsigned long)_kexec_end - (unsigned long)_kexec_start)
@@ -31,4 +34,12 @@ void do_kexec(void *kexec_page);
/* Assembler code for switching off paging and passing execution to new OS. */
void kexec_phys(void);
+/* Check kernel to match current architecture. */
+bool kexec_chk_arch(elf_ehdr *ehdr);
+
+/* Architecture specific ELF handling functions. */
+int kexec_arch_analyze_phdr(elf_ehdr *ehdr, elf_phdr *phdr);
+int kexec_arch_analyze_shdr(elf_ehdr *ehdr, elf_shdr *shdr);
+bool kexec_arch_need_analyze_shdrs(void);
+
#endif /* _KEXEC_H */
diff --git a/kexec.c b/kexec.c
index b69e145..7c0e637 100644
--- a/kexec.c
+++ b/kexec.c
@@ -31,6 +31,9 @@
#include <errno.h>
#include <mini-os/os.h>
#include <mini-os/lib.h>
+#include <mini-os/console.h>
+#include <mini-os/elf.h>
+#include <mini-os/err.h>
#include <mini-os/kexec.h>
/*
@@ -53,8 +56,122 @@
* - The new kernel is activated.
*/
+unsigned long kexec_last_addr;
+
+static int analyze_phdrs(elf_ehdr *ehdr)
+{
+ elf_phdr *phdr;
+ unsigned int n_hdr, i;
+ unsigned long paddr, offset, filesz, memsz;
+ int ret;
+
+ phdr = elf_ptr_add(ehdr, ehdr_val(ehdr, e_phoff));
+ n_hdr = ehdr_val(ehdr, e_phnum);
+ for ( i = 0; i < n_hdr; i++ )
+ {
+ ret = kexec_arch_analyze_phdr(ehdr, phdr);
+ if ( ret )
+ return ret;
+
+ if ( phdr_val(ehdr, phdr, p_type) == PT_LOAD &&
+ (phdr_val(ehdr, phdr, p_flags) & (PF_X | PF_W | PF_R)) )
+ {
+ paddr = phdr_val(ehdr, phdr, p_paddr);
+ offset = phdr_val(ehdr, phdr, p_offset);
+ filesz = phdr_val(ehdr, phdr, p_filesz);
+ memsz = phdr_val(ehdr, phdr, p_memsz);
+ if ( filesz > 0 )
+ {
+ ret = kexec_add_action(KEXEC_COPY, to_virt(paddr),
+ (char *)ehdr + offset, filesz);
+ if ( ret )
+ return ret;
+ }
+ if ( memsz > filesz )
+ {
+ ret = kexec_add_action(KEXEC_ZERO, to_virt(paddr + filesz),
+ NULL, memsz - filesz);
+ if ( ret )
+ return ret;
+ }
+ if ( paddr + memsz > kexec_last_addr )
+ kexec_last_addr = paddr + memsz;
+ }
+
+ phdr = elf_ptr_add(phdr, ehdr_val(ehdr, e_phentsize));
+ }
+
+ return 0;
+}
+
+static int analyze_shdrs(elf_ehdr *ehdr)
+{
+ elf_shdr *shdr;
+ unsigned int n_hdr, i;
+ int ret;
+
+ if ( !kexec_arch_need_analyze_shdrs() )
+ return 0;
+
+ shdr = elf_ptr_add(ehdr, ehdr_val(ehdr, e_shoff));
+ n_hdr = ehdr_val(ehdr, e_shnum);
+ for ( i = 0; i < n_hdr; i++ )
+ {
+ ret = kexec_arch_analyze_shdr(ehdr, shdr);
+ if ( ret )
+ return ret;
+
+ shdr = elf_ptr_add(shdr, ehdr_val(ehdr, e_shentsize));
+ }
+
+ return 0;
+}
+
+static int analyze_kernel(void *kernel, unsigned long size)
+{
+ elf_ehdr *ehdr = kernel;
+ int ret;
+
+ if ( !IS_ELF(ehdr->e32) )
+ {
+ printk("kexec: new kernel not an ELF file\n");
+ return ENOEXEC;
+ }
+ if ( ehdr->e32.e_ident[EI_DATA] != ELFDATA2LSB )
+ {
+ printk("kexec: ELF file of new kernel is big endian\n");
+ return ENOEXEC;
+ }
+ if ( !elf_is_32bit(ehdr) && !elf_is_64bit(ehdr) )
+ {
+ printk("kexec: ELF file of new kernel is neither 32 nor 64 bit\n");
+ return ENOEXEC;
+ }
+ if ( !kexec_chk_arch(ehdr) )
+ {
+ printk("kexec: ELF file of new kernel is not compatible with arch\n");
+ return ENOEXEC;
+ }
+
+ ret = analyze_phdrs(ehdr);
+ if ( ret )
+ return ret;
+
+ ret = analyze_shdrs(ehdr);
+ if ( ret )
+ return ret;
+
+ return 0;
+}
+
int kexec(void *kernel, unsigned long kernel_size, const char *cmdline)
{
+ int ret;
+
+ ret = analyze_kernel(kernel, kernel_size);
+ if ( ret )
+ return ret;
+
return ENOSYS;
}
EXPORT_SYMBOL(kexec);
--
generated by git-patchbot for /home/xen/git/mini-os.git#master
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |