Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
94c3aac5 MZ |
2 | /* |
3 | * Copyright (C) 2016 IBM Corporation | |
4 | * | |
5 | * Authors: | |
6 | * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> | |
7 | * Mimi Zohar <zohar@linux.vnet.ibm.com> | |
94c3aac5 | 8 | */ |
de636769 | 9 | |
7b8589cc MZ |
10 | #include <linux/seq_file.h> |
11 | #include <linux/vmalloc.h> | |
12 | #include <linux/kexec.h> | |
fee3ff99 | 13 | #include <linux/of.h> |
c6791349 | 14 | #include <linux/ima.h> |
94c3aac5 MZ |
15 | #include "ima.h" |
16 | ||
7b8589cc MZ |
17 | #ifdef CONFIG_IMA_KEXEC |
18 | static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, | |
19 | unsigned long segment_size) | |
20 | { | |
21 | struct ima_queue_entry *qe; | |
22 | struct seq_file file; | |
d68a6fe9 | 23 | struct ima_kexec_hdr khdr; |
7b8589cc MZ |
24 | int ret = 0; |
25 | ||
26 | /* segment size can't change between kexec load and execute */ | |
27 | file.buf = vmalloc(segment_size); | |
28 | if (!file.buf) { | |
29 | ret = -ENOMEM; | |
30 | goto out; | |
31 | } | |
32 | ||
9fa8e762 | 33 | file.file = NULL; |
7b8589cc MZ |
34 | file.size = segment_size; |
35 | file.read_pos = 0; | |
36 | file.count = sizeof(khdr); /* reserved space */ | |
37 | ||
d68a6fe9 MZ |
38 | memset(&khdr, 0, sizeof(khdr)); |
39 | khdr.version = 1; | |
7b8589cc MZ |
40 | list_for_each_entry_rcu(qe, &ima_measurements, later) { |
41 | if (file.count < file.size) { | |
42 | khdr.count++; | |
43 | ima_measurements_show(&file, qe); | |
44 | } else { | |
45 | ret = -EINVAL; | |
46 | break; | |
47 | } | |
48 | } | |
49 | ||
50 | if (ret < 0) | |
51 | goto out; | |
52 | ||
53 | /* | |
54 | * fill in reserved space with some buffer details | |
55 | * (eg. version, buffer size, number of measurements) | |
56 | */ | |
57 | khdr.buffer_size = file.count; | |
d68a6fe9 MZ |
58 | if (ima_canonical_fmt) { |
59 | khdr.version = cpu_to_le16(khdr.version); | |
60 | khdr.count = cpu_to_le64(khdr.count); | |
61 | khdr.buffer_size = cpu_to_le64(khdr.buffer_size); | |
62 | } | |
7b8589cc | 63 | memcpy(file.buf, &khdr, sizeof(khdr)); |
d68a6fe9 | 64 | |
520451e9 BM |
65 | print_hex_dump_debug("ima dump: ", DUMP_PREFIX_NONE, 16, 1, |
66 | file.buf, file.count < 100 ? file.count : 100, | |
67 | true); | |
7b8589cc MZ |
68 | |
69 | *buffer_size = file.count; | |
70 | *buffer = file.buf; | |
71 | out: | |
72 | if (ret == -EINVAL) | |
73 | vfree(file.buf); | |
74 | return ret; | |
75 | } | |
76 | ||
77 | /* | |
78 | * Called during kexec_file_load so that IMA can add a segment to the kexec | |
79 | * image for the measurement list for the next kernel. | |
80 | * | |
55e2b696 | 81 | * This function assumes that kexec_lock is held. |
7b8589cc MZ |
82 | */ |
83 | void ima_add_kexec_buffer(struct kimage *image) | |
84 | { | |
85 | struct kexec_buf kbuf = { .image = image, .buf_align = PAGE_SIZE, | |
86 | .buf_min = 0, .buf_max = ULONG_MAX, | |
87 | .top_down = true }; | |
88 | unsigned long binary_runtime_size; | |
89 | ||
90 | /* use more understandable variable names than defined in kbuf */ | |
91 | void *kexec_buffer = NULL; | |
92 | size_t kexec_buffer_size; | |
93 | size_t kexec_segment_size; | |
94 | int ret; | |
95 | ||
96 | /* | |
97 | * Reserve an extra half page of memory for additional measurements | |
98 | * added during the kexec load. | |
99 | */ | |
100 | binary_runtime_size = ima_get_binary_runtime_size(); | |
101 | if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE) | |
102 | kexec_segment_size = ULONG_MAX; | |
103 | else | |
104 | kexec_segment_size = ALIGN(ima_get_binary_runtime_size() + | |
105 | PAGE_SIZE / 2, PAGE_SIZE); | |
106 | if ((kexec_segment_size == ULONG_MAX) || | |
ca79b0c2 | 107 | ((kexec_segment_size >> PAGE_SHIFT) > totalram_pages() / 2)) { |
7b8589cc MZ |
108 | pr_err("Binary measurement list too large.\n"); |
109 | return; | |
110 | } | |
111 | ||
112 | ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer, | |
113 | kexec_segment_size); | |
114 | if (!kexec_buffer) { | |
115 | pr_err("Not enough memory for the kexec measurement buffer.\n"); | |
116 | return; | |
117 | } | |
118 | ||
119 | kbuf.buffer = kexec_buffer; | |
120 | kbuf.bufsz = kexec_buffer_size; | |
121 | kbuf.memsz = kexec_segment_size; | |
122 | ret = kexec_add_buffer(&kbuf); | |
123 | if (ret) { | |
124 | pr_err("Error passing over kexec measurement buffer.\n"); | |
6d14c651 | 125 | vfree(kexec_buffer); |
7b8589cc MZ |
126 | return; |
127 | } | |
128 | ||
0c605158 LR |
129 | image->ima_buffer_addr = kbuf.mem; |
130 | image->ima_buffer_size = kexec_segment_size; | |
f31e3386 LR |
131 | image->ima_buffer = kexec_buffer; |
132 | ||
a85ee18c BH |
133 | kexec_dprintk("kexec measurement buffer for the loaded kernel at 0x%lx.\n", |
134 | kbuf.mem); | |
7b8589cc MZ |
135 | } |
136 | #endif /* IMA_KEXEC */ | |
137 | ||
94c3aac5 MZ |
138 | /* |
139 | * Restore the measurement list from the previous kernel. | |
140 | */ | |
b69a2afd | 141 | void __init ima_load_kexec_buffer(void) |
94c3aac5 MZ |
142 | { |
143 | void *kexec_buffer = NULL; | |
144 | size_t kexec_buffer_size = 0; | |
145 | int rc; | |
146 | ||
147 | rc = ima_get_kexec_buffer(&kexec_buffer, &kexec_buffer_size); | |
148 | switch (rc) { | |
149 | case 0: | |
150 | rc = ima_restore_measurement_list(kexec_buffer_size, | |
151 | kexec_buffer); | |
152 | if (rc != 0) | |
153 | pr_err("Failed to restore the measurement list: %d\n", | |
154 | rc); | |
155 | ||
156 | ima_free_kexec_buffer(); | |
157 | break; | |
158 | case -ENOTSUPP: | |
159 | pr_debug("Restoring the measurement list not supported\n"); | |
160 | break; | |
161 | case -ENOENT: | |
162 | pr_debug("No measurement list to restore\n"); | |
163 | break; | |
164 | default: | |
165 | pr_debug("Error restoring the measurement list: %d\n", rc); | |
166 | } | |
167 | } |