espressif: Fix flash_area_write support for unaligned write accesses

Signed-off-by: Gustavo Henrique Nihei <gustavo.nihei@espressif.com>
Signed-off-by: Almir Okato <almir.okato@espressif.com>
diff --git a/boot/espressif/port/esp_mcuboot.c b/boot/espressif/port/esp_mcuboot.c
index 28cc6b0..3035515 100644
--- a/boot/espressif/port/esp_mcuboot.c
+++ b/boot/espressif/port/esp_mcuboot.c
@@ -210,6 +210,61 @@
     return 0;
 }
 
+static bool aligned_flash_write(size_t dest_addr, const void *src, size_t size)
+{
+    bool flash_encryption_enabled = esp_flash_encryption_enabled();
+
+    if (IS_ALIGNED(dest_addr, 4) && IS_ALIGNED((uintptr_t)src, 4) && IS_ALIGNED(size, 4)) {
+        /* A single write operation is enough when all parameters are aligned */
+
+        return bootloader_flash_write(dest_addr, (void *)src, size, flash_encryption_enabled) == ESP_OK;
+    }
+
+    const uint32_t aligned_addr = ALIGN_DOWN(dest_addr, 4);
+    const uint32_t addr_offset = ALIGN_OFFSET(dest_addr, 4);
+    uint32_t bytes_remaining = size;
+    uint8_t write_data[FLASH_BUFFER_SIZE] = {0};
+
+    /* Perform a read operation considering an offset not aligned to 4-byte boundary */
+
+    uint32_t bytes = MIN(bytes_remaining + addr_offset, sizeof(write_data));
+    if (bootloader_flash_read(aligned_addr, write_data, ALIGN_UP(bytes, 4), true) != ESP_OK) {
+        return false;
+    }
+
+    uint32_t bytes_written = bytes - addr_offset;
+    memcpy(&write_data[addr_offset], src, bytes_written);
+
+    if (bootloader_flash_write(aligned_addr, write_data, ALIGN_UP(bytes, 4), flash_encryption_enabled) != ESP_OK) {
+        return false;
+    }
+
+    bytes_remaining -= bytes_written;
+
+    /* Write remaining data to Flash if any */
+
+    uint32_t offset = bytes;
+
+    while (bytes_remaining != 0) {
+        bytes = MIN(bytes_remaining, sizeof(write_data));
+        if (bootloader_flash_read(aligned_addr + offset, write_data, ALIGN_UP(bytes, 4), true) != ESP_OK) {
+            return false;
+        }
+
+        memcpy(write_data, &((uint8_t *)src)[bytes_written], bytes);
+
+        if (bootloader_flash_write(aligned_addr + offset, write_data, ALIGN_UP(bytes, 4), flash_encryption_enabled) != ESP_OK) {
+            return false;
+        }
+
+        offset += bytes;
+        bytes_written += bytes;
+        bytes_remaining -= bytes;
+    }
+
+    return true;
+}
+
 int flash_area_write(const struct flash_area *fa, uint32_t off, const void *src,
                      uint32_t len)
 {
@@ -223,12 +278,11 @@
         return -1;
     }
 
-    bool flash_encryption_enabled = esp_flash_encryption_enabled();
-
     const uint32_t start_addr = fa->fa_off + off;
     BOOT_LOG_DBG("%s: Addr: 0x%08x Length: %d", __func__, (int)start_addr, (int)len);
 
-    if (bootloader_flash_write(start_addr, (void *)src, len, flash_encryption_enabled) != ESP_OK) {
+    bool success = aligned_flash_write(start_addr, src, len);
+    if (!success) {
         BOOT_LOG_ERR("%s: Flash write failed", __func__);
         return -1;
     }