+ kexec_file-add-mechanism-to-update-kexec-segments.patch added to -mm tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The patch titled
     Subject: kexec_file: add mechanism to update kexec segments
has been added to the -mm tree.  Its filename is
     kexec_file-add-mechanism-to-update-kexec-segments.patch

This patch should soon appear at
    http://ozlabs.org/~akpm/mmots/broken-out/kexec_file-add-mechanism-to-update-kexec-segments.patch
and later at
    http://ozlabs.org/~akpm/mmotm/broken-out/kexec_file-add-mechanism-to-update-kexec-segments.patch

Before you just go and hit "reply", please:
   a) Consider who else should be cc'ed
   b) Prefer to cc a suitable mailing list as well
   c) Ideally: find the original patch on the mailing list and do a
      reply-to-all to that, adding suitable additional cc's

*** Remember to use Documentation/SubmitChecklist when testing your code ***

The -mm tree is included into linux-next and is updated
there every 3-4 working days

------------------------------------------------------
From: Thiago Jung Bauermann <bauerman@xxxxxxxxxxxxxxxxxx>
Subject: kexec_file: add mechanism to update kexec segments

kexec_update_segment allows a given segment in kexec_image to have its
contents updated.  This is useful if the current kernel wants to send
information to the next kernel that is up-to-date at the time of reboot.

Before modifying the segment the image checksum is verified, and after the
segment is updated the checksum is recalculated and updated in the kexec
image.

Link: http://lkml.kernel.org/r/1473900890-1476-5-git-send-email-bauerman@xxxxxxxxxxxxxxxxxx
Signed-off-by: Thiago Jung Bauermann <bauerman@xxxxxxxxxxxxxxxxxx>
Suggested-by: Mimi Zohar <zohar@xxxxxxxxxxxxxxxxxx>
Cc: Eric Biederman <ebiederm@xxxxxxxxxxxx>
Cc: Dave Young <dyoung@xxxxxxxxxx>
Cc: Vivek Goyal <vgoyal@xxxxxxxxxx>
Cc: Baoquan He <bhe@xxxxxxxxxx>
Cc: Michael Ellerman <mpe@xxxxxxxxxxxxxx>
Cc: Stewart Smith <stewart@xxxxxxxxxxxxxxxxxx>
Cc: Eric Richter <erichte@xxxxxxxxxxxxxxxxxx>
Cc: Balbir Singh <bsingharora@xxxxxxxxx>
Signed-off-by: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx>
---

 include/linux/kexec.h   |    2 
 kernel/kexec_core.c     |    5 
 kernel/kexec_file.c     |  331 ++++++++++++++++++++++++++++++++++++++
 kernel/kexec_internal.h |    6 
 4 files changed, 339 insertions(+), 5 deletions(-)

diff -puN include/linux/kexec.h~kexec_file-add-mechanism-to-update-kexec-segments include/linux/kexec.h
--- a/include/linux/kexec.h~kexec_file-add-mechanism-to-update-kexec-segments
+++ a/include/linux/kexec.h
@@ -183,6 +183,8 @@ int __weak arch_kexec_walk_mem(struct ke
 			       int (*func)(u64, u64, void *));
 extern int kexec_add_buffer(struct kexec_buf *kbuf);
 int kexec_locate_mem_hole(struct kexec_buf *kbuf);
+int kexec_update_segment(const char *buffer, size_t bufsz,
+			 unsigned long load_addr, size_t memsz);
 #endif /* CONFIG_KEXEC_FILE */
 
 struct kimage {
diff -puN kernel/kexec_core.c~kexec_file-add-mechanism-to-update-kexec-segments kernel/kexec_core.c
--- a/kernel/kexec_core.c~kexec_file-add-mechanism-to-update-kexec-segments
+++ a/kernel/kexec_core.c
@@ -551,11 +551,6 @@ void kimage_terminate(struct kimage *ima
 	*image->entry = IND_DONE;
 }
 
-#define for_each_kimage_entry(image, ptr, entry) \
-	for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); \
-		ptr = (entry & IND_INDIRECTION) ? \
-			boot_phys_to_virt((entry & PAGE_MASK)) : ptr + 1)
-
 static void kimage_free_entry(kimage_entry_t entry)
 {
 	struct page *page;
diff -puN kernel/kexec_file.c~kexec_file-add-mechanism-to-update-kexec-segments kernel/kexec_file.c
--- a/kernel/kexec_file.c~kexec_file-add-mechanism-to-update-kexec-segments
+++ a/kernel/kexec_file.c
@@ -18,6 +18,7 @@
 #include <linux/kexec.h>
 #include <linux/mutex.h>
 #include <linux/list.h>
+#include <linux/highmem.h>
 #include <linux/fs.h>
 #include <crypto/hash.h>
 #include <crypto/sha.h>
@@ -591,6 +592,336 @@ int kexec_add_buffer(struct kexec_buf *k
 	return 0;
 }
 
+/**
+ * @kexec_image_visit_segments() - call function on each segment page
+ * @image:	kexec image to inspect.
+ * @func:	Function to call on each page.
+ * @data:	Data pointer to pass to @func.
+ *
+ * Iterate through the @image entries, calling @func with the given @data
+ * on each segment page. dest is the start address of the page in the next
+ * kernel's address space, and addr is the address of the page in this kernel's
+ * address space.
+ *
+ * Stop iterating if @func returns non-zero, and return that value.
+ *
+ * Return: zero if all pages were visited, @func return value if non-zero.
+ */
+static int kexec_image_visit_segments(struct kimage *image,
+				      int (*func)(void *data,
+						  unsigned long dest,
+						  void *addr),
+				      void *data)
+{
+	int ret;
+	unsigned long entry, dest = 0;
+	unsigned long *ptr = NULL;
+
+	for_each_kimage_entry(image, ptr, entry) {
+		void *addr = (void *) (entry & PAGE_MASK);
+
+		switch (entry & IND_FLAGS) {
+		case IND_DESTINATION:
+			dest = (unsigned long) addr;
+			break;
+		case IND_SOURCE:
+			/* Shouldn't happen, but verify just to be safe. */
+			if (WARN_ON(!dest)) {
+				pr_err("Invalid kexec entries list.");
+				return -EINVAL;
+			}
+
+			ret = func(data, dest, addr);
+			if (ret)
+				break;
+
+			dest += PAGE_SIZE;
+		}
+
+		/* Shouldn't happen, but verify just to be safe. */
+		if (WARN_ON(ptr == NULL)) {
+			pr_err("Invalid kexec entries list.");
+			return -EINVAL;
+		}
+	}
+
+	return ret;
+}
+
+struct image_digest_data {
+	unsigned long digest_load_addr;
+	struct shash_desc *desc;
+};
+
+static int calculate_image_digest(void *data, unsigned long dest, void *addr)
+{
+	struct image_digest_data *d = (struct image_digest_data *) data;
+	void *page_addr;
+	unsigned long offset;
+	int ret;
+
+	/* Assumption: the digest segment is PAGE_SIZE long. */
+	if (dest == d->digest_load_addr)
+		return 0;
+
+	page_addr = kmap_atomic(kmap_to_page(addr));
+
+	offset = dest & ~PAGE_MASK;
+	ret = crypto_shash_update(d->desc, page_addr + offset,
+				  PAGE_SIZE - offset);
+
+	kunmap_atomic(page_addr);
+
+	return ret;
+}
+
+/**
+ * kexec_calculate_image_digest() - calculate the digest of the kexec image
+ * @image:	kexec image with segments already loaded into it.
+ * @digest:	Buffer of at least SHA256_DIGEST_SIZE bytes.
+ *
+ * This function goes through the @image->head list, calculates the checksum
+ * of the segment contents and puts the result in @digest, which is assumed
+ * to be big enough to hold an SHA256 digest.
+ *
+ * Return: 0 on success, negative errno on error.
+ */
+static int kexec_calculate_image_digest(struct kimage *image, void *digest)
+{
+	struct crypto_shash *tfm;
+	struct shash_desc *desc;
+	int ret;
+	size_t desc_size;
+	struct purgatory_info *pi = &image->purgatory_info;
+	struct image_digest_data d;
+
+	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;
+	}
+
+	desc->tfm   = tfm;
+	desc->flags = 0;
+
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto out_free_desc;
+
+	d.desc = desc;
+	d.digest_load_addr = pi->digest_load_addr;
+	ret = kexec_image_visit_segments(image, calculate_image_digest, &d);
+
+	if (!ret)
+		ret = crypto_shash_final(desc, digest);
+
+out_free_desc:
+	kfree(desc);
+out_free_tfm:
+	kfree(tfm);
+out:
+	return ret;
+}
+
+struct get_digest_data {
+	void *digest;
+	unsigned long digest_load_addr;
+	size_t bufsz;
+	size_t memsz;
+};
+
+static int get_digest(void *data, unsigned long dest, void *addr)
+{
+	struct get_digest_data *d = (struct get_digest_data *) data;
+	void *page_addr;
+	unsigned long offset;
+	size_t uchunk, mchunk;
+
+	if (dest != d->digest_load_addr)
+		return 0;
+
+	page_addr = kmap_atomic(kmap_to_page(addr));
+
+	offset = dest & ~PAGE_MASK;
+	mchunk = min_t(size_t, d->memsz, PAGE_SIZE - offset);
+	uchunk = min(d->bufsz, mchunk);
+	memcpy(d->digest, page_addr + offset, uchunk);
+
+	kunmap_atomic(page_addr);
+
+	d->digest += mchunk;
+	d->bufsz -= uchunk;
+	d->digest_load_addr += mchunk;
+	d->memsz -= mchunk;
+
+	return d->memsz > 0 ? 0 : 1;
+}
+
+static int kexec_image_get_digest(struct kimage *image, void *digest)
+{
+	int ret;
+	struct get_digest_data d;
+
+	d.digest = digest;
+	d.digest_load_addr = image->purgatory_info.digest_load_addr;
+	d.bufsz = SHA256_DIGEST_SIZE;
+	d.memsz = SHA256_DIGEST_SIZE;
+	ret = kexec_image_visit_segments(image, get_digest, &d);
+
+	return d.bufsz == 0 ? 0 : -ENOENT;
+}
+
+struct update_segment_data {
+	const char *buffer;
+	size_t bufsz;
+	size_t memsz;
+	unsigned long load_addr;
+};
+
+static int update_segment(void *data, unsigned long dest, void *addr)
+{
+	struct update_segment_data *d = (struct update_segment_data *) data;
+	void *page_addr;
+	unsigned long offset;
+	size_t uchunk, mchunk;
+
+	if (dest != d->load_addr)
+		return 0;
+
+	page_addr = kmap_atomic(kmap_to_page(addr));
+
+	offset = d->load_addr & ~PAGE_MASK;
+	mchunk = min_t(size_t, d->memsz, PAGE_SIZE - offset);
+	uchunk = min(d->bufsz, mchunk);
+	memcpy(page_addr + offset, d->buffer, uchunk);
+
+	kunmap_atomic(page_addr);
+
+	d->bufsz -= uchunk;
+	d->load_addr += mchunk;
+	d->buffer += mchunk;
+	d->memsz -= mchunk;
+
+	return d->memsz > 0 ? 0 : 1;
+}
+
+static int do_kexec_update_segment(const char *buffer, size_t bufsz,
+				   unsigned long load_addr, size_t memsz)
+{
+	int i;
+	struct update_segment_data d;
+
+	if (kexec_image == NULL) {
+		pr_err("Can't update segment: no kexec image loaded.\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * kexec_add_buffer rounds up segment sizes to PAGE_SIZE, so
+	 * we have to do it here as well.
+	 */
+	memsz = ALIGN(memsz, PAGE_SIZE);
+
+	for (i = 0; i < kexec_image->nr_segments; i++)
+		/* We only support updating whole segments. */
+		if (load_addr == kexec_image->segment[i].mem &&
+		    memsz == kexec_image->segment[i].memsz)
+			break;
+
+	if (WARN_ON(i == kexec_image->nr_segments)) {
+		pr_debug("Couldn't find segment to update: 0x%lx, size 0x%zx\n",
+			 load_addr, memsz);
+		return -EINVAL;
+	}
+
+	d.buffer = buffer;
+	d.bufsz = bufsz;
+	d.load_addr = load_addr;
+	d.memsz = memsz;
+	kexec_image_visit_segments(kexec_image, update_segment, &d);
+
+	return 0;
+}
+
+
+/**
+ * kexec_update_segment() - update the contents of a kimage segment
+ * @buffer:	New contents of the segment.
+ * @bufsz:	@buffer size.
+ * @load_addr:	Segment's physical address in the next kernel.
+ * @memsz:	Segment size.
+ *
+ * This function assumes kexec_mutex is held.
+ *
+ * Return: 0 on success, negative errno on error.
+ */
+int kexec_update_segment(const char *buffer, size_t bufsz,
+			 unsigned long load_addr, size_t memsz)
+{
+	int ret;
+	void *digest, *orig_digest;
+	struct purgatory_info *pi = &kexec_image->purgatory_info;
+
+	digest = kzalloc(SHA256_DIGEST_SIZE, GFP_KERNEL);
+	if (!digest)
+		return -ENOMEM;
+
+	orig_digest = kzalloc(SHA256_DIGEST_SIZE, GFP_KERNEL);
+	if (!orig_digest) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* First, verify the kexec image integrity. */
+	ret = kexec_image_get_digest(kexec_image, orig_digest);
+	if (ret) {
+		pr_debug("Can't get kexec image checksum.\n");
+		goto out;
+	}
+
+	ret = kexec_calculate_image_digest(kexec_image, digest);
+	if (ret) {
+		pr_debug("Can't calculate kexec image checksum.\n");
+		goto out;
+	}
+
+	ret = memcmp(digest, orig_digest, SHA256_DIGEST_SIZE);
+	if (ret) {
+		pr_debug("The kexec image was corrupted in memory.\n");
+		ret = -ENOEXEC;
+		goto out;
+	}
+
+	/* Now, update the segment we were asked to update. */
+	ret = do_kexec_update_segment(buffer, bufsz, load_addr, memsz);
+	if (ret)
+		goto out;
+
+	/* Calculate the new kexec image checksum. */
+	ret = kexec_calculate_image_digest(kexec_image, digest);
+	if (ret) {
+		pr_debug("Can't calculate kexec image checksum.\n");
+		goto out;
+	}
+
+	/* Update the segment containing the kexec image checksum. */
+	ret = do_kexec_update_segment(digest, SHA256_DIGEST_SIZE,
+				      pi->digest_load_addr, SHA256_DIGEST_SIZE);
+
+out:
+	kfree(digest);
+	kfree(orig_digest);
+
+	return ret;
+}
+
 /* Calculate and store the digest of segments */
 static int kexec_calculate_store_digests(struct kimage *image)
 {
diff -puN kernel/kexec_internal.h~kexec_file-add-mechanism-to-update-kexec-segments kernel/kexec_internal.h
--- a/kernel/kexec_internal.h~kexec_file-add-mechanism-to-update-kexec-segments
+++ a/kernel/kexec_internal.h
@@ -14,6 +14,12 @@ int kimage_is_destination_range(struct k
 
 extern struct mutex kexec_mutex;
 
+#define for_each_kimage_entry(image, ptr, entry) \
+	for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); \
+		ptr = (entry & IND_INDIRECTION) ? \
+			boot_phys_to_virt((entry & PAGE_MASK)) : ptr + 1)
+
+
 #ifdef CONFIG_KEXEC_FILE
 struct kexec_sha_region {
 	unsigned long start;
_

Patches currently in -mm which might be from bauerman@xxxxxxxxxxxxxxxxxx are

kexec_file-allow-arch-specific-memory-walking-for-kexec_add_buffer.patch
kexec_file-change-kexec_add_buffer-to-take-kexec_buf-as-argument.patch
kexec_file-factor-out-kexec_locate_mem_hole-from-kexec_add_buffer.patch
powerpc-change-places-using-config_kexec-to-use-config_kexec_core-instead.patch
powerpc-factor-out-relocation-code-from-module_64c-to-elf_util_64c.patch
powerpc-generalize-elf64_apply_relocate_add.patch
powerpc-adapt-elf64_apply_relocate_add-for-kexec_file_load.patch
powerpc-add-functions-to-read-elf-files-of-any-endianness.patch
powerpc-implement-kexec_file_load.patch
powerpc-add-code-to-work-with-device-trees-in-kexec_file_load.patch
powerpc-add-support-for-loading-elf-kernels-with-kexec_file_load.patch
powerpc-add-purgatory-for-kexec_file_load-implementation.patch
powerpc-add-purgatory-for-kexec_file_load-implementation-fix.patch
powerpc-enable-config_kexec_file-in-powerpc-server-defconfigs.patch
kexec_file-include-the-purgatory-segment-in-the-kexec-image-checksum.patch
kexec_file-add-buffer-hand-over-support-for-the-next-kernel.patch
powerpc-kexec_file-add-buffer-hand-over-support-for-the-next-kernel.patch
kexec_file-add-mechanism-to-update-kexec-segments.patch
ima-on-soft-reboot-save-the-measurement-list.patch

--
To unsubscribe from this list: send the line "unsubscribe mm-commits" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Kernel Archive]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]
  Powered by Linux