diff options
-rw-r--r-- | arch/Kconfig | 3 | ||||
-rw-r--r-- | arch/powerpc/Kconfig | 1 | ||||
-rw-r--r-- | arch/powerpc/include/asm/ima.h | 13 | ||||
-rw-r--r-- | arch/powerpc/include/asm/kexec.h | 1 | ||||
-rw-r--r-- | arch/powerpc/kernel/Makefile | 4 | ||||
-rw-r--r-- | arch/powerpc/kernel/ima_kexec.c | 132 | ||||
-rw-r--r-- | arch/powerpc/kernel/machine_kexec_file_64.c | 5 |
7 files changed, 158 insertions, 1 deletions
diff --git a/arch/Kconfig b/arch/Kconfig index 19483aea4bbc..99839c23d453 100644 --- a/arch/Kconfig +++ b/arch/Kconfig | |||
@@ -5,6 +5,9 @@ | |||
5 | config KEXEC_CORE | 5 | config KEXEC_CORE |
6 | bool | 6 | bool |
7 | 7 | ||
8 | config HAVE_IMA_KEXEC | ||
9 | bool | ||
10 | |||
8 | config OPROFILE | 11 | config OPROFILE |
9 | tristate "OProfile system profiling" | 12 | tristate "OProfile system profiling" |
10 | depends on PROFILING | 13 | depends on PROFILING |
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 3da87e198878..a8ee573fe610 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig | |||
@@ -469,6 +469,7 @@ config KEXEC | |||
469 | config KEXEC_FILE | 469 | config KEXEC_FILE |
470 | bool "kexec file based system call" | 470 | bool "kexec file based system call" |
471 | select KEXEC_CORE | 471 | select KEXEC_CORE |
472 | select HAVE_IMA_KEXEC | ||
472 | select BUILD_BIN2C | 473 | select BUILD_BIN2C |
473 | depends on PPC64 | 474 | depends on PPC64 |
474 | depends on CRYPTO=y | 475 | depends on CRYPTO=y |
diff --git a/arch/powerpc/include/asm/ima.h b/arch/powerpc/include/asm/ima.h new file mode 100644 index 000000000000..d5a72dd9b499 --- /dev/null +++ b/arch/powerpc/include/asm/ima.h | |||
@@ -0,0 +1,13 @@ | |||
1 | #ifndef _ASM_POWERPC_IMA_H | ||
2 | #define _ASM_POWERPC_IMA_H | ||
3 | |||
4 | int ima_get_kexec_buffer(void **addr, size_t *size); | ||
5 | int ima_free_kexec_buffer(void); | ||
6 | |||
7 | #ifdef CONFIG_IMA | ||
8 | void remove_ima_buffer(void *fdt, int chosen_node); | ||
9 | #else | ||
10 | static inline void remove_ima_buffer(void *fdt, int chosen_node) {} | ||
11 | #endif | ||
12 | |||
13 | #endif /* _ASM_POWERPC_IMA_H */ | ||
diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h index 6c3b71502fbc..b0fdffb7179b 100644 --- a/arch/powerpc/include/asm/kexec.h +++ b/arch/powerpc/include/asm/kexec.h | |||
@@ -99,6 +99,7 @@ int setup_purgatory(struct kimage *image, const void *slave_code, | |||
99 | unsigned long fdt_load_addr); | 99 | unsigned long fdt_load_addr); |
100 | int setup_new_fdt(void *fdt, unsigned long initrd_load_addr, | 100 | int setup_new_fdt(void *fdt, unsigned long initrd_load_addr, |
101 | unsigned long initrd_len, const char *cmdline); | 101 | unsigned long initrd_len, const char *cmdline); |
102 | int delete_fdt_mem_rsv(void *fdt, unsigned long start, unsigned long size); | ||
102 | #endif /* CONFIG_KEXEC_FILE */ | 103 | #endif /* CONFIG_KEXEC_FILE */ |
103 | 104 | ||
104 | #else /* !CONFIG_KEXEC_CORE */ | 105 | #else /* !CONFIG_KEXEC_CORE */ |
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index a3a6047fd395..23f8082d7bfa 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile | |||
@@ -112,6 +112,10 @@ obj-$(CONFIG_PCI_MSI) += msi.o | |||
112 | obj-$(CONFIG_KEXEC_CORE) += machine_kexec.o crash.o \ | 112 | obj-$(CONFIG_KEXEC_CORE) += machine_kexec.o crash.o \ |
113 | machine_kexec_$(BITS).o | 113 | machine_kexec_$(BITS).o |
114 | obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file_$(BITS).o kexec_elf_$(BITS).o | 114 | obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file_$(BITS).o kexec_elf_$(BITS).o |
115 | ifeq ($(CONFIG_HAVE_IMA_KEXEC)$(CONFIG_IMA),yy) | ||
116 | obj-y += ima_kexec.o | ||
117 | endif | ||
118 | |||
115 | obj-$(CONFIG_AUDIT) += audit.o | 119 | obj-$(CONFIG_AUDIT) += audit.o |
116 | obj64-$(CONFIG_AUDIT) += compat_audit.o | 120 | obj64-$(CONFIG_AUDIT) += compat_audit.o |
117 | 121 | ||
diff --git a/arch/powerpc/kernel/ima_kexec.c b/arch/powerpc/kernel/ima_kexec.c new file mode 100644 index 000000000000..36e5a5df3804 --- /dev/null +++ b/arch/powerpc/kernel/ima_kexec.c | |||
@@ -0,0 +1,132 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 IBM Corporation | ||
3 | * | ||
4 | * Authors: | ||
5 | * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/slab.h> | ||
14 | #include <linux/kexec.h> | ||
15 | #include <linux/of.h> | ||
16 | #include <linux/memblock.h> | ||
17 | #include <linux/libfdt.h> | ||
18 | |||
19 | static int get_addr_size_cells(int *addr_cells, int *size_cells) | ||
20 | { | ||
21 | struct device_node *root; | ||
22 | |||
23 | root = of_find_node_by_path("/"); | ||
24 | if (!root) | ||
25 | return -EINVAL; | ||
26 | |||
27 | *addr_cells = of_n_addr_cells(root); | ||
28 | *size_cells = of_n_size_cells(root); | ||
29 | |||
30 | of_node_put(root); | ||
31 | |||
32 | return 0; | ||
33 | } | ||
34 | |||
35 | static int do_get_kexec_buffer(const void *prop, int len, unsigned long *addr, | ||
36 | size_t *size) | ||
37 | { | ||
38 | int ret, addr_cells, size_cells; | ||
39 | |||
40 | ret = get_addr_size_cells(&addr_cells, &size_cells); | ||
41 | if (ret) | ||
42 | return ret; | ||
43 | |||
44 | if (len < 4 * (addr_cells + size_cells)) | ||
45 | return -ENOENT; | ||
46 | |||
47 | *addr = of_read_number(prop, addr_cells); | ||
48 | *size = of_read_number(prop + 4 * addr_cells, size_cells); | ||
49 | |||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | /** | ||
54 | * ima_get_kexec_buffer - get IMA buffer from the previous kernel | ||
55 | * @addr: On successful return, set to point to the buffer contents. | ||
56 | * @size: On successful return, set to the buffer size. | ||
57 | * | ||
58 | * Return: 0 on success, negative errno on error. | ||
59 | */ | ||
60 | int ima_get_kexec_buffer(void **addr, size_t *size) | ||
61 | { | ||
62 | int ret, len; | ||
63 | unsigned long tmp_addr; | ||
64 | size_t tmp_size; | ||
65 | const void *prop; | ||
66 | |||
67 | prop = of_get_property(of_chosen, "linux,ima-kexec-buffer", &len); | ||
68 | if (!prop) | ||
69 | return -ENOENT; | ||
70 | |||
71 | ret = do_get_kexec_buffer(prop, len, &tmp_addr, &tmp_size); | ||
72 | if (ret) | ||
73 | return ret; | ||
74 | |||
75 | *addr = __va(tmp_addr); | ||
76 | *size = tmp_size; | ||
77 | |||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | /** | ||
82 | * ima_free_kexec_buffer - free memory used by the IMA buffer | ||
83 | */ | ||
84 | int ima_free_kexec_buffer(void) | ||
85 | { | ||
86 | int ret; | ||
87 | unsigned long addr; | ||
88 | size_t size; | ||
89 | struct property *prop; | ||
90 | |||
91 | prop = of_find_property(of_chosen, "linux,ima-kexec-buffer", NULL); | ||
92 | if (!prop) | ||
93 | return -ENOENT; | ||
94 | |||
95 | ret = do_get_kexec_buffer(prop->value, prop->length, &addr, &size); | ||
96 | if (ret) | ||
97 | return ret; | ||
98 | |||
99 | ret = of_remove_property(of_chosen, prop); | ||
100 | if (ret) | ||
101 | return ret; | ||
102 | |||
103 | return memblock_free(addr, size); | ||
104 | |||
105 | } | ||
106 | |||
107 | /** | ||
108 | * remove_ima_buffer - remove the IMA buffer property and reservation from @fdt | ||
109 | * | ||
110 | * The IMA measurement buffer is of no use to a subsequent kernel, so we always | ||
111 | * remove it from the device tree. | ||
112 | */ | ||
113 | void remove_ima_buffer(void *fdt, int chosen_node) | ||
114 | { | ||
115 | int ret, len; | ||
116 | unsigned long addr; | ||
117 | size_t size; | ||
118 | const void *prop; | ||
119 | |||
120 | prop = fdt_getprop(fdt, chosen_node, "linux,ima-kexec-buffer", &len); | ||
121 | if (!prop) | ||
122 | return; | ||
123 | |||
124 | ret = do_get_kexec_buffer(prop, len, &addr, &size); | ||
125 | fdt_delprop(fdt, chosen_node, "linux,ima-kexec-buffer"); | ||
126 | if (ret) | ||
127 | return; | ||
128 | |||
129 | ret = delete_fdt_mem_rsv(fdt, addr, size); | ||
130 | if (!ret) | ||
131 | pr_debug("Removed old IMA buffer reservation.\n"); | ||
132 | } | ||
diff --git a/arch/powerpc/kernel/machine_kexec_file_64.c b/arch/powerpc/kernel/machine_kexec_file_64.c index 7abc8a75ee48..29331fffad13 100644 --- a/arch/powerpc/kernel/machine_kexec_file_64.c +++ b/arch/powerpc/kernel/machine_kexec_file_64.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/memblock.h> | 27 | #include <linux/memblock.h> |
28 | #include <linux/of_fdt.h> | 28 | #include <linux/of_fdt.h> |
29 | #include <linux/libfdt.h> | 29 | #include <linux/libfdt.h> |
30 | #include <asm/ima.h> | ||
30 | 31 | ||
31 | #define SLAVE_CODE_SIZE 256 | 32 | #define SLAVE_CODE_SIZE 256 |
32 | 33 | ||
@@ -180,7 +181,7 @@ int setup_purgatory(struct kimage *image, const void *slave_code, | |||
180 | * | 181 | * |
181 | * Return: 0 on success, or negative errno on error. | 182 | * Return: 0 on success, or negative errno on error. |
182 | */ | 183 | */ |
183 | static int delete_fdt_mem_rsv(void *fdt, unsigned long start, unsigned long size) | 184 | int delete_fdt_mem_rsv(void *fdt, unsigned long start, unsigned long size) |
184 | { | 185 | { |
185 | int i, ret, num_rsvs = fdt_num_mem_rsv(fdt); | 186 | int i, ret, num_rsvs = fdt_num_mem_rsv(fdt); |
186 | 187 | ||
@@ -328,6 +329,8 @@ int setup_new_fdt(void *fdt, unsigned long initrd_load_addr, | |||
328 | } | 329 | } |
329 | } | 330 | } |
330 | 331 | ||
332 | remove_ima_buffer(fdt, chosen_node); | ||
333 | |||
331 | ret = fdt_setprop(fdt, chosen_node, "linux,booted-from-kexec", NULL, 0); | 334 | ret = fdt_setprop(fdt, chosen_node, "linux,booted-from-kexec", NULL, 0); |
332 | if (ret) { | 335 | if (ret) { |
333 | pr_err("Error setting up the new device tree.\n"); | 336 | pr_err("Error setting up the new device tree.\n"); |