mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-26 09:38:14 +00:00 
			
		
		
		
	As part of bringing the master branch back in to next, we need to allow for all of these changes to exist here. Reported-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Tom Rini <trini@konsulko.com>
		
			
				
	
	
		
			233 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			233 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  * Verified Boot for Embedded (VBE) OS request (device tree fixup) functions
 | |
|  *
 | |
|  * Copyright 2022 Google LLC
 | |
|  * Written by Simon Glass <sjg@chromium.org>
 | |
|  */
 | |
| 
 | |
| #define LOG_CATEGORY LOGC_BOOT
 | |
| 
 | |
| #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 (!CONFIG_IS_ENABLED(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(dest, node);
 | |
| 		if (ret)
 | |
| 			return log_msg_ret("cp", ret);
 | |
| 
 | |
| 		*result.err_str = '\0';
 | |
| 		ret = vbe_process_request(dest, &result);
 | |
| 		if (ret) {
 | |
| 			result.errnum = ret;
 | |
| 			log_warning("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_FULL(EVT_FT_FIXUP, bootmeth_vbe_ft_fixup);
 |