1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Verified Boot for Embedded (VBE) OS request (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
24struct vbe_result {
25	int errnum;
26	char err_str[VBE_ERR_STR_LEN];
27};
28
29typedef int (*vbe_req_func)(ofnode node, struct vbe_result *result);
30
31static 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 (!CONFIG_IS_ENABLED(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
75static int vbe_req_random_seed(ofnode node, struct vbe_result *result)
76{
77	return handle_random_req(node, 0, result);
78}
79
80static int vbe_req_aslr_move(ofnode node, struct vbe_result *result)
81{
82	return -ENOTSUPP;
83}
84
85static int vbe_req_aslr_rand(ofnode node, struct vbe_result *result)
86{
87	return handle_random_req(node, 4, result);
88}
89
90static int vbe_req_efi_runtime_rand(ofnode node, struct vbe_result *result)
91{
92	return handle_random_req(node, 4, result);
93}
94
95static 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
113static 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 */
152static 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(dest, node);
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_warning("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}
233EVENT_SPY_FULL(EVT_FT_FIXUP, bootmeth_vbe_ft_fixup);
234