bootutil: Allow larger minimum flash write

[kristine.jassmann@renesas.com: Allow larger minimum flash write]
[michael.thomas@renesas.com: Add changes for 1.8]
[michael.thomas@renesas.com: Add magic alignment fix]
[gustavo.nihei@espressif.com: bootutil: Address issues from PR 949]

Co-authored-by: Kristine Jassmann <kristine.jassmann@renesas.com>
Co-authored-by: Michael Thomas <michael.thomas@renesas.com>
Co-authored-by: Gustavo Henrique Nihei <gustavo.nihei@espressif.com>
Signed-off-by: Kristine Jassmann <kristine.jassmann@renesas.com>
Signed-off-by: Michael Thomas <michael.thomas@renesas.com>
Signed-off-by: Gustavo Henrique Nihei <gustavo.nihei@espressif.com>
diff --git a/scripts/imgtool/image.py b/scripts/imgtool/image.py
index 684c6b3..66cee43 100644
--- a/scripts/imgtool/image.py
+++ b/scripts/imgtool/image.py
@@ -43,7 +43,7 @@
 BIN_EXT = "bin"
 INTEL_HEX_EXT = "hex"
 DEFAULT_MAX_SECTORS = 128
-MAX_ALIGN = 8
+DEFAULT_MAX_ALIGN = 8
 DEP_IMAGES_KEY = "images"
 DEP_VERSIONS_KEY = "versions"
 MAX_SW_TYPE_LENGTH = 12  # Bytes
@@ -135,7 +135,7 @@
                  slot_size=0, max_sectors=DEFAULT_MAX_SECTORS,
                  overwrite_only=False, endian="little", load_addr=0,
                  rom_fixed=None, erased_val=None, save_enctlv=False,
-                 security_counter=None):
+                 security_counter=None, max_align=DEFAULT_MAX_ALIGN):
 
         if load_addr and rom_fixed:
             raise click.UsageError("Can not set rom_fixed and load_addr at the same time")
@@ -158,6 +158,7 @@
         self.enckey = None
         self.save_enctlv = save_enctlv
         self.enctlv_len = 0
+        self.max_align = DEFAULT_MAX_ALIGN if max_align is None else int(max_align)
 
         if security_counter == 'auto':
             # Security counter has not been explicitly provided,
@@ -231,9 +232,12 @@
                 trailer_addr = (self.base_addr + self.slot_size) - trailer_size
                 padding = bytearray([self.erased_val] * 
                                     (trailer_size - len(boot_magic)))
-                if self.confirm and not self.overwrite_only:
-                    padding[-MAX_ALIGN] = 0x01  # image_ok = 0x01
                 padding += boot_magic
+                if self.confirm and not self.overwrite_only:
+                    magic_size = 16
+                    magic_align_size = (int((magic_size - 1) / self.max_align) + 1) * self.max_align
+                    image_ok_idx = -(magic_align_size + self.max_align)
+                    padding[image_ok_idx] = 0x01  # image_ok = 0x01
                 h.puts(trailer_addr, bytes(padding))
             h.tofile(path, 'hex')
         else:
@@ -517,10 +521,11 @@
                       save_enctlv, enctlv_len):
         # NOTE: should already be checked by the argument parser
         magic_size = 16
+        magic_align_size = (int((magic_size - 1) / self.max_align) + 1) * self.max_align
         if overwrite_only:
-            return MAX_ALIGN * 2 + magic_size
+            return self.max_align * 2 + magic_align_size
         else:
-            if write_size not in set([1, 2, 4, 8]):
+            if write_size not in set([1, 2, 4, 8, 16, 32]):
                 raise click.BadParameter("Invalid alignment: {}".format(
                     write_size))
             m = DEFAULT_MAX_SECTORS if max_sectors is None else max_sectors
@@ -528,12 +533,12 @@
             if enckey is not None:
                 if save_enctlv:
                     # TLV saved by the bootloader is aligned
-                    keylen = (int((enctlv_len - 1) / MAX_ALIGN) + 1) * MAX_ALIGN
+                    keylen = (int((enctlv_len - 1) / self.max_align) + 1) * self.max_align
                 else:
-                    keylen = 16
+                    keylen = (int((16 - 1) / self.max_align) + 1) * self.max_align
                 trailer += keylen * 2  # encryption keys
-            trailer += MAX_ALIGN * 4  # image_ok/copy_done/swap_info/swap_size
-            trailer += magic_size
+            trailer += self.max_align * 4  # image_ok/copy_done/swap_info/swap_size
+            trailer += magic_align_size
             return trailer
 
     def pad_to(self, size):
@@ -544,9 +549,12 @@
         padding = size - (len(self.payload) + tsize)
         pbytes = bytearray([self.erased_val] * padding)
         pbytes += bytearray([self.erased_val] * (tsize - len(boot_magic)))
-        if self.confirm and not self.overwrite_only:
-            pbytes[-MAX_ALIGN] = 0x01  # image_ok = 0x01
         pbytes += boot_magic
+        if self.confirm and not self.overwrite_only:
+            magic_size = 16
+            magic_align_size = (int((magic_size - 1) / self.max_align) + 1) * self.max_align
+            image_ok_idx = -(magic_align_size + self.max_align)
+            pbytes[image_ok_idx] = 0x01  # image_ok = 0x01
         self.payload += pbytes
 
     @staticmethod
diff --git a/scripts/imgtool/main.py b/scripts/imgtool/main.py
index dd6c044..87eccd9 100755
--- a/scripts/imgtool/main.py
+++ b/scripts/imgtool/main.py
@@ -288,8 +288,10 @@
               help='Specify the value of security counter. Use the `auto` '
               'keyword to automatically generate it from the image version.')
 @click.option('-v', '--version', callback=validate_version,  required=True)
-@click.option('--align', type=click.Choice(['1', '2', '4', '8']),
+@click.option('--align', type=click.Choice(['1', '2', '4', '8', '16', '32']),
               required=True)
+@click.option('--max-align', type=click.Choice(['8', '16', '32']),
+              required=False)
 @click.option('--public-key-format', type=click.Choice(['hash', 'full']),
               default='hash', help='In what format to add the public key to '
               'the image manifest: full key or hash of the key.')
@@ -301,7 +303,7 @@
          pad_header, slot_size, pad, confirm, max_sectors, overwrite_only,
          endian, encrypt_keylen, encrypt, infile, outfile, dependencies,
          load_addr, hex_addr, erased_val, save_enctlv, security_counter,
-         boot_record, custom_tlv, rom_fixed):
+         boot_record, custom_tlv, rom_fixed, max_align):
 
     if confirm:
         # Confirmed but non-padded images don't make much sense, because
@@ -313,7 +315,7 @@
                       max_sectors=max_sectors, overwrite_only=overwrite_only,
                       endian=endian, load_addr=load_addr, rom_fixed=rom_fixed,
                       erased_val=erased_val, save_enctlv=save_enctlv,
-                      security_counter=security_counter)
+                      security_counter=security_counter, max_align=max_align)
     img.load(infile)
     key = load_key(key) if key else None
     enckey = load_key(encrypt) if encrypt else None