Edward O'Callaghan has submitted this change. ( https://review.coreboot.org/c/flashrom/+/61897 )
Change subject: libflashrom,linux_mtd: add linux_mtd writeprotect support ......................................................................
libflashrom,linux_mtd: add linux_mtd writeprotect support
This commit adds a generic framework to allow opaque programmers to implement writeprotect operations and uses the framework to support writeprotect operations on linux MTD device files.
The generic framework comprises three new functions in `struct opaque_master` that are called from libflashrom: - wp_write_cfg() - wp_read_cfg() - wp_get_ranges()
For linux_mtd, only the read/write functions are implemented. Linux's MTD interface doesn't provide a way to get available ranges, so calling get_wp_ranges() on the linux_mtd master will return FLASHROM_WP_ERR_RANGE_LIST_UNAVAILABLE.
BUG=b:182223106 BRANCH=none TEST=WP ops on hana DUT (MT8173) with W25Q32DW flash TEST=flashrom --wp-enable --wp-range <non-empty> succeeds TEST=flashrom --wp-enable --wp-range <empty> fails as expected TEST=flashrom --wp-disable --wp-range <empty> succeeds TEST=flashrom --wp-disable --wp-range <non-empty> fails as expected TEST=flashrom --wp-status succeeds
Change-Id: I5c86e28cdec44bec49ba1d36f8ab62241b9b01da Signed-off-by: Nikolai Artemiev nartemiev@google.com Reviewed-on: https://review.coreboot.org/c/flashrom/+/61897 Tested-by: build bot (Jenkins) no-reply@coreboot.org Reviewed-by: Anastasia Klimchuk aklm@chromium.org Reviewed-by: Edward O'Callaghan quasisec@chromium.org --- M libflashrom.c M linux_mtd.c M programmer.h 3 files changed, 132 insertions(+), 16 deletions(-)
Approvals: build bot (Jenkins): Verified Edward O'Callaghan: Looks good to me, approved Anastasia Klimchuk: Looks good to me, approved
diff --git a/libflashrom.c b/libflashrom.c index 39b0c67..658d812 100644 --- a/libflashrom.c +++ b/libflashrom.c @@ -714,14 +714,12 @@ */ enum flashrom_wp_result flashrom_wp_write_cfg(struct flashctx *flash, const struct flashrom_wp_cfg *cfg) { - /* - * TODO: Call custom implementation if the programmer is opaque, as - * direct WP operations require SPI access. In particular, linux_mtd - * has its own WP operations we should use instead. - */ if (flash->mst->buses_supported & BUS_SPI) return wp_write_cfg(flash, cfg);
+ if (flash->mst->buses_supported & BUS_PROG && flash->mst->opaque.wp_write_cfg) + return flash->mst->opaque.wp_write_cfg(flash, cfg); + return FLASHROM_WP_ERR_OTHER; }
@@ -736,14 +734,12 @@ */ enum flashrom_wp_result flashrom_wp_read_cfg(struct flashrom_wp_cfg *cfg, struct flashctx *flash) { - /* - * TODO: Call custom implementation if the programmer is opaque, as - * direct WP operations require SPI access. In particular, linux_mtd - * has its own WP operations we should use instead. - */ if (flash->mst->buses_supported & BUS_SPI) return wp_read_cfg(cfg, flash);
+ if (flash->mst->buses_supported & BUS_PROG && flash->mst->opaque.wp_read_cfg) + return flash->mst->opaque.wp_read_cfg(cfg, flash); + return FLASHROM_WP_ERR_OTHER; }
@@ -761,15 +757,12 @@ */ enum flashrom_wp_result flashrom_wp_get_available_ranges(struct flashrom_wp_ranges **list, struct flashrom_flashctx *flash) { - /* - * TODO: Call custom implementation if the programmer is opaque, as - * direct WP operations require SPI access. We actually can't implement - * this in linux_mtd right now, but we should adopt a proper generic - * architechure to match the read and write functions anyway. - */ if (flash->mst->buses_supported & BUS_SPI) return wp_get_available_ranges(list, flash);
+ if (flash->mst->buses_supported & BUS_PROG && flash->mst->opaque.wp_get_ranges) + return flash->mst->opaque.wp_get_ranges(list, flash); + return FLASHROM_WP_ERR_OTHER; }
diff --git a/linux_mtd.c b/linux_mtd.c index 9d80a51..445a903 100644 --- a/linux_mtd.c +++ b/linux_mtd.c @@ -308,6 +308,123 @@ return 0; }
+static enum flashrom_wp_result linux_mtd_wp_read_cfg(struct flashrom_wp_cfg *cfg, struct flashctx *flash) +{ + struct linux_mtd_data *data = flash->mst->opaque.data; + bool start_found = false; + bool end_found = false; + + cfg->mode = FLASHROM_WP_MODE_DISABLED; + cfg->range.start = 0; + cfg->range.len = 0; + + /* Check protection status of each block */ + for (size_t u = 0; u < data->total_size; u += data->erasesize) { + struct erase_info_user erase_info = { + .start = u, + .length = data->erasesize, + }; + + int ret = ioctl(fileno(data->dev_fp), MEMISLOCKED, &erase_info); + if (ret == 0) { + /* Block is unprotected. */ + + if (start_found) { + end_found = true; + } + } else if (ret == 1) { + /* Block is protected. */ + + if (end_found) { + /* + * We already found the end of another + * protection range, so this is the start of a + * new one. + */ + return FLASHROM_WP_ERR_OTHER; + } + if (!start_found) { + cfg->range.start = erase_info.start; + cfg->mode = FLASHROM_WP_MODE_HARDWARE; + start_found = true; + } + cfg->range.len += data->erasesize; + } else { + msg_perr("%s: ioctl: %s\n", __func__, strerror(errno)); + return FLASHROM_WP_ERR_READ_FAILED; + } + + } + + return FLASHROM_WP_OK; +} + +static enum flashrom_wp_result linux_mtd_wp_write_cfg(struct flashctx *flash, const struct flashrom_wp_cfg *cfg) +{ + const struct linux_mtd_data *data = flash->mst->opaque.data; + + const struct erase_info_user entire_chip = { + .start = 0, + .length = data->total_size, + }; + const struct erase_info_user desired_range = { + .start = cfg->range.start, + .length = cfg->range.len, + }; + + /* + * MTD ioctls will enable hardware status register protection if and + * only if the protected region is non-empty. Return an error if the + * cfg cannot be activated using the MTD interface. + */ + if ((cfg->range.len == 0) != (cfg->mode == FLASHROM_WP_MODE_DISABLED)) { + return FLASHROM_WP_ERR_OTHER; + } + + /* + * MTD handles write-protection additively, so whatever new range is + * specified is added to the range which is currently protected. To + * just protect the requsted range, we need to disable the current + * write protection and then enable it for the desired range. + */ + int ret = ioctl(fileno(data->dev_fp), MEMUNLOCK, &entire_chip); + if (ret < 0) { + msg_perr("%s: Failed to disable write-protection, MEMUNLOCK ioctl " + "retuned %d, error: %s\n", __func__, ret, strerror(errno)); + return FLASHROM_WP_ERR_WRITE_FAILED; + } + + if (cfg->range.len > 0) { + ret = ioctl(fileno(data->dev_fp), MEMLOCK, &desired_range); + if (ret < 0) { + msg_perr("%s: Failed to enable write-protection, " + "MEMLOCK ioctl retuned %d, error: %s\n", + __func__, ret, strerror(errno)); + return FLASHROM_WP_ERR_WRITE_FAILED; + } + } + + /* Verify */ + struct flashrom_wp_cfg readback_cfg; + enum flashrom_wp_result read_ret = linux_mtd_wp_read_cfg(&readback_cfg, flash); + if (read_ret != FLASHROM_WP_OK) + return read_ret; + + if (readback_cfg.mode != cfg->mode || + readback_cfg.range.start != cfg->range.start || + readback_cfg.range.len != cfg->range.len) { + return FLASHROM_WP_ERR_VERIFY_FAILED; + } + + return FLASHROM_WP_OK; +} + +static enum flashrom_wp_result linux_mtd_wp_get_available_ranges(struct flashrom_wp_ranges **list, struct flashctx *flash) +{ + /* Not supported by MTD interface. */ + return FLASHROM_WP_ERR_RANGE_LIST_UNAVAILABLE; +} + static const struct opaque_master linux_mtd_opaque_master = { /* max_data_{read,write} don't have any effect for this programmer */ .max_data_read = MAX_DATA_UNSPECIFIED, @@ -317,6 +434,9 @@ .write = linux_mtd_write, .erase = linux_mtd_erase, .shutdown = linux_mtd_shutdown, + .wp_read_cfg = linux_mtd_wp_read_cfg, + .wp_write_cfg = linux_mtd_wp_write_cfg, + .wp_get_ranges = linux_mtd_wp_get_available_ranges, };
/* Returns 0 if setup is successful, non-zero to indicate error */ diff --git a/programmer.h b/programmer.h index 0657bb1..47d2dc3 100644 --- a/programmer.h +++ b/programmer.h @@ -403,6 +403,9 @@ int (*read) (struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len); int (*write) (struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len); int (*erase) (struct flashctx *flash, unsigned int blockaddr, unsigned int blocklen); + enum flashrom_wp_result (*wp_write_cfg)(struct flashctx *, const struct flashrom_wp_cfg *); + enum flashrom_wp_result (*wp_read_cfg)(struct flashrom_wp_cfg *, struct flashctx *); + enum flashrom_wp_result (*wp_get_ranges)(struct flashrom_wp_ranges **, struct flashctx *); int (*shutdown)(void *data); void *data; };
38 is the latest approved patch-set. No files were changed between the latest approved patch-set and the submitted one.