]>
Commit | Line | Data |
---|---|---|
8aaacd61 SG |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
98bedf42 | 3 | * Verified Boot for Embedded (VBE) OS request (device tree fixup) functions |
8aaacd61 SG |
4 | * |
5 | * Copyright 2022 Google LLC | |
6 | * Written by Simon Glass <[email protected]> | |
7 | */ | |
8 | ||
9 | #define LOG_CATEGORY LOGC_BOOT | |
10 | ||
8aaacd61 SG |
11 | #include <dm.h> |
12 | #include <event.h> | |
13 | #include <image.h> | |
14 | #include <malloc.h> | |
15 | #include <rng.h> | |
16 | #include <dm/ofnode.h> | |
17 | ||
18 | #define VBE_PREFIX "vbe," | |
19 | #define VBE_PREFIX_LEN (sizeof(VBE_PREFIX) - 1) | |
20 | #define VBE_ERR_STR_LEN 128 | |
21 | #define VBE_MAX_RAND_SIZE 256 | |
22 | ||
23 | struct vbe_result { | |
24 | int errnum; | |
25 | char err_str[VBE_ERR_STR_LEN]; | |
26 | }; | |
27 | ||
28 | typedef int (*vbe_req_func)(ofnode node, struct vbe_result *result); | |
29 | ||
30 | static int handle_random_req(ofnode node, int default_size, | |
31 | struct vbe_result *result) | |
32 | { | |
33 | char buf[VBE_MAX_RAND_SIZE]; | |
34 | struct udevice *dev; | |
35 | u32 size; | |
36 | int ret; | |
37 | ||
591257b0 | 38 | if (!CONFIG_IS_ENABLED(DM_RNG)) |
8aaacd61 SG |
39 | return -ENOTSUPP; |
40 | ||
41 | if (ofnode_read_u32(node, "vbe,size", &size)) { | |
42 | if (!default_size) { | |
43 | snprintf(result->err_str, VBE_ERR_STR_LEN, | |
44 | "Missing vbe,size property"); | |
45 | return log_msg_ret("byt", -EINVAL); | |
46 | } | |
47 | size = default_size; | |
48 | } | |
49 | if (size > VBE_MAX_RAND_SIZE) { | |
50 | snprintf(result->err_str, VBE_ERR_STR_LEN, | |
51 | "vbe,size %#x exceeds max size %#x", size, | |
52 | VBE_MAX_RAND_SIZE); | |
53 | return log_msg_ret("siz", -E2BIG); | |
54 | } | |
55 | ret = uclass_first_device_err(UCLASS_RNG, &dev); | |
56 | if (ret) { | |
57 | snprintf(result->err_str, VBE_ERR_STR_LEN, | |
58 | "Cannot find random-number device (err=%d)", ret); | |
59 | return log_msg_ret("wr", ret); | |
60 | } | |
61 | ret = dm_rng_read(dev, buf, size); | |
62 | if (ret) { | |
63 | snprintf(result->err_str, VBE_ERR_STR_LEN, | |
64 | "Failed to read random-number device (err=%d)", ret); | |
65 | return log_msg_ret("rd", ret); | |
66 | } | |
67 | ret = ofnode_write_prop(node, "data", buf, size, true); | |
68 | if (ret) | |
69 | return log_msg_ret("wr", -EINVAL); | |
70 | ||
71 | return 0; | |
72 | } | |
73 | ||
74 | static int vbe_req_random_seed(ofnode node, struct vbe_result *result) | |
75 | { | |
76 | return handle_random_req(node, 0, result); | |
77 | } | |
78 | ||
79 | static int vbe_req_aslr_move(ofnode node, struct vbe_result *result) | |
80 | { | |
81 | return -ENOTSUPP; | |
82 | } | |
83 | ||
84 | static int vbe_req_aslr_rand(ofnode node, struct vbe_result *result) | |
85 | { | |
86 | return handle_random_req(node, 4, result); | |
87 | } | |
88 | ||
89 | static int vbe_req_efi_runtime_rand(ofnode node, struct vbe_result *result) | |
90 | { | |
91 | return handle_random_req(node, 4, result); | |
92 | } | |
93 | ||
94 | static struct vbe_req { | |
95 | const char *compat; | |
96 | vbe_req_func func; | |
97 | } vbe_reqs[] = { | |
98 | /* address space layout randomization - move the OS in memory */ | |
99 | { "aslr-move", vbe_req_aslr_move }, | |
100 | ||
101 | /* provide random data for address space layout randomization */ | |
102 | { "aslr-rand", vbe_req_aslr_rand }, | |
103 | ||
104 | /* provide random data for EFI-runtime-services address */ | |
105 | { "efi-runtime-rand", vbe_req_efi_runtime_rand }, | |
106 | ||
107 | /* generate random data bytes to see the OS's rand generator */ | |
108 | { "random-rand", vbe_req_random_seed }, | |
109 | ||
110 | }; | |
111 | ||
112 | static int vbe_process_request(ofnode node, struct vbe_result *result) | |
113 | { | |
114 | const char *compat, *req_name; | |
115 | int i; | |
116 | ||
117 | compat = ofnode_read_string(node, "compatible"); | |
118 | if (!compat) | |
119 | return 0; | |
120 | ||
121 | if (strlen(compat) <= VBE_PREFIX_LEN || | |
122 | strncmp(compat, VBE_PREFIX, VBE_PREFIX_LEN)) | |
123 | return -EINVAL; | |
124 | ||
125 | req_name = compat + VBE_PREFIX_LEN; /* drop "vbe," prefix */ | |
126 | for (i = 0; i < ARRAY_SIZE(vbe_reqs); i++) { | |
127 | if (!strcmp(vbe_reqs[i].compat, req_name)) { | |
128 | int ret; | |
129 | ||
130 | ret = vbe_reqs[i].func(node, result); | |
131 | if (ret) | |
132 | return log_msg_ret("req", ret); | |
133 | return 0; | |
134 | } | |
135 | } | |
136 | snprintf(result->err_str, VBE_ERR_STR_LEN, "Unknown request: %s", | |
137 | req_name); | |
138 | ||
139 | return -ENOTSUPP; | |
140 | } | |
141 | ||
142 | /** | |
143 | * bootmeth_vbe_ft_fixup() - Process VBE OS requests and do device tree fixups | |
144 | * | |
145 | * If there are no images provided, this does nothing and returns 0. | |
146 | * | |
147 | * @ctx: Context for event | |
148 | * @event: Event to process | |
149 | * @return 0 if OK, -ve on error | |
150 | */ | |
151 | static int bootmeth_vbe_ft_fixup(void *ctx, struct event *event) | |
152 | { | |
153 | const struct event_ft_fixup *fixup = &event->data.ft_fixup; | |
154 | const struct bootm_headers *images = fixup->images; | |
155 | ofnode parent, dest_parent, root, node; | |
156 | oftree fit; | |
157 | ||
158 | if (!images || !images->fit_hdr_os) | |
159 | return 0; | |
160 | ||
161 | /* Get the image node with requests in it */ | |
162 | log_debug("fit=%p, noffset=%d\n", images->fit_hdr_os, | |
163 | images->fit_noffset_os); | |
164 | fit = oftree_from_fdt(images->fit_hdr_os); | |
165 | root = oftree_root(fit); | |
166 | if (of_live_active()) { | |
167 | log_warning("Cannot fix up live tree\n"); | |
168 | return 0; | |
169 | } | |
170 | if (!ofnode_valid(root)) | |
171 | return log_msg_ret("rt", -EINVAL); | |
172 | parent = noffset_to_ofnode(root, images->fit_noffset_os); | |
173 | if (!ofnode_valid(parent)) | |
174 | return log_msg_ret("img", -EINVAL); | |
175 | dest_parent = oftree_path(fixup->tree, "/chosen"); | |
176 | if (!ofnode_valid(dest_parent)) | |
177 | return log_msg_ret("dst", -EINVAL); | |
178 | ||
179 | ofnode_for_each_subnode(node, parent) { | |
180 | const char *name = ofnode_get_name(node); | |
181 | struct vbe_result result; | |
182 | ofnode dest; | |
183 | int ret; | |
184 | ||
185 | log_debug("copy subnode: %s\n", name); | |
186 | ret = ofnode_add_subnode(dest_parent, name, &dest); | |
187 | if (ret && ret != -EEXIST) | |
188 | return log_msg_ret("add", ret); | |
24797097 | 189 | ret = ofnode_copy_props(dest, node); |
8aaacd61 SG |
190 | if (ret) |
191 | return log_msg_ret("cp", ret); | |
192 | ||
193 | *result.err_str = '\0'; | |
194 | ret = vbe_process_request(dest, &result); | |
195 | if (ret) { | |
196 | result.errnum = ret; | |
c3a148f3 SG |
197 | log_warning("Failed to process VBE request %s (err=%d)\n", |
198 | ofnode_get_name(dest), ret); | |
8aaacd61 SG |
199 | if (*result.err_str) { |
200 | char *msg = strdup(result.err_str); | |
201 | ||
202 | if (!msg) | |
203 | return log_msg_ret("msg", -ENOMEM); | |
204 | ret = ofnode_write_string(dest, "vbe,error", | |
205 | msg); | |
206 | if (ret) { | |
207 | free(msg); | |
208 | return log_msg_ret("str", -ENOMEM); | |
209 | } | |
210 | } | |
211 | if (result.errnum) { | |
212 | ret = ofnode_write_u32(dest, "vbe,errnum", | |
213 | result.errnum); | |
214 | if (ret) | |
215 | return log_msg_ret("num", -ENOMEM); | |
216 | if (result.errnum != -ENOTSUPP) | |
217 | return log_msg_ret("pro", | |
218 | result.errnum); | |
219 | if (result.errnum == -ENOTSUPP && | |
220 | ofnode_read_bool(dest, "vbe,required")) { | |
221 | log_err("Cannot handle required request: %s\n", | |
222 | ofnode_get_name(dest)); | |
223 | return log_msg_ret("req", | |
224 | result.errnum); | |
225 | } | |
226 | } | |
227 | } | |
228 | } | |
229 | ||
230 | return 0; | |
231 | } | |
6c4cad74 | 232 | EVENT_SPY_FULL(EVT_FT_FIXUP, bootmeth_vbe_ft_fixup); |