xref: /spdk/module/bdev/gpt/gpt.c (revision 024179d8eeab28c311bf40a9de14bf681e87af18)
107fe6a43SSeth Howell /*-
207fe6a43SSeth Howell  *   BSD LICENSE
307fe6a43SSeth Howell  *
407fe6a43SSeth Howell  *   Copyright (c) Intel Corporation.
507fe6a43SSeth Howell  *   All rights reserved.
607fe6a43SSeth Howell  *
707fe6a43SSeth Howell  *   Redistribution and use in source and binary forms, with or without
807fe6a43SSeth Howell  *   modification, are permitted provided that the following conditions
907fe6a43SSeth Howell  *   are met:
1007fe6a43SSeth Howell  *
1107fe6a43SSeth Howell  *     * Redistributions of source code must retain the above copyright
1207fe6a43SSeth Howell  *       notice, this list of conditions and the following disclaimer.
1307fe6a43SSeth Howell  *     * Redistributions in binary form must reproduce the above copyright
1407fe6a43SSeth Howell  *       notice, this list of conditions and the following disclaimer in
1507fe6a43SSeth Howell  *       the documentation and/or other materials provided with the
1607fe6a43SSeth Howell  *       distribution.
1707fe6a43SSeth Howell  *     * Neither the name of Intel Corporation nor the names of its
1807fe6a43SSeth Howell  *       contributors may be used to endorse or promote products derived
1907fe6a43SSeth Howell  *       from this software without specific prior written permission.
2007fe6a43SSeth Howell  *
2107fe6a43SSeth Howell  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2207fe6a43SSeth Howell  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2307fe6a43SSeth Howell  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2407fe6a43SSeth Howell  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2507fe6a43SSeth Howell  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2607fe6a43SSeth Howell  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2707fe6a43SSeth Howell  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2807fe6a43SSeth Howell  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2907fe6a43SSeth Howell  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3007fe6a43SSeth Howell  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3107fe6a43SSeth Howell  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3207fe6a43SSeth Howell  */
3307fe6a43SSeth Howell 
3407fe6a43SSeth Howell #include "gpt.h"
3507fe6a43SSeth Howell 
3607fe6a43SSeth Howell #include "spdk/crc32.h"
3707fe6a43SSeth Howell #include "spdk/endian.h"
3807fe6a43SSeth Howell #include "spdk/event.h"
3907fe6a43SSeth Howell 
404e8e97c8STomasz Zawadzki #include "spdk/log.h"
4107fe6a43SSeth Howell 
4207fe6a43SSeth Howell #define GPT_PRIMARY_PARTITION_TABLE_LBA 0x1
4307fe6a43SSeth Howell #define PRIMARY_PARTITION_NUMBER 4
4407fe6a43SSeth Howell #define GPT_PROTECTIVE_MBR 1
4507fe6a43SSeth Howell #define SPDK_MAX_NUM_PARTITION_ENTRIES 128
4607fe6a43SSeth Howell 
4707fe6a43SSeth Howell static uint64_t
481d2faf6dSSeth Howell gpt_get_expected_head_lba(struct spdk_gpt *gpt)
4907fe6a43SSeth Howell {
5007fe6a43SSeth Howell 	switch (gpt->parse_phase) {
5107fe6a43SSeth Howell 	case SPDK_GPT_PARSE_PHASE_PRIMARY:
5207fe6a43SSeth Howell 		return GPT_PRIMARY_PARTITION_TABLE_LBA;
5307fe6a43SSeth Howell 	case SPDK_GPT_PARSE_PHASE_SECONDARY:
5407fe6a43SSeth Howell 		return gpt->lba_end;
5507fe6a43SSeth Howell 	default:
5607fe6a43SSeth Howell 		assert(false);
5707fe6a43SSeth Howell 	}
5807fe6a43SSeth Howell 	return 0;
5907fe6a43SSeth Howell }
6007fe6a43SSeth Howell 
6107fe6a43SSeth Howell static struct spdk_gpt_header *
621d2faf6dSSeth Howell gpt_get_header_buf(struct spdk_gpt *gpt)
6307fe6a43SSeth Howell {
6407fe6a43SSeth Howell 	switch (gpt->parse_phase) {
6507fe6a43SSeth Howell 	case SPDK_GPT_PARSE_PHASE_PRIMARY:
6607fe6a43SSeth Howell 		return (struct spdk_gpt_header *)
6707fe6a43SSeth Howell 		       (gpt->buf + GPT_PRIMARY_PARTITION_TABLE_LBA * gpt->sector_size);
6807fe6a43SSeth Howell 	case SPDK_GPT_PARSE_PHASE_SECONDARY:
6907fe6a43SSeth Howell 		return (struct spdk_gpt_header *)
7007fe6a43SSeth Howell 		       (gpt->buf + (gpt->buf_size - gpt->sector_size));
7107fe6a43SSeth Howell 	default:
7207fe6a43SSeth Howell 		assert(false);
7307fe6a43SSeth Howell 	}
7407fe6a43SSeth Howell 	return NULL;
7507fe6a43SSeth Howell }
7607fe6a43SSeth Howell 
7707fe6a43SSeth Howell static struct spdk_gpt_partition_entry *
781d2faf6dSSeth Howell gpt_get_partitions_buf(struct spdk_gpt *gpt, uint64_t total_partition_size,
7907fe6a43SSeth Howell 		       uint64_t partition_start_lba)
8007fe6a43SSeth Howell {
8107fe6a43SSeth Howell 	uint64_t secondary_total_size;
8207fe6a43SSeth Howell 
8307fe6a43SSeth Howell 	switch (gpt->parse_phase) {
8407fe6a43SSeth Howell 	case SPDK_GPT_PARSE_PHASE_PRIMARY:
8507fe6a43SSeth Howell 		if ((total_partition_size + partition_start_lba * gpt->sector_size) >
8607fe6a43SSeth Howell 		    gpt->buf_size) {
8707fe6a43SSeth Howell 			SPDK_ERRLOG("Buffer size is not enough\n");
8807fe6a43SSeth Howell 			return NULL;
8907fe6a43SSeth Howell 		}
9007fe6a43SSeth Howell 		return (struct spdk_gpt_partition_entry *)
9107fe6a43SSeth Howell 		       (gpt->buf + partition_start_lba * gpt->sector_size);
9207fe6a43SSeth Howell 	case SPDK_GPT_PARSE_PHASE_SECONDARY:
9307fe6a43SSeth Howell 		secondary_total_size = (gpt->lba_end - partition_start_lba + 1) * gpt->sector_size;
9407fe6a43SSeth Howell 		if (secondary_total_size > gpt->buf_size) {
9507fe6a43SSeth Howell 			SPDK_ERRLOG("Buffer size is not enough\n");
9607fe6a43SSeth Howell 			return NULL;
9707fe6a43SSeth Howell 		}
9807fe6a43SSeth Howell 		return (struct spdk_gpt_partition_entry *)
9907fe6a43SSeth Howell 		       (gpt->buf + (gpt->buf_size - secondary_total_size));
10007fe6a43SSeth Howell 	default:
10107fe6a43SSeth Howell 		assert(false);
10207fe6a43SSeth Howell 	}
10307fe6a43SSeth Howell 	return NULL;
10407fe6a43SSeth Howell }
10507fe6a43SSeth Howell 
10607fe6a43SSeth Howell static int
1071d2faf6dSSeth Howell gpt_read_partitions(struct spdk_gpt *gpt)
10807fe6a43SSeth Howell {
10907fe6a43SSeth Howell 	uint32_t total_partition_size, num_partition_entries, partition_entry_size;
11007fe6a43SSeth Howell 	uint64_t partition_start_lba;
11107fe6a43SSeth Howell 	struct spdk_gpt_header *head = gpt->header;
11207fe6a43SSeth Howell 	uint32_t crc32;
11307fe6a43SSeth Howell 
11407fe6a43SSeth Howell 	num_partition_entries = from_le32(&head->num_partition_entries);
11507fe6a43SSeth Howell 	if (num_partition_entries > SPDK_MAX_NUM_PARTITION_ENTRIES) {
11607fe6a43SSeth Howell 		SPDK_ERRLOG("Num_partition_entries=%u which exceeds max=%u\n",
11707fe6a43SSeth Howell 			    num_partition_entries, SPDK_MAX_NUM_PARTITION_ENTRIES);
11807fe6a43SSeth Howell 		return -1;
11907fe6a43SSeth Howell 	}
12007fe6a43SSeth Howell 
12107fe6a43SSeth Howell 	partition_entry_size = from_le32(&head->size_of_partition_entry);
12207fe6a43SSeth Howell 	if (partition_entry_size != sizeof(struct spdk_gpt_partition_entry)) {
123*024179d8SNick Connolly 		SPDK_ERRLOG("Partition_entry_size(%x) != expected(%zx)\n",
12407fe6a43SSeth Howell 			    partition_entry_size, sizeof(struct spdk_gpt_partition_entry));
12507fe6a43SSeth Howell 		return -1;
12607fe6a43SSeth Howell 	}
12707fe6a43SSeth Howell 
12807fe6a43SSeth Howell 	total_partition_size = num_partition_entries * partition_entry_size;
12907fe6a43SSeth Howell 	partition_start_lba = from_le64(&head->partition_entry_lba);
1301d2faf6dSSeth Howell 	gpt->partitions = gpt_get_partitions_buf(gpt, total_partition_size,
13107fe6a43SSeth Howell 			  partition_start_lba);
13207fe6a43SSeth Howell 	if (!gpt->partitions) {
13307fe6a43SSeth Howell 		SPDK_ERRLOG("Failed to get gpt partitions buf\n");
13407fe6a43SSeth Howell 		return -1;
13507fe6a43SSeth Howell 	}
13607fe6a43SSeth Howell 
13707fe6a43SSeth Howell 	crc32 = spdk_crc32_ieee_update(gpt->partitions, total_partition_size, ~0);
13807fe6a43SSeth Howell 	crc32 ^= ~0;
13907fe6a43SSeth Howell 
14007fe6a43SSeth Howell 	if (crc32 != from_le32(&head->partition_entry_array_crc32)) {
14107fe6a43SSeth Howell 		SPDK_ERRLOG("GPT partition entry array crc32 did not match\n");
14207fe6a43SSeth Howell 		return -1;
14307fe6a43SSeth Howell 	}
14407fe6a43SSeth Howell 
14507fe6a43SSeth Howell 	return 0;
14607fe6a43SSeth Howell }
14707fe6a43SSeth Howell 
14807fe6a43SSeth Howell static int
1491d2faf6dSSeth Howell gpt_lba_range_check(struct spdk_gpt_header *head, uint64_t lba_end)
15007fe6a43SSeth Howell {
15107fe6a43SSeth Howell 	uint64_t usable_lba_start, usable_lba_end;
15207fe6a43SSeth Howell 
15307fe6a43SSeth Howell 	usable_lba_start = from_le64(&head->first_usable_lba);
15407fe6a43SSeth Howell 	usable_lba_end = from_le64(&head->last_usable_lba);
15507fe6a43SSeth Howell 
15607fe6a43SSeth Howell 	if (usable_lba_end < usable_lba_start) {
15707fe6a43SSeth Howell 		SPDK_ERRLOG("Head's usable_lba_end(%" PRIu64 ") < usable_lba_start(%" PRIu64 ")\n",
15807fe6a43SSeth Howell 			    usable_lba_end, usable_lba_start);
15907fe6a43SSeth Howell 		return -1;
16007fe6a43SSeth Howell 	}
16107fe6a43SSeth Howell 
16207fe6a43SSeth Howell 	if (usable_lba_end > lba_end) {
16307fe6a43SSeth Howell 		SPDK_ERRLOG("Head's usable_lba_end(%" PRIu64 ") > lba_end(%" PRIu64 ")\n",
16407fe6a43SSeth Howell 			    usable_lba_end, lba_end);
16507fe6a43SSeth Howell 		return -1;
16607fe6a43SSeth Howell 	}
16707fe6a43SSeth Howell 
16807fe6a43SSeth Howell 	if ((usable_lba_start < GPT_PRIMARY_PARTITION_TABLE_LBA) &&
16907fe6a43SSeth Howell 	    (GPT_PRIMARY_PARTITION_TABLE_LBA < usable_lba_end)) {
17007fe6a43SSeth Howell 		SPDK_ERRLOG("Head lba is not in the usable range\n");
17107fe6a43SSeth Howell 		return -1;
17207fe6a43SSeth Howell 	}
17307fe6a43SSeth Howell 
17407fe6a43SSeth Howell 	return 0;
17507fe6a43SSeth Howell }
17607fe6a43SSeth Howell 
17707fe6a43SSeth Howell static int
1781d2faf6dSSeth Howell gpt_read_header(struct spdk_gpt *gpt)
17907fe6a43SSeth Howell {
18007fe6a43SSeth Howell 	uint32_t head_size;
18107fe6a43SSeth Howell 	uint32_t new_crc, original_crc;
18207fe6a43SSeth Howell 	uint64_t my_lba, head_lba;
18307fe6a43SSeth Howell 	struct spdk_gpt_header *head;
18407fe6a43SSeth Howell 
1851d2faf6dSSeth Howell 	head = gpt_get_header_buf(gpt);
18607fe6a43SSeth Howell 	if (!head) {
18707fe6a43SSeth Howell 		SPDK_ERRLOG("Failed to get gpt header buf\n");
18807fe6a43SSeth Howell 		return -1;
18907fe6a43SSeth Howell 	}
19007fe6a43SSeth Howell 
19107fe6a43SSeth Howell 	head_size = from_le32(&head->header_size);
19207fe6a43SSeth Howell 	if (head_size < sizeof(*head) || head_size > gpt->sector_size) {
19307fe6a43SSeth Howell 		SPDK_ERRLOG("head_size=%u\n", head_size);
19407fe6a43SSeth Howell 		return -1;
19507fe6a43SSeth Howell 	}
19607fe6a43SSeth Howell 
19707fe6a43SSeth Howell 	original_crc = from_le32(&head->header_crc32);
19807fe6a43SSeth Howell 	head->header_crc32 = 0;
19907fe6a43SSeth Howell 	new_crc = spdk_crc32_ieee_update(head, from_le32(&head->header_size), ~0);
20007fe6a43SSeth Howell 	new_crc ^= ~0;
20107fe6a43SSeth Howell 	/* restore header crc32 */
20207fe6a43SSeth Howell 	to_le32(&head->header_crc32, original_crc);
20307fe6a43SSeth Howell 
20407fe6a43SSeth Howell 	if (new_crc != original_crc) {
20507fe6a43SSeth Howell 		SPDK_ERRLOG("head crc32 does not match, provided=%u, caculated=%u\n",
20607fe6a43SSeth Howell 			    original_crc, new_crc);
20707fe6a43SSeth Howell 		return -1;
20807fe6a43SSeth Howell 	}
20907fe6a43SSeth Howell 
21007fe6a43SSeth Howell 	if (memcmp(SPDK_GPT_SIGNATURE, head->gpt_signature,
21107fe6a43SSeth Howell 		   sizeof(head->gpt_signature))) {
21207fe6a43SSeth Howell 		SPDK_ERRLOG("signature did not match\n");
21307fe6a43SSeth Howell 		return -1;
21407fe6a43SSeth Howell 	}
21507fe6a43SSeth Howell 
2161d2faf6dSSeth Howell 	head_lba = gpt_get_expected_head_lba(gpt);
21707fe6a43SSeth Howell 	my_lba = from_le64(&head->my_lba);
21807fe6a43SSeth Howell 	if (my_lba != head_lba) {
21907fe6a43SSeth Howell 		SPDK_ERRLOG("head my_lba(%" PRIu64 ") != expected(%" PRIu64 ")\n",
22007fe6a43SSeth Howell 			    my_lba, head_lba);
22107fe6a43SSeth Howell 		return -1;
22207fe6a43SSeth Howell 	}
22307fe6a43SSeth Howell 
2241d2faf6dSSeth Howell 	if (gpt_lba_range_check(head, gpt->lba_end)) {
22507fe6a43SSeth Howell 		SPDK_ERRLOG("lba range check error\n");
22607fe6a43SSeth Howell 		return -1;
22707fe6a43SSeth Howell 	}
22807fe6a43SSeth Howell 
22907fe6a43SSeth Howell 	gpt->header = head;
23007fe6a43SSeth Howell 	return 0;
23107fe6a43SSeth Howell }
23207fe6a43SSeth Howell 
23307fe6a43SSeth Howell static int
2341d2faf6dSSeth Howell gpt_check_mbr(struct spdk_gpt *gpt)
23507fe6a43SSeth Howell {
23607fe6a43SSeth Howell 	int i, primary_partition = 0;
23707fe6a43SSeth Howell 	uint32_t total_lba_size = 0, ret = 0, expected_start_lba;
23807fe6a43SSeth Howell 	struct spdk_mbr *mbr;
23907fe6a43SSeth Howell 
24007fe6a43SSeth Howell 	mbr = (struct spdk_mbr *)gpt->buf;
24107fe6a43SSeth Howell 	if (from_le16(&mbr->mbr_signature) != SPDK_MBR_SIGNATURE) {
2422172c432STomasz Zawadzki 		SPDK_DEBUGLOG(gpt_parse, "Signature mismatch, provided=%x,"
24307fe6a43SSeth Howell 			      "expected=%x\n", from_le16(&mbr->disk_signature),
24407fe6a43SSeth Howell 			      SPDK_MBR_SIGNATURE);
24507fe6a43SSeth Howell 		return -1;
24607fe6a43SSeth Howell 	}
24707fe6a43SSeth Howell 
24807fe6a43SSeth Howell 	for (i = 0; i < PRIMARY_PARTITION_NUMBER; i++) {
24907fe6a43SSeth Howell 		if (mbr->partitions[i].os_type == SPDK_MBR_OS_TYPE_GPT_PROTECTIVE) {
25007fe6a43SSeth Howell 			primary_partition = i;
25107fe6a43SSeth Howell 			ret = GPT_PROTECTIVE_MBR;
25207fe6a43SSeth Howell 			break;
25307fe6a43SSeth Howell 		}
25407fe6a43SSeth Howell 	}
25507fe6a43SSeth Howell 
25607fe6a43SSeth Howell 	if (ret == GPT_PROTECTIVE_MBR) {
25707fe6a43SSeth Howell 		expected_start_lba = GPT_PRIMARY_PARTITION_TABLE_LBA;
25807fe6a43SSeth Howell 		if (from_le32(&mbr->partitions[primary_partition].start_lba) != expected_start_lba) {
2592172c432STomasz Zawadzki 			SPDK_DEBUGLOG(gpt_parse, "start lba mismatch, provided=%u, expected=%u\n",
26007fe6a43SSeth Howell 				      from_le32(&mbr->partitions[primary_partition].start_lba),
26107fe6a43SSeth Howell 				      expected_start_lba);
26207fe6a43SSeth Howell 			return -1;
26307fe6a43SSeth Howell 		}
26407fe6a43SSeth Howell 
26507fe6a43SSeth Howell 		total_lba_size = from_le32(&mbr->partitions[primary_partition].size_lba);
26607fe6a43SSeth Howell 		if ((total_lba_size != ((uint32_t) gpt->total_sectors - 1)) &&
26707fe6a43SSeth Howell 		    (total_lba_size != 0xFFFFFFFF)) {
2682172c432STomasz Zawadzki 			SPDK_DEBUGLOG(gpt_parse,
26907fe6a43SSeth Howell 				      "GPT Primary MBR size does not equal: (record_size %u != actual_size %u)!\n",
27007fe6a43SSeth Howell 				      total_lba_size, (uint32_t) gpt->total_sectors - 1);
27107fe6a43SSeth Howell 			return -1;
27207fe6a43SSeth Howell 		}
27307fe6a43SSeth Howell 	} else {
2742172c432STomasz Zawadzki 		SPDK_DEBUGLOG(gpt_parse, "Currently only support GPT Protective MBR format\n");
27507fe6a43SSeth Howell 		return -1;
27607fe6a43SSeth Howell 	}
27707fe6a43SSeth Howell 
27807fe6a43SSeth Howell 	return 0;
27907fe6a43SSeth Howell }
28007fe6a43SSeth Howell 
28107fe6a43SSeth Howell int
2821d2faf6dSSeth Howell gpt_parse_mbr(struct spdk_gpt *gpt)
28307fe6a43SSeth Howell {
28407fe6a43SSeth Howell 	int rc;
28507fe6a43SSeth Howell 
28607fe6a43SSeth Howell 	if (!gpt || !gpt->buf) {
28707fe6a43SSeth Howell 		SPDK_ERRLOG("Gpt and the related buffer should not be NULL\n");
28807fe6a43SSeth Howell 		return -1;
28907fe6a43SSeth Howell 	}
29007fe6a43SSeth Howell 
2911d2faf6dSSeth Howell 	rc = gpt_check_mbr(gpt);
29207fe6a43SSeth Howell 	if (rc) {
2932172c432STomasz Zawadzki 		SPDK_DEBUGLOG(gpt_parse, "Failed to detect gpt in MBR\n");
29407fe6a43SSeth Howell 		return rc;
29507fe6a43SSeth Howell 	}
29607fe6a43SSeth Howell 
29707fe6a43SSeth Howell 	return 0;
29807fe6a43SSeth Howell }
29907fe6a43SSeth Howell 
30007fe6a43SSeth Howell int
3011d2faf6dSSeth Howell gpt_parse_partition_table(struct spdk_gpt *gpt)
30207fe6a43SSeth Howell {
30307fe6a43SSeth Howell 	int rc;
30407fe6a43SSeth Howell 
3051d2faf6dSSeth Howell 	rc = gpt_read_header(gpt);
30607fe6a43SSeth Howell 	if (rc) {
30707fe6a43SSeth Howell 		SPDK_ERRLOG("Failed to read gpt header\n");
30807fe6a43SSeth Howell 		return rc;
30907fe6a43SSeth Howell 	}
31007fe6a43SSeth Howell 
3111d2faf6dSSeth Howell 	rc = gpt_read_partitions(gpt);
31207fe6a43SSeth Howell 	if (rc) {
31307fe6a43SSeth Howell 		SPDK_ERRLOG("Failed to read gpt partitions\n");
31407fe6a43SSeth Howell 		return rc;
31507fe6a43SSeth Howell 	}
31607fe6a43SSeth Howell 
31707fe6a43SSeth Howell 	return 0;
31807fe6a43SSeth Howell }
31907fe6a43SSeth Howell 
3202172c432STomasz Zawadzki SPDK_LOG_REGISTER_COMPONENT(gpt_parse)
321