[PATCH] cmd: sf: Support unaligned flash updates with 'sf update'
Frieder Schrempf
frieder.schrempf at kontron.de
Mon Oct 4 12:41:44 CEST 2021
On 30.09.21 23:21, Michael Walle wrote:
> Am 2021-09-30 18:19, schrieb Frieder Schrempf:
>> From: Frieder Schrempf <frieder.schrempf at kontron.de>
>>
>> Currently 'sf update' supports only offsets that are aligned to the
>> erase block size of the serial flash. Unaligned offsets result in
>> something like:
>>
>> => sf update ${kernel_addr_r} 0x400 ${filesize}
>> device 0 offset 0x400, size 0x11f758
>> SPI flash failed in erase step
>>
>> In order to support unaligned updates, we simply read the first full
>> block and check only the requested part of the block for changes. If
>> necessary, the block is erased, the first (unchanged) part of the block
>> is written back together with the second part of the block (updated
>> data).
>>
>> Signed-off-by: Frieder Schrempf <frieder.schrempf at kontron.de>
>> ---
>> cmd/sf.c | 17 +++++++++++------
>> 1 file changed, 11 insertions(+), 6 deletions(-)
>>
>> diff --git a/cmd/sf.c b/cmd/sf.c
>> index eac27ed2d7..c54b4b10bb 100644
>> --- a/cmd/sf.c
>> +++ b/cmd/sf.c
>> @@ -171,30 +171,32 @@ static int do_spi_flash_probe(int argc, char
>> *const argv[])
>> static const char *spi_flash_update_block(struct spi_flash *flash,
>> u32 offset,
>> size_t len, const char *buf, char *cmp_buf, size_t *skipped)
>> {
>> + u32 offset_aligned = ALIGN_DOWN(offset, flash->sector_size);
>> + u32 sector_offset = offset - offset_aligned;
>> char *ptr = (char *)buf;
>>
>> debug("offset=%#x, sector_size=%#x, len=%#zx\n",
>> offset, flash->sector_size, len);
>> /* Read the entire sector so to allow for rewriting */
>> - if (spi_flash_read(flash, offset, flash->sector_size, cmp_buf))
>> + if (spi_flash_read(flash, offset_aligned, flash->sector_size,
>> cmp_buf))
>> return "read";
>> /* Compare only what is meaningful (len) */
>> - if (memcmp(cmp_buf, buf, len) == 0) {
>> + if (memcmp(cmp_buf + sector_offset, buf, len) == 0) {
>> debug("Skip region %x size %zx: no change\n",
>> offset, len);
>> *skipped += len;
>> return NULL;
>> }
>> /* Erase the entire sector */
>> - if (spi_flash_erase(flash, offset, flash->sector_size))
>> + if (spi_flash_erase(flash, offset_aligned, flash->sector_size))
>> return "erase";
>> /* If it's a partial sector, copy the data into the temp-buffer */
>> if (len != flash->sector_size) {
>> - memcpy(cmp_buf, buf, len);
>> + memcpy(cmp_buf + sector_offset, buf, len);
>> ptr = cmp_buf;
>> }
>> /* Write one complete sector */
>> - if (spi_flash_write(flash, offset, flash->sector_size, ptr))
>> + if (spi_flash_write(flash, offset_aligned, flash->sector_size, ptr))
>> return "write";
>>
>> return NULL;
>> @@ -230,7 +232,10 @@ static int spi_flash_update(struct spi_flash
>> *flash, u32 offset,
>> ulong last_update = get_timer(0);
>>
>> for (; buf < end && !err_oper; buf += todo, offset += todo) {
>> - todo = min_t(size_t, end - buf, flash->sector_size);
>> + if (offset % flash->sector_size)
>
> do_div() to avoid linking errors on some archs, I guess.
Ok, will fix it.
>
>> + todo = flash->sector_size - (offset %
>> flash->sector_size);
>
> This is missing the end-buf calculation, no? I.e. if you update just one
> sector at an offset and the data you write is smaller than "sector_size
> - offset".
Indeed, thanks for catching this.
>
>> + else
>> + todo = min_t(size_t, end - buf, flash->sector_size);
>> if (get_timer(last_update) > 100) {
>> printf(" \rUpdating, %zu%% %lu B/s",
>> 100 - (end - buf) / scale,
>
> -michael
More information about the U-Boot
mailing list