1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2016 IBM Corporation
6 * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
9 #include <linux/slab.h>
10 #include <linux/kexec.h>
12 #include <linux/memblock.h>
13 #include <linux/libfdt.h>
15 static int get_addr_size_cells(int *addr_cells
, int *size_cells
)
17 struct device_node
*root
;
19 root
= of_find_node_by_path("/");
23 *addr_cells
= of_n_addr_cells(root
);
24 *size_cells
= of_n_size_cells(root
);
31 static int do_get_kexec_buffer(const void *prop
, int len
, unsigned long *addr
,
34 int ret
, addr_cells
, size_cells
;
36 ret
= get_addr_size_cells(&addr_cells
, &size_cells
);
40 if (len
< 4 * (addr_cells
+ size_cells
))
43 *addr
= of_read_number(prop
, addr_cells
);
44 *size
= of_read_number(prop
+ 4 * addr_cells
, size_cells
);
50 * ima_get_kexec_buffer - get IMA buffer from the previous kernel
51 * @addr: On successful return, set to point to the buffer contents.
52 * @size: On successful return, set to the buffer size.
54 * Return: 0 on success, negative errno on error.
56 int ima_get_kexec_buffer(void **addr
, size_t *size
)
59 unsigned long tmp_addr
;
63 prop
= of_get_property(of_chosen
, "linux,ima-kexec-buffer", &len
);
67 ret
= do_get_kexec_buffer(prop
, len
, &tmp_addr
, &tmp_size
);
71 *addr
= __va(tmp_addr
);
78 * ima_free_kexec_buffer - free memory used by the IMA buffer
80 int ima_free_kexec_buffer(void)
85 struct property
*prop
;
87 prop
= of_find_property(of_chosen
, "linux,ima-kexec-buffer", NULL
);
91 ret
= do_get_kexec_buffer(prop
->value
, prop
->length
, &addr
, &size
);
95 ret
= of_remove_property(of_chosen
, prop
);
99 return memblock_free(addr
, size
);
104 * remove_ima_buffer - remove the IMA buffer property and reservation from @fdt
106 * The IMA measurement buffer is of no use to a subsequent kernel, so we always
107 * remove it from the device tree.
109 void remove_ima_buffer(void *fdt
, int chosen_node
)
116 prop
= fdt_getprop(fdt
, chosen_node
, "linux,ima-kexec-buffer", &len
);
120 ret
= do_get_kexec_buffer(prop
, len
, &addr
, &size
);
121 fdt_delprop(fdt
, chosen_node
, "linux,ima-kexec-buffer");
125 ret
= delete_fdt_mem_rsv(fdt
, addr
, size
);
127 pr_debug("Removed old IMA buffer reservation.\n");
130 #ifdef CONFIG_IMA_KEXEC
132 * arch_ima_add_kexec_buffer - do arch-specific steps to add the IMA buffer
134 * Architectures should use this function to pass on the IMA buffer
135 * information to the next kernel.
137 * Return: 0 on success, negative errno on error.
139 int arch_ima_add_kexec_buffer(struct kimage
*image
, unsigned long load_addr
,
142 image
->arch
.ima_buffer_addr
= load_addr
;
143 image
->arch
.ima_buffer_size
= size
;
148 static int write_number(void *p
, u64 value
, int cells
)
156 tmp
= cpu_to_be32(value
);
157 memcpy(p
, &tmp
, sizeof(tmp
));
158 } else if (cells
== 2) {
161 tmp
= cpu_to_be64(value
);
162 memcpy(p
, &tmp
, sizeof(tmp
));
170 * setup_ima_buffer - add IMA buffer information to the fdt
171 * @image: kexec image being loaded.
172 * @fdt: Flattened device tree for the next kernel.
173 * @chosen_node: Offset to the chosen node.
175 * Return: 0 on success, or negative errno on error.
177 int setup_ima_buffer(const struct kimage
*image
, void *fdt
, int chosen_node
)
179 int ret
, addr_cells
, size_cells
, entry_size
;
182 remove_ima_buffer(fdt
, chosen_node
);
183 if (!image
->arch
.ima_buffer_size
)
186 ret
= get_addr_size_cells(&addr_cells
, &size_cells
);
190 entry_size
= 4 * (addr_cells
+ size_cells
);
192 if (entry_size
> sizeof(value
))
195 ret
= write_number(value
, image
->arch
.ima_buffer_addr
, addr_cells
);
199 ret
= write_number(value
+ 4 * addr_cells
, image
->arch
.ima_buffer_size
,
204 ret
= fdt_setprop(fdt
, chosen_node
, "linux,ima-kexec-buffer", value
,
209 ret
= fdt_add_mem_rsv(fdt
, image
->arch
.ima_buffer_addr
,
210 image
->arch
.ima_buffer_size
);
214 pr_debug("IMA buffer at 0x%llx, size = 0x%zx\n",
215 image
->arch
.ima_buffer_addr
, image
->arch
.ima_buffer_size
);
219 #endif /* CONFIG_IMA_KEXEC */