Nikolai Artemiev has uploaded this change for review.

View Change

libflashrom,linux_mtd: add linux_mtd writeprotect support

BUG=b:182223106
TEST=make
BRANCH=none

Change-Id: I5c86e28cdec44bec49ba1d36f8ab62241b9b01da
Signed-off-by: Nikolai Artemiev <nartemiev@google.com>
---
M libflashrom.c
M linux_mtd.c
M programmer.h
3 files changed, 138 insertions(+), 19 deletions(-)

git pull ssh://review.coreboot.org:29418/flashrom refs/changes/97/61897/1
diff --git a/libflashrom.c b/libflashrom.c
index eb283c8..1fabab6 100644
--- a/libflashrom.c
+++ b/libflashrom.c
@@ -711,12 +711,15 @@
*/
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.
- */
- return wp_write_cfg(flash, cfg);
+ if (flash->mst->buses_supported & BUS_SPI)
+ return wp_write_cfg(flash, cfg);
+
+ if (flash->mst->buses_supported & BUS_PROG) {
+ if (flash->mst->opaque.wp_write_cfg)
+ return flash->mst->opaque.wp_write_cfg(flash, cfg);
+ }
+
+ return FLASHROM_WP_ERR_OTHER;
}

/**
@@ -730,12 +733,15 @@
*/
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.
- */
- return wp_read_cfg(cfg, flash);
+ if (flash->mst->buses_supported & BUS_SPI)
+ return wp_read_cfg(cfg, flash);
+
+ if (flash->mst->buses_supported & BUS_PROG) {
+ if (flash->mst->opaque.wp_read_cfg)
+ return flash->mst->opaque.wp_read_cfg(cfg, flash);
+ }
+
+ return FLASHROM_WP_ERR_OTHER;
}

/**
@@ -752,13 +758,15 @@
*/
enum flashrom_wp_result flashrom_wp_get_available_ranges(struct flashrom_wp_range_list **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.
- */
- return wp_get_available_ranges(list, flash);
+ if (flash->mst->buses_supported & BUS_SPI)
+ return wp_get_available_ranges(list, flash);
+
+ if (flash->mst->buses_supported & BUS_PROG) {
+ if (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..7d394a5 100644
--- a/linux_mtd.c
+++ b/linux_mtd.c
@@ -308,6 +308,111 @@
return 0;
}

+static enum flashrom_wp_result linux_mtd_wp_write_cfg(struct flashctx *flash, const struct flashrom_wp_cfg *cfg)
+{
+ struct linux_mtd_data *data = flash->mst->opaque.data;
+
+ struct erase_info_user entire_chip = {
+ .start = 0,
+ .length = data->total_size,
+ };
+ struct erase_info_user desired_range = {
+ .start = cfg->range.start,
+ .length = cfg->range.len,
+ };
+
+ /*
+ * Enabling a protection range will enable hardware status register
+ * protection as well, bail out if the requested configuration is not
+ * supported by the MTD interface.
+ */
+ if (cfg->range.len > 0 && cfg->mode != FLASHROM_WP_MODE_HARDWARE) {
+ 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;
+ }
+ }
+
+ return FLASHROM_WP_OK;
+}
+
+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_get_available_ranges(struct flashrom_wp_range_list **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 +422,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 08e5e9c..9344685 100644
--- a/programmer.h
+++ b/programmer.h
@@ -400,6 +400,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_range_list **, struct flashctx *);
int (*shutdown)(void *data);
void *data;
};

To view, visit change 61897. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: I5c86e28cdec44bec49ba1d36f8ab62241b9b01da
Gerrit-Change-Number: 61897
Gerrit-PatchSet: 1
Gerrit-Owner: Nikolai Artemiev <nartemiev@google.com>
Gerrit-MessageType: newchange