Commit 0744f108 authored by lorneli's avatar lorneli Committed by Darek Stojaczyk
Browse files

bdev/gpt: support parsing secondary partition table



Modify existing code of parsing primary partition table to support parsing
the secondary.

Main difference of these two tables is that they have inverse buffer layout.
For primary table, header is in front of partition entries. And for secondary
table, header is after partition entries. So add helper functions to extract
header and partition entries buffer region from primary or secondary table
based on current parse phase.

Split the exported funtion spdk_gpt_parse into two functions spdk_gpt_parse_mbr
and spdk_gpt_parse_partition_table. So spdk_gpt_parse_partition_table could be
used to parse both primary and secondary table.

Change-Id: I7f7827e0ee7e3f1b2e88c56607ee5b702fb2490c
Signed-off-by: default avatarlorneli <lorneli@163.com>
Reviewed-on: https://review.gerrithub.io/c/441200


Reviewed-by: default avatarwuzhouhui <wuzhouhui@kingsoft.com>
Reviewed-by: default avatarShuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: default avatarGangCao <gang.cao@intel.com>
Reviewed-by: default avatarDarek Stojaczyk <dariusz.stojaczyk@intel.com>
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
parent 2240a8b2
Loading
Loading
Loading
Loading
+83 −11
Original line number Diff line number Diff line
@@ -44,6 +44,65 @@
#define GPT_PROTECTIVE_MBR 1
#define SPDK_MAX_NUM_PARTITION_ENTRIES 128

static uint64_t
spdk_gpt_get_expected_head_lba(struct spdk_gpt *gpt)
{
	switch (gpt->parse_phase) {
	case SPDK_GPT_PARSE_PHASE_PRIMARY:
		return GPT_PRIMARY_PARTITION_TABLE_LBA;
	case SPDK_GPT_PARSE_PHASE_SECONDARY:
		return gpt->lba_end;
	default:
		assert(false);
	}
	return 0;
}

static struct spdk_gpt_header *
spdk_gpt_get_header_buf(struct spdk_gpt *gpt)
{
	switch (gpt->parse_phase) {
	case SPDK_GPT_PARSE_PHASE_PRIMARY:
		return (struct spdk_gpt_header *)
		       (gpt->buf + GPT_PRIMARY_PARTITION_TABLE_LBA * gpt->sector_size);
	case SPDK_GPT_PARSE_PHASE_SECONDARY:
		return (struct spdk_gpt_header *)
		       (gpt->buf + (gpt->buf_size - gpt->sector_size));
	default:
		assert(false);
	}
	return NULL;
}

static struct spdk_gpt_partition_entry *
spdk_gpt_get_partitions_buf(struct spdk_gpt *gpt, uint64_t total_partition_size,
			    uint64_t partition_start_lba)
{
	uint64_t secondary_total_size;

	switch (gpt->parse_phase) {
	case SPDK_GPT_PARSE_PHASE_PRIMARY:
		if ((total_partition_size + partition_start_lba * gpt->sector_size) >
		    gpt->buf_size) {
			SPDK_ERRLOG("Buffer size is not enough\n");
			return NULL;
		}
		return (struct spdk_gpt_partition_entry *)
		       (gpt->buf + partition_start_lba * gpt->sector_size);
	case SPDK_GPT_PARSE_PHASE_SECONDARY:
		secondary_total_size = (gpt->lba_end - partition_start_lba + 1) * gpt->sector_size;
		if (secondary_total_size > gpt->buf_size) {
			SPDK_ERRLOG("Buffer size is not enough\n");
			return NULL;
		}
		return (struct spdk_gpt_partition_entry *)
		       (gpt->buf + (gpt->buf_size - secondary_total_size));
	default:
		assert(false);
	}
	return NULL;
}

static int
spdk_gpt_read_partitions(struct spdk_gpt *gpt)
{
@@ -68,14 +127,13 @@ spdk_gpt_read_partitions(struct spdk_gpt *gpt)

	total_partition_size = num_partition_entries * partition_entry_size;
	partition_start_lba = from_le64(&head->partition_entry_lba);
	if ((total_partition_size + partition_start_lba * gpt->sector_size) > SPDK_GPT_BUFFER_SIZE) {
		SPDK_ERRLOG("Buffer size is not enough\n");
	gpt->partitions = spdk_gpt_get_partitions_buf(gpt, total_partition_size,
			  partition_start_lba);
	if (!gpt->partitions) {
		SPDK_ERRLOG("Failed to get gpt partitions buf\n");
		return -1;
	}

	gpt->partitions = (struct spdk_gpt_partition_entry *)(gpt->buf +
			  partition_start_lba * gpt->sector_size);

	crc32 = spdk_crc32_ieee_update(gpt->partitions, total_partition_size, ~0);
	crc32 ^= ~0;

@@ -121,10 +179,15 @@ spdk_gpt_read_header(struct spdk_gpt *gpt)
{
	uint32_t head_size;
	uint32_t new_crc, original_crc;
	uint64_t my_lba;
	uint64_t my_lba, head_lba;
	struct spdk_gpt_header *head;

	head = (struct spdk_gpt_header *)(gpt->buf + GPT_PRIMARY_PARTITION_TABLE_LBA * gpt->sector_size);
	head = spdk_gpt_get_header_buf(gpt);
	if (!head) {
		SPDK_ERRLOG("Failed to get gpt header buf\n");
		return -1;
	}

	head_size = from_le32(&head->header_size);
	if (head_size < sizeof(*head) || head_size > gpt->sector_size) {
		SPDK_ERRLOG("head_size=%u\n", head_size);
@@ -150,10 +213,11 @@ spdk_gpt_read_header(struct spdk_gpt *gpt)
		return -1;
	}

	head_lba = spdk_gpt_get_expected_head_lba(gpt);
	my_lba = from_le64(&head->my_lba);
	if (my_lba != GPT_PRIMARY_PARTITION_TABLE_LBA) {
		SPDK_ERRLOG("head my_lba(%" PRIu64 ") != expected(%d)\n",
			    my_lba, GPT_PRIMARY_PARTITION_TABLE_LBA);
	if (my_lba != head_lba) {
		SPDK_ERRLOG("head my_lba(%" PRIu64 ") != expected(%" PRIu64 ")\n",
			    my_lba, head_lba);
		return -1;
	}

@@ -215,7 +279,7 @@ spdk_gpt_check_mbr(struct spdk_gpt *gpt)
}

int
spdk_gpt_parse(struct spdk_gpt *gpt)
spdk_gpt_parse_mbr(struct spdk_gpt *gpt)
{
	int rc;

@@ -230,6 +294,14 @@ spdk_gpt_parse(struct spdk_gpt *gpt)
		return rc;
	}

	return 0;
}

int
spdk_gpt_parse_partition_table(struct spdk_gpt *gpt)
{
	int rc;

	rc = spdk_gpt_read_header(gpt);
	if (rc) {
		SPDK_ERRLOG("Failed to read gpt header\n");
+9 −1
Original line number Diff line number Diff line
@@ -46,7 +46,14 @@
#define SPDK_GPT_BUFFER_SIZE 32768  /* 32KB */
#define	SPDK_GPT_GUID_EQUAL(x,y) (memcmp(x, y, sizeof(struct spdk_gpt_guid)) == 0)

enum spdk_gpt_parse_phase {
	SPDK_GPT_PARSE_PHASE_INVALID = 0,
	SPDK_GPT_PARSE_PHASE_PRIMARY,
	SPDK_GPT_PARSE_PHASE_SECONDARY,
};

struct spdk_gpt {
	uint8_t parse_phase;
	unsigned char *buf;
	uint64_t buf_size;
	uint64_t lba_start;
@@ -57,6 +64,7 @@ struct spdk_gpt {
	struct spdk_gpt_partition_entry *partitions;
};

int spdk_gpt_parse(struct spdk_gpt *gpt);
int spdk_gpt_parse_mbr(struct spdk_gpt *gpt);
int spdk_gpt_parse_partition_table(struct spdk_gpt *gpt);

#endif  /* SPDK_INTERNAL_GPT_H */
+9 −2
Original line number Diff line number Diff line
@@ -145,6 +145,7 @@ spdk_gpt_base_bdev_init(struct spdk_bdev *bdev)
	}

	gpt = &gpt_base->gpt;
	gpt->parse_phase = SPDK_GPT_PARSE_PHASE_PRIMARY;
	gpt->buf_size = spdk_max(SPDK_GPT_BUFFER_SIZE, bdev->blocklen);
	gpt->buf = spdk_dma_zmalloc(gpt->buf_size, spdk_bdev_get_buf_align(bdev), NULL);
	if (!gpt->buf) {
@@ -355,9 +356,15 @@ spdk_gpt_bdev_complete(struct spdk_bdev_io *bdev_io, bool status, void *arg)
		goto end;
	}

	rc = spdk_gpt_parse(&gpt_base->gpt);
	rc = spdk_gpt_parse_mbr(&gpt_base->gpt);
	if (rc) {
		SPDK_DEBUGLOG(SPDK_LOG_VBDEV_GPT, "Failed to parse gpt\n");
		SPDK_DEBUGLOG(SPDK_LOG_VBDEV_GPT, "Failed to parse mbr\n");
		goto end;
	}

	rc = spdk_gpt_parse_partition_table(&gpt_base->gpt);
	if (rc) {
		SPDK_DEBUGLOG(SPDK_LOG_VBDEV_GPT, "Failed to parse primary partition table\n");
		goto end;
	}

+87 −13
Original line number Diff line number Diff line
@@ -45,9 +45,15 @@ test_check_mbr(void)
	unsigned char a[SPDK_GPT_BUFFER_SIZE];
	int re;

	/* spdk_gpt_check_mbr(NULL) does not exist, NULL is filtered out in spdk_gpt_parse() */
	/* Set gpt is NULL */
	re = spdk_gpt_parse_mbr(NULL);
	CU_ASSERT(re == -1);

	/* Set gpt->buf is NULL */
	gpt = calloc(1, sizeof(*gpt));
	SPDK_CU_ASSERT_FATAL(gpt != NULL);
	re = spdk_gpt_parse_mbr(gpt);
	CU_ASSERT(re == -1);

	/* Set *gpt is "aaa...", all are mismatch include mbr_signature */
	memset(a, 'a', sizeof(a));
@@ -87,13 +93,16 @@ test_read_header(void)
	unsigned char a[SPDK_GPT_BUFFER_SIZE];
	int re;

	/* spdk_gpt_read_header(NULL) does not exist, NULL is filtered out in spdk_gpt_parse() */
	/* spdk_gpt_read_header(NULL) does not exist, NULL is filtered out in spdk_gpt_parse_mbr() */
	gpt = calloc(1, sizeof(*gpt));
	SPDK_CU_ASSERT_FATAL(gpt != NULL);
	gpt->parse_phase = SPDK_GPT_PARSE_PHASE_PRIMARY;
	gpt->sector_size = 512;

	/* Set *gpt is "aaa..." */
	memset(a, 'a', sizeof(a));
	gpt->buf = &a[0];
	gpt->buf_size = sizeof(a);

	/* Set header_size mismatch */
	gpt->sector_size = 512;
@@ -152,13 +161,16 @@ test_read_partitions(void)
	unsigned char a[SPDK_GPT_BUFFER_SIZE];
	int re;

	/* spdk_gpt_read_partitions(NULL) does not exist, NULL is filtered out in spdk_gpt_parse() */
	/* spdk_gpt_read_partitions(NULL) does not exist, NULL is filtered out in spdk_gpt_parse_mbr() */
	gpt = calloc(1, sizeof(*gpt));
	SPDK_CU_ASSERT_FATAL(gpt != NULL);
	gpt->parse_phase = SPDK_GPT_PARSE_PHASE_PRIMARY;
	gpt->sector_size = 512;

	/* Set *gpt is "aaa..." */
	memset(a, 'a', sizeof(a));
	gpt->buf = &a[0];
	gpt->buf_size = sizeof(a);

	/* Set num_partition_entries exceeds Max value of entries GPT supported */
	gpt->sector_size = 512;
@@ -200,7 +212,7 @@ test_read_partitions(void)
}

static void
test_parse(void)
test_parse_mbr_and_primary(void)
{
	struct spdk_gpt *gpt;
	struct spdk_mbr *mbr;
@@ -209,32 +221,38 @@ test_parse(void)
	int re;

	/* Set gpt is NULL */
	re = spdk_gpt_parse(NULL);
	re = spdk_gpt_parse_mbr(NULL);
	CU_ASSERT(re == -1);

	/* Set gpt->buf is NULL */
	gpt = calloc(1, sizeof(*gpt));
	SPDK_CU_ASSERT_FATAL(gpt != NULL);
	re = spdk_gpt_parse(gpt);
	gpt->parse_phase = SPDK_GPT_PARSE_PHASE_PRIMARY;
	gpt->sector_size = 512;
	re = spdk_gpt_parse_mbr(gpt);
	CU_ASSERT(re == -1);

	/* Set *gpt is "aaa...", check_mbr failed */
	memset(a, 'a', sizeof(a));
	gpt->buf = &a[0];
	re = spdk_gpt_parse(gpt);
	gpt->buf_size = sizeof(a);
	re = spdk_gpt_parse_mbr(gpt);
	CU_ASSERT(re == -1);

	/* Set check_mbr passed, read_header failed */
	/* Set check_mbr passed */
	mbr = (struct spdk_mbr *)gpt->buf;
	mbr->mbr_signature = 0xAA55;
	mbr->partitions[0].start_lba = 1;
	mbr->partitions[0].os_type = 0xEE;
	mbr->partitions[0].size_lba = 0xFFFFFFFF;
	re = spdk_gpt_parse(gpt);
	re = spdk_gpt_parse_mbr(gpt);
	CU_ASSERT(re == 0);

	/* Expect read_header failed */
	re = spdk_gpt_parse_partition_table(gpt);
	CU_ASSERT(re == -1);

	/* Set read_header passed, read_partitions failed */
	gpt->sector_size = 512;
	head = (struct spdk_gpt_header *)(gpt->buf + GPT_PRIMARY_PARTITION_TABLE_LBA * gpt->sector_size);
	head->header_size = sizeof(*head);
	head->gpt_signature[0] = 'E';
@@ -251,7 +269,7 @@ test_parse(void)
	to_le64(&gpt->lba_end, 0x2E935FFE);
	to_le64(&head->first_usable_lba, 0xA);
	to_le64(&head->last_usable_lba, 0xF4240);
	re = spdk_gpt_parse(gpt);
	re = spdk_gpt_parse_partition_table(gpt);
	CU_ASSERT(re == -1);

	/* Set read_partitions passed, all passed */
@@ -260,7 +278,61 @@ test_parse(void)
	to_le32(&head->header_crc32, 0x845A09AA);
	to_le32(&head->partition_entry_array_crc32, 0xEBEE44FB);
	to_le32(&head->num_partition_entries, 0x80);
	re = spdk_gpt_parse(gpt);
	re = spdk_gpt_parse_partition_table(gpt);
	CU_ASSERT(re == 0);

	free(gpt);
}

static void
test_parse_secondary(void)
{
	struct spdk_gpt *gpt;
	struct spdk_gpt_header *head;
	unsigned char a[SPDK_GPT_BUFFER_SIZE];
	int re;

	/* spdk_gpt_parse_partition_table(NULL) does not exist, NULL is filtered out in spdk_gpt_parse_mbr() */
	gpt = calloc(1, sizeof(*gpt));
	SPDK_CU_ASSERT_FATAL(gpt != NULL);
	gpt->parse_phase = SPDK_GPT_PARSE_PHASE_SECONDARY;
	gpt->sector_size = 512;

	/* Set *gpt is "aaa...", read_header failed */
	memset(a, 'a', sizeof(a));
	gpt->buf = &a[0];
	gpt->buf_size = sizeof(a);
	re = spdk_gpt_parse_partition_table(gpt);
	CU_ASSERT(re == -1);

	/* Set read_header passed, read_partitions failed */
	head = (struct spdk_gpt_header *)(gpt->buf + gpt->buf_size - gpt->sector_size);
	head->header_size = sizeof(*head);
	head->gpt_signature[0] = 'E';
	head->gpt_signature[1] = 'F';
	head->gpt_signature[2] = 'I';
	head->gpt_signature[3] = ' ';
	head->gpt_signature[4] = 'P';
	head->gpt_signature[5] = 'A';
	head->gpt_signature[6] = 'R';
	head->gpt_signature[7] = 'T';
	to_le32(&head->header_crc32, 0xAA68A167);
	to_le64(&head->my_lba, 0x63FFFFF);
	to_le64(&gpt->lba_start, 0x0);
	to_le64(&gpt->lba_end, 0x63FFFFF);
	to_le64(&gpt->total_sectors, 0x6400000);
	to_le64(&head->first_usable_lba, 0xA);
	to_le64(&head->last_usable_lba, 0x63FFFDE);
	re = spdk_gpt_parse_partition_table(gpt);
	CU_ASSERT(re == -1);

	/* Set read_partitions passed, all passed */
	to_le32(&head->size_of_partition_entry, 0x80);
	to_le64(&head->partition_entry_lba, 0x63FFFDF);
	to_le32(&head->header_crc32, 0x204129E8);
	to_le32(&head->partition_entry_array_crc32, 0xEBEE44FB);
	to_le32(&head->num_partition_entries, 0x80);
	re = spdk_gpt_parse_partition_table(gpt);
	CU_ASSERT(re == 0);

	free(gpt);
@@ -284,7 +356,9 @@ main(int argc, char **argv)

	if (
		CU_add_test(suite, "parse",
			    test_parse) == NULL ||
			    test_parse_mbr_and_primary) == NULL ||
		CU_add_test(suite, "parse secondary",
			    test_parse_secondary) == NULL ||
		CU_add_test(suite, "check mbr",
			    test_check_mbr) == NULL ||
		CU_add_test(suite, "read header",