diff options
Diffstat (limited to 'arch/powerpc/kernel/ima_kexec.c')
-rw-r--r-- | arch/powerpc/kernel/ima_kexec.c | 132 |
1 files changed, 132 insertions, 0 deletions
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 | } | ||