@@ -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 */
@@ -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 */
@@ -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);
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@suse.com> --- arch/x86/kexec.c | 91 +++++++++++++++++++++++++++++++++++ include/kexec.h | 11 +++++ kexec.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 220 insertions(+), 2 deletions(-)