[PATCH] flashcp: Add write last option.

Piotr Esden-Tempski piotr at esden.net
Thu Aug 31 17:55:03 PDT 2023


This option is useful for power fail resilient bootloader updates, for
systems that check only the partition header for validity before
attempting to load the bootloader. For example zynq.

Postponing the write of a specified amount of bytes at the beginning of
a bootloader makes sure a written bootloader slot is not considered
valid until all the data is written.
---
 misc-utils/flashcp.c | 69 +++++++++++++++++++++++++++++++++++++-------
 1 file changed, 59 insertions(+), 10 deletions(-)

diff --git a/misc-utils/flashcp.c b/misc-utils/flashcp.c
index e1be292..9c48637 100644
--- a/misc-utils/flashcp.c
+++ b/misc-utils/flashcp.c
@@ -64,6 +64,7 @@
 #define FLAG_DEVICE		0x08
 #define FLAG_ERASE_ALL	0x10
 #define FLAG_PARTITION	0x20
+#define FLAG_WR_LAST	0x40
 
 /* error levels */
 #define LOG_NORMAL	1
@@ -104,13 +105,14 @@ static NORETURN void showusage(bool error)
 			"       %1$s -h | --help\n"
 			"       %1$s -V | --version\n"
 			"\n"
-			"   -h | --help      Show this help message\n"
-			"   -v | --verbose   Show progress reports\n"
-			"   -p | --partition Only copy different block from file to device\n"
-			"   -A | --erase-all Erases the whole device regardless of the image size\n"
-			"   -V | --version   Show version information and exit\n"
-			"   <filename>       File which you want to copy to flash\n"
-			"   <device>         Flash device node or 'mtd:<name>' to write to (e.g. /dev/mtd0, /dev/mtd1, mtd:data, etc.)\n"
+			"   -h | --help           Show this help message\n"
+			"   -v | --verbose        Show progress reports\n"
+			"   -p | --partition      Only copy different block from file to device\n"
+			"   -A | --erase-all      Erases the whole device regardless of the image size\n"
+			"   -l | --wr-last=bytes  Write the first [bytes] last\n"
+			"   -V | --version        Show version information and exit\n"
+			"   <filename>            File which you want to copy to flash\n"
+			"   <device>              Flash device node or 'mtd:<name>' to write to (e.g. /dev/mtd0, /dev/mtd1, mtd:data, etc.)\n"
 			"\n",
 			PROGRAM_NAME);
 
@@ -219,7 +221,9 @@ int main (int argc,char *argv[])
 	struct mtd_info_user mtd;
 	struct erase_info_user erase;
 	struct stat filestat;
-	unsigned char *src,*dest;
+	unsigned char *src,*dest,*wrlast_buf;
+	unsigned long long wrlast_len = 0;
+	int error = 0;
 
 	/*********************
 	 * parse cmd-line
@@ -227,12 +231,13 @@ int main (int argc,char *argv[])
 
 	for (;;) {
 		int option_index = 0;
-		static const char *short_options = "hvpAV";
+		static const char *short_options = "hvpAl:V";
 		static const struct option long_options[] = {
 			{"help", no_argument, 0, 'h'},
 			{"verbose", no_argument, 0, 'v'},
 			{"partition", no_argument, 0, 'p'},
 			{"erase-all", no_argument, 0, 'A'},
+			{"wr-last", required_argument, 0, 'l'},
 			{"version", no_argument, 0, 'V'},
 			{0, 0, 0, 0},
 		};
@@ -260,6 +265,10 @@ int main (int argc,char *argv[])
 				flags |= FLAG_ERASE_ALL;
 				DEBUG("Got FLAG_ERASE_ALL\n");
 				break;
+			case 'l':
+				flags |= FLAG_WR_LAST;
+				wrlast_len = simple_strtoll(optarg, &error);
+				break;
 			case 'V':
 				common_print_version();
 				exit(EXIT_SUCCESS);
@@ -288,6 +297,10 @@ int main (int argc,char *argv[])
 	if (flags & FLAG_PARTITION && flags & FLAG_ERASE_ALL)
 		log_failure("Option --partition does not support --erase-all\n");
 
+	if (flags & FLAG_PARTITION && flags & FLAG_WR_LAST) {
+		log_failure("Option --partition does not support --wr-last\n");
+	}
+
 	atexit (cleanup);
 
 	/* get some info about the flash device */
@@ -364,10 +377,40 @@ int main (int argc,char *argv[])
 	 * write the entire file to flash *
 	 **********************************/
 
-	log_verbose ("Writing data: 0k/%lluk (0%%)",KB ((unsigned long long)filestat.st_size));
 	size = filestat.st_size;
 	i = mtd.erasesize;
 	written = 0;
+
+	if ((flags & FLAG_WR_LAST) && (filestat.st_size > wrlast_len)) {
+		if (wrlast_len > mtd.erasesize)
+			log_failure("The wrlast (%lluk) is larger than erasesize (%lluk)\n", KB (wrlast_len), KB ((unsigned long long)mtd.erasesize));
+
+		if (size < mtd.erasesize) i = size;
+
+		log_verbose ("Reading %lluk of data to write last.\n", KB ((unsigned long long)wrlast_len));
+		wrlast_buf = malloc(wrlast_len);
+		if (!wrlast_buf)
+			log_failure("Malloc failed");
+		safe_read (fil_fd, filename, wrlast_buf, wrlast_len);
+		safe_lseek(dev_fd, wrlast_len, SEEK_SET, device);
+		written += wrlast_len;
+		size -= wrlast_len;
+		i -= wrlast_len;
+
+		log_verbose ("Writing remaining erase block data: %dk/%lluk (%llu%%)\n",
+				KB (written + i),
+				KB ((unsigned long long)filestat.st_size),
+				PERCENTAGE ((unsigned long long)written + i, (unsigned long long)filestat.st_size));
+		safe_read (fil_fd, filename, src, i);
+		safe_write(dev_fd, src, i, written, (unsigned long long)filestat.st_size, device);
+
+		written += i;
+		size -= i;
+		i = mtd.erasesize;
+	} else {
+		log_verbose ("Writing data: 0k/%lluk (0%%)",KB ((unsigned long long)filestat.st_size));
+	}
+
 	while (size)
 	{
 		if (size < mtd.erasesize) i = size;
@@ -390,6 +433,12 @@ int main (int argc,char *argv[])
 			KB ((unsigned long long)filestat.st_size));
 	DEBUG("Wrote %d / %lluk bytes\n",written,(unsigned long long)filestat.st_size);
 
+	if ((flags & FLAG_WR_LAST) && (filestat.st_size > wrlast_len)) {
+		log_verbose ("Writing %lluk of the write last data.\n", KB ((unsigned long long)wrlast_len));
+		safe_rewind (dev_fd, device);
+		safe_write(dev_fd, wrlast_buf, wrlast_len, 0, wrlast_len, device);
+	}
+
 	/**********************************
 	 * verify that flash == file data *
 	 **********************************/
-- 
2.34.1




More information about the linux-mtd mailing list