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