[Concept,04/24] bloblist: Provide a way to remove a blob

Message ID 20251018084117.1798704-5-sjg@u-boot.org
State New
Headers
Series bootctl: Expand bootctl to include a new UI |

Commit Message

Simon Glass Oct. 18, 2025, 8:40 a.m. UTC
  From: Simon Glass <sjg@chromium.org>

Add a function to remove a blob of a particular type.

Signed-off-by: Simon Glass <sjg@chromium.org>
Co-developed-by: Claude <noreply@anthropic.com>
---

 common/bloblist.c      | 32 +++++++++++++++++++++
 include/bloblist.h     | 12 ++++++++
 test/common/bloblist.c | 64 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 108 insertions(+)
  

Patch

diff --git a/common/bloblist.c b/common/bloblist.c
index d1d7ddb956a..e5fa67b0f71 100644
--- a/common/bloblist.c
+++ b/common/bloblist.c
@@ -356,6 +356,38 @@  int bloblist_resize(uint tag, int new_size)
 	return 0;
 }
 
+int bloblist_remove(uint tag)
+{
+	struct bloblist_hdr *hdr = gd->bloblist;
+	struct bloblist_rec *rec;
+	ulong rec_start;	/* offset where record starts */
+	ulong next_ofs;		/* offset of the record after @rec */
+	ulong removed_size;	/* total size to remove, including alignment */
+
+	rec = bloblist_findrec(tag);
+	if (!rec)
+		return log_msg_ret("find", -ENOENT);
+
+	/* Calculate where this record starts and where the next one begins */
+	rec_start = (void *)rec - (void *)hdr;
+	next_ofs = bloblist_blob_end_ofs(hdr, rec);
+
+	/* Calculate total size to remove (record + alignment) */
+	removed_size = next_ofs - rec_start;
+
+	/* Move all following blobs backward to fill the gap */
+	if (next_ofs < hdr->used_size) {
+		memmove((void *)hdr + rec_start,
+			(void *)hdr + next_ofs,
+			hdr->used_size - next_ofs);
+	}
+
+	/* Update the used size */
+	hdr->used_size -= removed_size;
+
+	return 0;
+}
+
 static u32 bloblist_calc_chksum(struct bloblist_hdr *hdr)
 {
 	u8 chksum;
diff --git a/include/bloblist.h b/include/bloblist.h
index e55d71110c3..ff88f4c8d5e 100644
--- a/include/bloblist.h
+++ b/include/bloblist.h
@@ -350,6 +350,18 @@  int bloblist_ensure_size_ret(uint tag, int *sizep, void **blobp);
  */
 int bloblist_resize(uint tag, int new_size);
 
+/**
+ * bloblist_remove() - remove a blob from the bloblist
+ *
+ * This removes the blob with the given tag from the bloblist. Any blobs after
+ * this one are relocated backward to fill the gap. The space is reclaimed and
+ * used_size is reduced accordingly.
+ *
+ * @tag:	Tag to remove (enum bloblist_tag_t)
+ * Return: 0 if OK, -ENOENT if the tag is not found
+ */
+int bloblist_remove(uint tag);
+
 /**
  * bloblist_new() - Create a new, empty bloblist of a given size
  *
diff --git a/test/common/bloblist.c b/test/common/bloblist.c
index 797bde27025..ad87b104d9c 100644
--- a/test/common/bloblist.c
+++ b/test/common/bloblist.c
@@ -605,3 +605,67 @@  static int bloblist_test_blob_maxsize(struct unit_test_state *uts)
 	return 0;
 }
 BLOBLIST_TEST(bloblist_test_blob_maxsize, UFT_BLOBLIST);
+
+/* Test removing a blob */
+static int bloblist_test_remove(struct unit_test_state *uts)
+{
+	const uint small_size = 0x20;
+	struct bloblist_hdr *hdr;
+	void *blob1, *blob2, *blob3;
+	ulong used_before, used_after;
+
+	clear_bloblist();
+	ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+	hdr = map_sysmem(TEST_ADDR, TEST_BLOBLIST_SIZE);
+
+	/* Create three blobs */
+	blob1 = bloblist_add(TEST_TAG, small_size, 0);
+	ut_assertnonnull(blob1);
+	strcpy(blob1, test1_str);
+
+	blob2 = bloblist_add(TEST_TAG2, small_size, 0);
+	ut_assertnonnull(blob2);
+	strcpy(blob2, test2_str);
+
+	blob3 = bloblist_add(TEST_TAG_MISSING, small_size, 0);
+	ut_assertnonnull(blob3);
+
+	used_before = hdr->used_size;
+
+	/* Remove the middle blob */
+	ut_assertok(bloblist_remove(TEST_TAG2));
+
+	/* Check that the blob is gone */
+	ut_assertnull(bloblist_find(TEST_TAG2, 0));
+
+	/* Check that the first blob is still there and intact */
+	ut_asserteq_ptr(blob1, bloblist_find(TEST_TAG, small_size));
+	ut_asserteq_str(test1_str, blob1);
+
+	/* Check that the third blob is still there */
+	ut_assertnonnull(bloblist_find(TEST_TAG_MISSING, small_size));
+
+	/* Check that used_size was reduced */
+	used_after = hdr->used_size;
+	ut_assert(used_after < used_before);
+
+	/* Try to remove a non-existent blob */
+	ut_asserteq(-ENOENT, bloblist_remove(TEST_TAG2));
+
+	/* Remove the first blob */
+	ut_assertok(bloblist_remove(TEST_TAG));
+	ut_assertnull(bloblist_find(TEST_TAG, 0));
+
+	/* The third blob should still be accessible */
+	ut_assertnonnull(bloblist_find(TEST_TAG_MISSING, small_size));
+
+	/* Remove the last blob */
+	ut_assertok(bloblist_remove(TEST_TAG_MISSING));
+	ut_assertnull(bloblist_find(TEST_TAG_MISSING, 0));
+
+	/* Check that we're back to just the header */
+	ut_asserteq(sizeof(struct bloblist_hdr), hdr->used_size);
+
+	return 0;
+}
+BLOBLIST_TEST(bloblist_test_remove, UFT_BLOBLIST);