1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Verified Boot for Embedded (VBE) 'simple' method 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 <bootdev.h> 13#include <bootflow.h> 14#include <bootmeth.h> 15#include <dm.h> 16#include <log.h> 17#include <memalign.h> 18#include <mmc.h> 19#include <vbe.h> 20#include <dm/device-internal.h> 21#include <dm/ofnode.h> 22#include <u-boot/crc.h> 23#include "vbe_simple.h" 24 25/** struct simple_nvdata - storage format for non-volatile data */ 26struct simple_nvdata { 27 u8 crc8; 28 u8 hdr; 29 u16 spare1; 30 u32 fw_vernum; 31 u8 spare2[0x38]; 32}; 33 34static int simple_read_version(struct udevice *dev, struct blk_desc *desc, 35 u8 *buf, struct simple_state *state) 36{ 37 struct simple_priv *priv = dev_get_priv(dev); 38 int start; 39 40 if (priv->version_size > MMC_MAX_BLOCK_LEN) 41 return log_msg_ret("ver", -E2BIG); 42 43 start = priv->area_start + priv->version_offset; 44 if (start & (MMC_MAX_BLOCK_LEN - 1)) 45 return log_msg_ret("get", -EBADF); 46 start /= MMC_MAX_BLOCK_LEN; 47 48 if (blk_dread(desc, start, 1, buf) != 1) 49 return log_msg_ret("read", -EIO); 50 strlcpy(state->fw_version, buf, MAX_VERSION_LEN); 51 log_debug("version=%s\n", state->fw_version); 52 53 return 0; 54} 55 56static int simple_read_nvdata(struct udevice *dev, struct blk_desc *desc, 57 u8 *buf, struct simple_state *state) 58{ 59 struct simple_priv *priv = dev_get_priv(dev); 60 uint hdr_ver, hdr_size, size, crc; 61 const struct simple_nvdata *nvd; 62 int start; 63 64 if (priv->state_size > MMC_MAX_BLOCK_LEN) 65 return log_msg_ret("state", -E2BIG); 66 67 start = priv->area_start + priv->state_offset; 68 if (start & (MMC_MAX_BLOCK_LEN - 1)) 69 return log_msg_ret("get", -EBADF); 70 start /= MMC_MAX_BLOCK_LEN; 71 72 if (blk_dread(desc, start, 1, buf) != 1) 73 return log_msg_ret("read", -EIO); 74 nvd = (struct simple_nvdata *)buf; 75 hdr_ver = (nvd->hdr & NVD_HDR_VER_MASK) >> NVD_HDR_VER_SHIFT; 76 hdr_size = (nvd->hdr & NVD_HDR_SIZE_MASK) >> NVD_HDR_SIZE_SHIFT; 77 if (hdr_ver != NVD_HDR_VER_CUR) 78 return log_msg_ret("hdr", -EPERM); 79 size = 1 << hdr_size; 80 if (size > sizeof(*nvd)) 81 return log_msg_ret("sz", -ENOEXEC); 82 83 crc = crc8(0, buf + 1, size - 1); 84 if (crc != nvd->crc8) 85 return log_msg_ret("crc", -EPERM); 86 state->fw_vernum = nvd->fw_vernum; 87 88 log_debug("version=%s\n", state->fw_version); 89 90 return 0; 91} 92 93int vbe_simple_read_state(struct udevice *dev, struct simple_state *state) 94{ 95 ALLOC_CACHE_ALIGN_BUFFER(u8, buf, MMC_MAX_BLOCK_LEN); 96 struct simple_priv *priv = dev_get_priv(dev); 97 struct blk_desc *desc; 98 char devname[16]; 99 const char *end; 100 int devnum; 101 int ret; 102 103 /* First figure out the block device */ 104 log_debug("storage=%s\n", priv->storage); 105 devnum = trailing_strtoln_end(priv->storage, NULL, &end); 106 if (devnum == -1) 107 return log_msg_ret("num", -ENODEV); 108 if (end - priv->storage >= sizeof(devname)) 109 return log_msg_ret("end", -E2BIG); 110 strlcpy(devname, priv->storage, end - priv->storage + 1); 111 log_debug("dev=%s, %x\n", devname, devnum); 112 113 desc = blk_get_dev(devname, devnum); 114 if (!desc) 115 return log_msg_ret("get", -ENXIO); 116 117 ret = simple_read_version(dev, desc, buf, state); 118 if (ret) 119 return log_msg_ret("ver", ret); 120 121 ret = simple_read_nvdata(dev, desc, buf, state); 122 if (ret) 123 return log_msg_ret("nvd", ret); 124 125 return 0; 126} 127 128static int vbe_simple_get_state_desc(struct udevice *dev, char *buf, 129 int maxsize) 130{ 131 struct simple_state state; 132 int ret; 133 134 ret = vbe_simple_read_state(dev, &state); 135 if (ret) 136 return log_msg_ret("read", ret); 137 138 if (maxsize < 30) 139 return -ENOSPC; 140 snprintf(buf, maxsize, "Version: %s\nVernum: %x/%x", state.fw_version, 141 state.fw_vernum >> FWVER_KEY_SHIFT, 142 state.fw_vernum & FWVER_FW_MASK); 143 144 return 0; 145} 146 147static int vbe_simple_read_bootflow(struct udevice *dev, struct bootflow *bflow) 148{ 149 int ret; 150 151 if (CONFIG_IS_ENABLED(BOOTMETH_VBE_SIMPLE_FW)) { 152 if (vbe_phase() == VBE_PHASE_FIRMWARE) { 153 ret = vbe_simple_read_bootflow_fw(dev, bflow); 154 if (ret) 155 return log_msg_ret("fw", ret); 156 return 0; 157 } 158 } 159 160 return -EINVAL; 161} 162 163static int vbe_simple_read_file(struct udevice *dev, struct bootflow *bflow, 164 const char *file_path, ulong addr, ulong *sizep) 165{ 166 int ret; 167 168 if (vbe_phase() == VBE_PHASE_OS) { 169 ret = bootmeth_common_read_file(dev, bflow, file_path, addr, 170 sizep); 171 if (ret) 172 return log_msg_ret("os", ret); 173 } 174 175 /* To be implemented */ 176 return -EINVAL; 177} 178 179static struct bootmeth_ops bootmeth_vbe_simple_ops = { 180 .get_state_desc = vbe_simple_get_state_desc, 181 .read_bootflow = vbe_simple_read_bootflow, 182 .read_file = vbe_simple_read_file, 183}; 184 185static int bootmeth_vbe_simple_probe(struct udevice *dev) 186{ 187 struct simple_priv *priv = dev_get_priv(dev); 188 189 memset(priv, '\0', sizeof(*priv)); 190 if (dev_read_u32(dev, "area-start", &priv->area_start) || 191 dev_read_u32(dev, "area-size", &priv->area_size) || 192 dev_read_u32(dev, "version-offset", &priv->version_offset) || 193 dev_read_u32(dev, "version-size", &priv->version_size) || 194 dev_read_u32(dev, "state-offset", &priv->state_offset) || 195 dev_read_u32(dev, "state-size", &priv->state_size)) 196 return log_msg_ret("read", -EINVAL); 197 dev_read_u32(dev, "skip-offset", &priv->skip_offset); 198 priv->storage = strdup(dev_read_string(dev, "storage")); 199 if (!priv->storage) 200 return log_msg_ret("str", -EINVAL); 201 202 return 0; 203} 204 205static int bootmeth_vbe_simple_bind(struct udevice *dev) 206{ 207 struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev); 208 209 plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ? 210 "VBE simple" : "vbe-simple"; 211 plat->flags = BOOTMETHF_GLOBAL; 212 213 return 0; 214} 215 216#if CONFIG_IS_ENABLED(OF_REAL) 217static const struct udevice_id generic_simple_vbe_simple_ids[] = { 218 { .compatible = "fwupd,vbe-simple" }, 219 { } 220}; 221#endif 222 223U_BOOT_DRIVER(vbe_simple) = { 224 .name = "vbe_simple", 225 .id = UCLASS_BOOTMETH, 226 .of_match = of_match_ptr(generic_simple_vbe_simple_ids), 227 .ops = &bootmeth_vbe_simple_ops, 228 .bind = bootmeth_vbe_simple_bind, 229 .probe = bootmeth_vbe_simple_probe, 230 .flags = DM_FLAG_PRE_RELOC, 231 .priv_auto = sizeof(struct simple_priv), 232}; 233