Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.9.
  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
 19static 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
 35static 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 */
 60int 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 */
 84int 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 */
113void 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}
133
134#ifdef CONFIG_IMA_KEXEC
135/**
136 * arch_ima_add_kexec_buffer - do arch-specific steps to add the IMA buffer
137 *
138 * Architectures should use this function to pass on the IMA buffer
139 * information to the next kernel.
140 *
141 * Return: 0 on success, negative errno on error.
142 */
143int arch_ima_add_kexec_buffer(struct kimage *image, unsigned long load_addr,
144			      size_t size)
145{
146	image->arch.ima_buffer_addr = load_addr;
147	image->arch.ima_buffer_size = size;
148
149	return 0;
150}
151
152static int write_number(void *p, u64 value, int cells)
153{
154	if (cells == 1) {
155		u32 tmp;
156
157		if (value > U32_MAX)
158			return -EINVAL;
159
160		tmp = cpu_to_be32(value);
161		memcpy(p, &tmp, sizeof(tmp));
162	} else if (cells == 2) {
163		u64 tmp;
164
165		tmp = cpu_to_be64(value);
166		memcpy(p, &tmp, sizeof(tmp));
167	} else
168		return -EINVAL;
169
170	return 0;
171}
172
173/**
174 * setup_ima_buffer - add IMA buffer information to the fdt
175 * @image:		kexec image being loaded.
176 * @fdt:		Flattened device tree for the next kernel.
177 * @chosen_node:	Offset to the chosen node.
178 *
179 * Return: 0 on success, or negative errno on error.
180 */
181int setup_ima_buffer(const struct kimage *image, void *fdt, int chosen_node)
182{
183	int ret, addr_cells, size_cells, entry_size;
184	u8 value[16];
185
186	remove_ima_buffer(fdt, chosen_node);
187	if (!image->arch.ima_buffer_size)
188		return 0;
189
190	ret = get_addr_size_cells(&addr_cells, &size_cells);
191	if (ret)
192		return ret;
193
194	entry_size = 4 * (addr_cells + size_cells);
195
196	if (entry_size > sizeof(value))
197		return -EINVAL;
198
199	ret = write_number(value, image->arch.ima_buffer_addr, addr_cells);
200	if (ret)
201		return ret;
202
203	ret = write_number(value + 4 * addr_cells, image->arch.ima_buffer_size,
204			   size_cells);
205	if (ret)
206		return ret;
207
208	ret = fdt_setprop(fdt, chosen_node, "linux,ima-kexec-buffer", value,
209			  entry_size);
210	if (ret < 0)
211		return -EINVAL;
212
213	ret = fdt_add_mem_rsv(fdt, image->arch.ima_buffer_addr,
214			      image->arch.ima_buffer_size);
215	if (ret)
216		return -EINVAL;
217
218	pr_debug("IMA buffer at 0x%llx, size = 0x%zx\n",
219		 image->arch.ima_buffer_addr, image->arch.ima_buffer_size);
220
221	return 0;
222}
223#endif /* CONFIG_IMA_KEXEC */