vbe: Add fixups for a basic set of OS requests

As a starting point, add support for providing random data, if requested
by the OS. Also add ASLR, as a placeholder for now.

Signed-off-by: Simon Glass <sjg@chromium.org>
(fixed up to use uclass_first_device_err() instead)
diff --git a/boot/Makefile b/boot/Makefile
index 67e3352..dd45d78 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -47,5 +47,5 @@
 obj-$(CONFIG_SPL_LOAD_FIT) += common_fit.o
 endif
 
-obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE) += vbe.o
+obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE) += vbe.o vbe_fixup.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_SIMPLE) += vbe_simple.o
diff --git a/boot/vbe_fixup.c b/boot/vbe_fixup.c
new file mode 100644
index 0000000..53d8867
--- /dev/null
+++ b/boot/vbe_fixup.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Verified Boot for Embedded (VBE) device tree fixup functions
+ *
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#define LOG_CATEGORY LOGC_BOOT
+
+#include <common.h>
+#include <dm.h>
+#include <event.h>
+#include <image.h>
+#include <malloc.h>
+#include <rng.h>
+#include <dm/ofnode.h>
+
+#define VBE_PREFIX		"vbe,"
+#define VBE_PREFIX_LEN		(sizeof(VBE_PREFIX) - 1)
+#define VBE_ERR_STR_LEN		128
+#define VBE_MAX_RAND_SIZE	256
+
+struct vbe_result {
+	int errnum;
+	char err_str[VBE_ERR_STR_LEN];
+};
+
+typedef int (*vbe_req_func)(ofnode node, struct vbe_result *result);
+
+static int handle_random_req(ofnode node, int default_size,
+			     struct vbe_result *result)
+{
+	char buf[VBE_MAX_RAND_SIZE];
+	struct udevice *dev;
+	u32 size;
+	int ret;
+
+	if (!IS_ENABLED(CONFIG_DM_RNG))
+		return -ENOTSUPP;
+
+	if (ofnode_read_u32(node, "vbe,size", &size)) {
+		if (!default_size) {
+			snprintf(result->err_str, VBE_ERR_STR_LEN,
+				 "Missing vbe,size property");
+			return log_msg_ret("byt", -EINVAL);
+		}
+		size = default_size;
+	}
+	if (size > VBE_MAX_RAND_SIZE) {
+		snprintf(result->err_str, VBE_ERR_STR_LEN,
+			 "vbe,size %#x exceeds max size %#x", size,
+			 VBE_MAX_RAND_SIZE);
+		return log_msg_ret("siz", -E2BIG);
+	}
+	ret = uclass_first_device_err(UCLASS_RNG, &dev);
+	if (ret) {
+		snprintf(result->err_str, VBE_ERR_STR_LEN,
+			 "Cannot find random-number device (err=%d)", ret);
+		return log_msg_ret("wr", ret);
+	}
+	ret = dm_rng_read(dev, buf, size);
+	if (ret) {
+		snprintf(result->err_str, VBE_ERR_STR_LEN,
+			 "Failed to read random-number device (err=%d)", ret);
+		return log_msg_ret("rd", ret);
+	}
+	ret = ofnode_write_prop(node, "data", buf, size, true);
+	if (ret)
+		return log_msg_ret("wr", -EINVAL);
+
+	return 0;
+}
+
+static int vbe_req_random_seed(ofnode node, struct vbe_result *result)
+{
+	return handle_random_req(node, 0, result);
+}
+
+static int vbe_req_aslr_move(ofnode node, struct vbe_result *result)
+{
+	return -ENOTSUPP;
+}
+
+static int vbe_req_aslr_rand(ofnode node, struct vbe_result *result)
+{
+	return handle_random_req(node, 4, result);
+}
+
+static int vbe_req_efi_runtime_rand(ofnode node, struct vbe_result *result)
+{
+	return handle_random_req(node, 4, result);
+}
+
+static struct vbe_req {
+	const char *compat;
+	vbe_req_func func;
+} vbe_reqs[] = {
+	/* address space layout randomization - move the OS in memory */
+	{ "aslr-move", vbe_req_aslr_move },
+
+	/* provide random data for address space layout randomization */
+	{ "aslr-rand", vbe_req_aslr_rand },
+
+	/* provide random data for EFI-runtime-services address */
+	{ "efi-runtime-rand", vbe_req_efi_runtime_rand },
+
+	/* generate random data bytes to see the OS's rand generator */
+	{ "random-rand", vbe_req_random_seed },
+
+};
+
+static int vbe_process_request(ofnode node, struct vbe_result *result)
+{
+	const char *compat, *req_name;
+	int i;
+
+	compat = ofnode_read_string(node, "compatible");
+	if (!compat)
+		return 0;
+
+	if (strlen(compat) <= VBE_PREFIX_LEN ||
+	    strncmp(compat, VBE_PREFIX, VBE_PREFIX_LEN))
+		return -EINVAL;
+
+	req_name = compat + VBE_PREFIX_LEN; /* drop "vbe," prefix */
+	for (i = 0; i < ARRAY_SIZE(vbe_reqs); i++) {
+		if (!strcmp(vbe_reqs[i].compat, req_name)) {
+			int ret;
+
+			ret = vbe_reqs[i].func(node, result);
+			if (ret)
+				return log_msg_ret("req", ret);
+			return 0;
+		}
+	}
+	snprintf(result->err_str, VBE_ERR_STR_LEN, "Unknown request: %s",
+		 req_name);
+
+	return -ENOTSUPP;
+}
+
+/**
+ * bootmeth_vbe_ft_fixup() - Process VBE OS requests and do device tree fixups
+ *
+ * If there are no images provided, this does nothing and returns 0.
+ *
+ * @ctx: Context for event
+ * @event: Event to process
+ * @return 0 if OK, -ve on error
+ */
+static int bootmeth_vbe_ft_fixup(void *ctx, struct event *event)
+{
+	const struct event_ft_fixup *fixup = &event->data.ft_fixup;
+	const struct bootm_headers *images = fixup->images;
+	ofnode parent, dest_parent, root, node;
+	oftree fit;
+
+	if (!images || !images->fit_hdr_os)
+		return 0;
+
+	/* Get the image node with requests in it */
+	log_debug("fit=%p, noffset=%d\n", images->fit_hdr_os,
+		  images->fit_noffset_os);
+	fit = oftree_from_fdt(images->fit_hdr_os);
+	root = oftree_root(fit);
+	if (of_live_active()) {
+		log_warning("Cannot fix up live tree\n");
+		return 0;
+	}
+	if (!ofnode_valid(root))
+		return log_msg_ret("rt", -EINVAL);
+	parent = noffset_to_ofnode(root, images->fit_noffset_os);
+	if (!ofnode_valid(parent))
+		return log_msg_ret("img", -EINVAL);
+	dest_parent = oftree_path(fixup->tree, "/chosen");
+	if (!ofnode_valid(dest_parent))
+		return log_msg_ret("dst", -EINVAL);
+
+	ofnode_for_each_subnode(node, parent) {
+		const char *name = ofnode_get_name(node);
+		struct vbe_result result;
+		ofnode dest;
+		int ret;
+
+		log_debug("copy subnode: %s\n", name);
+		ret = ofnode_add_subnode(dest_parent, name, &dest);
+		if (ret && ret != -EEXIST)
+			return log_msg_ret("add", ret);
+		ret = ofnode_copy_props(node, dest);
+		if (ret)
+			return log_msg_ret("cp", ret);
+
+		*result.err_str = '\0';
+		ret = vbe_process_request(dest, &result);
+		if (ret) {
+			result.errnum = ret;
+			log_err("Failed to process VBE request %s (err=%d)\n",
+				ofnode_get_name(dest), ret);
+			if (*result.err_str) {
+				char *msg = strdup(result.err_str);
+
+				if (!msg)
+					return log_msg_ret("msg", -ENOMEM);
+				ret = ofnode_write_string(dest, "vbe,error",
+							  msg);
+				if (ret) {
+					free(msg);
+					return log_msg_ret("str", -ENOMEM);
+				}
+			}
+			if (result.errnum) {
+				ret = ofnode_write_u32(dest, "vbe,errnum",
+						       result.errnum);
+				if (ret)
+					return log_msg_ret("num", -ENOMEM);
+				if (result.errnum != -ENOTSUPP)
+					return log_msg_ret("pro",
+							   result.errnum);
+				if (result.errnum == -ENOTSUPP &&
+				    ofnode_read_bool(dest, "vbe,required")) {
+					log_err("Cannot handle required request: %s\n",
+						ofnode_get_name(dest));
+					return log_msg_ret("req",
+							   result.errnum);
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+EVENT_SPY(EVT_FT_FIXUP, bootmeth_vbe_ft_fixup);
diff --git a/test/py/tests/test_event_dump.py b/test/py/tests/test_event_dump.py
index bc54149..e63c25d 100644
--- a/test/py/tests/test_event_dump.py
+++ b/test/py/tests/test_event_dump.py
@@ -16,6 +16,7 @@
     out = util.run_and_log(cons, ['scripts/event_dump.py', sandbox])
     expect = '''.*Event type            Id                              Source location
 --------------------  ------------------------------  ------------------------------
+EVT_FT_FIXUP          bootmeth_vbe_ft_fixup           .*boot/vbe_fixup.c:.*
 EVT_FT_FIXUP          bootmeth_vbe_simple_ft_fixup    .*boot/vbe_simple.c:.*
 EVT_MISC_INIT_F       sandbox_misc_init_f             .*arch/sandbox/cpu/start.c:'''
     assert re.match(expect, out, re.MULTILINE) is not None