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