diff options
| author | AKASHI Takahiro <takahiro.akashi@linaro.org> | 2018-04-13 15:36:06 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-13 17:10:27 -0700 | 
| commit | babac4a84a88842bec477a5bdada1460f3bc374c (patch) | |
| tree | 88e84c21942b051c2dfe941cadb8d8cdb4aa7ec0 /kernel/kexec_file.c | |
| parent | eb7dae947ef5c50e0121f673fcb43ca3583e5849 (diff) | |
kexec_file, x86: move re-factored code to generic side
In the previous patches, commonly-used routines, exclude_mem_range() and
prepare_elf64_headers(), were carved out.  Now place them in kexec
common code.  A prefix "crash_" is given to each of their names to avoid
possible name collisions.
Link: http://lkml.kernel.org/r/20180306102303.9063-8-takahiro.akashi@linaro.org
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Acked-by: Dave Young <dyoung@redhat.com>
Tested-by: Dave Young <dyoung@redhat.com>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: Baoquan He <bhe@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/kexec_file.c')
| -rw-r--r-- | kernel/kexec_file.c | 175 | 
1 files changed, 175 insertions, 0 deletions
| diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c index 332c4fd12cb1..b06b1fac5252 100644 --- a/kernel/kexec_file.c +++ b/kernel/kexec_file.c @@ -22,6 +22,11 @@  #include <linux/ima.h>  #include <crypto/hash.h>  #include <crypto/sha.h> +#include <linux/elf.h> +#include <linux/elfcore.h> +#include <linux/kernel.h> +#include <linux/kexec.h> +#include <linux/slab.h>  #include <linux/syscalls.h>  #include <linux/vmalloc.h>  #include "kexec_internal.h" @@ -1079,3 +1084,173 @@ int kexec_purgatory_get_set_symbol(struct kimage *image, const char *name,  	return 0;  }  #endif /* CONFIG_ARCH_HAS_KEXEC_PURGATORY */ + +int crash_exclude_mem_range(struct crash_mem *mem, +			    unsigned long long mstart, unsigned long long mend) +{ +	int i, j; +	unsigned long long start, end; +	struct crash_mem_range temp_range = {0, 0}; + +	for (i = 0; i < mem->nr_ranges; i++) { +		start = mem->ranges[i].start; +		end = mem->ranges[i].end; + +		if (mstart > end || mend < start) +			continue; + +		/* Truncate any area outside of range */ +		if (mstart < start) +			mstart = start; +		if (mend > end) +			mend = end; + +		/* Found completely overlapping range */ +		if (mstart == start && mend == end) { +			mem->ranges[i].start = 0; +			mem->ranges[i].end = 0; +			if (i < mem->nr_ranges - 1) { +				/* Shift rest of the ranges to left */ +				for (j = i; j < mem->nr_ranges - 1; j++) { +					mem->ranges[j].start = +						mem->ranges[j+1].start; +					mem->ranges[j].end = +							mem->ranges[j+1].end; +				} +			} +			mem->nr_ranges--; +			return 0; +		} + +		if (mstart > start && mend < end) { +			/* Split original range */ +			mem->ranges[i].end = mstart - 1; +			temp_range.start = mend + 1; +			temp_range.end = end; +		} else if (mstart != start) +			mem->ranges[i].end = mstart - 1; +		else +			mem->ranges[i].start = mend + 1; +		break; +	} + +	/* If a split happened, add the split to array */ +	if (!temp_range.end) +		return 0; + +	/* Split happened */ +	if (i == mem->max_nr_ranges - 1) +		return -ENOMEM; + +	/* Location where new range should go */ +	j = i + 1; +	if (j < mem->nr_ranges) { +		/* Move over all ranges one slot towards the end */ +		for (i = mem->nr_ranges - 1; i >= j; i--) +			mem->ranges[i + 1] = mem->ranges[i]; +	} + +	mem->ranges[j].start = temp_range.start; +	mem->ranges[j].end = temp_range.end; +	mem->nr_ranges++; +	return 0; +} + +int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map, +			  void **addr, unsigned long *sz) +{ +	Elf64_Ehdr *ehdr; +	Elf64_Phdr *phdr; +	unsigned long nr_cpus = num_possible_cpus(), nr_phdr, elf_sz; +	unsigned char *buf; +	unsigned int cpu, i; +	unsigned long long notes_addr; +	unsigned long mstart, mend; + +	/* extra phdr for vmcoreinfo elf note */ +	nr_phdr = nr_cpus + 1; +	nr_phdr += mem->nr_ranges; + +	/* +	 * kexec-tools creates an extra PT_LOAD phdr for kernel text mapping +	 * area (for example, ffffffff80000000 - ffffffffa0000000 on x86_64). +	 * I think this is required by tools like gdb. So same physical +	 * memory will be mapped in two elf headers. One will contain kernel +	 * text virtual addresses and other will have __va(physical) addresses. +	 */ + +	nr_phdr++; +	elf_sz = sizeof(Elf64_Ehdr) + nr_phdr * sizeof(Elf64_Phdr); +	elf_sz = ALIGN(elf_sz, ELF_CORE_HEADER_ALIGN); + +	buf = vzalloc(elf_sz); +	if (!buf) +		return -ENOMEM; + +	ehdr = (Elf64_Ehdr *)buf; +	phdr = (Elf64_Phdr *)(ehdr + 1); +	memcpy(ehdr->e_ident, ELFMAG, SELFMAG); +	ehdr->e_ident[EI_CLASS] = ELFCLASS64; +	ehdr->e_ident[EI_DATA] = ELFDATA2LSB; +	ehdr->e_ident[EI_VERSION] = EV_CURRENT; +	ehdr->e_ident[EI_OSABI] = ELF_OSABI; +	memset(ehdr->e_ident + EI_PAD, 0, EI_NIDENT - EI_PAD); +	ehdr->e_type = ET_CORE; +	ehdr->e_machine = ELF_ARCH; +	ehdr->e_version = EV_CURRENT; +	ehdr->e_phoff = sizeof(Elf64_Ehdr); +	ehdr->e_ehsize = sizeof(Elf64_Ehdr); +	ehdr->e_phentsize = sizeof(Elf64_Phdr); + +	/* Prepare one phdr of type PT_NOTE for each present cpu */ +	for_each_present_cpu(cpu) { +		phdr->p_type = PT_NOTE; +		notes_addr = per_cpu_ptr_to_phys(per_cpu_ptr(crash_notes, cpu)); +		phdr->p_offset = phdr->p_paddr = notes_addr; +		phdr->p_filesz = phdr->p_memsz = sizeof(note_buf_t); +		(ehdr->e_phnum)++; +		phdr++; +	} + +	/* Prepare one PT_NOTE header for vmcoreinfo */ +	phdr->p_type = PT_NOTE; +	phdr->p_offset = phdr->p_paddr = paddr_vmcoreinfo_note(); +	phdr->p_filesz = phdr->p_memsz = VMCOREINFO_NOTE_SIZE; +	(ehdr->e_phnum)++; +	phdr++; + +	/* Prepare PT_LOAD type program header for kernel text region */ +	if (kernel_map) { +		phdr->p_type = PT_LOAD; +		phdr->p_flags = PF_R|PF_W|PF_X; +		phdr->p_vaddr = (Elf64_Addr)_text; +		phdr->p_filesz = phdr->p_memsz = _end - _text; +		phdr->p_offset = phdr->p_paddr = __pa_symbol(_text); +		ehdr->e_phnum++; +		phdr++; +	} + +	/* Go through all the ranges in mem->ranges[] and prepare phdr */ +	for (i = 0; i < mem->nr_ranges; i++) { +		mstart = mem->ranges[i].start; +		mend = mem->ranges[i].end; + +		phdr->p_type = PT_LOAD; +		phdr->p_flags = PF_R|PF_W|PF_X; +		phdr->p_offset  = mstart; + +		phdr->p_paddr = mstart; +		phdr->p_vaddr = (unsigned long long) __va(mstart); +		phdr->p_filesz = phdr->p_memsz = mend - mstart + 1; +		phdr->p_align = 0; +		ehdr->e_phnum++; +		phdr++; +		pr_debug("Crash PT_LOAD elf header. phdr=%p vaddr=0x%llx, paddr=0x%llx, sz=0x%llx e_phnum=%d p_offset=0x%llx\n", +			phdr, phdr->p_vaddr, phdr->p_paddr, phdr->p_filesz, +			ehdr->e_phnum, phdr->p_offset); +	} + +	*addr = buf; +	*sz = elf_sz; +	return 0; +} | 
