Skip to content

Commit

Permalink
powerpc: ima: send the kexec buffer to the next kernel
Browse files Browse the repository at this point in the history
The IMA kexec buffer allows the currently running kernel to pass the
measurement list via a kexec segment to the kernel that will be kexec'd.

This is the architecture-specific part of setting up the IMA kexec
buffer for the next kernel.  It will be used in the next patch.

Link: http://lkml.kernel.org/r/1480554346-29071-6-git-send-email-zohar@linux.vnet.ibm.com
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
Acked-by: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Andreas Steffen <andreas.steffen@strongswan.org>
Cc: Dmitry Kasatkin <dmitry.kasatkin@gmail.com>
Cc: Josh Sklar <sklar@linux.vnet.ibm.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Stewart Smith <stewart@linux.vnet.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
bauermann authored and torvalds committed Dec 20, 2016
1 parent d158847 commit ab6b1d1
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 6 deletions.
16 changes: 16 additions & 0 deletions arch/powerpc/include/asm/ima.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#ifndef _ASM_POWERPC_IMA_H
#define _ASM_POWERPC_IMA_H

struct kimage;

int ima_get_kexec_buffer(void **addr, size_t *size);
int ima_free_kexec_buffer(void);

Expand All @@ -10,4 +12,18 @@ void remove_ima_buffer(void *fdt, int chosen_node);
static inline void remove_ima_buffer(void *fdt, int chosen_node) {}
#endif

#ifdef CONFIG_IMA_KEXEC
int arch_ima_add_kexec_buffer(struct kimage *image, unsigned long load_addr,
size_t size);

int setup_ima_buffer(const struct kimage *image, void *fdt, int chosen_node);
#else
static inline int setup_ima_buffer(const struct kimage *image, void *fdt,
int chosen_node)
{
remove_ima_buffer(fdt, chosen_node);
return 0;
}
#endif /* CONFIG_IMA_KEXEC */

#endif /* _ASM_POWERPC_IMA_H */
14 changes: 12 additions & 2 deletions arch/powerpc/include/asm/kexec.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,21 @@ static inline bool kdump_in_progress(void)
#ifdef CONFIG_KEXEC_FILE
extern struct kexec_file_ops kexec_elf64_ops;

#ifdef CONFIG_IMA_KEXEC
#define ARCH_HAS_KIMAGE_ARCH

struct kimage_arch {
phys_addr_t ima_buffer_addr;
size_t ima_buffer_size;
};
#endif

int setup_purgatory(struct kimage *image, const void *slave_code,
const void *fdt, unsigned long kernel_load_addr,
unsigned long fdt_load_addr);
int setup_new_fdt(void *fdt, unsigned long initrd_load_addr,
unsigned long initrd_len, const char *cmdline);
int setup_new_fdt(const struct kimage *image, void *fdt,
unsigned long initrd_load_addr, unsigned long initrd_len,
const char *cmdline);
int delete_fdt_mem_rsv(void *fdt, unsigned long start, unsigned long size);
#endif /* CONFIG_KEXEC_FILE */

Expand Down
91 changes: 91 additions & 0 deletions arch/powerpc/kernel/ima_kexec.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,94 @@ void remove_ima_buffer(void *fdt, int chosen_node)
if (!ret)
pr_debug("Removed old IMA buffer reservation.\n");
}

#ifdef CONFIG_IMA_KEXEC
/**
* arch_ima_add_kexec_buffer - do arch-specific steps to add the IMA buffer
*
* Architectures should use this function to pass on the IMA buffer
* information to the next kernel.
*
* Return: 0 on success, negative errno on error.
*/
int arch_ima_add_kexec_buffer(struct kimage *image, unsigned long load_addr,
size_t size)
{
image->arch.ima_buffer_addr = load_addr;
image->arch.ima_buffer_size = size;

return 0;
}

static int write_number(void *p, u64 value, int cells)
{
if (cells == 1) {
u32 tmp;

if (value > U32_MAX)
return -EINVAL;

tmp = cpu_to_be32(value);
memcpy(p, &tmp, sizeof(tmp));
} else if (cells == 2) {
u64 tmp;

tmp = cpu_to_be64(value);
memcpy(p, &tmp, sizeof(tmp));
} else
return -EINVAL;

return 0;
}

/**
* setup_ima_buffer - add IMA buffer information to the fdt
* @image: kexec image being loaded.
* @fdt: Flattened device tree for the next kernel.
* @chosen_node: Offset to the chosen node.
*
* Return: 0 on success, or negative errno on error.
*/
int setup_ima_buffer(const struct kimage *image, void *fdt, int chosen_node)
{
int ret, addr_cells, size_cells, entry_size;
u8 value[16];

remove_ima_buffer(fdt, chosen_node);
if (!image->arch.ima_buffer_size)
return 0;

ret = get_addr_size_cells(&addr_cells, &size_cells);
if (ret)
return ret;

entry_size = 4 * (addr_cells + size_cells);

if (entry_size > sizeof(value))
return -EINVAL;

ret = write_number(value, image->arch.ima_buffer_addr, addr_cells);
if (ret)
return ret;

ret = write_number(value + 4 * addr_cells, image->arch.ima_buffer_size,
size_cells);
if (ret)
return ret;

ret = fdt_setprop(fdt, chosen_node, "linux,ima-kexec-buffer", value,
entry_size);
if (ret < 0)
return -EINVAL;

ret = fdt_add_mem_rsv(fdt, image->arch.ima_buffer_addr,
image->arch.ima_buffer_size);
if (ret)
return -EINVAL;

pr_debug("IMA buffer at 0x%llx, size = 0x%zx\n",
image->arch.ima_buffer_addr, image->arch.ima_buffer_size);

return 0;
}
#endif /* CONFIG_IMA_KEXEC */
2 changes: 1 addition & 1 deletion arch/powerpc/kernel/kexec_elf_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
goto out;
}

ret = setup_new_fdt(fdt, initrd_load_addr, initrd_len, cmdline);
ret = setup_new_fdt(image, fdt, initrd_load_addr, initrd_len, cmdline);
if (ret)
goto out;

Expand Down
12 changes: 9 additions & 3 deletions arch/powerpc/kernel/machine_kexec_file_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ int delete_fdt_mem_rsv(void *fdt, unsigned long start, unsigned long size)

/*
* 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.
Expand All @@ -218,8 +219,9 @@ int delete_fdt_mem_rsv(void *fdt, unsigned long start, unsigned long size)
*
* Return: 0 on success, or negative errno on error.
*/
int setup_new_fdt(void *fdt, unsigned long initrd_load_addr,
unsigned long initrd_len, const char *cmdline)
int 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;
Expand Down Expand Up @@ -329,7 +331,11 @@ int setup_new_fdt(void *fdt, unsigned long initrd_load_addr,
}
}

remove_ima_buffer(fdt, chosen_node);
ret = setup_ima_buffer(image, fdt, chosen_node);
if (ret) {
pr_err("Error setting up the new device tree.\n");
return ret;
}

ret = fdt_setprop(fdt, chosen_node, "linux,booted-from-kexec", NULL, 0);
if (ret) {
Expand Down

0 comments on commit ab6b1d1

Please sign in to comment.