// 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 */ #define LOG_CATEGORY LOGC_BOOT #include #include #include #include #include #include #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);