Edward O'Callaghan submitted this change.
libflashrom: Return progress state to the library user
Projects using libflashrom like fwupd expect the user to wait for the
operation to complete. To avoid the user thinking the process has
"hung" or "got stuck" report back the progress complete of the erase,
write and read operations.
Add a new --progress flag to the CLI to report progress of operations.
Include a test for the dummy spi25 device.
TEST=./test_build.sh; ./flashrom -p lspcon_i2c_spi:bus=7 -r /dev/null --progress
Change-Id: I7197572bb7f19e3bdb2bde855d70a0f50fd3854c
Signed-off-by: Richard Hughes <richard@hughsie.com>
Signed-off-by: Daniel Campello <campello@chromium.org>
Reviewed-on: https://review.coreboot.org/c/flashrom/+/49643
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Edward O'Callaghan <quasisec@chromium.org>
Reviewed-by: Anastasia Klimchuk <aklm@chromium.org>
Reviewed-by: Thomas Heijligen <src@posteo.de>
---
M 82802ab.c
M at45db.c
M cli_classic.c
M cli_output.c
M dediprog.c
M en29lv640b.c
M flashrom.8.tmpl
M include/flash.h
M include/libflashrom.h
M it87spi.c
M jedec.c
M libflashrom.c
M libflashrom.map
M linux_mtd.c
M lspcon_i2c_spi.c
M realtek_mst_i2c_spi.c
M spi.c
M spi25.c
M sst28sf040.c
M tests/spi25.c
M tests/tests.c
M tests/tests.h
22 files changed, 184 insertions(+), 3 deletions(-)
diff --git a/82802ab.c b/82802ab.c
index b485c11..1db69e6 100644
--- a/82802ab.c
+++ b/82802ab.c
@@ -134,6 +134,7 @@
chip_writeb(flash, 0x40, dst);
chip_writeb(flash, *src++, dst++);
wait_82802ab(flash);
+ update_progress(flash, FLASHROM_PROGRESS_WRITE, i + 1, len);
}
/* FIXME: Ignore errors for now. */
diff --git a/at45db.c b/at45db.c
index 5f949bb..3f586a2 100644
--- a/at45db.c
+++ b/at45db.c
@@ -550,6 +550,7 @@
msg_cerr("Writing page %u failed!\n", i);
return 1;
}
+ update_progress(flash, FLASHROM_PROGRESS_WRITE, i + page_size, len);
}
return 0;
}
diff --git a/cli_classic.c b/cli_classic.c
index 83ac538..212534c 100644
--- a/cli_classic.c
+++ b/cli_classic.c
@@ -79,6 +79,7 @@
#if CONFIG_PRINT_WIKI == 1
" -z | --list-supported-wiki print supported devices in wiki syntax\n"
#endif
+ " --progress show progress percentage on the standard output\n"
" -p | --programmer <name>[:<param>] specify the programmer device. One of\n");
list_programmers_linebreak(4, 80, 0);
printf(".\n\nYou can specify one of -h, -R, -L, "
@@ -472,6 +473,7 @@
uint32_t wp_start = 0, wp_len = 0;
int read_it = 0, extract_it = 0, write_it = 0, erase_it = 0, verify_it = 0;
int dont_verify_it = 0, dont_verify_all = 0, list_supported = 0, operation_specified = 0;
+ int show_progress = 0;
struct flashrom_layout *layout = NULL;
static const struct programmer_entry *prog = NULL;
enum {
@@ -487,6 +489,7 @@
OPTION_WP_ENABLE,
OPTION_WP_DISABLE,
OPTION_WP_LIST,
+ OPTION_PROGRESS,
};
int ret = 0;
@@ -523,6 +526,7 @@
{"help", 0, NULL, 'h'},
{"version", 0, NULL, 'R'},
{"output", 1, NULL, 'o'},
+ {"progress", 0, NULL, OPTION_PROGRESS},
{NULL, 0, NULL, 0},
};
@@ -763,6 +767,9 @@
cli_classic_abort_usage("No log filename specified.\n");
}
break;
+ case OPTION_PROGRESS:
+ show_progress = 1;
+ break;
default:
cli_classic_abort_usage(NULL);
break;
@@ -934,6 +941,13 @@
fill_flash = &flashes[0];
+ unsigned int progress_user_data[FLASHROM_PROGRESS_NR];
+ struct flashrom_progress progress_state = {
+ .user_data = progress_user_data
+ };
+ if (show_progress)
+ flashrom_set_progress_callback(fill_flash, &flashrom_progress_cb, &progress_state);
+
print_chip_support_status(fill_flash->chip);
unsigned int limitexceeded = count_max_decode_exceedings(fill_flash);
diff --git a/cli_output.c b/cli_output.c
index 629db67..e5b829a 100644
--- a/cli_output.c
+++ b/cli_output.c
@@ -64,6 +64,31 @@
verbose_screen = oldverbose_screen;
}
+static const char *flashrom_progress_stage_to_string(enum flashrom_progress_stage stage)
+{
+ if (stage == FLASHROM_PROGRESS_READ)
+ return "READ";
+ if (stage == FLASHROM_PROGRESS_WRITE)
+ return "WRITE";
+ if (stage == FLASHROM_PROGRESS_ERASE)
+ return "ERASE";
+ return "UNKNOWN";
+}
+
+void flashrom_progress_cb(struct flashrom_flashctx *flashctx)
+{
+ struct flashrom_progress *progress_state = flashctx->progress_state;
+ unsigned int pc = 0;
+ unsigned int *percentages = progress_state->user_data;
+ if (progress_state->current > 0 && progress_state->total > 0)
+ pc = ((unsigned long long) progress_state->current * 10000llu) /
+ ((unsigned long long) progress_state->total * 100llu);
+ if (percentages[progress_state->stage] != pc) {
+ percentages[progress_state->stage] = pc;
+ msg_ginfo("[%s] %u%% complete... ", flashrom_progress_stage_to_string(progress_state->stage), pc);
+ }
+}
+
/* Please note that level is the verbosity, not the importance of the message. */
int flashrom_print_cb(enum flashrom_log_level level, const char *fmt, va_list ap)
{
diff --git a/dediprog.c b/dediprog.c
index 8a05a7c..3bd61a0 100644
--- a/dediprog.c
+++ b/dediprog.c
@@ -658,6 +658,7 @@
msg_perr("SPI bulk write failed, expected %i, got %s!\n", 512, libusb_error_name(ret));
return 1;
}
+ update_progress(flash, FLASHROM_PROGRESS_WRITE, i + 1, count);
}
return 0;
diff --git a/en29lv640b.c b/en29lv640b.c
index 5b01904..6c0a565 100644
--- a/en29lv640b.c
+++ b/en29lv640b.c
@@ -48,6 +48,7 @@
#endif
dst += 2;
src += 2;
+ update_progress(flash, FLASHROM_PROGRESS_WRITE, i + 2, len);
}
/* FIXME: Ignore errors for now. */
diff --git a/flashrom.8.tmpl b/flashrom.8.tmpl
index 58f5ec0..3dac4b9 100644
--- a/flashrom.8.tmpl
+++ b/flashrom.8.tmpl
@@ -53,7 +53,8 @@
[\fB\-\-wp\-status\fR] [\fB\-\-wp\-list\fR] [\fB\-\-wp\-enable\fR|\fB\-\-wp\-disable\fR]
[\fB\-\-wp\-range\fR <start>,<length>|\fB\-\-wp\-region\fR <region>]
[\fB\-n\fR] [\fB\-N\fR] [\fB\-f\fR])]
- [\fB\-V\fR[\fBV\fR[\fBV\fR]]] [\fB-o\fR <logfile>]
+ [\fB\-V\fR[\fBV\fR[\fBV\fR]]] [\fB-o\fR <logfile>] [\fB\-\-progress\fR]
+
.SH DESCRIPTION
.B flashrom
is a utility for detecting, reading, writing, verifying and erasing flash
@@ -445,6 +446,9 @@
way to gather logs from flashrom because they will be verbose even if the
on-screen messages are not verbose and don't require output redirection.
.TP
+.B "\-\-progress"
+Show progress percentage of operations on the standard output.
+.TP
.B "\-R, \-\-version"
Show version information and exit.
.SH PROGRAMMER-SPECIFIC INFORMATION
diff --git a/include/flash.h b/include/flash.h
index 27ade09..45533ba 100644
--- a/include/flash.h
+++ b/include/flash.h
@@ -350,6 +350,9 @@
chip_restore_fn_cb_t func;
uint8_t status;
} chip_restore_fn[MAX_CHIP_RESTORE_FUNCTIONS];
+ /* Progress reporting */
+ flashrom_progress_callback *progress_callback;
+ struct flashrom_progress *progress_state;
};
/* Timing used in probe routines. ZERO is -2 to differentiate between an unset
@@ -446,6 +449,7 @@
int close_logfile(void);
void start_logging(void);
int flashrom_print_cb(enum flashrom_log_level level, const char *fmt, va_list ap);
+void flashrom_progress_cb(struct flashrom_flashctx *flashctx);
/* Let gcc and clang check for correct printf-style format strings. */
int print(enum flashrom_log_level level, const char *fmt, ...)
#ifdef __MINGW32__
@@ -474,6 +478,7 @@
#define msg_gspew(...) print(FLASHROM_MSG_SPEW, __VA_ARGS__) /* general debug spew */
#define msg_pspew(...) print(FLASHROM_MSG_SPEW, __VA_ARGS__) /* programmer debug spew */
#define msg_cspew(...) print(FLASHROM_MSG_SPEW, __VA_ARGS__) /* chip debug spew */
+void update_progress(struct flashctx *flash, enum flashrom_progress_stage stage, size_t current, size_t total);
/* spi.c */
struct spi_command {
diff --git a/include/libflashrom.h b/include/libflashrom.h
index cf1f462..21a5785 100644
--- a/include/libflashrom.h
+++ b/include/libflashrom.h
@@ -69,6 +69,34 @@
*/
void flashrom_set_log_callback(flashrom_log_callback *log_callback);
+enum flashrom_progress_stage {
+ FLASHROM_PROGRESS_READ,
+ FLASHROM_PROGRESS_WRITE,
+ FLASHROM_PROGRESS_ERASE,
+ FLASHROM_PROGRESS_NR,
+};
+struct flashrom_progress {
+ enum flashrom_progress_stage stage;
+ size_t current;
+ size_t total;
+ void *user_data;
+};
+struct flashrom_flashctx;
+typedef void(flashrom_progress_callback)(struct flashrom_flashctx *flashctx);
+/**
+ * @brief Set the progress callback function.
+ *
+ * Set a callback function which will be invoked whenever libflashrom wants
+ * to indicate the progress has changed. This allows frontends to do whatever
+ * they see fit with such values, e.g. update a progress bar in a GUI tool.
+ *
+ * @param progress_callback Pointer to the new progress callback function.
+ * @param progress_state Pointer to progress state to include with the progress
+ * callback.
+ */
+void flashrom_set_progress_callback(struct flashrom_flashctx *const flashctx,
+ flashrom_progress_callback *progress_callback, struct flashrom_progress *progress_state);
+
/** @} */ /* end flashrom-general */
/**
@@ -174,7 +202,6 @@
* @{
*/
-struct flashrom_flashctx;
/**
* @brief Probe for a flash chip.
*
diff --git a/it87spi.c b/it87spi.c
index 204816a..a24a172 100644
--- a/it87spi.c
+++ b/it87spi.c
@@ -291,6 +291,7 @@
int ret = it8716f_spi_page_program(flash, buf, start);
if (ret)
return ret;
+ update_progress(flash, FLASHROM_PROGRESS_WRITE, chip->page_size - len, chip->page_size);
start += chip->page_size;
len -= chip->page_size;
buf += chip->page_size;
diff --git a/jedec.c b/jedec.c
index 0709efa..2542f3e 100644
--- a/jedec.c
+++ b/jedec.c
@@ -421,6 +421,7 @@
if (write_byte_program_jedec_common(flash, src, dst, mask))
failed = 1;
dst++, src++;
+ update_progress(flash, FLASHROM_PROGRESS_WRITE, i + 1, len);
}
if (failed)
msg_cerr(" writing sector at 0x%" PRIxPTR " failed!\n", olddst);
@@ -487,6 +488,7 @@
* we're OK for now.
*/
unsigned int page_size = flash->chip->page_size;
+ unsigned int nwrites = (start + len - 1) / page_size;
/* Warning: This loop has a very unusual condition and body.
* The loop needs to go through each page with at least one affected
@@ -497,7 +499,7 @@
* (start + len - 1) / page_size. Since we want to include that last
* page as well, the loop condition uses <=.
*/
- for (i = start / page_size; i <= (start + len - 1) / page_size; i++) {
+ for (i = start / page_size; i <= nwrites; i++) {
/* Byte position of the first byte in the range in this page. */
/* starthere is an offset to the base address of the chip. */
starthere = max(start, i * page_size);
@@ -506,6 +508,7 @@
if (write_page_write_jedec_common(flash, buf + starthere - start, starthere, lenhere))
return 1;
+ update_progress(flash, FLASHROM_PROGRESS_WRITE, i + 1, nwrites + 1);
}
return 0;
diff --git a/libflashrom.c b/libflashrom.c
index e70c1d1..0c6613e 100644
--- a/libflashrom.c
+++ b/libflashrom.c
@@ -65,6 +65,25 @@
return 0;
}
+void flashrom_set_progress_callback(struct flashrom_flashctx *flashctx, flashrom_progress_callback *progress_callback, struct flashrom_progress *progress_state)
+{
+ flashctx->progress_callback = progress_callback;
+ flashctx->progress_state = progress_state;
+}
+/** @private */
+void update_progress(struct flashrom_flashctx *flashctx, enum flashrom_progress_stage stage, size_t current, size_t total)
+{
+ if (flashctx->progress_callback == NULL)
+ return;
+ if (current > total)
+ current = total;
+
+ flashctx->progress_state->stage = stage;
+ flashctx->progress_state->current = current;
+ flashctx->progress_state->total = total;
+ flashctx->progress_callback(flashctx);
+}
+
const char *flashrom_version_info(void)
{
return flashrom_version;
diff --git a/libflashrom.map b/libflashrom.map
index 2249d35..b3af70b 100644
--- a/libflashrom.map
+++ b/libflashrom.map
@@ -24,6 +24,7 @@
flashrom_programmer_init;
flashrom_programmer_shutdown;
flashrom_set_log_callback;
+ flashrom_set_progress_callback;
flashrom_shutdown;
flashrom_version_info;
local: *;
diff --git a/linux_mtd.c b/linux_mtd.c
index 445a903..88488c0 100644
--- a/linux_mtd.c
+++ b/linux_mtd.c
@@ -214,6 +214,7 @@
}
i += step;
+ update_progress(flash, FLASHROM_PROGRESS_READ, i, len);
}
return 0;
@@ -256,6 +257,7 @@
}
i += step;
+ update_progress(flash, FLASHROM_PROGRESS_WRITE, i, len);
}
return 0;
@@ -292,6 +294,7 @@
__func__, ret, strerror(errno));
return 1;
}
+ update_progress(flash, FLASHROM_PROGRESS_ERASE, u + data->erasesize, len);
}
return 0;
diff --git a/lspcon_i2c_spi.c b/lspcon_i2c_spi.c
index cabe21f..f3c11d6 100644
--- a/lspcon_i2c_spi.c
+++ b/lspcon_i2c_spi.c
@@ -354,6 +354,7 @@
for (i = 0; i < len; i += LSPCON_PAGE_SIZE) {
ret |= lspcon_i2c_spi_map_page(fd, start + i);
ret |= lspcon_i2c_spi_read_data(fd, PAGE_ADDRESS, buf + i, min(len - i, LSPCON_PAGE_SIZE));
+ update_progress(flash, FLASHROM_PROGRESS_READ, i + LSPCON_PAGE_SIZE, len);
}
return ret;
@@ -394,6 +395,7 @@
for (unsigned int i = 0; i < len; i += LSPCON_PAGE_SIZE) {
ret |= lspcon_i2c_spi_map_page(fd, start + i);
ret |= lspcon_i2c_spi_write_page(fd, buf + i, min(len - i, LSPCON_PAGE_SIZE));
+ update_progress(flash, FLASHROM_PROGRESS_WRITE, i + LSPCON_PAGE_SIZE, len);
}
ret |= lspcon_i2c_spi_enable_write_protection(fd);
diff --git a/realtek_mst_i2c_spi.c b/realtek_mst_i2c_spi.c
index 954150d..c7de2fe 100644
--- a/realtek_mst_i2c_spi.c
+++ b/realtek_mst_i2c_spi.c
@@ -398,6 +398,7 @@
ret |= realtek_mst_i2c_execute_write(fd);
if (ret)
break;
+ update_progress(flash, FLASHROM_PROGRESS_WRITE, i + RTK_PAGE_SIZE, len);
}
return ret;
diff --git a/spi.c b/spi.c
index f2e91c4..9f820d7 100644
--- a/spi.c
+++ b/spi.c
@@ -101,6 +101,8 @@
{
int ret;
size_t to_read;
+ size_t start_address = start;
+ size_t end_address = len - start;
for (; len; len -= to_read, buf += to_read, start += to_read) {
/* Do not cross 16MiB boundaries in a single transfer.
This helps with
@@ -110,6 +112,7 @@
ret = flash->mst->spi.read(flash, buf, start, to_read);
if (ret)
return ret;
+ update_progress(flash, FLASHROM_PROGRESS_READ, start - start_address + to_read, end_address);
}
return 0;
}
diff --git a/spi25.c b/spi25.c
index e96a38d..39a8876 100644
--- a/spi25.c
+++ b/spi25.c
@@ -674,11 +674,14 @@
{
int ret;
size_t to_read;
+ size_t start_address = start;
+ size_t end_address = len - start;
for (; len; len -= to_read, buf += to_read, start += to_read) {
to_read = min(chunksize, len);
ret = spi_nbyte_read(flash, start, buf, to_read);
if (ret)
return ret;
+ update_progress(flash, FLASHROM_PROGRESS_READ, start - start_address + to_read, end_address);
}
return 0;
}
@@ -698,6 +701,8 @@
* we're OK for now.
*/
unsigned int page_size = flash->chip->page_size;
+ size_t start_address = start;
+ size_t end_address = len - start;
/* Warning: This loop has a very unusual condition and body.
* The loop needs to go through each page with at least one affected
@@ -722,6 +727,7 @@
if (rc)
return rc;
}
+ update_progress(flash, FLASHROM_PROGRESS_WRITE, start - start_address + lenhere, end_address);
}
return 0;
@@ -741,6 +747,7 @@
for (i = start; i < start + len; i++) {
if (spi_nbyte_program(flash, i, buf + i - start, 1))
return 1;
+ update_progress(flash, FLASHROM_PROGRESS_WRITE, i - start, len - start);
}
return 0;
}
diff --git a/sst28sf040.c b/sst28sf040.c
index 3da25f1..9a08d6b 100644
--- a/sst28sf040.c
+++ b/sst28sf040.c
@@ -92,6 +92,7 @@
/* wait for Toggle bit ready */
toggle_ready_jedec(flash, bios);
+ update_progress(flash, FLASHROM_PROGRESS_WRITE, i + 1, len);
}
return 0;
diff --git a/tests/spi25.c b/tests/spi25.c
index f57251e..f101910 100644
--- a/tests/spi25.c
+++ b/tests/spi25.c
@@ -67,6 +67,65 @@
return 0;
}
+static void spi_read_progress_cb(struct flashrom_flashctx *flashctx)
+{
+ struct flashrom_progress *progress_state = flashctx->progress_state;
+ uint32_t *cnt = (uint32_t *) progress_state->user_data;
+ assert_int_equal(0x300, progress_state->total);
+ switch (*cnt) {
+ case 0:
+ assert_int_equal(0x100, progress_state->current);
+ break;
+ case 1:
+ assert_int_equal(0x200, progress_state->current);
+ break;
+ case 2:
+ assert_int_equal(0x300, progress_state->current);
+ break;
+ case 3:
+ assert_int_equal(0x300, progress_state->current);
+ break;
+ case 4:
+ assert_int_equal(0x300, progress_state->current);
+ break;
+ default:
+ fail();
+ }
+ (*cnt)++;
+}
+
+void spi_read_chunked_test_success(void **state)
+{
+ (void) state; /* unused */
+ uint8_t buf[0x400] = { 0x0 };
+ uint32_t cnt = 0;
+ const unsigned int max_data_read = 0x100;
+ const unsigned int offset = 0x100;
+ struct registered_master mst = {
+ .spi.read = default_spi_read,
+ .spi.max_data_read = max_data_read
+ };
+
+ /* setup initial test state */
+ struct flashctx flashctx = {
+ .chip = &mock_chip,
+ .mst = &mst
+ };
+ struct flashrom_progress progress_state = {
+ .user_data = (void *) &cnt,
+ };
+ flashrom_set_progress_callback(&flashctx, spi_read_progress_cb, &progress_state);
+ for (int i = 0; i < 4; i++) {
+ expect_memory(__wrap_spi_send_command, flash,
+ &flashctx, sizeof(flashctx));
+ will_return(__wrap_spi_send_command, JEDEC_WRDI);
+ will_return(__wrap_spi_send_command, JEDEC_READ);
+ will_return(__wrap_spi_send_command, max_data_read);
+ }
+ assert_int_equal(0, spi_chip_read(&flashctx, buf, offset, sizeof(buf)));
+ assert_int_equal(5, cnt);
+}
+
void spi_write_enable_test_success(void **state)
{
(void) state; /* unused */
diff --git a/tests/tests.c b/tests/tests.c
index 2f955ff..a719a8f 100644
--- a/tests/tests.c
+++ b/tests/tests.c
@@ -391,6 +391,7 @@
const struct CMUnitTest spi25_tests[] = {
cmocka_unit_test(spi_write_enable_test_success),
cmocka_unit_test(spi_write_disable_test_success),
+ cmocka_unit_test(spi_read_chunked_test_success),
cmocka_unit_test(probe_spi_rdid_test_success),
cmocka_unit_test(probe_spi_rdid4_test_success),
cmocka_unit_test(probe_spi_rems_test_success),
diff --git a/tests/tests.h b/tests/tests.h
index e2b2211..5ffc145 100644
--- a/tests/tests.h
+++ b/tests/tests.h
@@ -31,6 +31,7 @@
/* spi25.c */
void spi_write_enable_test_success(void **state);
void spi_write_disable_test_success(void **state);
+void spi_read_chunked_test_success(void **state);
void probe_spi_rdid_test_success(void **state);
void probe_spi_rdid4_test_success(void **state);
void probe_spi_rems_test_success(void **state);
To view, visit change 49643. To unsubscribe, or for help writing mail filters, visit settings.