Add a test that runs gpt_fill_header() and gpt_fill_pte() over a range of
block sizes and checks that the partition entry array is sized and placed
consistently, in particular that the backup array ends exactly where the
backup GPT header begins.

Neither function performs any block I/O, so the test builds a synthetic
struct blk_desc rather than needing a block device with a configurable
block size. The expected array size is taken from the GPT header fields
rather than from GPT_ENTRY_NUMBERS, so the test still holds for builds
with a non-default CONFIG_EFI_PARTITION_ENTRIES_NUMBERS, such as
ARCH_SUNXI with 56 entries.

Without the preceding fixes this fails on the first block size other than
512:

  test/dm/part.c:313, dm_test_part_gpt_blksz():
      entry_lba + pte_blks == first_lba: Expected 0x12 (18), got 0x22 (34)

Signed-off-by: Alexey Charkov <[email protected]>
---
 test/dm/part.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 100 insertions(+)

diff --git a/test/dm/part.c b/test/dm/part.c
index ad37d7f406f4..4b48322fb807 100644
--- a/test/dm/part.c
+++ b/test/dm/part.c
@@ -5,6 +5,8 @@
 
 #include <dm.h>
 #include <env.h>
+#include <malloc.h>
+#include <memalign.h>
 #include <mmc.h>
 #include <part.h>
 #include <part_efi.h>
@@ -248,3 +250,101 @@ static int dm_test_part_get_info_by_uuid(struct 
unit_test_state *uts)
        return 0;
 }
 DM_TEST(dm_test_part_get_info_by_uuid, UTF_SCAN_PDATA | UTF_SCAN_FDT);
+
+/*
+ * Check that the GPT layout adapts to the block size of the device. Neither
+ * gpt_fill_header() nor gpt_fill_pte() does any block I/O, so a synthetic
+ * descriptor is enough here; partition_entries_offset() reads the device tree
+ * '/config' node, which is available without scanning for devices.
+ */
+static int dm_test_part_gpt_blksz(struct unit_test_state *uts)
+{
+       static const struct {
+               unsigned long blksz;
+               lbaint_t lba;
+       } cases[] = {
+               {   512, 0x2000 },      /* 4MB, the traditional 34-block layout 
*/
+               {  1024, 0x1000 },
+               {  2048,  0x800 },
+               {  4096,  0x400 },      /* 4K native media, e.g. UFS */
+               {  8192,  0x200 },
+               { 16384,  0x100 },      /* the array fits in a single block... 
*/
+               { 32768,   0x40 },      /* ...on a device smaller than 34 
blocks */
+       };
+
+       char str_disk_guid[UUID_STR_LEN + 1] =
+               "8d60b397-1bb6-4d33-80ee-b1587d24c2f8";
+       struct disk_partition part;
+       struct blk_desc desc;
+       gpt_header gpt_h;
+       gpt_entry *gpt_e;
+       int i;
+
+       gpt_e = calloc(GPT_ENTRY_NUMBERS, sizeof(gpt_entry));
+       ut_assertnonnull(gpt_e);
+
+       for (i = 0; i < ARRAY_SIZE(cases); i++) {
+               u64 entry_lba, first_lba, last_lba;
+               u32 pte_blks;
+
+               memset(&desc, '\0', sizeof(desc));
+               memset(&gpt_h, '\0', sizeof(gpt_h));
+               desc.blksz = cases[i].blksz;
+               desc.log2blksz = LOG2(desc.blksz);
+               desc.lba = cases[i].lba;
+
+               ut_assertok(gpt_fill_header(&desc, &gpt_h, str_disk_guid, 1));
+
+               /*
+                * Size the array with BLOCK_CNT() rather than the
+                * DIV_ROUND_UP() that gpt_pte_blocks() uses, so that this
+                * checks the layout instead of restating the implementation.
+                */
+               pte_blks = BLOCK_CNT(le32_to_cpu(gpt_h.num_partition_entries) *
+                                    le32_to_cpu(gpt_h.sizeof_partition_entry),
+                                    (&desc));
+               entry_lba = le64_to_cpu(gpt_h.partition_entry_lba);
+               first_lba = le64_to_cpu(gpt_h.first_usable_lba);
+               last_lba = le64_to_cpu(gpt_h.last_usable_lba);
+
+               ut_asserteq_64(1, le64_to_cpu(gpt_h.my_lba));
+               ut_asserteq_64(desc.lba - 1, le64_to_cpu(gpt_h.alternate_lba));
+
+               /* the primary array ends where the usable area begins */
+               ut_assert(entry_lba >= 2);
+               ut_asserteq_64(entry_lba + pte_blks, first_lba);
+               ut_assert(first_lba < last_lba);
+
+               /*
+                * write_gpt_table() puts the backup array at
+                * last_usable_lba + 1, so it has to end exactly where the
+                * backup header begins
+                */
+               ut_asserteq_64(le64_to_cpu(gpt_h.alternate_lba),
+                              last_lba + 1 + pte_blks);
+
+               /* a partition with no start and no size fills the disk */
+               memset(&part, '\0', sizeof(part));
+               disk_partition_set_uuid(&part, str_disk_guid);
+               ut_assertok(gpt_fill_pte(&desc, &gpt_h, gpt_e, &part, 1));
+               ut_asserteq_64(first_lba, le64_to_cpu(gpt_e[0].starting_lba));
+               ut_asserteq_64(last_lba, le64_to_cpu(gpt_e[0].ending_lba));
+
+               /* a partition overlapping the entry array is rejected */
+               part.start = entry_lba;
+               part.size = 1;
+               ut_asserteq(-ENOSPC,
+                           gpt_fill_pte(&desc, &gpt_h, gpt_e, &part, 1));
+
+               /* so is one running past the end of the usable area */
+               part.start = first_lba;
+               part.size = last_lba - first_lba + 2;
+               ut_asserteq(-E2BIG,
+                           gpt_fill_pte(&desc, &gpt_h, gpt_e, &part, 1));
+       }
+
+       free(gpt_e);
+
+       return 0;
+}
+DM_TEST(dm_test_part_gpt_blksz, 0);

-- 
2.54.0

Reply via email to