summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kexec/crashdump-elf.c184
-rw-r--r--kexec/crashdump.c19
-rw-r--r--kexec/crashdump.h26
3 files changed, 229 insertions, 0 deletions
diff --git a/kexec/crashdump-elf.c b/kexec/crashdump-elf.c
new file mode 100644
index 0000000..9e3121f
--- /dev/null
+++ b/kexec/crashdump-elf.c
@@ -0,0 +1,184 @@
+
+#if !defined(FUNC) || !defined(EHDR) || !defined(PHDR)
+#error FUNC, EHDR and PHDR must be defined
+#endif
+
+/* Prepares the crash memory headers and stores in supplied buffer. */
+int FUNC(struct kexec_info *info,
+ struct crash_elf_info *elf_info,
+ struct memory_range *range, int ranges,
+ void **buf, unsigned long *size)
+{
+ EHDR *elf;
+ PHDR *phdr;
+ int i, sz;
+ char *bufp;
+ long int nr_cpus = 0;
+ int align = 1024;
+ uint64_t notes_addr, notes_len;
+ int (*get_note_info)(int cpu, uint64_t *addr, uint64_t *len);
+
+ nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
+ if (nr_cpus < 0) {
+ return -1;
+ }
+
+ sz = sizeof(EHDR) + nr_cpus * sizeof(PHDR) + ranges * sizeof(PHDR);
+
+ /*
+ * Certain architectures such as x86_64 and ia64 require a separate
+ * PT_LOAD program header for the kernel. This is controlled through
+ * info->kern_size.
+ *
+ * The separate PT_LOAD program header is required either because the
+ * kernel is mapped at a different location than the rest of the
+ * physical memory or because we need to support relocatable kernels.
+ * Or both as on x86_64.
+ *
+ * In the relocatable kernel case this PT_LOAD segment is used to tell
+ * where the kernel was actually loaded which may be different from
+ * the load address present in the vmlinux file.
+ *
+ * The extra kernel PT_LOAD program header results in a vmcore file
+ * which is larger than the size of the physical memory. This is
+ * because the memory for the kernel is present both in the kernel
+ * PT_LOAD program header and in the physical RAM program headers.
+ */
+
+ if (info->kern_size) {
+ sz += sizeof(PHDR);
+ }
+
+ sz += align - 1;
+ sz &= ~(align - 1);
+
+ bufp = xmalloc(sz);
+ memset(bufp, 0, sz);
+
+ *buf = bufp;
+ *size = sz;
+
+ /* Setup ELF Header*/
+ elf = (EHDR *) bufp;
+ bufp += sizeof(EHDR);
+ memcpy(elf->e_ident, ELFMAG, SELFMAG);
+ elf->e_ident[EI_CLASS] = elf_info->class;
+ elf->e_ident[EI_DATA] = elf_info->data;
+ elf->e_ident[EI_VERSION]= EV_CURRENT;
+ elf->e_ident[EI_OSABI] = ELFOSABI_NONE;
+ memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
+ elf->e_type = ET_CORE;
+ elf->e_machine = elf_info->machine;
+ elf->e_version = EV_CURRENT;
+ elf->e_entry = 0;
+ elf->e_phoff = sizeof(EHDR);
+ elf->e_shoff = 0;
+ elf->e_flags = 0;
+ elf->e_ehsize = sizeof(EHDR);
+ elf->e_phentsize= sizeof(PHDR);
+ elf->e_phnum = 0;
+ elf->e_shentsize= 0;
+ elf->e_shnum = 0;
+ elf->e_shstrndx = 0;
+
+ /* Default way to get crash notes is by get_crash_notes_per_cpu() */
+
+ get_note_info = elf_info->get_note_info;
+ if (!get_note_info)
+ get_note_info = get_crash_notes_per_cpu;
+
+ /* PT_NOTE program headers. One per cpu */
+
+ for (i = 0; i < nr_cpus; i++) {
+ if (get_note_info(i, &notes_addr, &notes_len) < 0) {
+ /* This cpu is not present. Skip it. */
+ continue;
+ }
+
+ phdr = (PHDR *) bufp;
+ bufp += sizeof(PHDR);
+ phdr->p_type = PT_NOTE;
+ phdr->p_flags = 0;
+ phdr->p_offset = phdr->p_paddr = notes_addr;
+ phdr->p_vaddr = 0;
+ phdr->p_filesz = phdr->p_memsz = notes_len;
+ /* Do we need any alignment of segments? */
+ phdr->p_align = 0;
+
+ /* Increment number of program headers. */
+ (elf->e_phnum)++;
+ dfprintf(stdout, "Elf header: p_type = %d, p_offset = 0x%lx "
+ "p_paddr = 0x%lx p_vaddr = 0x%lx "
+ "p_filesz = 0x%lx p_memsz = 0x%lx\n",
+ phdr->p_type, phdr->p_offset, phdr->p_paddr,
+ phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz);
+ }
+
+ /* Setup an PT_LOAD type program header for the region where
+ * Kernel is mapped if info->kern_size is non-zero.
+ */
+
+ if (info->kern_size) {
+ phdr = (PHDR *) bufp;
+ bufp += sizeof(PHDR);
+ phdr->p_type = PT_LOAD;
+ phdr->p_flags = PF_R|PF_W|PF_X;
+ phdr->p_offset = phdr->p_paddr = info->kern_paddr_start;
+ phdr->p_vaddr = info->kern_vaddr_start;
+ phdr->p_filesz = phdr->p_memsz = info->kern_size;
+ phdr->p_align = 0;
+ (elf->e_phnum)++;
+ dfprintf(stdout, "Kernel text Elf header: "
+ "p_type = %d, p_offset = 0x%lx "
+ "p_paddr = 0x%lx p_vaddr = 0x%lx "
+ "p_filesz = 0x%lx p_memsz = 0x%lx\n",
+ phdr->p_type, phdr->p_offset, phdr->p_paddr,
+ phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz);
+ }
+
+ /* Setup PT_LOAD type program header for every system RAM chunk.
+ * A seprate program header for Backup Region*/
+ for (i = 0; i < ranges; i++, range++) {
+ unsigned long long mstart, mend;
+ if (range->type != RANGE_RAM)
+ continue;
+ mstart = range->start;
+ mend = range->end;
+ if (!mstart && !mend)
+ continue;
+ phdr = (PHDR *) bufp;
+ bufp += sizeof(PHDR);
+ phdr->p_type = PT_LOAD;
+ phdr->p_flags = PF_R|PF_W|PF_X;
+ phdr->p_offset = mstart;
+
+ if (mstart == elf_info->backup_src_start
+ && mend == elf_info->backup_src_end)
+ phdr->p_offset = info->backup_start;
+
+ /* We already prepared the header for kernel text. Map
+ * rest of the memory segments to kernel linearly mapped
+ * memory region.
+ */
+ phdr->p_paddr = mstart;
+ phdr->p_vaddr = mstart + elf_info->page_offset;
+ phdr->p_filesz = phdr->p_memsz = mend - mstart + 1;
+ /* Do we need any alignment of segments? */
+ phdr->p_align = 0;
+
+ /* HIGMEM has a virtual address of -1 */
+
+ if (elf_info->lowmem_limit
+ && (mend > (elf_info->lowmem_limit - 1)))
+ phdr->p_vaddr = -1;
+
+ /* Increment number of program headers. */
+ (elf->e_phnum)++;
+ dfprintf(stdout, "Elf header: p_type = %d, p_offset = 0x%lx "
+ "p_paddr = 0x%lx p_vaddr = 0x%lx "
+ "p_filesz = 0x%lx p_memsz = 0x%lx\n",
+ phdr->p_type, phdr->p_offset, phdr->p_paddr,
+ phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz);
+ }
+ return 0;
+}
diff --git a/kexec/crashdump.c b/kexec/crashdump.c
index 0c10cf8..1cb8123 100644
--- a/kexec/crashdump.c
+++ b/kexec/crashdump.c
@@ -25,9 +25,28 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
+#include <elf.h>
#include "kexec.h"
#include "crashdump.h"
+/* include "crashdump-elf.c" twice to create two functions from one */
+
+#define FUNC crash_create_elf64_headers
+#define EHDR Elf64_Ehdr
+#define PHDR Elf64_Phdr
+#include "crashdump-elf.c"
+#undef PHDR
+#undef EHDR
+#undef FUNC
+
+#define FUNC crash_create_elf32_headers
+#define EHDR Elf32_Ehdr
+#define PHDR Elf32_Phdr
+#include "crashdump-elf.c"
+#undef PHDR
+#undef EHDR
+#undef FUNC
+
/* Returns the physical address of start of crash notes buffer for a cpu. */
int get_crash_notes_per_cpu(int cpu, uint64_t *addr, uint64_t *len)
{
diff --git a/kexec/crashdump.h b/kexec/crashdump.h
index dd3c317..4dbecf8 100644
--- a/kexec/crashdump.h
+++ b/kexec/crashdump.h
@@ -8,4 +8,30 @@ extern int get_crash_notes_per_cpu(int cpu, uint64_t *addr, uint64_t *len);
/* Expecting ELF headers to fit in 4K. Increase it if you need more. */
#define KCORE_ELF_HEADERS_SIZE 4096
+/* structure passed to crash_create_elf32/64_headers() */
+
+struct crash_elf_info {
+ unsigned long class;
+ unsigned long data;
+ unsigned long machine;
+
+ unsigned long backup_src_start;
+ unsigned long backup_src_end;
+
+ unsigned long page_offset;
+ unsigned long lowmem_limit;
+
+ int (*get_note_info)(int cpu, uint64_t *addr, uint64_t *len);
+};
+
+int crash_create_elf32_headers(struct kexec_info *info,
+ struct crash_elf_info *elf_info,
+ struct memory_range *range, int ranges,
+ void **buf, unsigned long *size);
+
+int crash_create_elf64_headers(struct kexec_info *info,
+ struct crash_elf_info *elf_info,
+ struct memory_range *range, int ranges,
+ void **buf, unsigned long *size);
+
#endif /* CRASHDUMP_H */