1
0
mirror of https://review.coreboot.org/flashrom.git synced 2025-04-26 22:52:34 +02:00

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>
This commit is contained in:
Richard Hughes 2021-01-15 09:48:12 +00:00 committed by Edward O'Callaghan
parent b86ae179ad
commit 40892b0c08
22 changed files with 184 additions and 3 deletions

View File

@ -134,6 +134,7 @@ int write_82802ab(struct flashctx *flash, const uint8_t *src, unsigned int start
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. */

View File

@ -550,6 +550,7 @@ int spi_write_at45db(struct flashctx *flash, const uint8_t *buf, unsigned int st
msg_cerr("Writing page %u failed!\n", i);
return 1;
}
update_progress(flash, FLASHROM_PROGRESS_WRITE, i + page_size, len);
}
return 0;
}

View File

@ -79,6 +79,7 @@ static void cli_classic_usage(const char *name)
#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 @@ int main(int argc, char *argv[])
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 @@ int main(int argc, char *argv[])
OPTION_WP_ENABLE,
OPTION_WP_DISABLE,
OPTION_WP_LIST,
OPTION_PROGRESS,
};
int ret = 0;
@ -523,6 +526,7 @@ int main(int argc, char *argv[])
{"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 @@ int main(int argc, char *argv[])
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 @@ int main(int argc, char *argv[])
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);

View File

@ -64,6 +64,31 @@ void start_logging(void)
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)
{

View File

@ -658,6 +658,7 @@ static int dediprog_spi_bulk_write(struct flashctx *flash, const uint8_t *buf, u
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;

View File

@ -48,6 +48,7 @@ int write_en29lv640b(struct flashctx *flash, const uint8_t *src, unsigned int st
#endif
dst += 2;
src += 2;
update_progress(flash, FLASHROM_PROGRESS_WRITE, i + 2, len);
}
/* FIXME: Ignore errors for now. */

View File

@ -53,7 +53,8 @@ flashrom \- detect, read, write, verify and erase flash chips
[\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 @@ If the file already exists, it will be overwritten. This is the recommended
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

View File

@ -350,6 +350,9 @@ struct flashrom_flashctx {
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 open_logfile(const char * const filename);
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 @@ __attribute__((format(printf, 2, 3)));
#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 {

View File

@ -69,6 +69,34 @@ typedef int(flashrom_log_callback)(enum flashrom_log_level, const char *format,
*/
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 @@ int flashrom_programmer_shutdown(struct flashrom_programmer *flashprog);
* @{
*/
struct flashrom_flashctx;
/**
* @brief Probe for a flash chip.
*

View File

@ -291,6 +291,7 @@ static int it8716f_spi_chip_write_256(struct flashctx *flash, const uint8_t *buf
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;

View File

@ -421,6 +421,7 @@ int write_jedec_1(struct flashctx *flash, const uint8_t *src, unsigned int start
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 @@ int write_jedec(struct flashctx *flash, const uint8_t *buf, unsigned int start,
* 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 @@ int write_jedec(struct flashctx *flash, const uint8_t *buf, unsigned int start,
* (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 @@ int write_jedec(struct flashctx *flash, const uint8_t *buf, unsigned int start,
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;

View File

@ -65,6 +65,25 @@ int print(const enum flashrom_log_level level, const char *const fmt, ...)
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;

View File

@ -24,6 +24,7 @@ LIBFLASHROM_1.0 {
flashrom_programmer_init;
flashrom_programmer_shutdown;
flashrom_set_log_callback;
flashrom_set_progress_callback;
flashrom_shutdown;
flashrom_version_info;
local: *;

View File

@ -214,6 +214,7 @@ static int linux_mtd_read(struct flashctx *flash, uint8_t *buf,
}
i += step;
update_progress(flash, FLASHROM_PROGRESS_READ, i, len);
}
return 0;
@ -256,6 +257,7 @@ static int linux_mtd_write(struct flashctx *flash, const uint8_t *buf,
}
i += step;
update_progress(flash, FLASHROM_PROGRESS_WRITE, i, len);
}
return 0;
@ -292,6 +294,7 @@ static int linux_mtd_erase(struct flashctx *flash,
__func__, ret, strerror(errno));
return 1;
}
update_progress(flash, FLASHROM_PROGRESS_ERASE, u + data->erasesize, len);
}
return 0;

View File

@ -354,6 +354,7 @@ static int lspcon_i2c_spi_read(struct flashctx *flash, uint8_t *buf,
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 @@ static int lspcon_i2c_spi_write_256(struct flashctx *flash, const uint8_t *buf,
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);

View File

@ -398,6 +398,7 @@ static int realtek_mst_i2c_spi_write_256(struct flashctx *flash, const uint8_t *
ret |= realtek_mst_i2c_execute_write(fd);
if (ret)
break;
update_progress(flash, FLASHROM_PROGRESS_WRITE, i + RTK_PAGE_SIZE, len);
}
return ret;

3
spi.c
View File

@ -101,6 +101,8 @@ int spi_chip_read(struct flashctx *flash, uint8_t *buf, unsigned int start,
{
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 @@ int spi_chip_read(struct flashctx *flash, uint8_t *buf, unsigned int start,
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;
}

View File

@ -674,11 +674,14 @@ int spi_read_chunked(struct flashctx *flash, uint8_t *buf, unsigned int start,
{
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 @@ int spi_write_chunked(struct flashctx *flash, const uint8_t *buf, unsigned int s
* 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 @@ int spi_write_chunked(struct flashctx *flash, const uint8_t *buf, unsigned int s
if (rc)
return rc;
}
update_progress(flash, FLASHROM_PROGRESS_WRITE, start - start_address + lenhere, end_address);
}
return 0;
@ -741,6 +747,7 @@ int spi_chip_write_1(struct flashctx *flash, const uint8_t *buf, unsigned int st
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;
}

View File

@ -92,6 +92,7 @@ int write_28sf040(struct flashctx *flash, const uint8_t *src, unsigned int start
/* wait for Toggle bit ready */
toggle_ready_jedec(flash, bios);
update_progress(flash, FLASHROM_PROGRESS_WRITE, i + 1, len);
}
return 0;

View File

@ -67,6 +67,65 @@ int __wrap_spi_send_command(const struct flashctx *flash,
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 */

View File

@ -391,6 +391,7 @@ int main(void)
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),

View File

@ -31,6 +31,7 @@ void flashbuses_to_text_test_success(void **state);
/* 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);