]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blob - arch/powerpc/kernel/ima_kexec.c
Merge remote-tracking branch 'mkp-scsi/4.10/scsi-fixes' into fixes
[mirror_ubuntu-zesty-kernel.git] / arch / powerpc / kernel / ima_kexec.c
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 }
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 */
143 int 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
152 static 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 */
181 int 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 */