--- v7
+++ v15
@@ -1,469 +1,304 @@
-The functions ima_get_kexec_buffer() and ima_free_kexec_buffer(),
-that handle carrying forward the IMA measurement logs on kexec for
-powerpc do not have architecture specific code, but they are currently
-defined for powerpc only.
-
-Move ima_get_kexec_buffer() and ima_free_kexec_buffer() to IMA
-subsystem. A later patch in this series will use these functions for
-carrying forward the IMA measurement log for ARM64.
-
-With the above refactoring arch/powerpc/kexec/ima.c contains only
-functions used when CONFIG_IMA_KEXEC is enabled. Update Makefile
-in arch/powerpc/kexec to include arch/powerpc/kexec/ima.c only
-when CONFIG_IMA_KEXEC is enabled.
-
-Move ima_dump_measurement_list() and ima_add_kexec_buffer() to
-a new file namely ima_kexec_fdt.c in IMA. Update
-security/integrity/ima/Makefile to include ima_kexec_fdt.c only
-when CONFIG_IMA_KEXEC is enabled.
-
-Co-developed-by: Prakhar Srivastava <prsriva@linux.microsoft.com>
-Signed-off-by: Prakhar Srivastava <prsriva@linux.microsoft.com>
-Signed-off-by: Lakshmi Ramasubramanian <nramas@linux.microsoft.com>
+From: Rob Herring <robh@kernel.org>
+
+Both arm64 and powerpc do essentially the same FDT /chosen setup for
+kexec. The differences are either omissions that arm64 should have
+or additional properties that will be ignored. The setup code can be
+combined and shared by both powerpc and arm64.
+
+The differences relative to the arm64 version:
+ - If /chosen doesn't exist, it will be created (should never happen).
+ - Any old dtb and initrd reserved memory will be released.
+ - The new initrd and elfcorehdr are marked reserved.
+ - "linux,booted-from-kexec" is set.
+
+The differences relative to the powerpc version:
+ - "kaslr-seed" and "rng-seed" may be set.
+ - "linux,elfcorehdr" is set.
+ - Any existing "linux,usable-memory-range" is removed.
+
+Combine the code for setting up the /chosen node in the FDT and updating
+the memory reservation for kexec, for powerpc and arm64, in
+of_kexec_setup_new_fdt() and move it to "drivers/of/kexec.c".
+
+Signed-off-by: Rob Herring <robh@kernel.org>
+Reviewed-by: Thiago Jung Bauermann <bauerman@linux.ibm.com>
+Reviewed-by: Lakshmi Ramasubramanian <nramas@linux.microsoft.com>
---
- arch/powerpc/include/asm/ima.h | 3 -
- arch/powerpc/kexec/Makefile | 7 +-
- arch/powerpc/kexec/ima.c | 56 ---------
- security/integrity/ima/Makefile | 1 +
- security/integrity/ima/ima_kexec.c | 154 +++++++------------------
- security/integrity/ima/ima_kexec_fdt.c | 136 ++++++++++++++++++++++
- 6 files changed, 182 insertions(+), 175 deletions(-)
- create mode 100644 security/integrity/ima/ima_kexec_fdt.c
-
-diff --git a/arch/powerpc/include/asm/ima.h b/arch/powerpc/include/asm/ima.h
-index 6355a85a3289..8975f5e0cab8 100644
---- a/arch/powerpc/include/asm/ima.h
-+++ b/arch/powerpc/include/asm/ima.h
-@@ -6,9 +6,6 @@
+ drivers/of/Makefile | 1 +
+ drivers/of/kexec.c | 236 ++++++++++++++++++++++++++++++++++++++++++++
+ include/linux/of.h | 5 +
+ 3 files changed, 242 insertions(+)
+ create mode 100644 drivers/of/kexec.c
+
+diff --git a/drivers/of/Makefile b/drivers/of/Makefile
+index 6e1e5212f058..8ce11955afde 100644
+--- a/drivers/of/Makefile
++++ b/drivers/of/Makefile
+@@ -13,5 +13,6 @@ obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
+ obj-$(CONFIG_OF_RESOLVE) += resolver.o
+ obj-$(CONFIG_OF_OVERLAY) += overlay.o
+ obj-$(CONFIG_OF_NUMA) += of_numa.o
++obj-$(CONFIG_KEXEC_FILE) += kexec.o
- struct kimage;
+ obj-$(CONFIG_OF_UNITTEST) += unittest-data/
+diff --git a/drivers/of/kexec.c b/drivers/of/kexec.c
+new file mode 100644
+index 000000000000..4afd3cc1c04a
+--- /dev/null
++++ b/drivers/of/kexec.c
+@@ -0,0 +1,236 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2020 Arm Limited
++ *
++ * Based on arch/arm64/kernel/machine_kexec_file.c:
++ * Copyright (C) 2018 Linaro Limited
++ *
++ * And arch/powerpc/kexec/file_load.c:
++ * Copyright (C) 2016 IBM Corporation
++ */
++
++#include <linux/kernel.h>
++#include <linux/kexec.h>
++#include <linux/libfdt.h>
++#include <linux/of.h>
++#include <linux/of_fdt.h>
++#include <linux/random.h>
++#include <linux/types.h>
++
++/* relevant device tree properties */
++#define FDT_PROP_KEXEC_ELFHDR "linux,elfcorehdr"
++#define FDT_PROP_MEM_RANGE "linux,usable-memory-range"
++#define FDT_PROP_INITRD_START "linux,initrd-start"
++#define FDT_PROP_INITRD_END "linux,initrd-end"
++#define FDT_PROP_BOOTARGS "bootargs"
++#define FDT_PROP_KASLR_SEED "kaslr-seed"
++#define FDT_PROP_RNG_SEED "rng-seed"
++#define RNG_SEED_SIZE 128
++
++/**
++ * fdt_find_and_del_mem_rsv - delete memory reservation with given address and size
++ *
++ * @fdt: Flattened device tree for the current kernel.
++ * @start: Starting address of the reserved memory.
++ * @size: Size of the reserved memory.
++ *
++ * Return: 0 on success, or negative errno on error.
++ */
++static int fdt_find_and_del_mem_rsv(void *fdt, unsigned long start, unsigned long size)
++{
++ int i, ret, num_rsvs = fdt_num_mem_rsv(fdt);
++
++ for (i = 0; i < num_rsvs; i++) {
++ u64 rsv_start, rsv_size;
++
++ ret = fdt_get_mem_rsv(fdt, i, &rsv_start, &rsv_size);
++ if (ret) {
++ pr_err("Malformed device tree.\n");
++ return -EINVAL;
++ }
++
++ if (rsv_start == start && rsv_size == size) {
++ ret = fdt_del_mem_rsv(fdt, i);
++ if (ret) {
++ pr_err("Error deleting device tree reservation.\n");
++ return -EINVAL;
++ }
++
++ return 0;
++ }
++ }
++
++ return -ENOENT;
++}
++
++/*
++ * of_kexec_setup_new_fdt - modify /chosen and memory reservation for the next kernel
++ *
++ * @image: kexec image being loaded.
++ * @fdt: Flattened device tree for the next kernel.
++ * @initrd_load_addr: Address where the next initrd will be loaded.
++ * @initrd_len: Size of the next initrd, or 0 if there will be none.
++ * @cmdline: Command line for the next kernel, or NULL if there will
++ * be none.
++ *
++ * Return: 0 on success, or negative errno on error.
++ */
++int of_kexec_setup_new_fdt(const struct kimage *image, void *fdt,
++ unsigned long initrd_load_addr, unsigned long initrd_len,
++ const char *cmdline)
++{
++ int ret, chosen_node;
++ const void *prop;
++
++ /* Remove memory reservation for the current device tree. */
++ ret = fdt_find_and_del_mem_rsv(fdt, __pa(initial_boot_params),
++ fdt_totalsize(initial_boot_params));
++ if (ret == -EINVAL)
++ return ret;
++
++ chosen_node = fdt_path_offset(fdt, "/chosen");
++ if (chosen_node == -FDT_ERR_NOTFOUND)
++ chosen_node = fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"),
++ "chosen");
++ if (chosen_node < 0) {
++ ret = chosen_node;
++ goto out;
++ }
++
++ ret = fdt_delprop(fdt, chosen_node, FDT_PROP_KEXEC_ELFHDR);
++ if (ret && ret != -FDT_ERR_NOTFOUND)
++ goto out;
++ ret = fdt_delprop(fdt, chosen_node, FDT_PROP_MEM_RANGE);
++ if (ret && ret != -FDT_ERR_NOTFOUND)
++ goto out;
++
++ /* Did we boot using an initrd? */
++ prop = fdt_getprop(fdt, chosen_node, "linux,initrd-start", NULL);
++ if (prop) {
++ u64 tmp_start, tmp_end, tmp_size;
++
++ tmp_start = fdt64_to_cpu(*((const fdt64_t *) prop));
++
++ prop = fdt_getprop(fdt, chosen_node, "linux,initrd-end", NULL);
++ if (!prop)
++ return -EINVAL;
++
++ tmp_end = fdt64_to_cpu(*((const fdt64_t *) prop));
++
++ /*
++ * kexec reserves exact initrd size, while firmware may
++ * reserve a multiple of PAGE_SIZE, so check for both.
++ */
++ tmp_size = tmp_end - tmp_start;
++ ret = fdt_find_and_del_mem_rsv(fdt, tmp_start, tmp_size);
++ if (ret == -ENOENT)
++ ret = fdt_find_and_del_mem_rsv(fdt, tmp_start,
++ round_up(tmp_size, PAGE_SIZE));
++ if (ret == -EINVAL)
++ return ret;
++ }
++
++ /* add initrd-* */
++ if (initrd_load_addr) {
++ ret = fdt_setprop_u64(fdt, chosen_node, FDT_PROP_INITRD_START,
++ initrd_load_addr);
++ if (ret)
++ goto out;
++
++ ret = fdt_setprop_u64(fdt, chosen_node, FDT_PROP_INITRD_END,
++ initrd_load_addr + initrd_len);
++ if (ret)
++ goto out;
++
++ ret = fdt_add_mem_rsv(fdt, initrd_load_addr, initrd_len);
++ if (ret)
++ goto out;
++
++ } else {
++ ret = fdt_delprop(fdt, chosen_node, FDT_PROP_INITRD_START);
++ if (ret && (ret != -FDT_ERR_NOTFOUND))
++ goto out;
++
++ ret = fdt_delprop(fdt, chosen_node, FDT_PROP_INITRD_END);
++ if (ret && (ret != -FDT_ERR_NOTFOUND))
++ goto out;
++ }
++
++ if (image->type == KEXEC_TYPE_CRASH) {
++ /* add linux,elfcorehdr */
++ ret = fdt_appendprop_addrrange(fdt, 0, chosen_node,
++ FDT_PROP_KEXEC_ELFHDR,
++ image->arch.elf_headers_mem,
++ image->arch.elf_headers_sz);
++ if (ret)
++ goto out;
++
++ /*
++ * Avoid elfcorehdr from being stomped on in kdump kernel by
++ * setting up memory reserve map.
++ */
++ ret = fdt_add_mem_rsv(fdt, image->arch.elf_headers_mem,
++ image->arch.elf_headers_sz);
++ if (ret)
++ goto out;
++
++ /* add linux,usable-memory-range */
++ ret = fdt_appendprop_addrrange(fdt, 0, chosen_node,
++ FDT_PROP_MEM_RANGE,
++ crashk_res.start,
++ crashk_res.end - crashk_res.start + 1);
++ if (ret)
++ goto out;
++ }
++
++ /* add bootargs */
++ if (cmdline) {
++ ret = fdt_setprop_string(fdt, chosen_node, FDT_PROP_BOOTARGS, cmdline);
++ if (ret)
++ goto out;
++ } else {
++ ret = fdt_delprop(fdt, chosen_node, FDT_PROP_BOOTARGS);
++ if (ret && (ret != -FDT_ERR_NOTFOUND))
++ goto out;
++ }
++
++ /* add kaslr-seed */
++ ret = fdt_delprop(fdt, chosen_node, FDT_PROP_KASLR_SEED);
++ if (ret == -FDT_ERR_NOTFOUND)
++ ret = 0;
++ else if (ret)
++ goto out;
++
++ if (rng_is_initialized()) {
++ u64 seed = get_random_u64();
++
++ ret = fdt_setprop_u64(fdt, chosen_node, FDT_PROP_KASLR_SEED, seed);
++ if (ret)
++ goto out;
++ } else {
++ pr_notice("RNG is not initialised: omitting \"%s\" property\n",
++ FDT_PROP_KASLR_SEED);
++ }
++
++ /* add rng-seed */
++ if (rng_is_initialized()) {
++ void *rng_seed;
++
++ ret = fdt_setprop_placeholder(fdt, chosen_node, FDT_PROP_RNG_SEED,
++ RNG_SEED_SIZE, &rng_seed);
++ if (ret)
++ goto out;
++ get_random_bytes(rng_seed, RNG_SEED_SIZE);
++ } else {
++ pr_notice("RNG is not initialised: omitting \"%s\" property\n",
++ FDT_PROP_RNG_SEED);
++ }
++
++ ret = fdt_setprop(fdt, chosen_node, "linux,booted-from-kexec", NULL, 0);
++
++out:
++ if (ret)
++ return (ret == -FDT_ERR_NOSPACE) ? -ENOMEM : -EINVAL;
++
++ return 0;
++}
+diff --git a/include/linux/of.h b/include/linux/of.h
+index 5d51891cbf1a..3375f5295875 100644
+--- a/include/linux/of.h
++++ b/include/linux/of.h
+@@ -558,6 +558,11 @@ int of_map_id(struct device_node *np, u32 id,
+ const char *map_name, const char *map_mask_name,
+ struct device_node **target, u32 *id_out);
--int ima_get_kexec_buffer(void **addr, size_t *size);
--int ima_free_kexec_buffer(void);
--
- #ifdef CONFIG_IMA_KEXEC
- int arch_ima_add_kexec_buffer(struct kimage *image, unsigned long load_addr,
- size_t size);
-diff --git a/arch/powerpc/kexec/Makefile b/arch/powerpc/kexec/Makefile
-index 4aff6846c772..f54a9dbff4c8 100644
---- a/arch/powerpc/kexec/Makefile
-+++ b/arch/powerpc/kexec/Makefile
-@@ -9,12 +9,7 @@ obj-$(CONFIG_PPC32) += relocate_32.o
++struct kimage;
++int of_kexec_setup_new_fdt(const struct kimage *image, void *fdt,
++ unsigned long initrd_load_addr, unsigned long initrd_len,
++ const char *cmdline);
++
+ #else /* CONFIG_OF */
- obj-$(CONFIG_KEXEC_FILE) += file_load.o ranges.o file_load_$(BITS).o elf_$(BITS).o
-
--ifdef CONFIG_HAVE_IMA_KEXEC
--ifdef CONFIG_IMA
--obj-y += ima.o
--endif
--endif
--
-+obj-$(CONFIG_IMA_KEXEC) += ima.o
-
- # Disable GCOV, KCOV & sanitizers in odd or sensitive code
- GCOV_PROFILE_core_$(BITS).o := n
-diff --git a/arch/powerpc/kexec/ima.c b/arch/powerpc/kexec/ima.c
-index 2b790230ea15..4fadd8d16891 100644
---- a/arch/powerpc/kexec/ima.c
-+++ b/arch/powerpc/kexec/ima.c
-@@ -30,61 +30,6 @@ static int get_addr_size_cells(int *addr_cells, int *size_cells)
- return 0;
- }
-
--/**
-- * ima_get_kexec_buffer - get IMA buffer from the previous kernel
-- * @addr: On successful return, set to point to the buffer contents.
-- * @size: On successful return, set to the buffer size.
-- *
-- * Return: 0 on success, negative errno on error.
-- */
--int ima_get_kexec_buffer(void **addr, size_t *size)
--{
-- int ret, len;
-- unsigned long tmp_addr;
-- size_t tmp_size;
-- const void *prop;
--
-- prop = of_get_property(of_chosen, "linux,ima-kexec-buffer", &len);
-- if (!prop)
-- return -ENOENT;
--
-- ret = do_get_kexec_buffer(prop, len, &tmp_addr, &tmp_size);
-- if (ret)
-- return ret;
--
-- *addr = __va(tmp_addr);
-- *size = tmp_size;
--
-- return 0;
--}
--
--/**
-- * ima_free_kexec_buffer - free memory used by the IMA buffer
-- */
--int ima_free_kexec_buffer(void)
--{
-- int ret;
-- unsigned long addr;
-- size_t size;
-- struct property *prop;
--
-- prop = of_find_property(of_chosen, "linux,ima-kexec-buffer", NULL);
-- if (!prop)
-- return -ENOENT;
--
-- ret = do_get_kexec_buffer(prop->value, prop->length, &addr, &size);
-- if (ret)
-- return ret;
--
-- ret = of_remove_property(of_chosen, prop);
-- if (ret)
-- return ret;
--
-- return memblock_free(addr, size);
--
--}
--
--#ifdef CONFIG_IMA_KEXEC
- /**
- * arch_ima_add_kexec_buffer - do arch-specific steps to add the IMA buffer
- *
-@@ -177,4 +122,3 @@ int setup_ima_buffer(const struct kimage *image, void *fdt, int chosen_node)
-
- return 0;
- }
--#endif /* CONFIG_IMA_KEXEC */
-diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
-index 67dabca670e2..0c440870a4e9 100644
---- a/security/integrity/ima/Makefile
-+++ b/security/integrity/ima/Makefile
-@@ -14,3 +14,4 @@ ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o
- ima-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
- ima-$(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) += ima_asymmetric_keys.o
- ima-$(CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS) += ima_queue_keys.o
-+ima-$(CONFIG_IMA_KEXEC) += ima_kexec_fdt.o
-diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
-index 121de3e04af2..987e464be889 100644
---- a/security/integrity/ima/ima_kexec.c
-+++ b/security/integrity/ima/ima_kexec.c
-@@ -7,131 +7,65 @@
- * Mimi Zohar <zohar@linux.vnet.ibm.com>
- */
-
--#include <linux/seq_file.h>
- #include <linux/vmalloc.h>
- #include <linux/kexec.h>
-+#include <linux/of.h>
-+#include <linux/memblock.h>
-+#include <linux/libfdt.h>
-+#include <linux/ima.h>
- #include "ima.h"
-
--#ifdef CONFIG_IMA_KEXEC
--static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
-- unsigned long segment_size)
-+/**
-+ * ima_get_kexec_buffer - get IMA buffer from the previous kernel
-+ * @addr: On successful return, set to point to the buffer contents.
-+ * @size: On successful return, set to the buffer size.
-+ *
-+ * Return: 0 on success, negative errno on error.
-+ */
-+static int ima_get_kexec_buffer(void **addr, size_t *size)
- {
-- struct ima_queue_entry *qe;
-- struct seq_file file;
-- struct ima_kexec_hdr khdr;
-- int ret = 0;
--
-- /* segment size can't change between kexec load and execute */
-- file.buf = vmalloc(segment_size);
-- if (!file.buf) {
-- ret = -ENOMEM;
-- goto out;
-- }
--
-- file.size = segment_size;
-- file.read_pos = 0;
-- file.count = sizeof(khdr); /* reserved space */
--
-- memset(&khdr, 0, sizeof(khdr));
-- khdr.version = 1;
-- list_for_each_entry_rcu(qe, &ima_measurements, later) {
-- if (file.count < file.size) {
-- khdr.count++;
-- ima_measurements_show(&file, qe);
-- } else {
-- ret = -EINVAL;
-- break;
-- }
-- }
--
-- if (ret < 0)
-- goto out;
--
-- /*
-- * fill in reserved space with some buffer details
-- * (eg. version, buffer size, number of measurements)
-- */
-- khdr.buffer_size = file.count;
-- if (ima_canonical_fmt) {
-- khdr.version = cpu_to_le16(khdr.version);
-- khdr.count = cpu_to_le64(khdr.count);
-- khdr.buffer_size = cpu_to_le64(khdr.buffer_size);
-- }
-- memcpy(file.buf, &khdr, sizeof(khdr));
--
-- print_hex_dump(KERN_DEBUG, "ima dump: ", DUMP_PREFIX_NONE,
-- 16, 1, file.buf,
-- file.count < 100 ? file.count : 100, true);
--
-- *buffer_size = file.count;
-- *buffer = file.buf;
--out:
-- if (ret == -EINVAL)
-- vfree(file.buf);
-- return ret;
-+ int ret, len;
-+ unsigned long tmp_addr;
-+ size_t tmp_size;
-+ const void *prop;
-+
-+ prop = of_get_property(of_chosen, FDT_PROP_IMA_KEXEC_BUFFER, &len);
-+ if (!prop)
-+ return -ENOENT;
-+
-+ ret = do_get_kexec_buffer(prop, len, &tmp_addr, &tmp_size);
-+ if (ret)
-+ return ret;
-+
-+ *addr = __va(tmp_addr);
-+ *size = tmp_size;
-+ return 0;
- }
-
--/*
-- * Called during kexec_file_load so that IMA can add a segment to the kexec
-- * image for the measurement list for the next kernel.
-- *
-- * This function assumes that kexec_mutex is held.
-+/**
-+ * ima_free_kexec_buffer - free memory used by the IMA buffer
- */
--void ima_add_kexec_buffer(struct kimage *image)
-+static int ima_free_kexec_buffer(void)
- {
-- struct kexec_buf kbuf = { .image = image, .buf_align = PAGE_SIZE,
-- .buf_min = 0, .buf_max = ULONG_MAX,
-- .top_down = true };
-- unsigned long binary_runtime_size;
--
-- /* use more understandable variable names than defined in kbuf */
-- void *kexec_buffer = NULL;
-- size_t kexec_buffer_size;
-- size_t kexec_segment_size;
- int ret;
-+ unsigned long addr;
-+ size_t size;
-+ struct property *prop;
-
-- /*
-- * Reserve an extra half page of memory for additional measurements
-- * added during the kexec load.
-- */
-- binary_runtime_size = ima_get_binary_runtime_size();
-- if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE)
-- kexec_segment_size = ULONG_MAX;
-- else
-- kexec_segment_size = ALIGN(ima_get_binary_runtime_size() +
-- PAGE_SIZE / 2, PAGE_SIZE);
-- if ((kexec_segment_size == ULONG_MAX) ||
-- ((kexec_segment_size >> PAGE_SHIFT) > totalram_pages() / 2)) {
-- pr_err("Binary measurement list too large.\n");
-- return;
-- }
-+ prop = of_find_property(of_chosen, FDT_PROP_IMA_KEXEC_BUFFER, NULL);
-+ if (!prop)
-+ return -ENOENT;
-
-- ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer,
-- kexec_segment_size);
-- if (!kexec_buffer) {
-- pr_err("Not enough memory for the kexec measurement buffer.\n");
-- return;
-- }
-+ ret = do_get_kexec_buffer(prop->value, prop->length, &addr, &size);
-+ if (ret)
-+ return ret;
-
-- kbuf.buffer = kexec_buffer;
-- kbuf.bufsz = kexec_buffer_size;
-- kbuf.memsz = kexec_segment_size;
-- ret = kexec_add_buffer(&kbuf);
-- if (ret) {
-- pr_err("Error passing over kexec measurement buffer.\n");
-- return;
-- }
--
-- ret = arch_ima_add_kexec_buffer(image, kbuf.mem, kexec_segment_size);
-- if (ret) {
-- pr_err("Error passing over kexec measurement buffer.\n");
-- return;
-- }
-+ ret = of_remove_property(of_chosen, prop);
-+ if (ret)
-+ return ret;
-
-- pr_debug("kexec measurement buffer for the loaded kernel at 0x%lx.\n",
-- kbuf.mem);
-+ return memblock_free(addr, size);
- }
--#endif /* IMA_KEXEC */
-
- /*
- * Restore the measurement list from the previous kernel.
-diff --git a/security/integrity/ima/ima_kexec_fdt.c b/security/integrity/ima/ima_kexec_fdt.c
-new file mode 100644
-index 000000000000..186051467e5b
---- /dev/null
-+++ b/security/integrity/ima/ima_kexec_fdt.c
-@@ -0,0 +1,136 @@
-+// SPDX-License-Identifier: GPL-2.0-or-later
-+/*
-+ * Copyright (C) 2016 IBM Corporation
-+ *
-+ * Authors:
-+ * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
-+ * Mimi Zohar <zohar@linux.vnet.ibm.com>
-+ */
-+
-+#include <linux/seq_file.h>
-+#include <linux/vmalloc.h>
-+#include <linux/kexec.h>
-+#include <linux/of.h>
-+#include <linux/memblock.h>
-+#include <linux/libfdt.h>
-+#include <linux/ima.h>
-+#include "ima.h"
-+
-+static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
-+ unsigned long segment_size)
-+{
-+ struct ima_queue_entry *qe;
-+ struct seq_file file;
-+ struct ima_kexec_hdr khdr;
-+ int ret = 0;
-+
-+ /* segment size can't change between kexec load and execute */
-+ file.buf = vmalloc(segment_size);
-+ if (!file.buf) {
-+ ret = -ENOMEM;
-+ goto out;
-+ }
-+
-+ file.size = segment_size;
-+ file.read_pos = 0;
-+ file.count = sizeof(khdr); /* reserved space */
-+
-+ memset(&khdr, 0, sizeof(khdr));
-+ khdr.version = 1;
-+ list_for_each_entry_rcu(qe, &ima_measurements, later) {
-+ if (file.count < file.size) {
-+ khdr.count++;
-+ ima_measurements_show(&file, qe);
-+ } else {
-+ ret = -EINVAL;
-+ break;
-+ }
-+ }
-+
-+ if (ret < 0)
-+ goto out;
-+
-+ /*
-+ * fill in reserved space with some buffer details
-+ * (eg. version, buffer size, number of measurements)
-+ */
-+ khdr.buffer_size = file.count;
-+ if (ima_canonical_fmt) {
-+ khdr.version = cpu_to_le16(khdr.version);
-+ khdr.count = cpu_to_le64(khdr.count);
-+ khdr.buffer_size = cpu_to_le64(khdr.buffer_size);
-+ }
-+ memcpy(file.buf, &khdr, sizeof(khdr));
-+
-+ print_hex_dump(KERN_DEBUG, "ima dump: ", DUMP_PREFIX_NONE,
-+ 16, 1, file.buf,
-+ file.count < 100 ? file.count : 100, true);
-+
-+ *buffer_size = file.count;
-+ *buffer = file.buf;
-+out:
-+ if (ret == -EINVAL)
-+ vfree(file.buf);
-+ return ret;
-+}
-+
-+/*
-+ * Called during kexec_file_load so that IMA can add a segment to the kexec
-+ * image for the measurement list for the next kernel.
-+ *
-+ * This function assumes that kexec_mutex is held.
-+ */
-+void ima_add_kexec_buffer(struct kimage *image)
-+{
-+ struct kexec_buf kbuf = { .image = image, .buf_align = PAGE_SIZE,
-+ .buf_min = 0, .buf_max = ULONG_MAX,
-+ .top_down = true };
-+ unsigned long binary_runtime_size;
-+
-+ /* use more understandable variable names than defined in kbuf */
-+ void *kexec_buffer = NULL;
-+ size_t kexec_buffer_size;
-+ size_t kexec_segment_size;
-+ int ret;
-+
-+ /*
-+ * Reserve an extra half page of memory for additional measurements
-+ * added during the kexec load.
-+ */
-+ binary_runtime_size = ima_get_binary_runtime_size();
-+ if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE)
-+ kexec_segment_size = ULONG_MAX;
-+ else
-+ kexec_segment_size = ALIGN(ima_get_binary_runtime_size() +
-+ PAGE_SIZE / 2, PAGE_SIZE);
-+ if ((kexec_segment_size == ULONG_MAX) ||
-+ ((kexec_segment_size >> PAGE_SHIFT) > totalram_pages() / 2)) {
-+ pr_err("Binary measurement list too large.\n");
-+ return;
-+ }
-+
-+ ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer,
-+ kexec_segment_size);
-+ if (!kexec_buffer) {
-+ pr_err("Not enough memory for the kexec measurement buffer.\n");
-+ return;
-+ }
-+
-+ kbuf.buffer = kexec_buffer;
-+ kbuf.bufsz = kexec_buffer_size;
-+ kbuf.memsz = kexec_segment_size;
-+ ret = kexec_add_buffer(&kbuf);
-+ if (ret) {
-+ pr_err("Error passing over kexec measurement buffer.\n");
-+ return;
-+ }
-+
-+ ret = arch_ima_add_kexec_buffer(image, kbuf.mem, kexec_segment_size);
-+ if (ret) {
-+ pr_err("Error passing over kexec measurement buffer.\n");
-+ return;
-+ }
-+
-+ pr_debug("kexec measurement buffer for the loaded kernel at 0x%lx.\n",
-+ kbuf.mem);
-+}
+ static inline void of_core_init(void)
--
-2.28.0
-
+2.30.0
+