/* Gather all the required information to prepare elf headers for ram regions */ static void fill_up_crash_elf_data(struct crash_elf_data *ced, struct kimage *image) { unsigned int nr_ranges = 0; ced->image = image; walk_system_ram_res(0, -1, &nr_ranges, get_nr_ram_ranges_callback); ced->max_nr_ranges = nr_ranges; /* * We don't create ELF headers for GART aperture as an attempt * to dump this memory in second kernel leads to hang/crash. * If gart aperture is present, one needs to exclude that region * and that could lead to need of extra phdr. */ walk_iomem_res("GART", IORESOURCE_MEM, 0, -1, ced, get_gart_ranges_callback); /* * If we have gart region, excluding that could potentially split * a memory range, resulting in extra header. Account for that. */ if (ced->gart_end) ced->max_nr_ranges++; /* Exclusion of crash region could split memory ranges */ ced->max_nr_ranges++; /* If crashk_low_res is not 0, another range split possible */ if (crashk_low_res.end != 0) ced->max_nr_ranges++; }
int crash_load_segments(struct kimage *image) { unsigned long src_start, src_sz, elf_sz; void *elf_addr; int ret; /* * Determine and load a segment for backup area. First 640K RAM * region is backup source */ ret = walk_system_ram_res(KEXEC_BACKUP_SRC_START, KEXEC_BACKUP_SRC_END, image, determine_backup_region); /* Zero or postive return values are ok */ if (ret < 0) return ret; src_start = image->arch.backup_src_start; src_sz = image->arch.backup_src_sz; /* Add backup segment. */ if (src_sz) { /* * Ideally there is no source for backup segment. This is * copied in purgatory after crash. Just add a zero filled * segment for now to make sure checksum logic works fine. */ ret = kexec_add_buffer(image, (char *)&crash_zero_bytes, sizeof(crash_zero_bytes), src_sz, PAGE_SIZE, 0, -1, 0, &image->arch.backup_load_addr); if (ret) return ret; pr_debug("Loaded backup region at 0x%lx backup_start=0x%lx memsz=0x%lx\n", image->arch.backup_load_addr, src_start, src_sz); } /* Prepare elf headers and add a segment */ ret = prepare_elf_headers(image, &elf_addr, &elf_sz); if (ret) return ret; image->arch.elf_headers = elf_addr; image->arch.elf_headers_sz = elf_sz; ret = kexec_add_buffer(image, (char *)elf_addr, elf_sz, elf_sz, ELF_CORE_HEADER_ALIGN, 0, -1, 0, &image->arch.elf_load_addr); if (ret) { vfree((void *)image->arch.elf_headers); return ret; } pr_debug("Loaded ELF headers at 0x%lx bufsz=0x%lx memsz=0x%lx\n", image->arch.elf_load_addr, elf_sz, elf_sz); return ret; }
static int prepare_elf64_headers(struct crash_elf_data *ced, 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, *bufp; unsigned int cpu; unsigned long long notes_addr; int ret; /* extra phdr for vmcoreinfo elf note */ nr_phdr = nr_cpus + 1; nr_phdr += ced->max_nr_ranges; /* * kexec-tools creates an extra PT_LOAD phdr for kernel text mapping * area on x86_64 (ffffffff80000000 - ffffffffa0000000). * 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; bufp = buf; ehdr = (Elf64_Ehdr *)bufp; bufp += sizeof(Elf64_Ehdr); 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 = (Elf64_Phdr *)bufp; bufp += sizeof(Elf64_Phdr); 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)++; } /* Prepare one PT_NOTE header for vmcoreinfo */ phdr = (Elf64_Phdr *)bufp; bufp += sizeof(Elf64_Phdr); phdr->p_type = PT_NOTE; phdr->p_offset = phdr->p_paddr = paddr_vmcoreinfo_note(); phdr->p_filesz = phdr->p_memsz = sizeof(vmcoreinfo_note); (ehdr->e_phnum)++; #ifdef CONFIG_X86_64 /* Prepare PT_LOAD type program header for kernel text region */ phdr = (Elf64_Phdr *)bufp; bufp += sizeof(Elf64_Phdr); 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)++; #endif /* Prepare PT_LOAD headers for system ram chunks. */ ced->ehdr = ehdr; ced->bufp = bufp; ret = walk_system_ram_res(0, -1, ced, prepare_elf64_ram_headers_callback); if (ret < 0) return ret; *addr = buf; *sz = elf_sz; return 0; }
/* * Helper function for placing a buffer in a kexec segment. This assumes * that kexec_mutex is held. */ int kexec_add_buffer(struct kimage *image, char *buffer, unsigned long bufsz, unsigned long memsz, unsigned long buf_align, unsigned long buf_min, unsigned long buf_max, bool top_down, unsigned long *load_addr) { struct kexec_segment *ksegment; struct kexec_buf buf, *kbuf; int ret; /* Currently adding segment this way is allowed only in file mode */ if (!image->file_mode) return -EINVAL; if (image->nr_segments >= KEXEC_SEGMENT_MAX) return -EINVAL; /* * Make sure we are not trying to add buffer after allocating * control pages. All segments need to be placed first before * any control pages are allocated. As control page allocation * logic goes through list of segments to make sure there are * no destination overlaps. */ if (!list_empty(&image->control_pages)) { WARN_ON(1); return -EINVAL; } memset(&buf, 0, sizeof(struct kexec_buf)); kbuf = &buf; kbuf->image = image; kbuf->buffer = buffer; kbuf->bufsz = bufsz; kbuf->memsz = ALIGN(memsz, PAGE_SIZE); kbuf->buf_align = max(buf_align, PAGE_SIZE); kbuf->buf_min = buf_min; kbuf->buf_max = buf_max; kbuf->top_down = top_down; /* Walk the RAM ranges and allocate a suitable range for the buffer */ if (image->type == KEXEC_TYPE_CRASH) ret = walk_iomem_res_desc(crashk_res.desc, IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY, crashk_res.start, crashk_res.end, kbuf, locate_mem_hole_callback); else ret = walk_system_ram_res(0, -1, kbuf, locate_mem_hole_callback); if (ret != 1) { /* A suitable memory range could not be found for buffer */ return -EADDRNOTAVAIL; } /* Found a suitable memory range */ ksegment = &image->segment[image->nr_segments]; ksegment->kbuf = kbuf->buffer; ksegment->bufsz = kbuf->bufsz; ksegment->mem = kbuf->mem; ksegment->memsz = kbuf->memsz; image->nr_segments++; *load_addr = ksegment->mem; return 0; }