diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst
index b091114fb91..1ab6d012b83 100644
--- a/tools/binman/binman.rst
+++ b/tools/binman/binman.rst
@@ -1012,6 +1012,28 @@ For the BSS case, a 'spl-bss-pad' entry arg controls whether it is present. All
 entry args are provided by the U-Boot Makefile.
 
 
+Optional entries
+----------------
+
+Some entries need to exist only if certain conditions are met. For example, an
+entry may want to appear in the image only if a file has a particular format.
+Obviously the entry must exist in the image description for it to be processed
+at all, so a way needs to be found to have the entry remove itself.
+
+To handle this, when entry.ObtainContents() is called, the entry can call
+entry.mark_absent() to mark itself as absent, passing a suitable message as the
+reason.
+
+Any absent entries are dropped immediately after ObtainContents() has been
+called on all entries.
+
+It is not possible for an entry to mark itself absent at any other point in the
+processing. It must happen in the ObtainContents() method.
+
+The effect is as if the entry had never been present at all, since the image
+is packed without it and it disappears from the list of entries.
+
+
 Compression
 -----------
 
diff --git a/tools/binman/control.py b/tools/binman/control.py
index 964c6984f9b..07225381146 100644
--- a/tools/binman/control.py
+++ b/tools/binman/control.py
@@ -552,6 +552,7 @@ def ProcessImage(image, update_fdt, write_map, get_contents=True,
         image.SetAllowMissing(allow_missing)
         image.SetAllowFakeBlob(allow_fake_blobs)
         image.GetEntryContents()
+        image.drop_absent()
     image.GetEntryOffsets()
 
     # We need to pack the entries to figure out where everything
diff --git a/tools/binman/entry.py b/tools/binman/entry.py
index 1be31a05e00..637aece3705 100644
--- a/tools/binman/entry.py
+++ b/tools/binman/entry.py
@@ -91,6 +91,10 @@ class Entry(object):
             file, or is a binary file produced from an ELF file
         auto_write_symbols (bool): True to write ELF symbols into this entry's
             contents
+        absent (bool): True if this entry is absent. This can be controlled by
+            the entry itself, allowing it to vanish in certain circumstances.
+            An absent entry is removed during processing so that it does not
+            appear in the map
     """
     fake_dir = None
 
@@ -133,6 +137,7 @@ class Entry(object):
         self.comp_bintool = None
         self.elf_fname = None
         self.auto_write_symbols = auto_write_symbols
+        self.absent = False
 
     @staticmethod
     def FindEntryClass(etype, expanded):
@@ -1281,3 +1286,7 @@ features to produce new behaviours.
                 not_present.append(prop)
         if not_present:
             self.Raise(f"'{self.etype}' entry is missing properties: {' '.join(not_present)}")
+
+    def mark_absent(self, msg):
+        tout.info("Entry '%s' marked absent: %s" % (self._node.path, msg))
+        self.absent = True
diff --git a/tools/binman/etype/_testing.py b/tools/binman/etype/_testing.py
index 69600487814..1c1efb21a44 100644
--- a/tools/binman/etype/_testing.py
+++ b/tools/binman/etype/_testing.py
@@ -63,6 +63,7 @@ class Entry__testing(Entry):
                                                     'bad-update-contents-twice')
         self.return_contents_later = fdt_util.GetBool(self._node,
                                                      'return-contents-later')
+        self.set_to_absent = fdt_util.GetBool(self._node, 'set-to-absent')
 
         # Set to True when the entry is ready to process the FDT.
         self.process_fdt_ready = False
@@ -119,6 +120,8 @@ class Entry__testing(Entry):
         if self.require_bintool_for_contents:
             if self.bintool_for_contents is None:
                 self.Raise("Required bintool unusable in ObtainContents()")
+        if self.set_to_absent:
+            self.mark_absent('for testing purposes')
         return True
 
     def GetOffsets(self):
diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py
index 305155c8461..dcb7a062047 100644
--- a/tools/binman/etype/section.py
+++ b/tools/binman/etype/section.py
@@ -672,6 +672,9 @@ class Entry_section(Entry):
 
     def GetEntryContents(self, skip_entry=None):
         """Call ObtainContents() for each entry in the section
+
+        Note that this may set entry.absent to True if the entry is not
+        actually needed
         """
         def _CheckDone(entry):
             if entry != skip_entry:
@@ -716,6 +719,10 @@ class Entry_section(Entry):
                        todo)
         return True
 
+    def drop_absent(self):
+        """Drop entries which are absent"""
+        self._entries = {n: e for n, e in self._entries.items() if not e.absent}
+
     def _SetEntryOffsetSize(self, name, offset, size):
         """Set the offset and size of an entry
 
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index c3cb32dca26..f47a745f1e1 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -6090,6 +6090,11 @@ fdt         fdtmap                Extract the devicetree blob from the fdtmap
         sect_data = tools.read_file(sect_fname)
         self.assertEqual(U_BOOT_DATA, sect_data)
 
+    def testAbsent(self):
+        """Check handling of absent entries"""
+        data = self._DoReadFile('262_absent.dts')
+        self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/tools/binman/test/262_absent.dts b/tools/binman/test/262_absent.dts
new file mode 100644
index 00000000000..2ab8766c878
--- /dev/null
+++ b/tools/binman/test/262_absent.dts
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u-boot {
+		};
+
+		_testing {
+			set-to-absent;
+		};
+
+		u-boot-img {
+		};
+	};
+};
