/* Maximum size in bytes for kernel/initrd files. */ #define KEXEC_FILE_SIZE_MAX min_t(s64, 4LL << 30, SSIZE_MAX)
/* * Currently this is the only default function that is exported as some * architectures need it to do additional handlings. * In the future, other default functions may be exported too if required.
*/ int kexec_image_probe_default(struct kimage *image, void *buf, unsignedlong buf_len)
{ conststruct kexec_file_ops * const *fops; int ret = -ENOEXEC;
for (fops = &kexec_file_loaders[0]; *fops && (*fops)->probe; ++fops) {
ret = (*fops)->probe(buf, buf_len); if (!ret) {
image->fops = *fops; return ret;
}
}
/* * Free up memory used by kernel, initrd, and command line. This is temporary * memory allocation which is not needed any more after these buffers have * been loaded into separate segments and have been copied elsewhere.
*/ void kimage_file_post_load_cleanup(struct kimage *image)
{ struct purgatory_info *pi = &image->purgatory_info;
/* See if architecture has anything to cleanup post load */
arch_kimage_file_post_load_cleanup(image);
/* * Above call should have called into bootloader to free up * any data stored in kimage->image_loader_data. It should * be ok now to free it up.
*/
kfree(image->image_loader_data);
image->image_loader_data = NULL;
kexec_file_dbg_print = false;
}
#ifdef CONFIG_KEXEC_SIG #ifdef CONFIG_SIGNED_PE_FILE_VERIFICATION int kexec_kernel_verify_pe_sig(constchar *kernel, unsignedlong kernel_len)
{ int ret;
ret = verify_pefile_signature(kernel, kernel_len,
VERIFY_USE_SECONDARY_KEYRING,
VERIFYING_KEXEC_PE_SIGNATURE); if (ret == -ENOKEY && IS_ENABLED(CONFIG_INTEGRITY_PLATFORM_KEYRING)) {
ret = verify_pefile_signature(kernel, kernel_len,
VERIFY_USE_PLATFORM_KEYRING,
VERIFYING_KEXEC_PE_SIGNATURE);
} return ret;
} #endif
staticint kexec_image_verify_sig(struct kimage *image, void *buf, unsignedlong buf_len)
{ if (!image->fops || !image->fops->verify_sig) {
pr_debug("kernel loader does not support signature verification.\n"); return -EKEYREJECTED;
}
return image->fops->verify_sig(buf, buf_len);
}
staticint
kimage_validate_signature(struct kimage *image)
{ int ret;
ret = kexec_image_verify_sig(image, image->kernel_buf,
image->kernel_buf_len); if (ret) {
/* * If IMA is guaranteed to appraise a signature on the kexec * image, permit it even if the kernel is otherwise locked * down.
*/ if (!ima_appraise_signature(READING_KEXEC_IMAGE) &&
security_locked_down(LOCKDOWN_KEXEC)) return -EPERM;
/* * In file mode list of segments is prepared by kernel. Copy relevant * data from user space, do error checking, prepare segment list
*/ staticint
kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd, constchar __user *cmdline_ptr, unsignedlong cmdline_len, unsigned flags)
{
ssize_t ret; void *ldata;
/* Call arch image probe handlers */
ret = arch_kexec_kernel_image_probe(image, image->kernel_buf,
image->kernel_buf_len); if (ret) goto out;
#ifdef CONFIG_KEXEC_SIG
ret = kimage_validate_signature(image);
if (ret) goto out; #endif /* It is possible that there no initramfs is being loaded */ if (!(flags & KEXEC_FILE_NO_INITRAMFS)) {
ret = kernel_read_file_from_fd(initrd_fd, 0, &image->initrd_buf,
KEXEC_FILE_SIZE_MAX, NULL,
READING_KEXEC_INITRAMFS); if (ret < 0) goto out;
image->initrd_buf_len = ret;
ret = 0;
}
image->no_cma = !!(flags & KEXEC_FILE_NO_CMA);
if (cmdline_len) {
image->cmdline_buf = memdup_user(cmdline_ptr, cmdline_len); if (IS_ERR(image->cmdline_buf)) {
ret = PTR_ERR(image->cmdline_buf);
image->cmdline_buf = NULL; goto out;
}
image->cmdline_buf_len = cmdline_len;
/* command line should be a string with last byte null */ if (image->cmdline_buf[cmdline_len - 1] != '\0') {
ret = -EINVAL; goto out;
}
if (IS_ERR(ldata)) {
ret = PTR_ERR(ldata); goto out;
}
image->image_loader_data = ldata;
out: /* In case of error, free up all allocated memory in this function */ if (ret)
kimage_file_post_load_cleanup(image); return ret;
}
staticint
kimage_file_alloc_init(struct kimage **rimage, int kernel_fd, int initrd_fd, constchar __user *cmdline_ptr, unsignedlong cmdline_len, unsignedlong flags)
{ int ret; struct kimage *image; bool kexec_on_panic = flags & KEXEC_FILE_ON_CRASH;
image = do_kimage_alloc_init(); if (!image) return -ENOMEM;
/* We only trust the superuser with rebooting the system. */ if (!kexec_load_permitted(image_type)) return -EPERM;
/* Make sure we have a legal set of flags */ if (flags != (flags & KEXEC_FILE_FLAGS)) return -EINVAL;
image = NULL;
if (!kexec_trylock()) return -EBUSY;
#ifdef CONFIG_CRASH_DUMP if (image_type == KEXEC_TYPE_CRASH) {
dest_image = &kexec_crash_image; if (kexec_crash_image)
arch_kexec_unprotect_crashkres();
} else #endif
dest_image = &kexec_image;
if (flags & KEXEC_FILE_UNLOAD) goto exchange;
/* * In case of crash, new kernel gets loaded in reserved region. It is * same memory where old crash kernel might be loaded. Free any * current crash dump kernel before we corrupt it.
*/ if (flags & KEXEC_FILE_ON_CRASH)
kimage_free(xchg(&kexec_crash_image, NULL));
ret = kimage_file_alloc_init(&image, kernel_fd, initrd_fd, cmdline_ptr,
cmdline_len, flags); if (ret) goto out;
ret = machine_kexec_prepare(image); if (ret) goto out;
/* * Some architecture(like S390) may touch the crash memory before * machine_kexec_prepare(), we must copy vmcoreinfo data after it.
*/
ret = kimage_crash_copy_vmcoreinfo(image); if (ret) goto out;
ret = kexec_calculate_store_digests(image); if (ret) goto out;
kexec_dprintk("nr_segments = %lu\n", image->nr_segments); for (i = 0; i < image->nr_segments; i++) { struct kexec_segment *ksegment;
ret = kimage_load_segment(image, i); if (ret) goto out;
}
kimage_terminate(image);
ret = kexec_post_load(image, flags); if (ret) goto out;
kexec_dprintk("kexec_file_load: type:%u, start:0x%lx head:0x%lx flags:0x%lx\n",
image->type, image->start, image->head, flags); /* * Free up any temporary buffers allocated which are not needed * after image has been loaded
*/
kimage_file_post_load_cleanup(image);
exchange:
image = xchg(dest_image, image);
out: #ifdef CONFIG_CRASH_DUMP if ((flags & KEXEC_FILE_ON_CRASH) && kexec_crash_image)
arch_kexec_protect_crashkres(); #endif
do { /* align down start */
temp_start = ALIGN_DOWN(temp_start, kbuf->buf_align);
if (temp_start < start || temp_start < kbuf->buf_min) return 0;
temp_end = temp_start + kbuf->memsz - 1;
/* * Make sure this does not conflict with any of existing * segments
*/ if (kimage_is_destination_range(image, temp_start, temp_end)) {
temp_start = temp_start - PAGE_SIZE; continue;
}
/* Make sure this does not conflict with exclude range */ if (arch_check_excluded_range(image, temp_start, temp_end)) {
temp_start = temp_start - PAGE_SIZE; continue;
}
/* We found a suitable memory range */ break;
} while (1);
/* If we are here, we found a suitable memory range */
kbuf->mem = temp_start;
/* Success, stop navigating through remaining System RAM ranges */ return 1;
}
if (temp_end > end || temp_end > kbuf->buf_max) return 0; /* * Make sure this does not conflict with any of existing * segments
*/ if (kimage_is_destination_range(image, temp_start, temp_end)) {
temp_start = temp_start + PAGE_SIZE; continue;
}
/* Make sure this does not conflict with exclude range */ if (arch_check_excluded_range(image, temp_start, temp_end)) {
temp_start = temp_start + PAGE_SIZE; continue;
}
/* We found a suitable memory range */ break;
} while (1);
/* If we are here, we found a suitable memory range */
kbuf->mem = temp_start;
/* Success, stop navigating through remaining System RAM ranges */ return 1;
}
/* Don't use memory that will be detected and handled by a driver. */ if (res->flags & IORESOURCE_SYSRAM_DRIVER_MANAGED) return 0;
if (sz < kbuf->memsz) return 0;
if (end < kbuf->buf_min || start > kbuf->buf_max) return 0;
/* * Allocate memory top down with-in ram range. Otherwise bottom up * allocation.
*/ if (kbuf->top_down) return locate_mem_hole_top_down(start, end, kbuf); return locate_mem_hole_bottom_up(start, end, kbuf);
}
#ifdef CONFIG_ARCH_KEEP_MEMBLOCK staticint kexec_walk_memblock(struct kexec_buf *kbuf, int (*func)(struct resource *, void *))
{ int ret = 0;
u64 i;
phys_addr_t mstart, mend; struct resource res = { };
#ifdef CONFIG_CRASH_DUMP if (kbuf->image->type == KEXEC_TYPE_CRASH) return func(&crashk_res, kbuf); #endif
/* * Using MEMBLOCK_NONE will properly skip MEMBLOCK_DRIVER_MANAGED. See * IORESOURCE_SYSRAM_DRIVER_MANAGED handling in * locate_mem_hole_callback().
*/ if (kbuf->top_down) {
for_each_free_mem_range_reverse(i, NUMA_NO_NODE, MEMBLOCK_NONE,
&mstart, &mend, NULL) { /* * In memblock, end points to the first byte after the * range while in kexec, end points to the last byte * in the range.
*/
res.start = mstart;
res.end = mend - 1;
ret = func(&res, kbuf); if (ret) break;
}
} else {
for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE,
&mstart, &mend, NULL) { /* * In memblock, end points to the first byte after the * range while in kexec, end points to the last byte * in the range.
*/
res.start = mstart;
res.end = mend - 1;
ret = func(&res, kbuf); if (ret) break;
}
}
/** * kexec_walk_resources - call func(data) on free memory regions * @kbuf: Context info for the search. Also passed to @func. * @func: Function to call for each memory region. * * Return: The memory walk will stop when func returns a non-zero value * and that value will be returned. If all free regions are visited without * func returning non-zero, then zero will be returned.
*/ staticint kexec_walk_resources(struct kexec_buf *kbuf, int (*func)(struct resource *, void *))
{ #ifdef CONFIG_CRASH_DUMP if (kbuf->image->type == KEXEC_TYPE_CRASH) return walk_iomem_res_desc(crashk_res.desc,
IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY,
crashk_res.start, crashk_res.end,
kbuf, func); #endif if (kbuf->top_down) return walk_system_ram_res_rev(0, ULONG_MAX, kbuf, func); else return walk_system_ram_res(0, ULONG_MAX, kbuf, func);
}
/* User space disabled CMA allocations, bail out. */ if (kbuf->image->no_cma) return -EPERM;
/* Skip CMA logic for crash kernel */ if (kbuf->image->type == KEXEC_TYPE_CRASH) return -EPERM;
p = dma_alloc_from_contiguous(NULL, nr_pages, get_order(kbuf->buf_align), true); if (!p) return -ENOMEM;
pr_debug("allocated %zu DMA pages at 0x%lx", nr_pages, page_to_boot_pfn(p));
mem = page_to_boot_pfn(p) << PAGE_SHIFT;
if (kimage_is_destination_range(kbuf->image, mem, mem + kbuf->memsz)) { /* Our region is already in use by a statically defined one. Bail out. */
pr_debug("CMA overlaps existing mem: 0x%lx+0x%lx\n", mem, kbuf->memsz);
dma_release_from_contiguous(NULL, p, nr_pages); return -EBUSY;
}
/** * kexec_locate_mem_hole - find free memory for the purgatory or the next kernel * @kbuf: Parameters for the memory search. * * On success, kbuf->mem will have the start address of the memory region found. * * Return: 0 on success, negative errno on error.
*/ int kexec_locate_mem_hole(struct kexec_buf *kbuf)
{ int ret;
/* Arch knows where to place */ if (kbuf->mem != KEXEC_BUF_MEM_UNKNOWN) return 0;
/* * If KHO is active, only use KHO scratch memory. All other memory * could potentially be handed over.
*/
ret = kho_locate_mem_hole(kbuf, locate_mem_hole_callback); if (ret <= 0) return ret;
/* * Try to find a free physically contiguous block of memory first. With that, we * can avoid any copying at kexec time.
*/ if (!kexec_alloc_contig(kbuf)) return 0;
if (!IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK))
ret = kexec_walk_resources(kbuf, locate_mem_hole_callback); else
ret = kexec_walk_memblock(kbuf, locate_mem_hole_callback);
return ret == 1 ? 0 : -EADDRNOTAVAIL;
}
/** * kexec_add_buffer - place a buffer in a kexec segment * @kbuf: Buffer contents and memory parameters. * * This function assumes that kexec_lock is held. * On successful return, @kbuf->mem will have the physical address of * the buffer in memory. * * Return: 0 on success, negative errno on error.
*/ int kexec_add_buffer(struct kexec_buf *kbuf)
{ struct kexec_segment *ksegment; int ret;
/* Currently adding segment this way is allowed only in file mode */ if (!kbuf->image->file_mode) return -EINVAL;
if (kbuf->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(&kbuf->image->control_pages)) {
WARN_ON(1); return -EINVAL;
}
for (j = i = 0; i < image->nr_segments; i++) { struct kexec_segment *ksegment;
#ifdef CONFIG_CRASH_HOTPLUG /* Exclude elfcorehdr segment to allow future changes via hotplug */ if (i == image->elfcorehdr_index) continue; #endif
ksegment = &image->segment[i]; /* * Skip purgatory as it will be modified once we put digest * info in purgatory.
*/ if (ksegment->kbuf == pi->purgatory_buf) continue;
/* * Skip the segment if ima_segment_index is set and matches * the current index
*/ if (check_ima_segment_index(image, i)) continue;
/* * Assume rest of the buffer is filled with zero and * update digest accordingly.
*/
nullsz = ksegment->memsz - ksegment->bufsz; while (nullsz) { unsignedlong bytes = nullsz;
#ifdef CONFIG_ARCH_SUPPORTS_KEXEC_PURGATORY /* * kexec_purgatory_setup_kbuf - prepare buffer to load purgatory. * @pi: Purgatory to be loaded. * @kbuf: Buffer to setup. * * Allocates the memory needed for the buffer. Caller is responsible to free * the memory after use. * * Return: 0 on success, negative errno on error.
*/ staticint kexec_purgatory_setup_kbuf(struct purgatory_info *pi, struct kexec_buf *kbuf)
{ const Elf_Shdr *sechdrs; unsignedlong bss_align; unsignedlong bss_sz; unsignedlong align; int i, ret;
/* * kexec_purgatory_setup_sechdrs - prepares the pi->sechdrs buffer. * @pi: Purgatory to be loaded. * @kbuf: Buffer prepared to store purgatory. * * Allocates the memory needed for the buffer. Caller is responsible to free * the memory after use. * * Return: 0 on success, negative errno on error.
*/ staticint kexec_purgatory_setup_sechdrs(struct purgatory_info *pi, struct kexec_buf *kbuf)
{ unsignedlong bss_addr; unsignedlong offset;
size_t sechdrs_size;
Elf_Shdr *sechdrs; int i;
/* * The section headers in kexec_purgatory are read-only. In order to * have them modifiable make a temporary copy.
*/
sechdrs_size = array_size(sizeof(Elf_Shdr), pi->ehdr->e_shnum);
sechdrs = vzalloc(sechdrs_size); if (!sechdrs) return -ENOMEM;
memcpy(sechdrs, (void *)pi->ehdr + pi->ehdr->e_shoff, sechdrs_size);
pi->sechdrs = sechdrs;
/* * Check if the segment contains the entry point, if so, * calculate the value of image->start based on it. * If the compiler has produced more than one .text section * (Eg: .text.hot), they are generally after the main .text * section, and they shall not be used to calculate * image->start. So do not re-calculate image->start if it * is not set to the initial value, and warn the user so they * have a chance to fix their purgatory's linker script.
*/ if (sechdrs[i].sh_flags & SHF_EXECINSTR &&
pi->ehdr->e_entry >= sechdrs[i].sh_addr &&
pi->ehdr->e_entry < (sechdrs[i].sh_addr
+ sechdrs[i].sh_size) &&
!WARN_ON(kbuf->image->start != pi->ehdr->e_entry)) {
kbuf->image->start -= sechdrs[i].sh_addr;
kbuf->image->start += kbuf->mem + offset;
}
staticint kexec_apply_relocations(struct kimage *image)
{ int i, ret; struct purgatory_info *pi = &image->purgatory_info; const Elf_Shdr *sechdrs;
sechdrs = (void *)pi->ehdr + pi->ehdr->e_shoff;
for (i = 0; i < pi->ehdr->e_shnum; i++) { const Elf_Shdr *relsec; const Elf_Shdr *symtab;
Elf_Shdr *section;
relsec = sechdrs + i;
if (relsec->sh_type != SHT_RELA &&
relsec->sh_type != SHT_REL) continue;
/* * For section of type SHT_RELA/SHT_REL, * ->sh_link contains section header index of associated * symbol table. And ->sh_info contains section header * index of section to which relocations apply.
*/ if (relsec->sh_info >= pi->ehdr->e_shnum ||
relsec->sh_link >= pi->ehdr->e_shnum) return -ENOEXEC;
/* * symtab->sh_link contain section header index of associated * string table.
*/ if (symtab->sh_link >= pi->ehdr->e_shnum) /* Invalid section number? */ continue;
/* * Respective architecture needs to provide support for applying * relocations of type SHT_RELA/SHT_REL.
*/ if (relsec->sh_type == SHT_RELA)
ret = arch_kexec_apply_relocations_add(pi, section,
relsec, symtab); elseif (relsec->sh_type == SHT_REL)
ret = arch_kexec_apply_relocations(pi, section,
relsec, symtab); if (ret) return ret;
}
return 0;
}
/* * kexec_load_purgatory - Load and relocate the purgatory object. * @image: Image to add the purgatory to. * @kbuf: Memory parameters to use. * * Allocates the memory needed for image->purgatory_info.sechdrs and * image->purgatory_info.purgatory_buf/kbuf->buffer. Caller is responsible * to free the memory after use. * * Return: 0 on success, negative errno on error.
*/ int kexec_load_purgatory(struct kimage *image, struct kexec_buf *kbuf)
{ struct purgatory_info *pi = &image->purgatory_info; int ret;
if (kexec_purgatory_size <= 0) return -EINVAL;
pi->ehdr = (const Elf_Ehdr *)kexec_purgatory;
ret = kexec_purgatory_setup_kbuf(pi, kbuf); if (ret) return ret;
ret = kexec_purgatory_setup_sechdrs(pi, kbuf); if (ret) goto out_free_kbuf;
ret = kexec_apply_relocations(image); if (ret) goto out;
/* * kexec_purgatory_find_symbol - find a symbol in the purgatory * @pi: Purgatory to search in. * @name: Name of the symbol. * * Return: pointer to symbol in read-only symtab on success, NULL on error.
*/ staticconst Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi, constchar *name)
{ const Elf_Shdr *sechdrs; const Elf_Ehdr *ehdr; const Elf_Sym *syms; constchar *strtab; int i, k;
/* Go through symbols for a match */ for (k = 0; k < sechdrs[i].sh_size/sizeof(Elf_Sym); k++) { if (ELF_ST_BIND(syms[k].st_info) != STB_GLOBAL) continue;
if (strcmp(strtab + syms[k].st_name, name) != 0) continue;
if (syms[k].st_shndx == SHN_UNDEF ||
syms[k].st_shndx >= ehdr->e_shnum) {
pr_debug("Symbol: %s has bad section index %d.\n",
name, syms[k].st_shndx); return NULL;
}
/* Found the symbol we are looking for */ return &syms[k];
}
}
sym = kexec_purgatory_find_symbol(pi, name); if (!sym) return ERR_PTR(-EINVAL);
sechdr = &pi->sechdrs[sym->st_shndx];
/* * Returns the address where symbol will finally be loaded after * kexec_load_segment()
*/ return (void *)(sechdr->sh_addr + sym->st_value);
}
/* * Get or set value of a symbol. If "get_value" is true, symbol value is * returned in buf otherwise symbol value is set based on value in buf.
*/ int kexec_purgatory_get_set_symbol(struct kimage *image, constchar *name, void *buf, unsignedint size, bool get_value)
{ struct purgatory_info *pi = &image->purgatory_info; const Elf_Sym *sym;
Elf_Shdr *sec; char *sym_buf;
sym = kexec_purgatory_find_symbol(pi, name); if (!sym) return -EINVAL;
if (sym->st_size != size) {
pr_err("symbol %s size mismatch: expected %lu actual %u\n",
name, (unsignedlong)sym->st_size, size); return -EINVAL;
}
sec = pi->sechdrs + sym->st_shndx;
if (sec->sh_type == SHT_NOBITS) {
pr_err("symbol %s is in a bss section. Cannot %s\n", name,
get_value ? "get" : "set"); return -EINVAL;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.