2015-09-10 06:38:51 +08:00
|
|
|
/*
|
|
|
|
* kexec: kexec_file_load system call
|
|
|
|
*
|
|
|
|
* Copyright (C) 2014 Red Hat Inc.
|
|
|
|
* Authors:
|
|
|
|
* Vivek Goyal <vgoyal@redhat.com>
|
|
|
|
*
|
|
|
|
* This source code is licensed under the GNU General Public License,
|
|
|
|
* Version 2. See the file COPYING for more details.
|
|
|
|
*/
|
|
|
|
|
kexec: use file name as the output message prefix
kexec output message misses the prefix "kexec", when Dave Young split the
kexec code. Now, we use file name as the output message prefix.
Currently, the format of output message:
[ 140.290795] SYSC_kexec_load: hello, world
[ 140.291534] kexec: sanity_check_segment_list: hello, world
Ideally, the format of output message:
[ 30.791503] kexec: SYSC_kexec_load, Hello, world
[ 79.182752] kexec_core: sanity_check_segment_list, Hello, world
Remove the custom prefix "kexec" in output message.
Signed-off-by: Minfei Huang <mnfhuang@gmail.com>
Acked-by: Dave Young <dyoung@redhat.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2015-11-07 08:32:45 +08:00
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
|
2015-09-10 06:38:51 +08:00
|
|
|
#include <linux/capability.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/file.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/kexec.h>
|
2018-11-15 13:52:43 +08:00
|
|
|
#include <linux/memblock.h>
|
2015-09-10 06:38:51 +08:00
|
|
|
#include <linux/mutex.h>
|
|
|
|
#include <linux/list.h>
|
2016-01-15 09:59:14 +08:00
|
|
|
#include <linux/fs.h>
|
2016-12-20 08:22:48 +08:00
|
|
|
#include <linux/ima.h>
|
2015-09-10 06:38:51 +08:00
|
|
|
#include <crypto/hash.h>
|
|
|
|
#include <crypto/sha.h>
|
2018-04-14 06:36:06 +08:00
|
|
|
#include <linux/elf.h>
|
|
|
|
#include <linux/elfcore.h>
|
|
|
|
#include <linux/kernel.h>
|
2015-09-10 06:38:51 +08:00
|
|
|
#include <linux/syscalls.h>
|
|
|
|
#include <linux/vmalloc.h>
|
|
|
|
#include "kexec_internal.h"
|
|
|
|
|
|
|
|
static int kexec_calculate_store_digests(struct kimage *image);
|
|
|
|
|
2018-04-14 06:35:49 +08:00
|
|
|
/*
|
|
|
|
* 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,
|
|
|
|
unsigned long buf_len)
|
|
|
|
{
|
|
|
|
const struct 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-09-10 06:38:51 +08:00
|
|
|
/* Architectures can provide this probe function */
|
|
|
|
int __weak arch_kexec_kernel_image_probe(struct kimage *image, void *buf,
|
|
|
|
unsigned long buf_len)
|
|
|
|
{
|
2018-04-14 06:35:49 +08:00
|
|
|
return kexec_image_probe_default(image, buf, buf_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *kexec_image_load_default(struct kimage *image)
|
|
|
|
{
|
|
|
|
if (!image->fops || !image->fops->load)
|
|
|
|
return ERR_PTR(-ENOEXEC);
|
|
|
|
|
|
|
|
return image->fops->load(image, image->kernel_buf,
|
|
|
|
image->kernel_buf_len, image->initrd_buf,
|
|
|
|
image->initrd_buf_len, image->cmdline_buf,
|
|
|
|
image->cmdline_buf_len);
|
2015-09-10 06:38:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void * __weak arch_kexec_kernel_image_load(struct kimage *image)
|
|
|
|
{
|
2018-04-14 06:35:49 +08:00
|
|
|
return kexec_image_load_default(image);
|
|
|
|
}
|
|
|
|
|
2018-11-15 13:52:41 +08:00
|
|
|
int kexec_image_post_load_cleanup_default(struct kimage *image)
|
2018-04-14 06:35:49 +08:00
|
|
|
{
|
|
|
|
if (!image->fops || !image->fops->cleanup)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return image->fops->cleanup(image->image_loader_data);
|
2015-09-10 06:38:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int __weak arch_kimage_file_post_load_cleanup(struct kimage *image)
|
|
|
|
{
|
2018-04-14 06:35:49 +08:00
|
|
|
return kexec_image_post_load_cleanup_default(image);
|
2015-09-10 06:38:51 +08:00
|
|
|
}
|
|
|
|
|
2016-01-21 07:00:36 +08:00
|
|
|
#ifdef CONFIG_KEXEC_VERIFY_SIG
|
2018-04-14 06:35:49 +08:00
|
|
|
static int kexec_image_verify_sig_default(struct kimage *image, void *buf,
|
|
|
|
unsigned long 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);
|
|
|
|
}
|
|
|
|
|
2015-09-10 06:38:51 +08:00
|
|
|
int __weak arch_kexec_kernel_verify_sig(struct kimage *image, void *buf,
|
|
|
|
unsigned long buf_len)
|
|
|
|
{
|
2018-04-14 06:35:49 +08:00
|
|
|
return kexec_image_verify_sig_default(image, buf, buf_len);
|
2015-09-10 06:38:51 +08:00
|
|
|
}
|
2016-01-21 07:00:36 +08:00
|
|
|
#endif
|
2015-09-10 06:38:51 +08:00
|
|
|
|
2018-04-14 06:36:24 +08:00
|
|
|
/*
|
|
|
|
* arch_kexec_apply_relocations_add - apply relocations of type RELA
|
|
|
|
* @pi: Purgatory to be relocated.
|
|
|
|
* @section: Section relocations applying to.
|
|
|
|
* @relsec: Section containing RELAs.
|
|
|
|
* @symtab: Corresponding symtab.
|
|
|
|
*
|
|
|
|
* Return: 0 on success, negative errno on error.
|
|
|
|
*/
|
2015-09-10 06:38:51 +08:00
|
|
|
int __weak
|
2018-04-14 06:36:24 +08:00
|
|
|
arch_kexec_apply_relocations_add(struct purgatory_info *pi, Elf_Shdr *section,
|
|
|
|
const Elf_Shdr *relsec, const Elf_Shdr *symtab)
|
2015-09-10 06:38:51 +08:00
|
|
|
{
|
|
|
|
pr_err("RELA relocation unsupported.\n");
|
|
|
|
return -ENOEXEC;
|
|
|
|
}
|
|
|
|
|
2018-04-14 06:36:24 +08:00
|
|
|
/*
|
|
|
|
* arch_kexec_apply_relocations - apply relocations of type REL
|
|
|
|
* @pi: Purgatory to be relocated.
|
|
|
|
* @section: Section relocations applying to.
|
|
|
|
* @relsec: Section containing RELs.
|
|
|
|
* @symtab: Corresponding symtab.
|
|
|
|
*
|
|
|
|
* Return: 0 on success, negative errno on error.
|
|
|
|
*/
|
2015-09-10 06:38:51 +08:00
|
|
|
int __weak
|
2018-04-14 06:36:24 +08:00
|
|
|
arch_kexec_apply_relocations(struct purgatory_info *pi, Elf_Shdr *section,
|
|
|
|
const Elf_Shdr *relsec, const Elf_Shdr *symtab)
|
2015-09-10 06:38:51 +08:00
|
|
|
{
|
|
|
|
pr_err("REL relocation unsupported.\n");
|
|
|
|
return -ENOEXEC;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
vfree(image->kernel_buf);
|
|
|
|
image->kernel_buf = NULL;
|
|
|
|
|
|
|
|
vfree(image->initrd_buf);
|
|
|
|
image->initrd_buf = NULL;
|
|
|
|
|
|
|
|
kfree(image->cmdline_buf);
|
|
|
|
image->cmdline_buf = NULL;
|
|
|
|
|
|
|
|
vfree(pi->purgatory_buf);
|
|
|
|
pi->purgatory_buf = NULL;
|
|
|
|
|
|
|
|
vfree(pi->sechdrs);
|
|
|
|
pi->sechdrs = NULL;
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In file mode list of segments is prepared by kernel. Copy relevant
|
|
|
|
* data from user space, do error checking, prepare segment list
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
|
|
|
|
const char __user *cmdline_ptr,
|
|
|
|
unsigned long cmdline_len, unsigned flags)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
void *ldata;
|
2016-01-15 09:59:14 +08:00
|
|
|
loff_t size;
|
2015-09-10 06:38:51 +08:00
|
|
|
|
2016-01-15 09:59:14 +08:00
|
|
|
ret = kernel_read_file_from_fd(kernel_fd, &image->kernel_buf,
|
|
|
|
&size, INT_MAX, READING_KEXEC_IMAGE);
|
2015-09-10 06:38:51 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2016-01-15 09:59:14 +08:00
|
|
|
image->kernel_buf_len = size;
|
2015-09-10 06:38:51 +08:00
|
|
|
|
2016-12-20 08:22:48 +08:00
|
|
|
/* IMA needs to pass the measurement list to the next kernel. */
|
|
|
|
ima_add_kexec_buffer(image);
|
|
|
|
|
2015-09-10 06:38:51 +08:00
|
|
|
/* 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_VERIFY_SIG
|
|
|
|
ret = arch_kexec_kernel_verify_sig(image, image->kernel_buf,
|
|
|
|
image->kernel_buf_len);
|
|
|
|
if (ret) {
|
|
|
|
pr_debug("kernel signature verification failed.\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
pr_debug("kernel signature verification successful.\n");
|
|
|
|
#endif
|
|
|
|
/* It is possible that there no initramfs is being loaded */
|
|
|
|
if (!(flags & KEXEC_FILE_NO_INITRAMFS)) {
|
2016-01-15 09:59:14 +08:00
|
|
|
ret = kernel_read_file_from_fd(initrd_fd, &image->initrd_buf,
|
|
|
|
&size, INT_MAX,
|
|
|
|
READING_KEXEC_INITRAMFS);
|
2015-09-10 06:38:51 +08:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
2016-01-15 09:59:14 +08:00
|
|
|
image->initrd_buf_len = size;
|
2015-09-10 06:38:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cmdline_len) {
|
2017-05-14 06:39:01 +08:00
|
|
|
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;
|
2015-09-10 06:38:51 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Call arch image load handlers */
|
|
|
|
ldata = arch_kexec_kernel_image_load(image);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
kimage_file_alloc_init(struct kimage **rimage, int kernel_fd,
|
|
|
|
int initrd_fd, const char __user *cmdline_ptr,
|
|
|
|
unsigned long cmdline_len, unsigned long flags)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct kimage *image;
|
|
|
|
bool kexec_on_panic = flags & KEXEC_FILE_ON_CRASH;
|
|
|
|
|
|
|
|
image = do_kimage_alloc_init();
|
|
|
|
if (!image)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
image->file_mode = 1;
|
|
|
|
|
|
|
|
if (kexec_on_panic) {
|
|
|
|
/* Enable special crash kernel control page alloc policy. */
|
|
|
|
image->control_page = crashk_res.start;
|
|
|
|
image->type = KEXEC_TYPE_CRASH;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = kimage_file_prepare_segments(image, kernel_fd, initrd_fd,
|
|
|
|
cmdline_ptr, cmdline_len, flags);
|
|
|
|
if (ret)
|
|
|
|
goto out_free_image;
|
|
|
|
|
|
|
|
ret = sanity_check_segment_list(image);
|
|
|
|
if (ret)
|
|
|
|
goto out_free_post_load_bufs;
|
|
|
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
image->control_code_page = kimage_alloc_control_pages(image,
|
|
|
|
get_order(KEXEC_CONTROL_PAGE_SIZE));
|
|
|
|
if (!image->control_code_page) {
|
|
|
|
pr_err("Could not allocate control_code_buffer\n");
|
|
|
|
goto out_free_post_load_bufs;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!kexec_on_panic) {
|
|
|
|
image->swap_page = kimage_alloc_control_pages(image, 0);
|
|
|
|
if (!image->swap_page) {
|
|
|
|
pr_err("Could not allocate swap buffer\n");
|
|
|
|
goto out_free_control_pages;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*rimage = image;
|
|
|
|
return 0;
|
|
|
|
out_free_control_pages:
|
|
|
|
kimage_free_page_list(&image->control_pages);
|
|
|
|
out_free_post_load_bufs:
|
|
|
|
kimage_file_post_load_cleanup(image);
|
|
|
|
out_free_image:
|
|
|
|
kfree(image);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd,
|
|
|
|
unsigned long, cmdline_len, const char __user *, cmdline_ptr,
|
|
|
|
unsigned long, flags)
|
|
|
|
{
|
|
|
|
int ret = 0, i;
|
|
|
|
struct kimage **dest_image, *image;
|
|
|
|
|
|
|
|
/* We only trust the superuser with rebooting the system. */
|
|
|
|
if (!capable(CAP_SYS_BOOT) || kexec_load_disabled)
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
/* Make sure we have a legal set of flags */
|
|
|
|
if (flags != (flags & KEXEC_FILE_FLAGS))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
image = NULL;
|
|
|
|
|
|
|
|
if (!mutex_trylock(&kexec_mutex))
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
dest_image = &kexec_image;
|
2016-05-24 07:24:10 +08:00
|
|
|
if (flags & KEXEC_FILE_ON_CRASH) {
|
2015-09-10 06:38:51 +08:00
|
|
|
dest_image = &kexec_crash_image;
|
2016-05-24 07:24:10 +08:00
|
|
|
if (kexec_crash_image)
|
|
|
|
arch_kexec_unprotect_crashkres();
|
|
|
|
}
|
2015-09-10 06:38:51 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
kdump: protect vmcoreinfo data under the crash memory
Currently vmcoreinfo data is updated at boot time subsys_initcall(), it
has the risk of being modified by some wrong code during system is
running.
As a result, vmcore dumped may contain the wrong vmcoreinfo. Later on,
when using "crash", "makedumpfile", etc utility to parse this vmcore, we
probably will get "Segmentation fault" or other unexpected errors.
E.g. 1) wrong code overwrites vmcoreinfo_data; 2) further crashes the
system; 3) trigger kdump, then we obviously will fail to recognize the
crash context correctly due to the corrupted vmcoreinfo.
Now except for vmcoreinfo, all the crash data is well
protected(including the cpu note which is fully updated in the crash
path, thus its correctness is guaranteed). Given that vmcoreinfo data
is a large chunk prepared for kdump, we better protect it as well.
To solve this, we relocate and copy vmcoreinfo_data to the crash memory
when kdump is loading via kexec syscalls. Because the whole crash
memory will be protected by existing arch_kexec_protect_crashkres()
mechanism, we naturally protect vmcoreinfo_data from write(even read)
access under kernel direct mapping after kdump is loaded.
Since kdump is usually loaded at the very early stage after boot, we can
trust the correctness of the vmcoreinfo data copied.
On the other hand, we still need to operate the vmcoreinfo safe copy
when crash happens to generate vmcoreinfo_note again, we rely on vmap()
to map out a new kernel virtual address and update to use this new one
instead in the following crash_save_vmcoreinfo().
BTW, we do not touch vmcoreinfo_note, because it will be fully updated
using the protected vmcoreinfo_data after crash which is surely correct
just like the cpu crash note.
Link: http://lkml.kernel.org/r/1493281021-20737-3-git-send-email-xlpang@redhat.com
Signed-off-by: Xunlei Pang <xlpang@redhat.com>
Tested-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Dave Young <dyoung@redhat.com>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: Hari Bathini <hbathini@linux.vnet.ibm.com>
Cc: Juergen Gross <jgross@suse.com>
Cc: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2017-07-13 05:33:21 +08:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2015-09-10 06:38:51 +08:00
|
|
|
ret = kexec_calculate_store_digests(image);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
for (i = 0; i < image->nr_segments; i++) {
|
|
|
|
struct kexec_segment *ksegment;
|
|
|
|
|
|
|
|
ksegment = &image->segment[i];
|
|
|
|
pr_debug("Loading segment %d: buf=0x%p bufsz=0x%zx mem=0x%lx memsz=0x%zx\n",
|
|
|
|
i, ksegment->buf, ksegment->bufsz, ksegment->mem,
|
|
|
|
ksegment->memsz);
|
|
|
|
|
|
|
|
ret = kimage_load_segment(image, &image->segment[i]);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
kimage_terminate(image);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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:
|
2016-05-24 07:24:10 +08:00
|
|
|
if ((flags & KEXEC_FILE_ON_CRASH) && kexec_crash_image)
|
|
|
|
arch_kexec_protect_crashkres();
|
|
|
|
|
2015-09-10 06:38:51 +08:00
|
|
|
mutex_unlock(&kexec_mutex);
|
|
|
|
kimage_free(image);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int locate_mem_hole_top_down(unsigned long start, unsigned long end,
|
|
|
|
struct kexec_buf *kbuf)
|
|
|
|
{
|
|
|
|
struct kimage *image = kbuf->image;
|
|
|
|
unsigned long temp_start, temp_end;
|
|
|
|
|
|
|
|
temp_end = min(end, kbuf->buf_max);
|
|
|
|
temp_start = temp_end - kbuf->memsz;
|
|
|
|
|
|
|
|
do {
|
|
|
|
/* align down start */
|
|
|
|
temp_start = temp_start & (~(kbuf->buf_align - 1));
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int locate_mem_hole_bottom_up(unsigned long start, unsigned long end,
|
|
|
|
struct kexec_buf *kbuf)
|
|
|
|
{
|
|
|
|
struct kimage *image = kbuf->image;
|
|
|
|
unsigned long temp_start, temp_end;
|
|
|
|
|
|
|
|
temp_start = max(start, kbuf->buf_min);
|
|
|
|
|
|
|
|
do {
|
|
|
|
temp_start = ALIGN(temp_start, kbuf->buf_align);
|
|
|
|
temp_end = temp_start + kbuf->memsz - 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2017-10-20 22:30:51 +08:00
|
|
|
static int locate_mem_hole_callback(struct resource *res, void *arg)
|
2015-09-10 06:38:51 +08:00
|
|
|
{
|
|
|
|
struct kexec_buf *kbuf = (struct kexec_buf *)arg;
|
2017-10-20 22:30:51 +08:00
|
|
|
u64 start = res->start, end = res->end;
|
2015-09-10 06:38:51 +08:00
|
|
|
unsigned long sz = end - start + 1;
|
|
|
|
|
|
|
|
/* Returning 0 will take to next memory range */
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-11-15 13:52:43 +08:00
|
|
|
#ifdef CONFIG_ARCH_DISCARD_MEMBLOCK
|
|
|
|
static int kexec_walk_memblock(struct kexec_buf *kbuf,
|
|
|
|
int (*func)(struct resource *, void *))
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static int 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 = { };
|
|
|
|
|
2018-11-15 13:52:44 +08:00
|
|
|
if (kbuf->image->type == KEXEC_TYPE_CRASH)
|
|
|
|
return func(&crashk_res, kbuf);
|
|
|
|
|
2018-11-15 13:52:43 +08:00
|
|
|
if (kbuf->top_down) {
|
2018-11-15 13:52:44 +08:00
|
|
|
for_each_free_mem_range_reverse(i, NUMA_NO_NODE, MEMBLOCK_NONE,
|
2018-11-15 13:52:43 +08:00
|
|
|
&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 {
|
2018-11-15 13:52:44 +08:00
|
|
|
for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE,
|
|
|
|
&mstart, &mend, NULL) {
|
2018-11-15 13:52:43 +08:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-11-29 20:45:47 +08:00
|
|
|
/**
|
2018-11-15 13:52:43 +08:00
|
|
|
* kexec_walk_resources - call func(data) on free memory regions
|
2016-11-29 20:45:47 +08:00
|
|
|
* @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.
|
|
|
|
*/
|
2018-11-15 13:52:43 +08:00
|
|
|
static int kexec_walk_resources(struct kexec_buf *kbuf,
|
|
|
|
int (*func)(struct resource *, void *))
|
2016-11-29 20:45:47 +08:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
else
|
|
|
|
return walk_system_ram_res(0, ULONG_MAX, kbuf, func);
|
|
|
|
}
|
|
|
|
|
2016-11-29 20:45:49 +08:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
2018-11-15 13:52:42 +08:00
|
|
|
/* Arch knows where to place */
|
|
|
|
if (kbuf->mem != KEXEC_BUF_MEM_UNKNOWN)
|
|
|
|
return 0;
|
|
|
|
|
2018-11-15 13:52:43 +08:00
|
|
|
if (IS_ENABLED(CONFIG_ARCH_DISCARD_MEMBLOCK))
|
|
|
|
ret = kexec_walk_resources(kbuf, locate_mem_hole_callback);
|
|
|
|
else
|
|
|
|
ret = kexec_walk_memblock(kbuf, locate_mem_hole_callback);
|
2016-11-29 20:45:49 +08:00
|
|
|
|
|
|
|
return ret == 1 ? 0 : -EADDRNOTAVAIL;
|
|
|
|
}
|
|
|
|
|
2016-11-29 20:45:48 +08:00
|
|
|
/**
|
|
|
|
* kexec_add_buffer - place a buffer in a kexec segment
|
|
|
|
* @kbuf: Buffer contents and memory parameters.
|
|
|
|
*
|
|
|
|
* This function assumes that kexec_mutex 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.
|
2015-09-10 06:38:51 +08:00
|
|
|
*/
|
2016-11-29 20:45:48 +08:00
|
|
|
int kexec_add_buffer(struct kexec_buf *kbuf)
|
2015-09-10 06:38:51 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
struct kexec_segment *ksegment;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Currently adding segment this way is allowed only in file mode */
|
2016-11-29 20:45:48 +08:00
|
|
|
if (!kbuf->image->file_mode)
|
2015-09-10 06:38:51 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2016-11-29 20:45:48 +08:00
|
|
|
if (kbuf->image->nr_segments >= KEXEC_SEGMENT_MAX)
|
2015-09-10 06:38:51 +08:00
|
|
|
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.
|
|
|
|
*/
|
2016-11-29 20:45:48 +08:00
|
|
|
if (!list_empty(&kbuf->image->control_pages)) {
|
2015-09-10 06:38:51 +08:00
|
|
|
WARN_ON(1);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2016-11-29 20:45:48 +08:00
|
|
|
/* Ensure minimum alignment needed for segments. */
|
|
|
|
kbuf->memsz = ALIGN(kbuf->memsz, PAGE_SIZE);
|
|
|
|
kbuf->buf_align = max(kbuf->buf_align, PAGE_SIZE);
|
2015-09-10 06:38:51 +08:00
|
|
|
|
|
|
|
/* Walk the RAM ranges and allocate a suitable range for the buffer */
|
2016-11-29 20:45:49 +08:00
|
|
|
ret = kexec_locate_mem_hole(kbuf);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2015-09-10 06:38:51 +08:00
|
|
|
|
|
|
|
/* Found a suitable memory range */
|
2016-11-29 20:45:48 +08:00
|
|
|
ksegment = &kbuf->image->segment[kbuf->image->nr_segments];
|
2015-09-10 06:38:51 +08:00
|
|
|
ksegment->kbuf = kbuf->buffer;
|
|
|
|
ksegment->bufsz = kbuf->bufsz;
|
|
|
|
ksegment->mem = kbuf->mem;
|
|
|
|
ksegment->memsz = kbuf->memsz;
|
2016-11-29 20:45:48 +08:00
|
|
|
kbuf->image->nr_segments++;
|
2015-09-10 06:38:51 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate and store the digest of segments */
|
|
|
|
static int kexec_calculate_store_digests(struct kimage *image)
|
|
|
|
{
|
|
|
|
struct crypto_shash *tfm;
|
|
|
|
struct shash_desc *desc;
|
|
|
|
int ret = 0, i, j, zero_buf_sz, sha_region_sz;
|
|
|
|
size_t desc_size, nullsz;
|
|
|
|
char *digest;
|
|
|
|
void *zero_buf;
|
|
|
|
struct kexec_sha_region *sha_regions;
|
|
|
|
struct purgatory_info *pi = &image->purgatory_info;
|
|
|
|
|
kexec_file: make use of purgatory optional
Patch series "kexec_file, x86, powerpc: refactoring for other
architecutres", v2.
This is a preparatory patchset for adding kexec_file support on arm64.
It was originally included in a arm64 patch set[1], but Philipp is also
working on their kexec_file support on s390[2] and some changes are now
conflicting.
So these common parts were extracted and put into a separate patch set
for better integration. What's more, my original patch#4 was split into
a few small chunks for easier review after Dave's comment.
As such, the resulting code is basically identical with my original, and
the only *visible* differences are:
- renaming of _kexec_kernel_image_probe() and _kimage_file_post_load_cleanup()
- change one of types of arguments at prepare_elf64_headers()
Those, unfortunately, require a couple of trivial changes on the rest
(#1, #6 to #13) of my arm64 kexec_file patch set[1].
Patch #1 allows making a use of purgatory optional, particularly useful
for arm64.
Patch #2 commonalizes arch_kexec_kernel_{image_probe, image_load,
verify_sig}() and arch_kimage_file_post_load_cleanup() across
architectures.
Patches #3-#7 are also intended to generalize parse_elf64_headers(),
along with exclude_mem_range(), to be made best re-use of.
[1] http://lists.infradead.org/pipermail/linux-arm-kernel/2018-February/561182.html
[2] http://lkml.iu.edu//hypermail/linux/kernel/1802.1/02596.html
This patch (of 7):
On arm64, crash dump kernel's usable memory is protected by *unmapping*
it from kernel virtual space unlike other architectures where the region
is just made read-only. It is highly unlikely that the region is
accidentally corrupted and this observation rationalizes that digest
check code can also be dropped from purgatory. The resulting code is so
simple as it doesn't require a bit ugly re-linking/relocation stuff,
i.e. arch_kexec_apply_relocations_add().
Please see:
http://lists.infradead.org/pipermail/linux-arm-kernel/2017-December/545428.html
All that the purgatory does is to shuffle arguments and jump into a new
kernel, while we still need to have some space for a hash value
(purgatory_sha256_digest) which is never checked against.
As such, it doesn't make sense to have trampline code between old kernel
and new kernel on arm64.
This patch introduces a new configuration, ARCH_HAS_KEXEC_PURGATORY, and
allows related code to be compiled in only if necessary.
[takahiro.akashi@linaro.org: fix trivial screwup]
Link: http://lkml.kernel.org/r/20180309093346.GF25863@linaro.org
Link: http://lkml.kernel.org/r/20180306102303.9063-2-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>
2018-04-14 06:35:45 +08:00
|
|
|
if (!IS_ENABLED(CONFIG_ARCH_HAS_KEXEC_PURGATORY))
|
|
|
|
return 0;
|
|
|
|
|
2015-09-10 06:38:51 +08:00
|
|
|
zero_buf = __va(page_to_pfn(ZERO_PAGE(0)) << PAGE_SHIFT);
|
|
|
|
zero_buf_sz = PAGE_SIZE;
|
|
|
|
|
|
|
|
tfm = crypto_alloc_shash("sha256", 0, 0);
|
|
|
|
if (IS_ERR(tfm)) {
|
|
|
|
ret = PTR_ERR(tfm);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
|
|
|
|
desc = kzalloc(desc_size, GFP_KERNEL);
|
|
|
|
if (!desc) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out_free_tfm;
|
|
|
|
}
|
|
|
|
|
|
|
|
sha_region_sz = KEXEC_SEGMENT_MAX * sizeof(struct kexec_sha_region);
|
|
|
|
sha_regions = vzalloc(sha_region_sz);
|
|
|
|
if (!sha_regions)
|
|
|
|
goto out_free_desc;
|
|
|
|
|
|
|
|
desc->tfm = tfm;
|
|
|
|
desc->flags = 0;
|
|
|
|
|
|
|
|
ret = crypto_shash_init(desc);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out_free_sha_regions;
|
|
|
|
|
|
|
|
digest = kzalloc(SHA256_DIGEST_SIZE, GFP_KERNEL);
|
|
|
|
if (!digest) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out_free_sha_regions;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j = i = 0; i < image->nr_segments; i++) {
|
|
|
|
struct kexec_segment *ksegment;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
ret = crypto_shash_update(desc, ksegment->kbuf,
|
|
|
|
ksegment->bufsz);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Assume rest of the buffer is filled with zero and
|
|
|
|
* update digest accordingly.
|
|
|
|
*/
|
|
|
|
nullsz = ksegment->memsz - ksegment->bufsz;
|
|
|
|
while (nullsz) {
|
|
|
|
unsigned long bytes = nullsz;
|
|
|
|
|
|
|
|
if (bytes > zero_buf_sz)
|
|
|
|
bytes = zero_buf_sz;
|
|
|
|
ret = crypto_shash_update(desc, zero_buf, bytes);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
nullsz -= bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
|
|
|
|
sha_regions[j].start = ksegment->mem;
|
|
|
|
sha_regions[j].len = ksegment->memsz;
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
ret = crypto_shash_final(desc, digest);
|
|
|
|
if (ret)
|
|
|
|
goto out_free_digest;
|
2017-03-10 20:17:18 +08:00
|
|
|
ret = kexec_purgatory_get_set_symbol(image, "purgatory_sha_regions",
|
|
|
|
sha_regions, sha_region_sz, 0);
|
2015-09-10 06:38:51 +08:00
|
|
|
if (ret)
|
|
|
|
goto out_free_digest;
|
|
|
|
|
2017-03-10 20:17:18 +08:00
|
|
|
ret = kexec_purgatory_get_set_symbol(image, "purgatory_sha256_digest",
|
|
|
|
digest, SHA256_DIGEST_SIZE, 0);
|
2015-09-10 06:38:51 +08:00
|
|
|
if (ret)
|
|
|
|
goto out_free_digest;
|
|
|
|
}
|
|
|
|
|
|
|
|
out_free_digest:
|
|
|
|
kfree(digest);
|
|
|
|
out_free_sha_regions:
|
|
|
|
vfree(sha_regions);
|
|
|
|
out_free_desc:
|
|
|
|
kfree(desc);
|
|
|
|
out_free_tfm:
|
|
|
|
kfree(tfm);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
kexec_file: make use of purgatory optional
Patch series "kexec_file, x86, powerpc: refactoring for other
architecutres", v2.
This is a preparatory patchset for adding kexec_file support on arm64.
It was originally included in a arm64 patch set[1], but Philipp is also
working on their kexec_file support on s390[2] and some changes are now
conflicting.
So these common parts were extracted and put into a separate patch set
for better integration. What's more, my original patch#4 was split into
a few small chunks for easier review after Dave's comment.
As such, the resulting code is basically identical with my original, and
the only *visible* differences are:
- renaming of _kexec_kernel_image_probe() and _kimage_file_post_load_cleanup()
- change one of types of arguments at prepare_elf64_headers()
Those, unfortunately, require a couple of trivial changes on the rest
(#1, #6 to #13) of my arm64 kexec_file patch set[1].
Patch #1 allows making a use of purgatory optional, particularly useful
for arm64.
Patch #2 commonalizes arch_kexec_kernel_{image_probe, image_load,
verify_sig}() and arch_kimage_file_post_load_cleanup() across
architectures.
Patches #3-#7 are also intended to generalize parse_elf64_headers(),
along with exclude_mem_range(), to be made best re-use of.
[1] http://lists.infradead.org/pipermail/linux-arm-kernel/2018-February/561182.html
[2] http://lkml.iu.edu//hypermail/linux/kernel/1802.1/02596.html
This patch (of 7):
On arm64, crash dump kernel's usable memory is protected by *unmapping*
it from kernel virtual space unlike other architectures where the region
is just made read-only. It is highly unlikely that the region is
accidentally corrupted and this observation rationalizes that digest
check code can also be dropped from purgatory. The resulting code is so
simple as it doesn't require a bit ugly re-linking/relocation stuff,
i.e. arch_kexec_apply_relocations_add().
Please see:
http://lists.infradead.org/pipermail/linux-arm-kernel/2017-December/545428.html
All that the purgatory does is to shuffle arguments and jump into a new
kernel, while we still need to have some space for a hash value
(purgatory_sha256_digest) which is never checked against.
As such, it doesn't make sense to have trampline code between old kernel
and new kernel on arm64.
This patch introduces a new configuration, ARCH_HAS_KEXEC_PURGATORY, and
allows related code to be compiled in only if necessary.
[takahiro.akashi@linaro.org: fix trivial screwup]
Link: http://lkml.kernel.org/r/20180309093346.GF25863@linaro.org
Link: http://lkml.kernel.org/r/20180306102303.9063-2-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>
2018-04-14 06:35:45 +08:00
|
|
|
#ifdef CONFIG_ARCH_HAS_KEXEC_PURGATORY
|
2018-04-14 06:36:28 +08:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
static int kexec_purgatory_setup_kbuf(struct purgatory_info *pi,
|
|
|
|
struct kexec_buf *kbuf)
|
2015-09-10 06:38:51 +08:00
|
|
|
{
|
2018-04-14 06:36:28 +08:00
|
|
|
const Elf_Shdr *sechdrs;
|
|
|
|
unsigned long bss_align;
|
|
|
|
unsigned long bss_sz;
|
|
|
|
unsigned long align;
|
|
|
|
int i, ret;
|
2015-09-10 06:38:51 +08:00
|
|
|
|
2018-04-14 06:36:28 +08:00
|
|
|
sechdrs = (void *)pi->ehdr + pi->ehdr->e_shoff;
|
2018-04-14 06:36:43 +08:00
|
|
|
kbuf->buf_align = bss_align = 1;
|
|
|
|
kbuf->bufsz = bss_sz = 0;
|
2018-04-14 06:36:28 +08:00
|
|
|
|
|
|
|
for (i = 0; i < pi->ehdr->e_shnum; i++) {
|
|
|
|
if (!(sechdrs[i].sh_flags & SHF_ALLOC))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
align = sechdrs[i].sh_addralign;
|
|
|
|
if (sechdrs[i].sh_type != SHT_NOBITS) {
|
|
|
|
if (kbuf->buf_align < align)
|
|
|
|
kbuf->buf_align = align;
|
|
|
|
kbuf->bufsz = ALIGN(kbuf->bufsz, align);
|
|
|
|
kbuf->bufsz += sechdrs[i].sh_size;
|
|
|
|
} else {
|
|
|
|
if (bss_align < align)
|
|
|
|
bss_align = align;
|
|
|
|
bss_sz = ALIGN(bss_sz, align);
|
|
|
|
bss_sz += sechdrs[i].sh_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
kbuf->bufsz = ALIGN(kbuf->bufsz, bss_align);
|
|
|
|
kbuf->memsz = kbuf->bufsz + bss_sz;
|
|
|
|
if (kbuf->buf_align < bss_align)
|
|
|
|
kbuf->buf_align = bss_align;
|
|
|
|
|
|
|
|
kbuf->buffer = vzalloc(kbuf->bufsz);
|
|
|
|
if (!kbuf->buffer)
|
|
|
|
return -ENOMEM;
|
|
|
|
pi->purgatory_buf = kbuf->buffer;
|
|
|
|
|
|
|
|
ret = kexec_add_buffer(kbuf);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
out:
|
|
|
|
vfree(pi->purgatory_buf);
|
|
|
|
pi->purgatory_buf = NULL;
|
|
|
|
return 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.
|
|
|
|
*/
|
|
|
|
static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
|
|
|
|
struct kexec_buf *kbuf)
|
|
|
|
{
|
|
|
|
unsigned long bss_addr;
|
|
|
|
unsigned long offset;
|
|
|
|
Elf_Shdr *sechdrs;
|
|
|
|
int i;
|
2015-09-10 06:38:51 +08:00
|
|
|
|
2018-04-14 06:36:39 +08:00
|
|
|
/*
|
|
|
|
* The section headers in kexec_purgatory are read-only. In order to
|
|
|
|
* have them modifiable make a temporary copy.
|
|
|
|
*/
|
treewide: Use array_size() in vzalloc()
The vzalloc() function has no 2-factor argument form, so multiplication
factors need to be wrapped in array_size(). This patch replaces cases of:
vzalloc(a * b)
with:
vzalloc(array_size(a, b))
as well as handling cases of:
vzalloc(a * b * c)
with:
vzalloc(array3_size(a, b, c))
This does, however, attempt to ignore constant size factors like:
vzalloc(4 * 1024)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
vzalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
vzalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
vzalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
vzalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
vzalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
vzalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
vzalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
vzalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
vzalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
vzalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
vzalloc(
- sizeof(TYPE) * (COUNT_ID)
+ array_size(COUNT_ID, sizeof(TYPE))
, ...)
|
vzalloc(
- sizeof(TYPE) * COUNT_ID
+ array_size(COUNT_ID, sizeof(TYPE))
, ...)
|
vzalloc(
- sizeof(TYPE) * (COUNT_CONST)
+ array_size(COUNT_CONST, sizeof(TYPE))
, ...)
|
vzalloc(
- sizeof(TYPE) * COUNT_CONST
+ array_size(COUNT_CONST, sizeof(TYPE))
, ...)
|
vzalloc(
- sizeof(THING) * (COUNT_ID)
+ array_size(COUNT_ID, sizeof(THING))
, ...)
|
vzalloc(
- sizeof(THING) * COUNT_ID
+ array_size(COUNT_ID, sizeof(THING))
, ...)
|
vzalloc(
- sizeof(THING) * (COUNT_CONST)
+ array_size(COUNT_CONST, sizeof(THING))
, ...)
|
vzalloc(
- sizeof(THING) * COUNT_CONST
+ array_size(COUNT_CONST, sizeof(THING))
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
vzalloc(
- SIZE * COUNT
+ array_size(COUNT, SIZE)
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
vzalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
vzalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
vzalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
vzalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
vzalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
vzalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
vzalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
vzalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
vzalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
vzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
vzalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
vzalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
vzalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
vzalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
vzalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vzalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vzalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vzalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vzalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vzalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vzalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
vzalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
vzalloc(C1 * C2 * C3, ...)
|
vzalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants.
@@
expression E1, E2;
constant C1, C2;
@@
(
vzalloc(C1 * C2, ...)
|
vzalloc(
- E1 * E2
+ array_size(E1, E2)
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 05:27:37 +08:00
|
|
|
sechdrs = vzalloc(array_size(sizeof(Elf_Shdr), pi->ehdr->e_shnum));
|
2015-09-10 06:38:51 +08:00
|
|
|
if (!sechdrs)
|
|
|
|
return -ENOMEM;
|
2018-04-14 06:36:28 +08:00
|
|
|
memcpy(sechdrs, (void *)pi->ehdr + pi->ehdr->e_shoff,
|
|
|
|
pi->ehdr->e_shnum * sizeof(Elf_Shdr));
|
|
|
|
pi->sechdrs = sechdrs;
|
2015-09-10 06:38:51 +08:00
|
|
|
|
2018-04-14 06:36:35 +08:00
|
|
|
offset = 0;
|
|
|
|
bss_addr = kbuf->mem + kbuf->bufsz;
|
2018-04-14 06:36:32 +08:00
|
|
|
kbuf->image->start = pi->ehdr->e_entry;
|
2015-09-10 06:38:51 +08:00
|
|
|
|
|
|
|
for (i = 0; i < pi->ehdr->e_shnum; i++) {
|
2018-04-14 06:36:28 +08:00
|
|
|
unsigned long align;
|
2018-04-14 06:36:35 +08:00
|
|
|
void *src, *dst;
|
2018-04-14 06:36:28 +08:00
|
|
|
|
2015-09-10 06:38:51 +08:00
|
|
|
if (!(sechdrs[i].sh_flags & SHF_ALLOC))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
align = sechdrs[i].sh_addralign;
|
2018-04-14 06:36:32 +08:00
|
|
|
if (sechdrs[i].sh_type == SHT_NOBITS) {
|
2015-09-10 06:38:51 +08:00
|
|
|
bss_addr = ALIGN(bss_addr, align);
|
|
|
|
sechdrs[i].sh_addr = bss_addr;
|
|
|
|
bss_addr += sechdrs[i].sh_size;
|
2018-04-14 06:36:32 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-04-14 06:36:35 +08:00
|
|
|
offset = ALIGN(offset, align);
|
2018-04-14 06:36:32 +08:00
|
|
|
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)) {
|
|
|
|
kbuf->image->start -= sechdrs[i].sh_addr;
|
2018-04-14 06:36:35 +08:00
|
|
|
kbuf->image->start += kbuf->mem + offset;
|
2015-09-10 06:38:51 +08:00
|
|
|
}
|
|
|
|
|
2018-04-14 06:36:39 +08:00
|
|
|
src = (void *)pi->ehdr + sechdrs[i].sh_offset;
|
2018-04-14 06:36:35 +08:00
|
|
|
dst = pi->purgatory_buf + offset;
|
|
|
|
memcpy(dst, src, sechdrs[i].sh_size);
|
|
|
|
|
|
|
|
sechdrs[i].sh_addr = kbuf->mem + offset;
|
2018-04-14 06:36:39 +08:00
|
|
|
sechdrs[i].sh_offset = offset;
|
2018-04-14 06:36:35 +08:00
|
|
|
offset += sechdrs[i].sh_size;
|
2018-04-14 06:36:32 +08:00
|
|
|
}
|
2015-09-10 06:38:51 +08:00
|
|
|
|
2018-04-14 06:36:28 +08:00
|
|
|
return 0;
|
2015-09-10 06:38:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int kexec_apply_relocations(struct kimage *image)
|
|
|
|
{
|
|
|
|
int i, ret;
|
|
|
|
struct purgatory_info *pi = &image->purgatory_info;
|
2018-04-14 06:36:24 +08:00
|
|
|
const Elf_Shdr *sechdrs;
|
|
|
|
|
|
|
|
sechdrs = (void *)pi->ehdr + pi->ehdr->e_shoff;
|
2015-09-10 06:38:51 +08:00
|
|
|
|
|
|
|
for (i = 0; i < pi->ehdr->e_shnum; i++) {
|
2018-04-14 06:36:24 +08:00
|
|
|
const Elf_Shdr *relsec;
|
|
|
|
const Elf_Shdr *symtab;
|
|
|
|
Elf_Shdr *section;
|
|
|
|
|
|
|
|
relsec = sechdrs + i;
|
2015-09-10 06:38:51 +08:00
|
|
|
|
2018-04-14 06:36:24 +08:00
|
|
|
if (relsec->sh_type != SHT_RELA &&
|
|
|
|
relsec->sh_type != SHT_REL)
|
2015-09-10 06:38:51 +08:00
|
|
|
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.
|
|
|
|
*/
|
2018-04-14 06:36:24 +08:00
|
|
|
if (relsec->sh_info >= pi->ehdr->e_shnum ||
|
|
|
|
relsec->sh_link >= pi->ehdr->e_shnum)
|
2015-09-10 06:38:51 +08:00
|
|
|
return -ENOEXEC;
|
|
|
|
|
2018-04-14 06:36:24 +08:00
|
|
|
section = pi->sechdrs + relsec->sh_info;
|
|
|
|
symtab = sechdrs + relsec->sh_link;
|
2015-09-10 06:38:51 +08:00
|
|
|
|
|
|
|
if (!(section->sh_flags & SHF_ALLOC))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2018-04-14 06:36:24 +08:00
|
|
|
if (relsec->sh_type == SHT_RELA)
|
|
|
|
ret = arch_kexec_apply_relocations_add(pi, section,
|
|
|
|
relsec, symtab);
|
|
|
|
else if (relsec->sh_type == SHT_REL)
|
|
|
|
ret = arch_kexec_apply_relocations(pi, section,
|
|
|
|
relsec, symtab);
|
2015-09-10 06:38:51 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-04-14 06:36:43 +08:00
|
|
|
/*
|
|
|
|
* 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)
|
2015-09-10 06:38:51 +08:00
|
|
|
{
|
|
|
|
struct purgatory_info *pi = &image->purgatory_info;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (kexec_purgatory_size <= 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2018-04-14 06:36:17 +08:00
|
|
|
pi->ehdr = (const Elf_Ehdr *)kexec_purgatory;
|
2015-09-10 06:38:51 +08:00
|
|
|
|
2018-04-14 06:36:43 +08:00
|
|
|
ret = kexec_purgatory_setup_kbuf(pi, kbuf);
|
2015-09-10 06:38:51 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2018-04-14 06:36:43 +08:00
|
|
|
ret = kexec_purgatory_setup_sechdrs(pi, kbuf);
|
2018-04-14 06:36:28 +08:00
|
|
|
if (ret)
|
|
|
|
goto out_free_kbuf;
|
|
|
|
|
2015-09-10 06:38:51 +08:00
|
|
|
ret = kexec_apply_relocations(image);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
out:
|
|
|
|
vfree(pi->sechdrs);
|
2016-09-02 07:14:44 +08:00
|
|
|
pi->sechdrs = NULL;
|
2018-04-14 06:36:28 +08:00
|
|
|
out_free_kbuf:
|
2015-09-10 06:38:51 +08:00
|
|
|
vfree(pi->purgatory_buf);
|
2016-09-02 07:14:44 +08:00
|
|
|
pi->purgatory_buf = NULL;
|
2015-09-10 06:38:51 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-04-14 06:36:21 +08:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
|
|
|
|
const char *name)
|
2015-09-10 06:38:51 +08:00
|
|
|
{
|
2018-04-14 06:36:21 +08:00
|
|
|
const Elf_Shdr *sechdrs;
|
2018-04-14 06:36:17 +08:00
|
|
|
const Elf_Ehdr *ehdr;
|
2018-04-14 06:36:21 +08:00
|
|
|
const Elf_Sym *syms;
|
2015-09-10 06:38:51 +08:00
|
|
|
const char *strtab;
|
2018-04-14 06:36:21 +08:00
|
|
|
int i, k;
|
2015-09-10 06:38:51 +08:00
|
|
|
|
2018-04-14 06:36:21 +08:00
|
|
|
if (!pi->ehdr)
|
2015-09-10 06:38:51 +08:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ehdr = pi->ehdr;
|
2018-04-14 06:36:21 +08:00
|
|
|
sechdrs = (void *)ehdr + ehdr->e_shoff;
|
2015-09-10 06:38:51 +08:00
|
|
|
|
|
|
|
for (i = 0; i < ehdr->e_shnum; i++) {
|
|
|
|
if (sechdrs[i].sh_type != SHT_SYMTAB)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (sechdrs[i].sh_link >= ehdr->e_shnum)
|
|
|
|
/* Invalid strtab section number */
|
|
|
|
continue;
|
2018-04-14 06:36:21 +08:00
|
|
|
strtab = (void *)ehdr + sechdrs[sechdrs[i].sh_link].sh_offset;
|
|
|
|
syms = (void *)ehdr + sechdrs[i].sh_offset;
|
2015-09-10 06:38:51 +08:00
|
|
|
|
|
|
|
/* 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];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *kexec_purgatory_get_symbol_addr(struct kimage *image, const char *name)
|
|
|
|
{
|
|
|
|
struct purgatory_info *pi = &image->purgatory_info;
|
2018-04-14 06:36:21 +08:00
|
|
|
const Elf_Sym *sym;
|
2015-09-10 06:38:51 +08:00
|
|
|
Elf_Shdr *sechdr;
|
|
|
|
|
|
|
|
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, const char *name,
|
|
|
|
void *buf, unsigned int size, bool get_value)
|
|
|
|
{
|
|
|
|
struct purgatory_info *pi = &image->purgatory_info;
|
2018-04-14 06:36:21 +08:00
|
|
|
const Elf_Sym *sym;
|
|
|
|
Elf_Shdr *sec;
|
2015-09-10 06:38:51 +08:00
|
|
|
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, (unsigned long)sym->st_size, size);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-04-14 06:36:21 +08:00
|
|
|
sec = pi->sechdrs + sym->st_shndx;
|
2015-09-10 06:38:51 +08:00
|
|
|
|
2018-04-14 06:36:21 +08:00
|
|
|
if (sec->sh_type == SHT_NOBITS) {
|
2015-09-10 06:38:51 +08:00
|
|
|
pr_err("symbol %s is in a bss section. Cannot %s\n", name,
|
|
|
|
get_value ? "get" : "set");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-04-14 06:36:39 +08:00
|
|
|
sym_buf = (char *)pi->purgatory_buf + sec->sh_offset + sym->st_value;
|
2015-09-10 06:38:51 +08:00
|
|
|
|
|
|
|
if (get_value)
|
|
|
|
memcpy((void *)buf, sym_buf, size);
|
|
|
|
else
|
|
|
|
memcpy((void *)sym_buf, buf, size);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
kexec_file: make use of purgatory optional
Patch series "kexec_file, x86, powerpc: refactoring for other
architecutres", v2.
This is a preparatory patchset for adding kexec_file support on arm64.
It was originally included in a arm64 patch set[1], but Philipp is also
working on their kexec_file support on s390[2] and some changes are now
conflicting.
So these common parts were extracted and put into a separate patch set
for better integration. What's more, my original patch#4 was split into
a few small chunks for easier review after Dave's comment.
As such, the resulting code is basically identical with my original, and
the only *visible* differences are:
- renaming of _kexec_kernel_image_probe() and _kimage_file_post_load_cleanup()
- change one of types of arguments at prepare_elf64_headers()
Those, unfortunately, require a couple of trivial changes on the rest
(#1, #6 to #13) of my arm64 kexec_file patch set[1].
Patch #1 allows making a use of purgatory optional, particularly useful
for arm64.
Patch #2 commonalizes arch_kexec_kernel_{image_probe, image_load,
verify_sig}() and arch_kimage_file_post_load_cleanup() across
architectures.
Patches #3-#7 are also intended to generalize parse_elf64_headers(),
along with exclude_mem_range(), to be made best re-use of.
[1] http://lists.infradead.org/pipermail/linux-arm-kernel/2018-February/561182.html
[2] http://lkml.iu.edu//hypermail/linux/kernel/1802.1/02596.html
This patch (of 7):
On arm64, crash dump kernel's usable memory is protected by *unmapping*
it from kernel virtual space unlike other architectures where the region
is just made read-only. It is highly unlikely that the region is
accidentally corrupted and this observation rationalizes that digest
check code can also be dropped from purgatory. The resulting code is so
simple as it doesn't require a bit ugly re-linking/relocation stuff,
i.e. arch_kexec_apply_relocations_add().
Please see:
http://lists.infradead.org/pipermail/linux-arm-kernel/2017-December/545428.html
All that the purgatory does is to shuffle arguments and jump into a new
kernel, while we still need to have some space for a hash value
(purgatory_sha256_digest) which is never checked against.
As such, it doesn't make sense to have trampline code between old kernel
and new kernel on arm64.
This patch introduces a new configuration, ARCH_HAS_KEXEC_PURGATORY, and
allows related code to be compiled in only if necessary.
[takahiro.akashi@linaro.org: fix trivial screwup]
Link: http://lkml.kernel.org/r/20180309093346.GF25863@linaro.org
Link: http://lkml.kernel.org/r/20180306102303.9063-2-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>
2018-04-14 06:35:45 +08:00
|
|
|
#endif /* CONFIG_ARCH_HAS_KEXEC_PURGATORY */
|
2018-04-14 06:36:06 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|