summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMagnus Damm <magnus@valinux.co.jp>2006-11-22 00:01:47 +0900
committerSimon Horman <horms@verge.net.au>2006-11-27 12:25:12 +0900
commitc80198e78ce26783e092645b9ac8587e1374f22f (patch)
tree5ca85a6bb221c09d6855eb4a6ad4fb04c5e0cc01
parentf8b0123231e9ca2cb124aa290b9df7785ff135e0 (diff)
kexec-tools: Introduce crashdump-elf.c and crash_create_elf32/64_headers() V2
kexec-tools: Introduce crashdump-elf.c and crash_create_elf32/64_headers() V2 This patch adds the new file crashdump-elf.c that implements a single generic function which is used to create elf headers. The file gets included twice by crash.c to create two functions from the same source. These two functions are named crash_create_elf32_headers() and crash_create_elf64_headers(). The new code differs from prepare_crash_memory_elf32/64_headers() in the sense that both allocation and setup now are done in the same function. This patch only adds the new code, following patches make sure the different architecture-specific files make use of the new code. This version contains a new comment as suggested by Vivek, together with that the alignment now is fixed at 1024 regardless of architecture. The cpu elf note callback code has been slightly reworked to become cleaner. Also, FUNCTION has been renamed to FUNC. Signed-off-by: Magnus Damm <magnus@valinux.co.jp> Removed trainling whitespace Signed-off-by: Simon Horman <horms@verge.net.au>
-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 */