[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [MINI-OS PATCH 04/12] kexec: 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> --- arch/x86/kexec.c | 91 +++++++++++++++++++++++++++++++++++ include/kexec.h | 11 +++++ kexec.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 220 insertions(+), 2 deletions(-) diff --git a/arch/x86/kexec.c b/arch/x86/kexec.c index bf247797..2069f3c6 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_entry = ~0UL; + /* * Final stage of kexec. Copies all data to the final destinations, zeroes * .bss and activates new kernel. @@ -106,4 +113,88 @@ void do_kexec(void *kexec_page) :"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 check_notes_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_entry = *(uint8_t *)val; + return; + case 2: + kernel_entry = *(uint16_t *)val; + return; + case 4: + kernel_entry = *(uint32_t *)val; + return; + case 8: + kernel_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_entry != ~0UL ) + return 0; + + notes_start = elf_ptr_add(ehdr, phdr_val(ehdr, phdr, p_offset)); + notes_len = phdr_val(ehdr, phdr, p_filesz); + check_notes_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_entry != ~0UL ) + return 0; + + notes_start = elf_ptr_add(ehdr, shdr_val(ehdr, shdr, sh_offset)); + notes_len = shdr_val(ehdr, shdr, sh_size); + check_notes_entry(ehdr, notes_start, notes_len); + + return 0; +} + +bool kexec_arch_need_analyze_shdrs(void) +{ + return kernel_entry == ~0UL; +} #endif /* CONFIG_KEXEC */ diff --git a/include/kexec.h b/include/kexec.h index 722be456..f54cbb90 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 849a98e4..3ff4ea07 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> /* @@ -54,9 +57,122 @@ * - The new kernel is activated. */ -int kexec(void *kernel, unsigned long kernel_size, - const char *cmdline) +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); -- 2.43.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |