923 lines · 23.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * crash.c - kernel crash support code. * Copyright (C) 2002-2004 Eric Biederman  <ebiederm@xmission.com> */ #include <linux/buildid.h>#include <linux/crash_core.h>#include <linux/init.h>#include <linux/utsname.h>#include <linux/vmalloc.h>#include <linux/sizes.h>#include <linux/kexec.h>#include <linux/memory.h>#include <linux/cpuhotplug.h> #include <asm/page.h>#include <asm/sections.h> #include <crypto/sha1.h> #include "kallsyms_internal.h"#include "kexec_internal.h" /* Per cpu memory for storing cpu states in case of system crash. */note_buf_t __percpu *crash_notes; /* vmcoreinfo stuff */unsigned char *vmcoreinfo_data;size_t vmcoreinfo_size;u32 *vmcoreinfo_note; /* trusted vmcoreinfo, e.g. we can make a copy in the crash memory */static unsigned char *vmcoreinfo_data_safecopy; /* * parsing the "crashkernel" commandline * * this code is intended to be called from architecture specific code */  /* * This function parses command lines in the format * *   crashkernel=ramsize-range:size[,...][@offset] * * The function returns 0 on success and -EINVAL on failure. */static int __init parse_crashkernel_mem(char *cmdline,					unsigned long long system_ram,					unsigned long long *crash_size,					unsigned long long *crash_base){	char *cur = cmdline, *tmp;	unsigned long long total_mem = system_ram; 	/*	 * Firmware sometimes reserves some memory regions for its own use,	 * so the system memory size is less than the actual physical memory	 * size. Work around this by rounding up the total size to 128M,	 * which is enough for most test cases.	 */	total_mem = roundup(total_mem, SZ_128M); 	/* for each entry of the comma-separated list */	do {		unsigned long long start, end = ULLONG_MAX, size; 		/* get the start of the range */		start = memparse(cur, &tmp);		if (cur == tmp) {			pr_warn("crashkernel: Memory value expected\n");			return -EINVAL;		}		cur = tmp;		if (*cur != '-') {			pr_warn("crashkernel: '-' expected\n");			return -EINVAL;		}		cur++; 		/* if no ':' is here, than we read the end */		if (*cur != ':') {			end = memparse(cur, &tmp);			if (cur == tmp) {				pr_warn("crashkernel: Memory value expected\n");				return -EINVAL;			}			cur = tmp;			if (end <= start) {				pr_warn("crashkernel: end <= start\n");				return -EINVAL;			}		} 		if (*cur != ':') {			pr_warn("crashkernel: ':' expected\n");			return -EINVAL;		}		cur++; 		size = memparse(cur, &tmp);		if (cur == tmp) {			pr_warn("Memory value expected\n");			return -EINVAL;		}		cur = tmp;		if (size >= total_mem) {			pr_warn("crashkernel: invalid size\n");			return -EINVAL;		} 		/* match ? */		if (total_mem >= start && total_mem < end) {			*crash_size = size;			break;		}	} while (*cur++ == ','); 	if (*crash_size > 0) {		while (*cur && *cur != ' ' && *cur != '@')			cur++;		if (*cur == '@') {			cur++;			*crash_base = memparse(cur, &tmp);			if (cur == tmp) {				pr_warn("Memory value expected after '@'\n");				return -EINVAL;			}		}	} else		pr_info("crashkernel size resulted in zero bytes\n"); 	return 0;} /* * That function parses "simple" (old) crashkernel command lines like * *	crashkernel=size[@offset] * * It returns 0 on success and -EINVAL on failure. */static int __init parse_crashkernel_simple(char *cmdline,					   unsigned long long *crash_size,					   unsigned long long *crash_base){	char *cur = cmdline; 	*crash_size = memparse(cmdline, &cur);	if (cmdline == cur) {		pr_warn("crashkernel: memory value expected\n");		return -EINVAL;	} 	if (*cur == '@')		*crash_base = memparse(cur+1, &cur);	else if (*cur != ' ' && *cur != '\0') {		pr_warn("crashkernel: unrecognized char: %c\n", *cur);		return -EINVAL;	} 	return 0;} #define SUFFIX_HIGH 0#define SUFFIX_LOW  1#define SUFFIX_NULL 2static __initdata char *suffix_tbl[] = {	[SUFFIX_HIGH] = ",high",	[SUFFIX_LOW]  = ",low",	[SUFFIX_NULL] = NULL,}; /* * That function parses "suffix"  crashkernel command lines like * *	crashkernel=size,[high|low] * * It returns 0 on success and -EINVAL on failure. */static int __init parse_crashkernel_suffix(char *cmdline,					   unsigned long long	*crash_size,					   const char *suffix){	char *cur = cmdline; 	*crash_size = memparse(cmdline, &cur);	if (cmdline == cur) {		pr_warn("crashkernel: memory value expected\n");		return -EINVAL;	} 	/* check with suffix */	if (strncmp(cur, suffix, strlen(suffix))) {		pr_warn("crashkernel: unrecognized char: %c\n", *cur);		return -EINVAL;	}	cur += strlen(suffix);	if (*cur != ' ' && *cur != '\0') {		pr_warn("crashkernel: unrecognized char: %c\n", *cur);		return -EINVAL;	} 	return 0;} static __init char *get_last_crashkernel(char *cmdline,			     const char *name,			     const char *suffix){	char *p = cmdline, *ck_cmdline = NULL; 	/* find crashkernel and use the last one if there are more */	p = strstr(p, name);	while (p) {		char *end_p = strchr(p, ' ');		char *q; 		if (!end_p)			end_p = p + strlen(p); 		if (!suffix) {			int i; 			/* skip the one with any known suffix */			for (i = 0; suffix_tbl[i]; i++) {				q = end_p - strlen(suffix_tbl[i]);				if (!strncmp(q, suffix_tbl[i],					     strlen(suffix_tbl[i])))					goto next;			}			ck_cmdline = p;		} else {			q = end_p - strlen(suffix);			if (!strncmp(q, suffix, strlen(suffix)))				ck_cmdline = p;		}next:		p = strstr(p+1, name);	} 	return ck_cmdline;} static int __init __parse_crashkernel(char *cmdline,			     unsigned long long system_ram,			     unsigned long long *crash_size,			     unsigned long long *crash_base,			     const char *name,			     const char *suffix){	char	*first_colon, *first_space;	char	*ck_cmdline; 	BUG_ON(!crash_size || !crash_base);	*crash_size = 0;	*crash_base = 0; 	ck_cmdline = get_last_crashkernel(cmdline, name, suffix);	if (!ck_cmdline)		return -ENOENT; 	ck_cmdline += strlen(name); 	if (suffix)		return parse_crashkernel_suffix(ck_cmdline, crash_size,				suffix);	/*	 * if the commandline contains a ':', then that's the extended	 * syntax -- if not, it must be the classic syntax	 */	first_colon = strchr(ck_cmdline, ':');	first_space = strchr(ck_cmdline, ' ');	if (first_colon && (!first_space || first_colon < first_space))		return parse_crashkernel_mem(ck_cmdline, system_ram,				crash_size, crash_base); 	return parse_crashkernel_simple(ck_cmdline, crash_size, crash_base);} /* * That function is the entry point for command line parsing and should be * called from the arch-specific code. */int __init parse_crashkernel(char *cmdline,			     unsigned long long system_ram,			     unsigned long long *crash_size,			     unsigned long long *crash_base){	return __parse_crashkernel(cmdline, system_ram, crash_size, crash_base,					"crashkernel=", NULL);} int __init parse_crashkernel_high(char *cmdline,			     unsigned long long system_ram,			     unsigned long long *crash_size,			     unsigned long long *crash_base){	return __parse_crashkernel(cmdline, system_ram, crash_size, crash_base,				"crashkernel=", suffix_tbl[SUFFIX_HIGH]);} int __init parse_crashkernel_low(char *cmdline,			     unsigned long long system_ram,			     unsigned long long *crash_size,			     unsigned long long *crash_base){	return __parse_crashkernel(cmdline, system_ram, crash_size, crash_base,				"crashkernel=", suffix_tbl[SUFFIX_LOW]);} /* * Add a dummy early_param handler to mark crashkernel= as a known command line * parameter and suppress incorrect warnings in init/main.c. */static int __init parse_crashkernel_dummy(char *arg){	return 0;}early_param("crashkernel", parse_crashkernel_dummy); int crash_prepare_elf64_headers(struct crash_mem *mem, int need_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 possible CPU */	for_each_possible_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 (need_kernel_map) {		phdr->p_type = PT_LOAD;		phdr->p_flags = PF_R|PF_W|PF_X;		phdr->p_vaddr = (unsigned long) _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) __va(mstart);		phdr->p_filesz = phdr->p_memsz = mend - mstart + 1;		phdr->p_align = 0;		ehdr->e_phnum++;		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);		phdr++;	} 	*addr = buf;	*sz = elf_sz;	return 0;} 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, p_start, p_end;	struct range temp_range = {0, 0}; 	for (i = 0; i < mem->nr_ranges; i++) {		start = mem->ranges[i].start;		end = mem->ranges[i].end;		p_start = mstart;		p_end = mend; 		if (mstart > end || mend < start)			continue; 		/* Truncate any area outside of range */		if (mstart < start)			p_start = start;		if (mend > end)			p_end = end; 		/* Found completely overlapping range */		if (p_start == start && p_end == 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;				} 				/*				 * Continue to check if there are another overlapping ranges				 * from the current position because of shifting the above				 * mem ranges.				 */				i--;				mem->nr_ranges--;				continue;			}			mem->nr_ranges--;			return 0;		} 		if (p_start > start && p_end < end) {			/* Split original range */			mem->ranges[i].end = p_start - 1;			temp_range.start = p_end + 1;			temp_range.end = end;		} else if (p_start != start)			mem->ranges[i].end = p_start - 1;		else			mem->ranges[i].start = p_end + 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;

Lines 1–500 of 923.