zephyr: Add support for automatically calculcating max sectors

Adds a feature that will calculate the maximum number of sectors
that are needed for a build. Can be disabled to revert back to
the old behaviour by disabling CONFIG_BOOT_MAX_IMG_SECTORS_AUTO

Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt
index 45548e0..d02f93a 100644
--- a/boot/zephyr/CMakeLists.txt
+++ b/boot/zephyr/CMakeLists.txt
@@ -363,23 +363,66 @@
   zephyr_sources(kernel/banner.c)
 endif()
 
-if(SYSBUILD)
-  function(align_up num align result)
-    math(EXPR out "(((${num}) + ((${align}) - 1)) & ~((${align}) - 1))")
-    set(${result} "${out}" PARENT_SCOPE)
-  endfunction()
+function(align_up num align result)
+  math(EXPR out "(((${num}) + ((${align}) - 1)) & ~((${align}) - 1))")
+  set(${result} "${out}" PARENT_SCOPE)
+endfunction()
 
-  function(dt_get_parent node)
-    string(FIND "${${node}}" "/" pos REVERSE)
+function(dt_get_parent node)
+  string(FIND "${${node}}" "/" pos REVERSE)
 
-    if(pos EQUAL -1)
-      message(ERROR "Unable to get parent of node: ${${node}}")
+  if(pos EQUAL -1)
+    message(ERROR "Unable to get parent of node: ${${node}}")
+  endif()
+
+  string(SUBSTRING "${${node}}" 0 ${pos} ${node})
+  set(${node} "${${node}}" PARENT_SCOPE)
+endfunction()
+
+if(CONFIG_BOOT_MAX_IMG_SECTORS_AUTO)
+  dt_nodelabel(slot0_flash NODELABEL "slot0_partition")
+  dt_prop(slot0_size PATH "${slot0_flash}" PROPERTY "reg" INDEX 1)
+  dt_get_parent(slot0_flash)
+  dt_get_parent(slot0_flash)
+  dt_prop(erase_size_slot0 PATH "${slot0_flash}" PROPERTY "erase-block-size")
+
+  if(NOT DEFINED slot0_size)
+    message(WARNING "Unable to determine size of slot0 partition, cannot calculate minimum sector usage")
+  elseif(NOT DEFINED erase_size_slot0)
+    message(WARNING "Unable to determine erase size of slot0 partition, cannot calculate minimum sector usage")
+  else()
+    math(EXPR slot_min_sectors "${slot0_size} / ${erase_size_slot0}")
+  endif()
+
+  if(NOT CONFIG_SINGLE_APPLICATION_SLOT)
+    dt_nodelabel(slot1_flash NODELABEL "slot1_partition")
+    dt_prop(slot1_size PATH "${slot1_flash}" PROPERTY "reg" INDEX 1)
+    dt_get_parent(slot1_flash)
+    dt_get_parent(slot1_flash)
+    dt_prop(erase_size_slot1 PATH "${slot1_flash}" PROPERTY "erase-block-size")
+
+    if(NOT DEFINED slot1_size)
+      message(WARNING "Unable to determine size of slot1 partition, cannot calculate minimum sector usage")
+    elseif(NOT DEFINED erase_size_slot1)
+      message(WARNING "Unable to determine erase size of slot1 partition, cannot calculate minimum sector usage")
+    else()
+      math(EXPR slot1_min_sectors "${slot1_size} / ${erase_size_slot1}")
+
+      if("${slot1_min_sectors}" GREATER "${slot_min_sectors}")
+        set(slot_min_sectors ${slot1_min_sectors})
+      endif()
     endif()
+  endif()
 
-    string(SUBSTRING "${${node}}" 0 ${pos} ${node})
-    set(${node} "${${node}}" PARENT_SCOPE)
-  endfunction()
+  if(DEFINED slot_min_sectors AND "${slot_min_sectors}" GREATER "0")
+    zephyr_compile_definitions("MIN_SECTOR_COUNT=${slot_min_sectors}")
+    message("Calculated maximum number of sectors: ${slot_min_sectors}")
+  else()
+    message(WARNING "Unable to calculate minimum number of sector sizes, falling back to 128 sector default. Please disable CONFIG_BOOT_MAX_IMG_SECTORS_AUTO and set CONFIG_BOOT_MAX_IMG_SECTORS to the required value")
+  endif()
+endif()
 
+if(SYSBUILD)
   if(CONFIG_SINGLE_APPLICATION_SLOT OR CONFIG_BOOT_FIRMWARE_LOADER OR CONFIG_BOOT_SWAP_USING_SCRATCH OR CONFIG_BOOT_SWAP_USING_MOVE OR CONFIG_BOOT_UPGRADE_ONLY OR CONFIG_BOOT_DIRECT_XIP OR CONFIG_BOOT_RAM_LOAD)
     # TODO: RAM LOAD support
     dt_nodelabel(slot0_flash NODELABEL "slot0_partition")
@@ -495,7 +538,11 @@
     endif()
 
     if(CONFIG_BOOT_SWAP_USING_SCRATCH OR CONFIG_BOOT_SWAP_USING_MOVE)
-      math(EXPR boot_status_data_size "${CONFIG_BOOT_MAX_IMG_SECTORS} * (3 * ${write_size})")
+      if(CONFIG_BOOT_MAX_IMG_SECTORS_AUTO AND DEFINED slot_min_sectors AND "${slot_min_sectors}" GREATER "0")
+        math(EXPR boot_status_data_size "${slot_min_sectors} * (3 * ${write_size})")
+      else()
+        math(EXPR boot_status_data_size "${CONFIG_BOOT_MAX_IMG_SECTORS} * (3 * ${write_size})")
+      endif()
     else()
       set(boot_status_data_size 0)
     endif()
diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig
index effedfb..bf772ca 100644
--- a/boot/zephyr/Kconfig
+++ b/boot/zephyr/Kconfig
@@ -380,9 +380,21 @@
 	  with the public key information will be written in a format expected by
 	  MCUboot.
 
+config BOOT_MAX_IMG_SECTORS_AUTO
+	bool "Calculate maximum sectors automatically"
+	default y
+	help
+	  If this option is enabled then the maximum number of supported sectors per image will
+	  be calculated automatically from the flash erase sizes and size of each partition for
+	  the first image.
+
+	  If this information is not available, or multiple images are used, then this option
+	  should be disabled and BOOT_MAX_IMG_SECTORS should be set instead
+
 config BOOT_MAX_IMG_SECTORS
 	int "Maximum number of sectors per image slot"
 	default 128
+	depends on !BOOT_MAX_IMG_SECTORS_AUTO
 	help
 	  This option controls the maximum number of sectors that each of
 	  the two image areas can contain. Smaller values reduce MCUboot's
diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h
index 8f5d17b..ab2e8ba 100644
--- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h
+++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h
@@ -270,7 +270,11 @@
 #  endif
 #endif
 
-#ifdef CONFIG_BOOT_MAX_IMG_SECTORS
+#if defined(CONFIG_BOOT_MAX_IMG_SECTORS_AUTO) && defined(MIN_SECTOR_COUNT)
+
+#define MCUBOOT_MAX_IMG_SECTORS       MIN_SECTOR_COUNT
+
+#elif defined(CONFIG_BOOT_MAX_IMG_SECTORS)
 
 #define MCUBOOT_MAX_IMG_SECTORS       CONFIG_BOOT_MAX_IMG_SECTORS