diff mbox series

[v2] Carry forward IMA measurement log on kexec on x86_64

Message ID YmgjXZphkmDKgaOA@noodles-fedora-PC23Y6EG (mailing list archive)
State New, archived
Headers show
Series [v2] Carry forward IMA measurement log on kexec on x86_64 | expand

Commit Message

Jonathan McDowell April 26, 2022, 4:52 p.m. UTC
On kexec file load Integrity Measurement Architecture (IMA) subsystem
may verify the IMA signature of the kernel and initramfs, and measure
it. The command line parameters passed to the kernel in the kexec call
may also be measured by IMA. A remote attestation service can verify
a TPM quote based on the TPM event log, the IMA measurement list, and
the TPM PCR data. This can be achieved only if the IMA measurement log
is carried over from the current kernel to the next kernel across
the kexec call.

powerpc and ARM64 both achieve this using device tree with a
"linux,ima-kexec-buffer" node. x86 platforms generally don't make use of
device tree, so the IMA infrastructure is extended to allow non device
tree platforms to provide a log buffer. x86 then passes the IMA buffer
to the new kernel via the setup_data mechanism.

Signed-off-by: Jonathan McDowell <noodles@fb.com>
---
v2:
 - Fix operation with EFI systems
---
 arch/x86/Kconfig                      |  1 +
 arch/x86/include/uapi/asm/bootparam.h |  9 ++++
 arch/x86/kernel/e820.c                |  6 +--
 arch/x86/kernel/kexec-bzimage64.c     | 39 +++++++++++++++++-
 arch/x86/kernel/setup.c               | 26 ++++++++++++
 include/linux/ima.h                   |  1 +
 security/integrity/ima/ima_kexec.c    | 59 ++++++++++++++++++++++++++-
 7 files changed, 136 insertions(+), 5 deletions(-)

Comments

Mimi Zohar April 29, 2022, 9:30 p.m. UTC | #1
> diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
> index 13753136f03f..419c50cfe6b9 100644
> --- a/security/integrity/ima/ima_kexec.c
> +++ b/security/integrity/ima/ima_kexec.c
> @@ -10,6 +10,7 @@
>  #include <linux/seq_file.h>
>  #include <linux/vmalloc.h>
>  #include <linux/kexec.h>
> +#include <linux/memblock.h>
>  #include <linux/of.h>
>  #include <linux/ima.h>
>  #include "ima.h"
> @@ -134,10 +135,66 @@ void ima_add_kexec_buffer(struct kimage *image)
>  }
>  #endif /* IMA_KEXEC */
>  
> +#ifndef CONFIG_OF
> +static phys_addr_t ima_early_kexec_buffer_phys;
> +static size_t ima_early_kexec_buffer_size;
> +
> +void __init ima_set_kexec_buffer(phys_addr_t phys_addr, size_t size)
> +{
> +	if (size == 0)
> +		return;
> +
> +	ima_early_kexec_buffer_phys = phys_addr;
> +	ima_early_kexec_buffer_size = size;
> +}
> +
> +int __init ima_free_kexec_buffer(void)
> +{
> +	int rc;
> +
> +	if (!IS_ENABLED(CONFIG_HAVE_IMA_KEXEC))
> +		return -ENOTSUPP;
> +
> +	if (ima_early_kexec_buffer_size == 0)
> +		return -ENOENT;
> +
> +	rc = memblock_phys_free(ima_early_kexec_buffer_phys,
> +				ima_early_kexec_buffer_size);
> +	if (rc)
> +		return rc;
> +
> +	ima_early_kexec_buffer_phys = 0;
> +	ima_early_kexec_buffer_size = 0;
> +
> +	return 0;
> +}
> +
> +int __init ima_get_kexec_buffer(void **addr, size_t *size)
> +{
> +	if (!IS_ENABLED(CONFIG_HAVE_IMA_KEXEC))
> +		return -ENOTSUPP;
> +
> +	if (ima_early_kexec_buffer_size == 0)
> +		return -ENOENT;
> +
> +	*addr = __va(ima_early_kexec_buffer_phys);
> +	*size = ima_early_kexec_buffer_size;
> +
> +	return 0;
> +}
> +

Originally both ima_get_kexec_buffer() and ima_free_kexec_buffer() were
architecture specific.  Refer to commit 467d27824920 ("powerpc: ima:
get the kexec buffer passed by the previous kernel").  Is there any
need for defining them here behind an "#ifndef CONFIG_OF"?

> +#else
> +
> +void __init ima_set_kexec_buffer(phys_addr_t phys_addr, size_t size)
> +{
> +	pr_warn("CONFIG_OF enabled, ignoring call to set buffer details.\n");
> +}
> +#endif /* CONFIG_OF */
> +

Only when "HAVE_IMA_KEXEC" is defined is this file included.  Why is
this warning needed?

thanks,

Mimi

>  /*
>   * Restore the measurement list from the previous kernel.
>   */
> -void ima_load_kexec_buffer(void)
> +void __init ima_load_kexec_buffer(void)
>  {
>  	void *kexec_buffer = NULL;
>  	size_t kexec_buffer_size = 0;
Jonathan McDowell May 3, 2022, 12:02 p.m. UTC | #2
On Fri, Apr 29, 2022 at 05:30:10PM -0400, Mimi Zohar wrote:
> > diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
> > index 13753136f03f..419c50cfe6b9 100644
> > --- a/security/integrity/ima/ima_kexec.c
> > +++ b/security/integrity/ima/ima_kexec.c
> > @@ -10,6 +10,7 @@
> >  #include <linux/seq_file.h>
> >  #include <linux/vmalloc.h>
> >  #include <linux/kexec.h>
> > +#include <linux/memblock.h>
> >  #include <linux/of.h>
> >  #include <linux/ima.h>
> >  #include "ima.h"
> > @@ -134,10 +135,66 @@ void ima_add_kexec_buffer(struct kimage *image)
> >  }
> >  #endif /* IMA_KEXEC */
> >  
> > +#ifndef CONFIG_OF
> > +static phys_addr_t ima_early_kexec_buffer_phys;
> > +static size_t ima_early_kexec_buffer_size;
> > +
> > +void __init ima_set_kexec_buffer(phys_addr_t phys_addr, size_t size)
> > +{
> > +	if (size == 0)
> > +		return;
> > +
> > +	ima_early_kexec_buffer_phys = phys_addr;
> > +	ima_early_kexec_buffer_size = size;
> > +}
> > +
> > +int __init ima_free_kexec_buffer(void)
> > +{
> > +	int rc;
> > +
> > +	if (!IS_ENABLED(CONFIG_HAVE_IMA_KEXEC))
> > +		return -ENOTSUPP;
> > +
> > +	if (ima_early_kexec_buffer_size == 0)
> > +		return -ENOENT;
> > +
> > +	rc = memblock_phys_free(ima_early_kexec_buffer_phys,
> > +				ima_early_kexec_buffer_size);
> > +	if (rc)
> > +		return rc;
> > +
> > +	ima_early_kexec_buffer_phys = 0;
> > +	ima_early_kexec_buffer_size = 0;
> > +
> > +	return 0;
> > +}
> > +
> > +int __init ima_get_kexec_buffer(void **addr, size_t *size)
> > +{
> > +	if (!IS_ENABLED(CONFIG_HAVE_IMA_KEXEC))
> > +		return -ENOTSUPP;
> > +
> > +	if (ima_early_kexec_buffer_size == 0)
> > +		return -ENOENT;
> > +
> > +	*addr = __va(ima_early_kexec_buffer_phys);
> > +	*size = ima_early_kexec_buffer_size;
> > +
> > +	return 0;
> > +}
> > +
> 
> Originally both ima_get_kexec_buffer() and ima_free_kexec_buffer() were
> architecture specific.  Refer to commit 467d27824920 ("powerpc: ima:
> get the kexec buffer passed by the previous kernel").  Is there any
> need for defining them here behind an "#ifndef CONFIG_OF"?

Commit fee3ff99bc67 (powerpc: Move arch independent ima kexec functions
to drivers/of/kexec.c) moved those functions to drivers/of/kexec.c as a
more generic implementation so that ARM64 could use them too.

I think for platforms that use device tree that's the way to go, but the
functions to generically set + get the IMA buffer for non device tree
systems were useful enough to put in the IMA code rather than being x86
specific. If you disagree I can move them under arch/x86/ (assuming the
x86 folk agree using setup_data is the right way to go, I haven't seen
any of them comment on this approach yet).

> > +#else
> > +
> > +void __init ima_set_kexec_buffer(phys_addr_t phys_addr, size_t size)
> > +{
> > +	pr_warn("CONFIG_OF enabled, ignoring call to set buffer details.\n");
> > +}
> > +#endif /* CONFIG_OF */
> > +
> 
> Only when "HAVE_IMA_KEXEC" is defined is this file included.  Why is
> this warning needed?

x86 *can* have device tree enabled, but the only platform I'm aware that
did it was OLPC and I haven't seen any of the distros enable it. I put
this in so there's a warning if we have CONFIG_OF enabled on x86 and
tried to pass the IMA log via setup_data. Can remove (or fold into the
x86 code if we go that way).

> >  /*
> >   * Restore the measurement list from the previous kernel.
> >   */
> > -void ima_load_kexec_buffer(void)
> > +void __init ima_load_kexec_buffer(void)
> >  {
> >  	void *kexec_buffer = NULL;
> >  	size_t kexec_buffer_size = 0;

J.
Mimi Zohar May 4, 2022, 1:49 p.m. UTC | #3
On Tue, 2022-05-03 at 12:02 +0000, Jonathan McDowell wrote:
> On Fri, Apr 29, 2022 at 05:30:10PM -0400, Mimi Zohar wrote:
> > > diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
> > > index 13753136f03f..419c50cfe6b9 100644
> > > --- a/security/integrity/ima/ima_kexec.c
> > > +++ b/security/integrity/ima/ima_kexec.c
> > > @@ -10,6 +10,7 @@
> > >  #include <linux/seq_file.h>
> > >  #include <linux/vmalloc.h>
> > >  #include <linux/kexec.h>
> > > +#include <linux/memblock.h>
> > >  #include <linux/of.h>
> > >  #include <linux/ima.h>
> > >  #include "ima.h"
> > > @@ -134,10 +135,66 @@ void ima_add_kexec_buffer(struct kimage *image)
> > >  }
> > >  #endif /* IMA_KEXEC */
> > >  
> > > +#ifndef CONFIG_OF
> > > +static phys_addr_t ima_early_kexec_buffer_phys;
> > > +static size_t ima_early_kexec_buffer_size;
> > > +
> > > +void __init ima_set_kexec_buffer(phys_addr_t phys_addr, size_t size)
> > > +{
> > > +	if (size == 0)
> > > +		return;
> > > +
> > > +	ima_early_kexec_buffer_phys = phys_addr;
> > > +	ima_early_kexec_buffer_size = size;
> > > +}
> > > +
> > > +int __init ima_free_kexec_buffer(void)
> > > +{
> > > +	int rc;
> > > +
> > > +	if (!IS_ENABLED(CONFIG_HAVE_IMA_KEXEC))
> > > +		return -ENOTSUPP;
> > > +
> > > +	if (ima_early_kexec_buffer_size == 0)
> > > +		return -ENOENT;
> > > +
> > > +	rc = memblock_phys_free(ima_early_kexec_buffer_phys,
> > > +				ima_early_kexec_buffer_size);
> > > +	if (rc)
> > > +		return rc;
> > > +
> > > +	ima_early_kexec_buffer_phys = 0;
> > > +	ima_early_kexec_buffer_size = 0;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +int __init ima_get_kexec_buffer(void **addr, size_t *size)
> > > +{
> > > +	if (!IS_ENABLED(CONFIG_HAVE_IMA_KEXEC))
> > > +		return -ENOTSUPP;

The Kconfig conditionally compiles ima_kexec.c based on
CONFIG_HAVE_IMA_KEXEC.  This test should be removed from here and from
ima_get_kexec_buffer().

CONFIG_IMA_KEXEC controls whether or not to carry the measurement list
to the next kernel, not whether the measurement list should be
restored.  Notice that ima_load_kexec_buffer() is not within the ifdef
CONFIG_IMA_KEXEC.

> > > +
> > > +	if (ima_early_kexec_buffer_size == 0)
> > > +		return -ENOENT;

There should always be at least one measurement - the boot_aggregate.

> > > +
> > > +	*addr = __va(ima_early_kexec_buffer_phys);
> > > +	*size = ima_early_kexec_buffer_size;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > 
> > Originally both ima_get_kexec_buffer() and ima_free_kexec_buffer() were
> > architecture specific.  Refer to commit 467d27824920 ("powerpc: ima:
> > get the kexec buffer passed by the previous kernel").  Is there any
> > need for defining them here behind an "#ifndef CONFIG_OF"?
> 
> Commit fee3ff99bc67 (powerpc: Move arch independent ima kexec functions
> to drivers/of/kexec.c) moved those functions to drivers/of/kexec.c as a
> more generic implementation so that ARM64 could use them too.
> 
> I think for platforms that use device tree that's the way to go, but the
> functions to generically set + get the IMA buffer for non device tree
> systems were useful enough to put in the IMA code rather than being x86
> specific. If you disagree I can move them under arch/x86/ (assuming the
> x86 folk agree using setup_data is the right way to go, I haven't seen
> any of them comment on this approach yet).

So other architectures will need to define CONFIG_HAVE_IMA_KEXEC, a
function to call ima_set_kexec_buffer() to restore the measurement
list, and a function equivalent to ima_setup_state().

After removing the unnecessary tests mentioned above, consider whether
there is still any benefit to defining these functions.

> > > +#else
> > > +
> > > +void __init ima_set_kexec_buffer(phys_addr_t phys_addr, size_t size)
> > > +{
> > > +	pr_warn("CONFIG_OF enabled, ignoring call to set buffer details.\n");
> > > +}
> > > +#endif /* CONFIG_OF */
> > > +
> > 
> > Only when "HAVE_IMA_KEXEC" is defined is this file included.  Why is
> > this warning needed?
> 
> x86 *can* have device tree enabled, but the only platform I'm aware that
> did it was OLPC and I haven't seen any of the distros enable it. I put
> this in so there's a warning if we have CONFIG_OF enabled on x86 and
> tried to pass the IMA log via setup_data. Can remove (or fold into the
> x86 code if we go that way).

Thanks for the explanation.

> > >  /*
> > >   * Restore the measurement list from the previous kernel.
> > >   */
> > > -void ima_load_kexec_buffer(void)
> > > +void __init ima_load_kexec_buffer(void)
> > >  {
> > >  	void *kexec_buffer = NULL;
> > >  	size_t kexec_buffer_size = 0;
> 
> J.

thanks,

Mimi
Jonathan McDowell May 9, 2022, 10:40 a.m. UTC | #4
Can someone from the x86 side provide some feedback on whether using
setup_data to pass this data across the kexec boundary is appropriate,
and if not point me in a better direction?

On Tue, Apr 26, 2022 at 05:53:47PM +0100, Jonathan McDowell wrote:
> On kexec file load Integrity Measurement Architecture (IMA) subsystem
> may verify the IMA signature of the kernel and initramfs, and measure
> it. The command line parameters passed to the kernel in the kexec call
> may also be measured by IMA. A remote attestation service can verify
> a TPM quote based on the TPM event log, the IMA measurement list, and
> the TPM PCR data. This can be achieved only if the IMA measurement log
> is carried over from the current kernel to the next kernel across
> the kexec call.
> 
> powerpc and ARM64 both achieve this using device tree with a
> "linux,ima-kexec-buffer" node. x86 platforms generally don't make use of
> device tree, so the IMA infrastructure is extended to allow non device
> tree platforms to provide a log buffer. x86 then passes the IMA buffer
> to the new kernel via the setup_data mechanism.
> 
> Signed-off-by: Jonathan McDowell <noodles@fb.com>
> ---
> v2:
>  - Fix operation with EFI systems
> ---
>  arch/x86/Kconfig                      |  1 +
>  arch/x86/include/uapi/asm/bootparam.h |  9 ++++
>  arch/x86/kernel/e820.c                |  6 +--
>  arch/x86/kernel/kexec-bzimage64.c     | 39 +++++++++++++++++-
>  arch/x86/kernel/setup.c               | 26 ++++++++++++
>  include/linux/ima.h                   |  1 +
>  security/integrity/ima/ima_kexec.c    | 59 ++++++++++++++++++++++++++-
>  7 files changed, 136 insertions(+), 5 deletions(-)
> 
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index b0142e01002e..bde4959d9bdc 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -2017,6 +2017,7 @@ config KEXEC_FILE
>  	bool "kexec file based system call"
>  	select KEXEC_CORE
>  	select BUILD_BIN2C
> +	select HAVE_IMA_KEXEC if IMA
>  	depends on X86_64
>  	depends on CRYPTO=y
>  	depends on CRYPTO_SHA256=y
> diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h
> index b25d3f82c2f3..2f7b138a9388 100644
> --- a/arch/x86/include/uapi/asm/bootparam.h
> +++ b/arch/x86/include/uapi/asm/bootparam.h
> @@ -10,6 +10,7 @@
>  #define SETUP_EFI			4
>  #define SETUP_APPLE_PROPERTIES		5
>  #define SETUP_JAILHOUSE			6
> +#define SETUP_IMA			7
>  
>  #define SETUP_INDIRECT			(1<<31)
>  
> @@ -171,6 +172,14 @@ struct jailhouse_setup_data {
>  	} __attribute__((packed)) v2;
>  } __attribute__((packed));
>  
> +/*
> + * IMA buffer setup data information from the previous kernel during kexec
> + */
> +struct ima_setup_data {
> +	__u64 addr;
> +	__u64 size;
> +} __attribute__((packed));
> +
>  /* The so-called "zeropage" */
>  struct boot_params {
>  	struct screen_info screen_info;			/* 0x000 */
> diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c
> index f267205f2d5a..9dac24680ff8 100644
> --- a/arch/x86/kernel/e820.c
> +++ b/arch/x86/kernel/e820.c
> @@ -1017,10 +1017,10 @@ void __init e820__reserve_setup_data(void)
>  		e820__range_update(pa_data, sizeof(*data)+data->len, E820_TYPE_RAM, E820_TYPE_RESERVED_KERN);
>  
>  		/*
> -		 * SETUP_EFI is supplied by kexec and does not need to be
> -		 * reserved.
> +		 * SETUP_EFI and SETUP_IMA are supplied by kexec and do not need
> +		 * to be reserved.
>  		 */
> -		if (data->type != SETUP_EFI)
> +		if (data->type != SETUP_EFI && data->type != SETUP_IMA)
>  			e820__range_update_kexec(pa_data,
>  						 sizeof(*data) + data->len,
>  						 E820_TYPE_RAM, E820_TYPE_RESERVED_KERN);
> diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c
> index 170d0fd68b1f..cdc73e081585 100644
> --- a/arch/x86/kernel/kexec-bzimage64.c
> +++ b/arch/x86/kernel/kexec-bzimage64.c
> @@ -186,6 +186,32 @@ setup_efi_state(struct boot_params *params, unsigned long params_load_addr,
>  }
>  #endif /* CONFIG_EFI */
>  
> +#ifdef CONFIG_IMA_KEXEC
> +static void
> +setup_ima_state(const struct kimage *image, struct boot_params *params,
> +		unsigned long params_load_addr,
> +		unsigned int ima_setup_data_offset)
> +{
> +	struct setup_data *sd = (void *)params + ima_setup_data_offset;
> +	struct ima_setup_data *ima = (void *)sd + sizeof(struct setup_data);
> +	unsigned long setup_data_phys;
> +
> +	if (!image->ima_buffer_size)
> +		return;
> +
> +	sd->type = SETUP_IMA;
> +	sd->len = sizeof(*ima);
> +
> +	ima->addr = image->ima_buffer_addr;
> +	ima->size = image->ima_buffer_size;
> +
> +	/* Add setup data */
> +	setup_data_phys = params_load_addr + ima_setup_data_offset;
> +	sd->next = params->hdr.setup_data;
> +	params->hdr.setup_data = setup_data_phys;
> +}
> +#endif /* CONFIG_IMA_KEXEC */
> +
>  static int
>  setup_boot_parameters(struct kimage *image, struct boot_params *params,
>  		      unsigned long params_load_addr,
> @@ -247,6 +273,15 @@ setup_boot_parameters(struct kimage *image, struct boot_params *params,
>  	setup_efi_state(params, params_load_addr, efi_map_offset, efi_map_sz,
>  			efi_setup_data_offset);
>  #endif
> +
> +#ifdef CONFIG_IMA_KEXEC
> +	/* Setup IMA log buffer state */
> +	setup_ima_state(image, params, params_load_addr,
> +			efi_setup_data_offset +
> +			sizeof(struct setup_data) +
> +			sizeof(struct efi_setup_data));
> +#endif
> +
>  	/* Setup EDD info */
>  	memcpy(params->eddbuf, boot_params.eddbuf,
>  				EDDMAXNR * sizeof(struct edd_info));
> @@ -401,7 +436,9 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
>  	params_cmdline_sz = ALIGN(params_cmdline_sz, 16);
>  	kbuf.bufsz = params_cmdline_sz + ALIGN(efi_map_sz, 16) +
>  				sizeof(struct setup_data) +
> -				sizeof(struct efi_setup_data);
> +				sizeof(struct efi_setup_data) +
> +				sizeof(struct setup_data) +
> +				sizeof(struct ima_setup_data);
>  
>  	params = kzalloc(kbuf.bufsz, GFP_KERNEL);
>  	if (!params)
> diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
> index c95b9ac5a457..8b0e7725f918 100644
> --- a/arch/x86/kernel/setup.c
> +++ b/arch/x86/kernel/setup.c
> @@ -11,6 +11,7 @@
>  #include <linux/dma-map-ops.h>
>  #include <linux/dmi.h>
>  #include <linux/efi.h>
> +#include <linux/ima.h>
>  #include <linux/init_ohci1394_dma.h>
>  #include <linux/initrd.h>
>  #include <linux/iscsi_ibft.h>
> @@ -335,6 +336,28 @@ static void __init reserve_initrd(void)
>  }
>  #endif /* CONFIG_BLK_DEV_INITRD */
>  
> +#ifdef CONFIG_IMA_KEXEC
> +static void __init add_early_ima_buffer(u64 phys_addr)
> +{
> +	struct ima_setup_data *data;
> +
> +	data = early_memremap(phys_addr + sizeof(struct setup_data),
> +			      sizeof(*data));
> +	if (!data) {
> +		pr_warn("setup: failed to memremap ima_setup_data entry\n");
> +		return;
> +	}
> +	memblock_reserve(data->addr, data->size);
> +	ima_set_kexec_buffer(data->addr, data->size);
> +	early_memunmap(data, sizeof(*data));
> +}
> +#else
> +static void __init add_early_ima_buffer(u64 phys_addr)
> +{
> +	pr_warn("Passed IMA kexec data, but CONFIG_IMA_KEXEC not set. Ignoring.\n");
> +}
> +#endif
> +
>  static void __init parse_setup_data(void)
>  {
>  	struct setup_data *data;
> @@ -360,6 +383,9 @@ static void __init parse_setup_data(void)
>  		case SETUP_EFI:
>  			parse_efi_setup(pa_data, data_len);
>  			break;
> +		case SETUP_IMA:
> +			add_early_ima_buffer(pa_data);
> +			break;
>  		default:
>  			break;
>  		}
> diff --git a/include/linux/ima.h b/include/linux/ima.h
> index 426b1744215e..f58aed7acad4 100644
> --- a/include/linux/ima.h
> +++ b/include/linux/ima.h
> @@ -48,6 +48,7 @@ static inline void ima_appraise_parse_cmdline(void) {}
>  
>  #ifdef CONFIG_IMA_KEXEC
>  extern void ima_add_kexec_buffer(struct kimage *image);
> +extern void ima_set_kexec_buffer(phys_addr_t phys_addr, size_t size);
>  #endif
>  
>  #else
> diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
> index 13753136f03f..419c50cfe6b9 100644
> --- a/security/integrity/ima/ima_kexec.c
> +++ b/security/integrity/ima/ima_kexec.c
> @@ -10,6 +10,7 @@
>  #include <linux/seq_file.h>
>  #include <linux/vmalloc.h>
>  #include <linux/kexec.h>
> +#include <linux/memblock.h>
>  #include <linux/of.h>
>  #include <linux/ima.h>
>  #include "ima.h"
> @@ -134,10 +135,66 @@ void ima_add_kexec_buffer(struct kimage *image)
>  }
>  #endif /* IMA_KEXEC */
>  
> +#ifndef CONFIG_OF
> +static phys_addr_t ima_early_kexec_buffer_phys;
> +static size_t ima_early_kexec_buffer_size;
> +
> +void __init ima_set_kexec_buffer(phys_addr_t phys_addr, size_t size)
> +{
> +	if (size == 0)
> +		return;
> +
> +	ima_early_kexec_buffer_phys = phys_addr;
> +	ima_early_kexec_buffer_size = size;
> +}
> +
> +int __init ima_free_kexec_buffer(void)
> +{
> +	int rc;
> +
> +	if (!IS_ENABLED(CONFIG_HAVE_IMA_KEXEC))
> +		return -ENOTSUPP;
> +
> +	if (ima_early_kexec_buffer_size == 0)
> +		return -ENOENT;
> +
> +	rc = memblock_phys_free(ima_early_kexec_buffer_phys,
> +				ima_early_kexec_buffer_size);
> +	if (rc)
> +		return rc;
> +
> +	ima_early_kexec_buffer_phys = 0;
> +	ima_early_kexec_buffer_size = 0;
> +
> +	return 0;
> +}
> +
> +int __init ima_get_kexec_buffer(void **addr, size_t *size)
> +{
> +	if (!IS_ENABLED(CONFIG_HAVE_IMA_KEXEC))
> +		return -ENOTSUPP;
> +
> +	if (ima_early_kexec_buffer_size == 0)
> +		return -ENOENT;
> +
> +	*addr = __va(ima_early_kexec_buffer_phys);
> +	*size = ima_early_kexec_buffer_size;
> +
> +	return 0;
> +}
> +
> +#else
> +
> +void __init ima_set_kexec_buffer(phys_addr_t phys_addr, size_t size)
> +{
> +	pr_warn("CONFIG_OF enabled, ignoring call to set buffer details.\n");
> +}
> +#endif /* CONFIG_OF */
> +
>  /*
>   * Restore the measurement list from the previous kernel.
>   */
> -void ima_load_kexec_buffer(void)
> +void __init ima_load_kexec_buffer(void)
>  {
>  	void *kexec_buffer = NULL;
>  	size_t kexec_buffer_size = 0;
> -- 
> 2.34.1
>
Borislav Petkov May 9, 2022, 11:25 a.m. UTC | #5
On May 9, 2022 10:40:01 AM UTC, Jonathan McDowell <noodles@fb.com> wrote:
>> powerpc and ARM64 both achieve this using device tree with a
>> "linux,ima-kexec-buffer" node. x86 platforms generally don't make use of
>> device tree

What's wrong with making x86 use the same devicetree node(s)?
Jonathan McDowell May 9, 2022, 5:46 p.m. UTC | #6
On Mon, May 09, 2022 at 11:25:22AM +0000, Boris Petkov wrote:
> On May 9, 2022 10:40:01 AM UTC, Jonathan McDowell <noodles@fb.com> wrote:
> >> powerpc and ARM64 both achieve this using device tree with a
> >> "linux,ima-kexec-buffer" node. x86 platforms generally don't make use of
> >> device tree
> 
> What's wrong with making x86 use the same devicetree node(s)?

Device tree on x86 doesn't seem to be a thing; none of the distros I
regularly use enable CONFIG_OF for x86, I can only find 2 32-bit x86
platforms that actually select it and none of the plumbing for kexec on
x86 ties in device tree. I agree for platforms that make active use of
device tree that's the appropriate path, but it doesn't seem to be the
case for x86.

J.
Borislav Petkov May 9, 2022, 6:09 p.m. UTC | #7
On Mon, May 09, 2022 at 05:46:22PM +0000, Jonathan McDowell wrote:
> Device tree on x86 doesn't seem to be a thing;

Not a thing? What does that even mean?

We have arch/x86/kernel/devicetree.c which adds some minimal devicetree
support.

> none of the distros I regularly use enable CONFIG_OF for x86, I can
> only find 2 32-bit x86 platforms that actually select it and none of
> the plumbing for kexec on x86 ties in device tree.

And? That can get changed and enabled and so on.

> I agree for platforms that make active use of device tree that's the
> appropriate path, but it doesn't seem to be the case for x86.

I'm not sure what you're aim here is?

You want to pass that IMA measurement to the kexec kernel with minimal
changes, i.e., change only the kernel?

Why can't distros be also changed to use devicetree for the IMA
measurement, like the other arches do? Why does x86 need to do it
differently?

We also pass info to the kexec kernel by reading it from sysfs
and having kexec tools pass it to the kexec-ed kernel, see
Documentation/ABI/testing/sysfs-firmware-efi-runtime-map

kexec(8) itself can do:

kexec -l kernel-image --append=command-line-options
			^^^^^^^^^^^^^^^^^

and add those cmdline options which are dug out from the first kernel.

So is there any particular reason/pressing need to pass the measurement
with setup_data?
Jonathan McDowell May 9, 2022, 6:41 p.m. UTC | #8
On Mon, May 09, 2022 at 08:09:55PM +0200, Borislav Petkov wrote:
> On Mon, May 09, 2022 at 05:46:22PM +0000, Jonathan McDowell wrote:
> > Device tree on x86 doesn't seem to be a thing;
> 
> Not a thing? What does that even mean?

Let me rephrase more verbosely. Device tree on x86 seems to be a rarely
enabled config option that is only required on a couple of platforms and
lacks the same level of integration with the x86 boot process (compared
to PowerPC and ARM64) such that a) there's a lot more code that would
need to be written to even get to the point that it could be used in the
same manner for passing the IMA buffer as on other platforms and b) I'm
not sure whether or not it might introduce other issues due to this lack
of use/testing.

> We have arch/x86/kernel/devicetree.c which adds some minimal devicetree
> support.
> 
> > none of the distros I regularly use enable CONFIG_OF for x86, I can
> > only find 2 32-bit x86 platforms that actually select it and none of
> > the plumbing for kexec on x86 ties in device tree.
> 
> And? That can get changed and enabled and so on.
> 
> > I agree for platforms that make active use of device tree that's the
> > appropriate path, but it doesn't seem to be the case for x86.
> 
> I'm not sure what you're aim here is?
> 
> You want to pass that IMA measurement to the kexec kernel with minimal
> changes, i.e., change only the kernel?

I'd like to be able to take an internal kernel buffer and pass it to the
newly kexeced kernel, yes. I'd like that to be possible with the
kexec_file_load() path, which allows for things like kernel signing,
which I believe precludes providing the data from user space.

> Why can't distros be also changed to use devicetree for the IMA
> measurement, like the other arches do? Why does x86 need to do it
> differently?

> We also pass info to the kexec kernel by reading it from sysfs
> and having kexec tools pass it to the kexec-ed kernel, see
> Documentation/ABI/testing/sysfs-firmware-efi-runtime-map

AFAICT kexec passes EFI details using SETUP_EFI, which is what I
modelled the SETUP_IMA support on
(1fec0533693cd74f2d1a46edd29449cfee429df0). In the old syscall variant
the kexec tools do read /sys/firmware/efi/runtime-map and create a
setup_data entry of type SETUP_EFI, in the new file based kexec that is
done inside the kernel in a similar fashion to what I've done with
SETUP_IMA.

> So is there any particular reason/pressing need to pass the measurement
> with setup_data?

I'm not tied to setup_data but given the concerns I raise above with
device tree on x86 and the need to handle this in the kernel it seemed
like a reasonable first approach. You seem to be saying it's not and
either adding the device tree infrastructure or doing a command line
hack would be preferable?

J.
Borislav Petkov May 9, 2022, 7:40 p.m. UTC | #9
On Mon, May 09, 2022 at 06:41:17PM +0000, Jonathan McDowell wrote:
> I'm not tied to setup_data but given the concerns I raise above with
> device tree on x86 and the need to handle this in the kernel it seemed
> like a reasonable first approach. You seem to be saying it's not and
> either adding the device tree infrastructure or doing a command line
> hack would be preferable?

All I'm doing is asking more questions to make you give more details as
to why you wanna do it this way. I'll take a detailed look tomorrow but
it looks ok from a quick glance.

Thx.
Jonathan McDowell May 10, 2022, 8:02 a.m. UTC | #10
On Mon, May 09, 2022 at 09:40:28PM +0200, Borislav Petkov wrote:
> On Mon, May 09, 2022 at 06:41:17PM +0000, Jonathan McDowell wrote:
> > I'm not tied to setup_data but given the concerns I raise above with
> > device tree on x86 and the need to handle this in the kernel it seemed
> > like a reasonable first approach. You seem to be saying it's not and
> > either adding the device tree infrastructure or doing a command line
> > hack would be preferable?
> 
> All I'm doing is asking more questions to make you give more details as
> to why you wanna do it this way. I'll take a detailed look tomorrow but
> it looks ok from a quick glance.

That's reasonable, thanks for taking the time to do so. I realised
another problem with the command line approach is that this is a flow
involving attestation and potentially signing across the kexec boundary,
so if the command line changes every time due to the memory address we
pass the IMA buffer in then we have to recalculate the expected PCR etc
values for every kexec after we've done the user space buffer
allocation, rather than being able to do so once + offline in advance
for a particular kexec across multiple machines.

J.
Borislav Petkov May 10, 2022, 10:46 a.m. UTC | #11
On Tue, Apr 26, 2022 at 04:52:49PM +0000, Jonathan McDowell wrote:
> Subject: Re: [PATCH v2] Carry forward IMA measurement log on kexec on x86_64

The tip tree preferred format for patch subject prefixes is
'subsys/component:', e.g. 'x86/apic:', 'x86/mm/fault:', 'sched/fair:',
'genirq/core:'. Please do not use file names or complete file paths as
prefix. 'git log path/to/file' should give you a reasonable hint in most
cases.

The condensed patch description in the subject line should start with a
uppercase letter and should be written in imperative tone.

I guess in your case

"x86/kexec: Carry ..."

> diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h
> index b25d3f82c2f3..2f7b138a9388 100644
> --- a/arch/x86/include/uapi/asm/bootparam.h
> +++ b/arch/x86/include/uapi/asm/bootparam.h
> @@ -10,6 +10,7 @@
>  #define SETUP_EFI			4
>  #define SETUP_APPLE_PROPERTIES		5
>  #define SETUP_JAILHOUSE			6
> +#define SETUP_IMA			7

There's already

 #define SETUP_CC_BLOB                  7

in the tip tree - please redo your patch against current tip/master.

> diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c
> index 170d0fd68b1f..cdc73e081585 100644
> --- a/arch/x86/kernel/kexec-bzimage64.c
> +++ b/arch/x86/kernel/kexec-bzimage64.c
> @@ -186,6 +186,32 @@ setup_efi_state(struct boot_params *params, unsigned long params_load_addr,
>  }
>  #endif /* CONFIG_EFI */
>  
> +#ifdef CONFIG_IMA_KEXEC
> +static void
> +setup_ima_state(const struct kimage *image, struct boot_params *params,

You can push that ugly ifdeffery inside the function like so:

diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c
index cdc73e081585..47ba7083fd44 100644
--- a/arch/x86/kernel/kexec-bzimage64.c
+++ b/arch/x86/kernel/kexec-bzimage64.c
@@ -186,12 +186,12 @@ setup_efi_state(struct boot_params *params, unsigned long params_load_addr,
 }
 #endif /* CONFIG_EFI */
 
-#ifdef CONFIG_IMA_KEXEC
 static void
 setup_ima_state(const struct kimage *image, struct boot_params *params,
 		unsigned long params_load_addr,
 		unsigned int ima_setup_data_offset)
 {
+#ifdef CONFIG_IMA_KEXEC
 	struct setup_data *sd = (void *)params + ima_setup_data_offset;
 	struct ima_setup_data *ima = (void *)sd + sizeof(struct setup_data);
 	unsigned long setup_data_phys;
@@ -209,8 +209,8 @@ setup_ima_state(const struct kimage *image, struct boot_params *params,
 	setup_data_phys = params_load_addr + ima_setup_data_offset;
 	sd->next = params->hdr.setup_data;
 	params->hdr.setup_data = setup_data_phys;
-}
 #endif /* CONFIG_IMA_KEXEC */
+}
 
 static int
 setup_boot_parameters(struct kimage *image, struct boot_params *params,
@@ -274,13 +274,11 @@ setup_boot_parameters(struct kimage *image, struct boot_params *params,
 			efi_setup_data_offset);
 #endif
 
-#ifdef CONFIG_IMA_KEXEC
 	/* Setup IMA log buffer state */
 	setup_ima_state(image, params, params_load_addr,
 			efi_setup_data_offset +
 			sizeof(struct setup_data) +
 			sizeof(struct efi_setup_data));
-#endif
 
 	/* Setup EDD info */
 	memcpy(params->eddbuf, boot_params.eddbuf,

> +		unsigned long params_load_addr,
> +		unsigned int ima_setup_data_offset)
> +{
> +	struct setup_data *sd = (void *)params + ima_setup_data_offset;
> +	struct ima_setup_data *ima = (void *)sd + sizeof(struct setup_data);
> +	unsigned long setup_data_phys;

The tip-tree preferred ordering of variable declarations at the
beginning of a function is reverse fir tree order::

	struct long_struct_name *descriptive_name;
	unsigned long foo, bar;
	unsigned int tmp;
	int ret;

The above is faster to parse than the reverse ordering::

	int ret;
	unsigned int tmp;
	unsigned long foo, bar;
	struct long_struct_name *descriptive_name;

And even more so than random ordering::

	unsigned long foo, bar;
	int ret;
	struct long_struct_name *descriptive_name;
	unsigned int tmp;

> +
> +	if (!image->ima_buffer_size)
> +		return;
> +
> +	sd->type = SETUP_IMA;
> +	sd->len = sizeof(*ima);
> +
> +	ima->addr = image->ima_buffer_addr;
> +	ima->size = image->ima_buffer_size;
> +
> +	/* Add setup data */
> +	setup_data_phys = params_load_addr + ima_setup_data_offset;
> +	sd->next = params->hdr.setup_data;
> +	params->hdr.setup_data = setup_data_phys;
> +}
> +#endif /* CONFIG_IMA_KEXEC */
> +
>  static int
>  setup_boot_parameters(struct kimage *image, struct boot_params *params,
>  		      unsigned long params_load_addr,
> @@ -247,6 +273,15 @@ setup_boot_parameters(struct kimage *image, struct boot_params *params,
>  	setup_efi_state(params, params_load_addr, efi_map_offset, efi_map_sz,
>  			efi_setup_data_offset);
>  #endif
> +
> +#ifdef CONFIG_IMA_KEXEC
> +	/* Setup IMA log buffer state */
> +	setup_ima_state(image, params, params_load_addr,
> +			efi_setup_data_offset +
> +			sizeof(struct setup_data) +
> +			sizeof(struct efi_setup_data));
> +#endif
> +
>  	/* Setup EDD info */
>  	memcpy(params->eddbuf, boot_params.eddbuf,
>  				EDDMAXNR * sizeof(struct edd_info));
> @@ -401,7 +436,9 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
>  	params_cmdline_sz = ALIGN(params_cmdline_sz, 16);
>  	kbuf.bufsz = params_cmdline_sz + ALIGN(efi_map_sz, 16) +
>  				sizeof(struct setup_data) +
> -				sizeof(struct efi_setup_data);
> +				sizeof(struct efi_setup_data) +
> +				sizeof(struct setup_data) +
> +				sizeof(struct ima_setup_data);

Just because the EFI thing did it unconditionally, regardless of
CONFIG_EFI, you don't have to copy that sloppiness:

	unsigned long ima_buf_sz = 0;

	...

	if (IS_ENABLED(CONFIG_IMA_EXEC))
		ima_buf_sz = ...

	kbuf.bufsz = ... + ima_buf_sz);

> diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
> index c95b9ac5a457..8b0e7725f918 100644
> --- a/arch/x86/kernel/setup.c
> +++ b/arch/x86/kernel/setup.c
> @@ -11,6 +11,7 @@
>  #include <linux/dma-map-ops.h>
>  #include <linux/dmi.h>
>  #include <linux/efi.h>
> +#include <linux/ima.h>
>  #include <linux/init_ohci1394_dma.h>
>  #include <linux/initrd.h>
>  #include <linux/iscsi_ibft.h>
> @@ -335,6 +336,28 @@ static void __init reserve_initrd(void)
>  }
>  #endif /* CONFIG_BLK_DEV_INITRD */
>  
> +#ifdef CONFIG_IMA_KEXEC
> +static void __init add_early_ima_buffer(u64 phys_addr)
> +{
> +	struct ima_setup_data *data;
> +
> +	data = early_memremap(phys_addr + sizeof(struct setup_data),
> +			      sizeof(*data));
> +	if (!data) {
> +		pr_warn("setup: failed to memremap ima_setup_data entry\n");
> +		return;
> +	}
> +	memblock_reserve(data->addr, data->size);
> +	ima_set_kexec_buffer(data->addr, data->size);
> +	early_memunmap(data, sizeof(*data));
> +}
> +#else
> +static void __init add_early_ima_buffer(u64 phys_addr)
> +{
> +	pr_warn("Passed IMA kexec data, but CONFIG_IMA_KEXEC not set. Ignoring.\n");
> +}
> +#endif

ditto:

diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 9324c30755c5..32403d693bf3 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -336,9 +336,9 @@ static void __init reserve_initrd(void)
 }
 #endif /* CONFIG_BLK_DEV_INITRD */
 
-#ifdef CONFIG_IMA_KEXEC
 static void __init add_early_ima_buffer(u64 phys_addr)
 {
+#ifdef CONFIG_IMA_KEXEC
        struct ima_setup_data *data;
 
        data = early_memremap(phys_addr + sizeof(struct setup_data),
@@ -350,13 +350,10 @@ static void __init add_early_ima_buffer(u64 phys_addr)
        memblock_reserve(data->addr, data->size);
        ima_set_kexec_buffer(data->addr, data->size);
        early_memunmap(data, sizeof(*data));
-}
 #else
-static void __init add_early_ima_buffer(u64 phys_addr)
-{
        pr_warn("Passed IMA kexec data, but CONFIG_IMA_KEXEC not set. Ignoring.\n");
-}
 #endif
+}
 
 static void __init parse_setup_data(void)
 {

> diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
> index 13753136f03f..419c50cfe6b9 100644
> --- a/security/integrity/ima/ima_kexec.c
> +++ b/security/integrity/ima/ima_kexec.c
> @@ -10,6 +10,7 @@
>  #include <linux/seq_file.h>
>  #include <linux/vmalloc.h>
>  #include <linux/kexec.h>
> +#include <linux/memblock.h>
>  #include <linux/of.h>
>  #include <linux/ima.h>
>  #include "ima.h"
> @@ -134,10 +135,66 @@ void ima_add_kexec_buffer(struct kimage *image)
>  }
>  #endif /* IMA_KEXEC */
>  
> +#ifndef CONFIG_OF
> +static phys_addr_t ima_early_kexec_buffer_phys;
> +static size_t ima_early_kexec_buffer_size;
> +
> +void __init ima_set_kexec_buffer(phys_addr_t phys_addr, size_t size)
> +{
> +	if (size == 0)
> +		return;
> +
> +	ima_early_kexec_buffer_phys = phys_addr;
> +	ima_early_kexec_buffer_size = size;
> +}
> +
> +int __init ima_free_kexec_buffer(void)

WARNING: modpost: vmlinux.o(.text+0xe4e785): Section mismatch in reference from the function ima_free_kexec_buffer() to the function .meminit.text:memblock_phys_free()
The function ima_free_kexec_buffer() references
the function __meminit memblock_phys_free().
This is often because ima_free_kexec_buffer lacks a __meminit 
annotation or the annotation of memblock_phys_free is wrong.

Thx.
diff mbox series

Patch

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index b0142e01002e..bde4959d9bdc 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2017,6 +2017,7 @@  config KEXEC_FILE
 	bool "kexec file based system call"
 	select KEXEC_CORE
 	select BUILD_BIN2C
+	select HAVE_IMA_KEXEC if IMA
 	depends on X86_64
 	depends on CRYPTO=y
 	depends on CRYPTO_SHA256=y
diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h
index b25d3f82c2f3..2f7b138a9388 100644
--- a/arch/x86/include/uapi/asm/bootparam.h
+++ b/arch/x86/include/uapi/asm/bootparam.h
@@ -10,6 +10,7 @@ 
 #define SETUP_EFI			4
 #define SETUP_APPLE_PROPERTIES		5
 #define SETUP_JAILHOUSE			6
+#define SETUP_IMA			7
 
 #define SETUP_INDIRECT			(1<<31)
 
@@ -171,6 +172,14 @@  struct jailhouse_setup_data {
 	} __attribute__((packed)) v2;
 } __attribute__((packed));
 
+/*
+ * IMA buffer setup data information from the previous kernel during kexec
+ */
+struct ima_setup_data {
+	__u64 addr;
+	__u64 size;
+} __attribute__((packed));
+
 /* The so-called "zeropage" */
 struct boot_params {
 	struct screen_info screen_info;			/* 0x000 */
diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c
index f267205f2d5a..9dac24680ff8 100644
--- a/arch/x86/kernel/e820.c
+++ b/arch/x86/kernel/e820.c
@@ -1017,10 +1017,10 @@  void __init e820__reserve_setup_data(void)
 		e820__range_update(pa_data, sizeof(*data)+data->len, E820_TYPE_RAM, E820_TYPE_RESERVED_KERN);
 
 		/*
-		 * SETUP_EFI is supplied by kexec and does not need to be
-		 * reserved.
+		 * SETUP_EFI and SETUP_IMA are supplied by kexec and do not need
+		 * to be reserved.
 		 */
-		if (data->type != SETUP_EFI)
+		if (data->type != SETUP_EFI && data->type != SETUP_IMA)
 			e820__range_update_kexec(pa_data,
 						 sizeof(*data) + data->len,
 						 E820_TYPE_RAM, E820_TYPE_RESERVED_KERN);
diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c
index 170d0fd68b1f..cdc73e081585 100644
--- a/arch/x86/kernel/kexec-bzimage64.c
+++ b/arch/x86/kernel/kexec-bzimage64.c
@@ -186,6 +186,32 @@  setup_efi_state(struct boot_params *params, unsigned long params_load_addr,
 }
 #endif /* CONFIG_EFI */
 
+#ifdef CONFIG_IMA_KEXEC
+static void
+setup_ima_state(const struct kimage *image, struct boot_params *params,
+		unsigned long params_load_addr,
+		unsigned int ima_setup_data_offset)
+{
+	struct setup_data *sd = (void *)params + ima_setup_data_offset;
+	struct ima_setup_data *ima = (void *)sd + sizeof(struct setup_data);
+	unsigned long setup_data_phys;
+
+	if (!image->ima_buffer_size)
+		return;
+
+	sd->type = SETUP_IMA;
+	sd->len = sizeof(*ima);
+
+	ima->addr = image->ima_buffer_addr;
+	ima->size = image->ima_buffer_size;
+
+	/* Add setup data */
+	setup_data_phys = params_load_addr + ima_setup_data_offset;
+	sd->next = params->hdr.setup_data;
+	params->hdr.setup_data = setup_data_phys;
+}
+#endif /* CONFIG_IMA_KEXEC */
+
 static int
 setup_boot_parameters(struct kimage *image, struct boot_params *params,
 		      unsigned long params_load_addr,
@@ -247,6 +273,15 @@  setup_boot_parameters(struct kimage *image, struct boot_params *params,
 	setup_efi_state(params, params_load_addr, efi_map_offset, efi_map_sz,
 			efi_setup_data_offset);
 #endif
+
+#ifdef CONFIG_IMA_KEXEC
+	/* Setup IMA log buffer state */
+	setup_ima_state(image, params, params_load_addr,
+			efi_setup_data_offset +
+			sizeof(struct setup_data) +
+			sizeof(struct efi_setup_data));
+#endif
+
 	/* Setup EDD info */
 	memcpy(params->eddbuf, boot_params.eddbuf,
 				EDDMAXNR * sizeof(struct edd_info));
@@ -401,7 +436,9 @@  static void *bzImage64_load(struct kimage *image, char *kernel,
 	params_cmdline_sz = ALIGN(params_cmdline_sz, 16);
 	kbuf.bufsz = params_cmdline_sz + ALIGN(efi_map_sz, 16) +
 				sizeof(struct setup_data) +
-				sizeof(struct efi_setup_data);
+				sizeof(struct efi_setup_data) +
+				sizeof(struct setup_data) +
+				sizeof(struct ima_setup_data);
 
 	params = kzalloc(kbuf.bufsz, GFP_KERNEL);
 	if (!params)
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index c95b9ac5a457..8b0e7725f918 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -11,6 +11,7 @@ 
 #include <linux/dma-map-ops.h>
 #include <linux/dmi.h>
 #include <linux/efi.h>
+#include <linux/ima.h>
 #include <linux/init_ohci1394_dma.h>
 #include <linux/initrd.h>
 #include <linux/iscsi_ibft.h>
@@ -335,6 +336,28 @@  static void __init reserve_initrd(void)
 }
 #endif /* CONFIG_BLK_DEV_INITRD */
 
+#ifdef CONFIG_IMA_KEXEC
+static void __init add_early_ima_buffer(u64 phys_addr)
+{
+	struct ima_setup_data *data;
+
+	data = early_memremap(phys_addr + sizeof(struct setup_data),
+			      sizeof(*data));
+	if (!data) {
+		pr_warn("setup: failed to memremap ima_setup_data entry\n");
+		return;
+	}
+	memblock_reserve(data->addr, data->size);
+	ima_set_kexec_buffer(data->addr, data->size);
+	early_memunmap(data, sizeof(*data));
+}
+#else
+static void __init add_early_ima_buffer(u64 phys_addr)
+{
+	pr_warn("Passed IMA kexec data, but CONFIG_IMA_KEXEC not set. Ignoring.\n");
+}
+#endif
+
 static void __init parse_setup_data(void)
 {
 	struct setup_data *data;
@@ -360,6 +383,9 @@  static void __init parse_setup_data(void)
 		case SETUP_EFI:
 			parse_efi_setup(pa_data, data_len);
 			break;
+		case SETUP_IMA:
+			add_early_ima_buffer(pa_data);
+			break;
 		default:
 			break;
 		}
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 426b1744215e..f58aed7acad4 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -48,6 +48,7 @@  static inline void ima_appraise_parse_cmdline(void) {}
 
 #ifdef CONFIG_IMA_KEXEC
 extern void ima_add_kexec_buffer(struct kimage *image);
+extern void ima_set_kexec_buffer(phys_addr_t phys_addr, size_t size);
 #endif
 
 #else
diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
index 13753136f03f..419c50cfe6b9 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -10,6 +10,7 @@ 
 #include <linux/seq_file.h>
 #include <linux/vmalloc.h>
 #include <linux/kexec.h>
+#include <linux/memblock.h>
 #include <linux/of.h>
 #include <linux/ima.h>
 #include "ima.h"
@@ -134,10 +135,66 @@  void ima_add_kexec_buffer(struct kimage *image)
 }
 #endif /* IMA_KEXEC */
 
+#ifndef CONFIG_OF
+static phys_addr_t ima_early_kexec_buffer_phys;
+static size_t ima_early_kexec_buffer_size;
+
+void __init ima_set_kexec_buffer(phys_addr_t phys_addr, size_t size)
+{
+	if (size == 0)
+		return;
+
+	ima_early_kexec_buffer_phys = phys_addr;
+	ima_early_kexec_buffer_size = size;
+}
+
+int __init ima_free_kexec_buffer(void)
+{
+	int rc;
+
+	if (!IS_ENABLED(CONFIG_HAVE_IMA_KEXEC))
+		return -ENOTSUPP;
+
+	if (ima_early_kexec_buffer_size == 0)
+		return -ENOENT;
+
+	rc = memblock_phys_free(ima_early_kexec_buffer_phys,
+				ima_early_kexec_buffer_size);
+	if (rc)
+		return rc;
+
+	ima_early_kexec_buffer_phys = 0;
+	ima_early_kexec_buffer_size = 0;
+
+	return 0;
+}
+
+int __init ima_get_kexec_buffer(void **addr, size_t *size)
+{
+	if (!IS_ENABLED(CONFIG_HAVE_IMA_KEXEC))
+		return -ENOTSUPP;
+
+	if (ima_early_kexec_buffer_size == 0)
+		return -ENOENT;
+
+	*addr = __va(ima_early_kexec_buffer_phys);
+	*size = ima_early_kexec_buffer_size;
+
+	return 0;
+}
+
+#else
+
+void __init ima_set_kexec_buffer(phys_addr_t phys_addr, size_t size)
+{
+	pr_warn("CONFIG_OF enabled, ignoring call to set buffer details.\n");
+}
+#endif /* CONFIG_OF */
+
 /*
  * Restore the measurement list from the previous kernel.
  */
-void ima_load_kexec_buffer(void)
+void __init ima_load_kexec_buffer(void)
 {
 	void *kexec_buffer = NULL;
 	size_t kexec_buffer_size = 0;