"Das U-Boot" Source Tree

FWU: Add FWU metadata access driver for MTD storage regions

In the FWU Multi Bank Update feature, the information about the
updatable images is stored as part of the metadata, on a separate
region. Add a driver for reading from and writing to the metadata
when the updatable images and the metadata are stored on a raw
MTD region.
The code is divided into core under drivers/fwu-mdata/ and some helper
functions clubbed together under lib/fwu_updates/

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@linaro.org>
Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>

authored by

Masami Hiramatsu and committed by
Tom Rini
4898679e 72168b92

+503
+15
drivers/fwu-mdata/Kconfig
··· 6 6 FWU Metadata partitions reside on the same storage device 7 7 which contains the other FWU updatable firmware images. 8 8 9 + choice 10 + prompt "Storage Layout Scheme" 11 + depends on FWU_MDATA 12 + default FWU_MDATA_GPT_BLK 13 + 9 14 config FWU_MDATA_GPT_BLK 10 15 bool "FWU Metadata access for GPT partitioned Block devices" 11 16 select PARTITION_TYPE_GUID ··· 14 19 help 15 20 Enable support for accessing FWU Metadata on GPT partitioned 16 21 block devices. 22 + 23 + config FWU_MDATA_MTD 24 + bool "Raw MTD devices" 25 + depends on MTD 26 + help 27 + Enable support for accessing FWU Metadata on non-partitioned 28 + (or non-GPT partitioned, e.g. partition nodes in devicetree) 29 + MTD devices. 30 + 31 + endchoice
+1
drivers/fwu-mdata/Makefile
··· 6 6 7 7 obj-$(CONFIG_FWU_MDATA) += fwu-mdata-uclass.o 8 8 obj-$(CONFIG_FWU_MDATA_GPT_BLK) += gpt_blk.o 9 + obj-$(CONFIG_FWU_MDATA_MTD) += raw_mtd.o
+269
drivers/fwu-mdata/raw_mtd.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Copyright (c) 2023, Linaro Limited 4 + */ 5 + 6 + #define LOG_CATEGORY UCLASS_FWU_MDATA 7 + 8 + #include <fwu.h> 9 + #include <fwu_mdata.h> 10 + #include <memalign.h> 11 + 12 + #include <linux/errno.h> 13 + #include <linux/types.h> 14 + 15 + /* Internal helper structure to move data around */ 16 + struct fwu_mdata_mtd_priv { 17 + struct mtd_info *mtd; 18 + char pri_label[50]; 19 + char sec_label[50]; 20 + u32 pri_offset; 21 + u32 sec_offset; 22 + }; 23 + 24 + enum fwu_mtd_op { 25 + FWU_MTD_READ, 26 + FWU_MTD_WRITE, 27 + }; 28 + 29 + extern struct fwu_mtd_image_info fwu_mtd_images[]; 30 + 31 + static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size) 32 + { 33 + return !do_div(size, mtd->erasesize); 34 + } 35 + 36 + static int mtd_io_data(struct mtd_info *mtd, u32 offs, u32 size, void *data, 37 + enum fwu_mtd_op op) 38 + { 39 + struct mtd_oob_ops io_op = {}; 40 + u64 lock_len; 41 + size_t len; 42 + void *buf; 43 + int ret; 44 + 45 + if (!mtd_is_aligned_with_block_size(mtd, offs)) { 46 + log_err("Offset unaligned with a block (0x%x)\n", mtd->erasesize); 47 + return -EINVAL; 48 + } 49 + 50 + /* This will expand erase size to align with the block size */ 51 + lock_len = round_up(size, mtd->erasesize); 52 + 53 + ret = mtd_unlock(mtd, offs, lock_len); 54 + if (ret && ret != -EOPNOTSUPP) 55 + return ret; 56 + 57 + if (op == FWU_MTD_WRITE) { 58 + struct erase_info erase_op = {}; 59 + 60 + erase_op.mtd = mtd; 61 + erase_op.addr = offs; 62 + erase_op.len = lock_len; 63 + erase_op.scrub = 0; 64 + 65 + ret = mtd_erase(mtd, &erase_op); 66 + if (ret) 67 + goto lock; 68 + } 69 + 70 + /* Also, expand the write size to align with the write size */ 71 + len = round_up(size, mtd->writesize); 72 + 73 + buf = memalign(ARCH_DMA_MINALIGN, len); 74 + if (!buf) { 75 + ret = -ENOMEM; 76 + goto lock; 77 + } 78 + memset(buf, 0xff, len); 79 + 80 + io_op.mode = MTD_OPS_AUTO_OOB; 81 + io_op.len = len; 82 + io_op.datbuf = buf; 83 + 84 + if (op == FWU_MTD_WRITE) { 85 + memcpy(buf, data, size); 86 + ret = mtd_write_oob(mtd, offs, &io_op); 87 + } else { 88 + ret = mtd_read_oob(mtd, offs, &io_op); 89 + if (!ret) 90 + memcpy(data, buf, size); 91 + } 92 + free(buf); 93 + 94 + lock: 95 + mtd_lock(mtd, offs, lock_len); 96 + 97 + return ret; 98 + } 99 + 100 + static int fwu_mtd_read_mdata(struct udevice *dev, struct fwu_mdata *mdata, bool primary) 101 + { 102 + struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev); 103 + struct mtd_info *mtd = mtd_priv->mtd; 104 + u32 offs = primary ? mtd_priv->pri_offset : mtd_priv->sec_offset; 105 + 106 + return mtd_io_data(mtd, offs, sizeof(struct fwu_mdata), mdata, FWU_MTD_READ); 107 + } 108 + 109 + static int fwu_mtd_write_mdata(struct udevice *dev, struct fwu_mdata *mdata, bool primary) 110 + { 111 + struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev); 112 + struct mtd_info *mtd = mtd_priv->mtd; 113 + u32 offs = primary ? mtd_priv->pri_offset : mtd_priv->sec_offset; 114 + 115 + return mtd_io_data(mtd, offs, sizeof(struct fwu_mdata), mdata, FWU_MTD_WRITE); 116 + } 117 + 118 + static int flash_partition_offset(struct udevice *dev, const char *part_name, fdt_addr_t *offset) 119 + { 120 + ofnode node, parts_node; 121 + fdt_addr_t size = 0; 122 + 123 + parts_node = ofnode_by_compatible(dev_ofnode(dev), "fixed-partitions"); 124 + node = ofnode_by_prop_value(parts_node, "label", part_name, strlen(part_name) + 1); 125 + if (!ofnode_valid(node)) { 126 + log_err("Warning: Failed to find partition by label <%s>\n", part_name); 127 + return -ENOENT; 128 + } 129 + 130 + *offset = ofnode_get_addr_size_index_notrans(node, 0, &size); 131 + 132 + return (int)size; 133 + } 134 + 135 + static int fwu_mdata_mtd_of_to_plat(struct udevice *dev) 136 + { 137 + struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev); 138 + const fdt32_t *phandle_p = NULL; 139 + struct udevice *mtd_dev; 140 + struct mtd_info *mtd; 141 + const char *label; 142 + fdt_addr_t offset; 143 + int ret, size; 144 + u32 phandle; 145 + ofnode bank; 146 + int off_img; 147 + 148 + /* Find the FWU mdata storage device */ 149 + phandle_p = ofnode_get_property(dev_ofnode(dev), 150 + "fwu-mdata-store", &size); 151 + if (!phandle_p) { 152 + log_err("FWU meta data store not defined in device-tree\n"); 153 + return -ENOENT; 154 + } 155 + 156 + phandle = fdt32_to_cpu(*phandle_p); 157 + 158 + ret = device_get_global_by_ofnode(ofnode_get_by_phandle(phandle), 159 + &mtd_dev); 160 + if (ret) { 161 + log_err("FWU: failed to get mtd device\n"); 162 + return ret; 163 + } 164 + 165 + mtd_probe_devices(); 166 + 167 + mtd_for_each_device(mtd) { 168 + if (mtd->dev == mtd_dev) { 169 + mtd_priv->mtd = mtd; 170 + log_debug("Found the FWU mdata mtd device %s\n", mtd->name); 171 + break; 172 + } 173 + } 174 + if (!mtd_priv->mtd) { 175 + log_err("Failed to find mtd device by fwu-mdata-store\n"); 176 + return -ENODEV; 177 + } 178 + 179 + /* Get the offset of primary and secondary mdata */ 180 + ret = ofnode_read_string_index(dev_ofnode(dev), "mdata-parts", 0, &label); 181 + if (ret) 182 + return ret; 183 + strncpy(mtd_priv->pri_label, label, 50); 184 + 185 + ret = flash_partition_offset(mtd_dev, mtd_priv->pri_label, &offset); 186 + if (ret <= 0) 187 + return ret; 188 + mtd_priv->pri_offset = offset; 189 + 190 + ret = ofnode_read_string_index(dev_ofnode(dev), "mdata-parts", 1, &label); 191 + if (ret) 192 + return ret; 193 + strncpy(mtd_priv->sec_label, label, 50); 194 + 195 + ret = flash_partition_offset(mtd_dev, mtd_priv->sec_label, &offset); 196 + if (ret <= 0) 197 + return ret; 198 + mtd_priv->sec_offset = offset; 199 + 200 + off_img = 0; 201 + 202 + ofnode_for_each_subnode(bank, dev_ofnode(dev)) { 203 + int bank_num, bank_offset, bank_size; 204 + const char *bank_name; 205 + ofnode image; 206 + 207 + ofnode_read_u32(bank, "id", &bank_num); 208 + bank_name = ofnode_read_string(bank, "label"); 209 + bank_size = flash_partition_offset(mtd_dev, bank_name, &offset); 210 + if (bank_size <= 0) 211 + return bank_size; 212 + bank_offset = offset; 213 + log_debug("Bank%d: %s [0x%x - 0x%x]\n", 214 + bank_num, bank_name, bank_offset, bank_offset + bank_size); 215 + 216 + ofnode_for_each_subnode(image, bank) { 217 + int image_num, image_offset, image_size; 218 + const char *uuid; 219 + 220 + if (off_img == CONFIG_FWU_NUM_BANKS * 221 + CONFIG_FWU_NUM_IMAGES_PER_BANK) { 222 + log_err("DT provides more images than configured!\n"); 223 + break; 224 + } 225 + 226 + uuid = ofnode_read_string(image, "uuid"); 227 + ofnode_read_u32(image, "id", &image_num); 228 + ofnode_read_u32(image, "offset", &image_offset); 229 + ofnode_read_u32(image, "size", &image_size); 230 + 231 + fwu_mtd_images[off_img].start = bank_offset + image_offset; 232 + fwu_mtd_images[off_img].size = image_size; 233 + fwu_mtd_images[off_img].bank_num = bank_num; 234 + fwu_mtd_images[off_img].image_num = image_num; 235 + strcpy(fwu_mtd_images[off_img].uuidbuf, uuid); 236 + log_debug("\tImage%d: %s @0x%x\n\n", 237 + image_num, uuid, bank_offset + image_offset); 238 + off_img++; 239 + } 240 + } 241 + 242 + return 0; 243 + } 244 + 245 + static int fwu_mdata_mtd_probe(struct udevice *dev) 246 + { 247 + /* Ensure the metadata can be read. */ 248 + return fwu_get_mdata(NULL); 249 + } 250 + 251 + static struct fwu_mdata_ops fwu_mtd_ops = { 252 + .read_mdata = fwu_mtd_read_mdata, 253 + .write_mdata = fwu_mtd_write_mdata, 254 + }; 255 + 256 + static const struct udevice_id fwu_mdata_ids[] = { 257 + { .compatible = "u-boot,fwu-mdata-mtd" }, 258 + { } 259 + }; 260 + 261 + U_BOOT_DRIVER(fwu_mdata_mtd) = { 262 + .name = "fwu-mdata-mtd", 263 + .id = UCLASS_FWU_MDATA, 264 + .of_match = fwu_mdata_ids, 265 + .ops = &fwu_mtd_ops, 266 + .probe = fwu_mdata_mtd_probe, 267 + .of_to_plat = fwu_mdata_mtd_of_to_plat, 268 + .priv_auto = sizeof(struct fwu_mdata_mtd_priv), 269 + };
+32
include/fwu.h
··· 8 8 9 9 #include <blk.h> 10 10 #include <efi.h> 11 + #include <mtd.h> 12 + #include <uuid.h> 11 13 12 14 #include <linux/types.h> 13 15 ··· 16 18 17 19 struct fwu_mdata_gpt_blk_priv { 18 20 struct udevice *blk_dev; 21 + }; 22 + 23 + struct fwu_mtd_image_info { 24 + u32 start, size; 25 + int bank_num, image_num; 26 + char uuidbuf[UUID_STR_LEN + 1]; 19 27 }; 20 28 21 29 struct fwu_mdata_ops { ··· 250 258 * 251 259 */ 252 260 int fwu_trial_state_ctr_start(void); 261 + 262 + /** 263 + * fwu_gen_alt_info_from_mtd() - Parse dfu_alt_info from metadata in mtd 264 + * @buf: Buffer into which the dfu_alt_info is filled 265 + * @len: Maximum characters that can be written in buf 266 + * @mtd: Pointer to underlying MTD device 267 + * 268 + * Parse dfu_alt_info from metadata in mtd. Used for setting the env. 269 + * 270 + * Return: 0 if OK, -ve on error 271 + */ 272 + int fwu_gen_alt_info_from_mtd(char *buf, size_t len, struct mtd_info *mtd); 273 + 274 + /** 275 + * fwu_mtd_get_alt_num() - Mapping of fwu_plat_get_alt_num for MTD device 276 + * @image_guid: Image GUID for which DFU alt number needs to be retrieved 277 + * @alt_num: Pointer to the alt_num 278 + * @mtd_dev: Name of mtd device instance 279 + * 280 + * To map fwu_plat_get_alt_num onto mtd based metadata implementation. 281 + * 282 + * Return: 0 if OK, -ve on error 283 + */ 284 + int fwu_mtd_get_alt_num(efi_guid_t *image_guid, u8 *alt_num, const char *mtd_dev); 253 285 254 286 #endif /* _FWU_H_ */
+1
lib/fwu_updates/Makefile
··· 5 5 6 6 obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o 7 7 obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o 8 + obj-$(CONFIG_FWU_MDATA_MTD) += fwu_mtd.o
+185
lib/fwu_updates/fwu_mtd.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Copyright (c) 2023, Linaro Limited 4 + */ 5 + 6 + #include <dm.h> 7 + #include <dfu.h> 8 + #include <fwu.h> 9 + #include <fwu_mdata.h> 10 + #include <log.h> 11 + #include <malloc.h> 12 + #include <mtd.h> 13 + #include <uuid.h> 14 + #include <vsprintf.h> 15 + 16 + #include <dm/ofnode.h> 17 + 18 + struct fwu_mtd_image_info 19 + fwu_mtd_images[CONFIG_FWU_NUM_BANKS * CONFIG_FWU_NUM_IMAGES_PER_BANK]; 20 + 21 + static struct fwu_mtd_image_info *mtd_img_by_uuid(const char *uuidbuf) 22 + { 23 + int num_images = ARRAY_SIZE(fwu_mtd_images); 24 + 25 + for (int i = 0; i < num_images; i++) 26 + if (!strcmp(uuidbuf, fwu_mtd_images[i].uuidbuf)) 27 + return &fwu_mtd_images[i]; 28 + 29 + return NULL; 30 + } 31 + 32 + int fwu_mtd_get_alt_num(efi_guid_t *image_id, u8 *alt_num, 33 + const char *mtd_dev) 34 + { 35 + struct fwu_mtd_image_info *mtd_img_info; 36 + char uuidbuf[UUID_STR_LEN + 1]; 37 + fdt_addr_t offset, size = 0; 38 + struct dfu_entity *dfu; 39 + int i, nalt, ret; 40 + 41 + mtd_probe_devices(); 42 + 43 + uuid_bin_to_str(image_id->b, uuidbuf, UUID_STR_FORMAT_STD); 44 + 45 + mtd_img_info = mtd_img_by_uuid(uuidbuf); 46 + if (!mtd_img_info) { 47 + log_err("%s: Not found partition for image %s\n", __func__, uuidbuf); 48 + return -ENOENT; 49 + } 50 + 51 + offset = mtd_img_info->start; 52 + size = mtd_img_info->size; 53 + 54 + ret = dfu_init_env_entities(NULL, NULL); 55 + if (ret) 56 + return -ENOENT; 57 + 58 + nalt = 0; 59 + list_for_each_entry(dfu, &dfu_list, list) 60 + nalt++; 61 + 62 + if (!nalt) { 63 + log_warning("No entities in dfu_alt_info\n"); 64 + dfu_free_entities(); 65 + return -ENOENT; 66 + } 67 + 68 + ret = -ENOENT; 69 + for (i = 0; i < nalt; i++) { 70 + dfu = dfu_get_entity(i); 71 + 72 + /* Only MTD RAW access */ 73 + if (!dfu || dfu->dev_type != DFU_DEV_MTD || 74 + dfu->layout != DFU_RAW_ADDR || 75 + dfu->data.mtd.start != offset || 76 + dfu->data.mtd.size != size) 77 + continue; 78 + 79 + *alt_num = dfu->alt; 80 + ret = 0; 81 + break; 82 + } 83 + 84 + dfu_free_entities(); 85 + 86 + log_debug("%s: %s -> %d\n", __func__, uuidbuf, *alt_num); 87 + return ret; 88 + } 89 + 90 + /** 91 + * fwu_plat_get_alt_num() - Get the DFU Alt Num for the image from the platform 92 + * @dev: FWU device 93 + * @image_id: Image GUID for which DFU alt number needs to be retrieved 94 + * @alt_num: Pointer to the alt_num 95 + * 96 + * Get the DFU alt number from the platform for the image specified by the 97 + * image GUID. 98 + * 99 + * Note: This is a weak function and platforms can override this with 100 + * their own implementation for obtaining the alt number value. 101 + * 102 + * Return: 0 if OK, -ve on error 103 + */ 104 + __weak int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_id, 105 + u8 *alt_num) 106 + { 107 + return fwu_mtd_get_alt_num(image_id, alt_num, "nor1"); 108 + } 109 + 110 + static int gen_image_alt_info(char *buf, size_t len, int sidx, 111 + struct fwu_image_entry *img, struct mtd_info *mtd) 112 + { 113 + char *p = buf, *end = buf + len; 114 + int i; 115 + 116 + p += snprintf(p, end - p, "mtd %s", mtd->name); 117 + if (end < p) { 118 + log_err("%s:%d Run out of buffer\n", __func__, __LINE__); 119 + return -E2BIG; 120 + } 121 + 122 + /* 123 + * List the image banks in the FWU mdata and search the corresponding 124 + * partition based on partition's uuid. 125 + */ 126 + for (i = 0; i < CONFIG_FWU_NUM_BANKS; i++) { 127 + struct fwu_mtd_image_info *mtd_img_info; 128 + struct fwu_image_bank_info *bank; 129 + char uuidbuf[UUID_STR_LEN + 1]; 130 + u32 offset, size; 131 + 132 + /* Query a partition by image UUID */ 133 + bank = &img->img_bank_info[i]; 134 + uuid_bin_to_str(bank->image_uuid.b, uuidbuf, UUID_STR_FORMAT_STD); 135 + 136 + mtd_img_info = mtd_img_by_uuid(uuidbuf); 137 + if (!mtd_img_info) { 138 + log_err("%s: Not found partition for image %s\n", __func__, uuidbuf); 139 + break; 140 + } 141 + 142 + offset = mtd_img_info->start; 143 + size = mtd_img_info->size; 144 + 145 + p += snprintf(p, end - p, "%sbank%d raw %x %x", 146 + i == 0 ? "=" : ";", i, offset, size); 147 + if (end < p) { 148 + log_err("%s:%d Run out of buffer\n", __func__, __LINE__); 149 + return -E2BIG; 150 + } 151 + } 152 + 153 + if (i == CONFIG_FWU_NUM_BANKS) 154 + return 0; 155 + 156 + return -ENOENT; 157 + } 158 + 159 + int fwu_gen_alt_info_from_mtd(char *buf, size_t len, struct mtd_info *mtd) 160 + { 161 + struct fwu_mdata mdata; 162 + int i, l, ret; 163 + 164 + ret = fwu_get_mdata(&mdata); 165 + if (ret < 0) { 166 + log_err("Failed to get the FWU mdata.\n"); 167 + return ret; 168 + } 169 + 170 + for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) { 171 + ret = gen_image_alt_info(buf, len, i * CONFIG_FWU_NUM_BANKS, 172 + &mdata.img_entry[i], mtd); 173 + if (ret) 174 + break; 175 + 176 + l = strlen(buf); 177 + /* Replace the last ';' with '&' if there is another image. */ 178 + if (i != CONFIG_FWU_NUM_IMAGES_PER_BANK - 1 && l) 179 + buf[l - 1] = '&'; 180 + len -= l; 181 + buf += l; 182 + } 183 + 184 + return ret; 185 + }