1488570ebSJim Harris /* SPDX-License-Identifier: BSD-3-Clause
2*a6dbe372Spaul luse * Copyright (C) 2017 Intel Corporation.
307fe6a43SSeth Howell * All rights reserved.
407fe6a43SSeth Howell */
507fe6a43SSeth Howell
607fe6a43SSeth Howell #include "gpt.h"
707fe6a43SSeth Howell
807fe6a43SSeth Howell #include "spdk/crc32.h"
907fe6a43SSeth Howell #include "spdk/endian.h"
1007fe6a43SSeth Howell #include "spdk/event.h"
1107fe6a43SSeth Howell
124e8e97c8STomasz Zawadzki #include "spdk/log.h"
1307fe6a43SSeth Howell
1407fe6a43SSeth Howell #define GPT_PRIMARY_PARTITION_TABLE_LBA 0x1
1507fe6a43SSeth Howell #define PRIMARY_PARTITION_NUMBER 4
1607fe6a43SSeth Howell #define GPT_PROTECTIVE_MBR 1
1707fe6a43SSeth Howell #define SPDK_MAX_NUM_PARTITION_ENTRIES 128
1807fe6a43SSeth Howell
1907fe6a43SSeth Howell static uint64_t
gpt_get_expected_head_lba(struct spdk_gpt * gpt)201d2faf6dSSeth Howell gpt_get_expected_head_lba(struct spdk_gpt *gpt)
2107fe6a43SSeth Howell {
2207fe6a43SSeth Howell switch (gpt->parse_phase) {
2307fe6a43SSeth Howell case SPDK_GPT_PARSE_PHASE_PRIMARY:
2407fe6a43SSeth Howell return GPT_PRIMARY_PARTITION_TABLE_LBA;
2507fe6a43SSeth Howell case SPDK_GPT_PARSE_PHASE_SECONDARY:
2607fe6a43SSeth Howell return gpt->lba_end;
2707fe6a43SSeth Howell default:
2807fe6a43SSeth Howell assert(false);
2907fe6a43SSeth Howell }
3007fe6a43SSeth Howell return 0;
3107fe6a43SSeth Howell }
3207fe6a43SSeth Howell
3307fe6a43SSeth Howell static struct spdk_gpt_header *
gpt_get_header_buf(struct spdk_gpt * gpt)341d2faf6dSSeth Howell gpt_get_header_buf(struct spdk_gpt *gpt)
3507fe6a43SSeth Howell {
3607fe6a43SSeth Howell switch (gpt->parse_phase) {
3707fe6a43SSeth Howell case SPDK_GPT_PARSE_PHASE_PRIMARY:
3807fe6a43SSeth Howell return (struct spdk_gpt_header *)
3907fe6a43SSeth Howell (gpt->buf + GPT_PRIMARY_PARTITION_TABLE_LBA * gpt->sector_size);
4007fe6a43SSeth Howell case SPDK_GPT_PARSE_PHASE_SECONDARY:
4107fe6a43SSeth Howell return (struct spdk_gpt_header *)
4207fe6a43SSeth Howell (gpt->buf + (gpt->buf_size - gpt->sector_size));
4307fe6a43SSeth Howell default:
4407fe6a43SSeth Howell assert(false);
4507fe6a43SSeth Howell }
4607fe6a43SSeth Howell return NULL;
4707fe6a43SSeth Howell }
4807fe6a43SSeth Howell
4907fe6a43SSeth Howell static struct spdk_gpt_partition_entry *
gpt_get_partitions_buf(struct spdk_gpt * gpt,uint64_t total_partition_size,uint64_t partition_start_lba)501d2faf6dSSeth Howell gpt_get_partitions_buf(struct spdk_gpt *gpt, uint64_t total_partition_size,
5107fe6a43SSeth Howell uint64_t partition_start_lba)
5207fe6a43SSeth Howell {
5307fe6a43SSeth Howell uint64_t secondary_total_size;
5407fe6a43SSeth Howell
5507fe6a43SSeth Howell switch (gpt->parse_phase) {
5607fe6a43SSeth Howell case SPDK_GPT_PARSE_PHASE_PRIMARY:
5707fe6a43SSeth Howell if ((total_partition_size + partition_start_lba * gpt->sector_size) >
5807fe6a43SSeth Howell gpt->buf_size) {
5907fe6a43SSeth Howell SPDK_ERRLOG("Buffer size is not enough\n");
6007fe6a43SSeth Howell return NULL;
6107fe6a43SSeth Howell }
6207fe6a43SSeth Howell return (struct spdk_gpt_partition_entry *)
6307fe6a43SSeth Howell (gpt->buf + partition_start_lba * gpt->sector_size);
6407fe6a43SSeth Howell case SPDK_GPT_PARSE_PHASE_SECONDARY:
6507fe6a43SSeth Howell secondary_total_size = (gpt->lba_end - partition_start_lba + 1) * gpt->sector_size;
6607fe6a43SSeth Howell if (secondary_total_size > gpt->buf_size) {
6707fe6a43SSeth Howell SPDK_ERRLOG("Buffer size is not enough\n");
6807fe6a43SSeth Howell return NULL;
6907fe6a43SSeth Howell }
7007fe6a43SSeth Howell return (struct spdk_gpt_partition_entry *)
7107fe6a43SSeth Howell (gpt->buf + (gpt->buf_size - secondary_total_size));
7207fe6a43SSeth Howell default:
7307fe6a43SSeth Howell assert(false);
7407fe6a43SSeth Howell }
7507fe6a43SSeth Howell return NULL;
7607fe6a43SSeth Howell }
7707fe6a43SSeth Howell
7807fe6a43SSeth Howell static int
gpt_read_partitions(struct spdk_gpt * gpt)791d2faf6dSSeth Howell gpt_read_partitions(struct spdk_gpt *gpt)
8007fe6a43SSeth Howell {
8107fe6a43SSeth Howell uint32_t total_partition_size, num_partition_entries, partition_entry_size;
8207fe6a43SSeth Howell uint64_t partition_start_lba;
8307fe6a43SSeth Howell struct spdk_gpt_header *head = gpt->header;
8407fe6a43SSeth Howell uint32_t crc32;
8507fe6a43SSeth Howell
8607fe6a43SSeth Howell num_partition_entries = from_le32(&head->num_partition_entries);
8707fe6a43SSeth Howell if (num_partition_entries > SPDK_MAX_NUM_PARTITION_ENTRIES) {
8807fe6a43SSeth Howell SPDK_ERRLOG("Num_partition_entries=%u which exceeds max=%u\n",
8907fe6a43SSeth Howell num_partition_entries, SPDK_MAX_NUM_PARTITION_ENTRIES);
9007fe6a43SSeth Howell return -1;
9107fe6a43SSeth Howell }
9207fe6a43SSeth Howell
9307fe6a43SSeth Howell partition_entry_size = from_le32(&head->size_of_partition_entry);
9407fe6a43SSeth Howell if (partition_entry_size != sizeof(struct spdk_gpt_partition_entry)) {
95024179d8SNick Connolly SPDK_ERRLOG("Partition_entry_size(%x) != expected(%zx)\n",
9607fe6a43SSeth Howell partition_entry_size, sizeof(struct spdk_gpt_partition_entry));
9707fe6a43SSeth Howell return -1;
9807fe6a43SSeth Howell }
9907fe6a43SSeth Howell
10007fe6a43SSeth Howell total_partition_size = num_partition_entries * partition_entry_size;
10107fe6a43SSeth Howell partition_start_lba = from_le64(&head->partition_entry_lba);
1021d2faf6dSSeth Howell gpt->partitions = gpt_get_partitions_buf(gpt, total_partition_size,
10307fe6a43SSeth Howell partition_start_lba);
10407fe6a43SSeth Howell if (!gpt->partitions) {
10507fe6a43SSeth Howell SPDK_ERRLOG("Failed to get gpt partitions buf\n");
10607fe6a43SSeth Howell return -1;
10707fe6a43SSeth Howell }
10807fe6a43SSeth Howell
10907fe6a43SSeth Howell crc32 = spdk_crc32_ieee_update(gpt->partitions, total_partition_size, ~0);
11007fe6a43SSeth Howell crc32 ^= ~0;
11107fe6a43SSeth Howell
11207fe6a43SSeth Howell if (crc32 != from_le32(&head->partition_entry_array_crc32)) {
11307fe6a43SSeth Howell SPDK_ERRLOG("GPT partition entry array crc32 did not match\n");
11407fe6a43SSeth Howell return -1;
11507fe6a43SSeth Howell }
11607fe6a43SSeth Howell
11707fe6a43SSeth Howell return 0;
11807fe6a43SSeth Howell }
11907fe6a43SSeth Howell
12007fe6a43SSeth Howell static int
gpt_lba_range_check(struct spdk_gpt_header * head,uint64_t lba_end)1211d2faf6dSSeth Howell gpt_lba_range_check(struct spdk_gpt_header *head, uint64_t lba_end)
12207fe6a43SSeth Howell {
12307fe6a43SSeth Howell uint64_t usable_lba_start, usable_lba_end;
12407fe6a43SSeth Howell
12507fe6a43SSeth Howell usable_lba_start = from_le64(&head->first_usable_lba);
12607fe6a43SSeth Howell usable_lba_end = from_le64(&head->last_usable_lba);
12707fe6a43SSeth Howell
12807fe6a43SSeth Howell if (usable_lba_end < usable_lba_start) {
12907fe6a43SSeth Howell SPDK_ERRLOG("Head's usable_lba_end(%" PRIu64 ") < usable_lba_start(%" PRIu64 ")\n",
13007fe6a43SSeth Howell usable_lba_end, usable_lba_start);
13107fe6a43SSeth Howell return -1;
13207fe6a43SSeth Howell }
13307fe6a43SSeth Howell
13407fe6a43SSeth Howell if (usable_lba_end > lba_end) {
13507fe6a43SSeth Howell SPDK_ERRLOG("Head's usable_lba_end(%" PRIu64 ") > lba_end(%" PRIu64 ")\n",
13607fe6a43SSeth Howell usable_lba_end, lba_end);
13707fe6a43SSeth Howell return -1;
13807fe6a43SSeth Howell }
13907fe6a43SSeth Howell
14007fe6a43SSeth Howell if ((usable_lba_start < GPT_PRIMARY_PARTITION_TABLE_LBA) &&
14107fe6a43SSeth Howell (GPT_PRIMARY_PARTITION_TABLE_LBA < usable_lba_end)) {
14207fe6a43SSeth Howell SPDK_ERRLOG("Head lba is not in the usable range\n");
14307fe6a43SSeth Howell return -1;
14407fe6a43SSeth Howell }
14507fe6a43SSeth Howell
14607fe6a43SSeth Howell return 0;
14707fe6a43SSeth Howell }
14807fe6a43SSeth Howell
14907fe6a43SSeth Howell static int
gpt_read_header(struct spdk_gpt * gpt)1501d2faf6dSSeth Howell gpt_read_header(struct spdk_gpt *gpt)
15107fe6a43SSeth Howell {
15207fe6a43SSeth Howell uint32_t head_size;
15307fe6a43SSeth Howell uint32_t new_crc, original_crc;
15407fe6a43SSeth Howell uint64_t my_lba, head_lba;
15507fe6a43SSeth Howell struct spdk_gpt_header *head;
15607fe6a43SSeth Howell
1571d2faf6dSSeth Howell head = gpt_get_header_buf(gpt);
15807fe6a43SSeth Howell if (!head) {
15907fe6a43SSeth Howell SPDK_ERRLOG("Failed to get gpt header buf\n");
16007fe6a43SSeth Howell return -1;
16107fe6a43SSeth Howell }
16207fe6a43SSeth Howell
16307fe6a43SSeth Howell head_size = from_le32(&head->header_size);
16407fe6a43SSeth Howell if (head_size < sizeof(*head) || head_size > gpt->sector_size) {
16507fe6a43SSeth Howell SPDK_ERRLOG("head_size=%u\n", head_size);
16607fe6a43SSeth Howell return -1;
16707fe6a43SSeth Howell }
16807fe6a43SSeth Howell
16907fe6a43SSeth Howell original_crc = from_le32(&head->header_crc32);
17007fe6a43SSeth Howell head->header_crc32 = 0;
17107fe6a43SSeth Howell new_crc = spdk_crc32_ieee_update(head, from_le32(&head->header_size), ~0);
17207fe6a43SSeth Howell new_crc ^= ~0;
17307fe6a43SSeth Howell /* restore header crc32 */
17407fe6a43SSeth Howell to_le32(&head->header_crc32, original_crc);
17507fe6a43SSeth Howell
17607fe6a43SSeth Howell if (new_crc != original_crc) {
1771960ef16SJosh Soref SPDK_ERRLOG("head crc32 does not match, provided=%u, calculated=%u\n",
17807fe6a43SSeth Howell original_crc, new_crc);
17907fe6a43SSeth Howell return -1;
18007fe6a43SSeth Howell }
18107fe6a43SSeth Howell
18207fe6a43SSeth Howell if (memcmp(SPDK_GPT_SIGNATURE, head->gpt_signature,
18307fe6a43SSeth Howell sizeof(head->gpt_signature))) {
18407fe6a43SSeth Howell SPDK_ERRLOG("signature did not match\n");
18507fe6a43SSeth Howell return -1;
18607fe6a43SSeth Howell }
18707fe6a43SSeth Howell
1881d2faf6dSSeth Howell head_lba = gpt_get_expected_head_lba(gpt);
18907fe6a43SSeth Howell my_lba = from_le64(&head->my_lba);
19007fe6a43SSeth Howell if (my_lba != head_lba) {
19107fe6a43SSeth Howell SPDK_ERRLOG("head my_lba(%" PRIu64 ") != expected(%" PRIu64 ")\n",
19207fe6a43SSeth Howell my_lba, head_lba);
19307fe6a43SSeth Howell return -1;
19407fe6a43SSeth Howell }
19507fe6a43SSeth Howell
1961d2faf6dSSeth Howell if (gpt_lba_range_check(head, gpt->lba_end)) {
19707fe6a43SSeth Howell SPDK_ERRLOG("lba range check error\n");
19807fe6a43SSeth Howell return -1;
19907fe6a43SSeth Howell }
20007fe6a43SSeth Howell
20107fe6a43SSeth Howell gpt->header = head;
20207fe6a43SSeth Howell return 0;
20307fe6a43SSeth Howell }
20407fe6a43SSeth Howell
20507fe6a43SSeth Howell static int
gpt_check_mbr(struct spdk_gpt * gpt)2061d2faf6dSSeth Howell gpt_check_mbr(struct spdk_gpt *gpt)
20707fe6a43SSeth Howell {
20807fe6a43SSeth Howell int i, primary_partition = 0;
20907fe6a43SSeth Howell uint32_t total_lba_size = 0, ret = 0, expected_start_lba;
21007fe6a43SSeth Howell struct spdk_mbr *mbr;
21107fe6a43SSeth Howell
21207fe6a43SSeth Howell mbr = (struct spdk_mbr *)gpt->buf;
21307fe6a43SSeth Howell if (from_le16(&mbr->mbr_signature) != SPDK_MBR_SIGNATURE) {
2142172c432STomasz Zawadzki SPDK_DEBUGLOG(gpt_parse, "Signature mismatch, provided=%x,"
21507fe6a43SSeth Howell "expected=%x\n", from_le16(&mbr->disk_signature),
21607fe6a43SSeth Howell SPDK_MBR_SIGNATURE);
21707fe6a43SSeth Howell return -1;
21807fe6a43SSeth Howell }
21907fe6a43SSeth Howell
22007fe6a43SSeth Howell for (i = 0; i < PRIMARY_PARTITION_NUMBER; i++) {
22107fe6a43SSeth Howell if (mbr->partitions[i].os_type == SPDK_MBR_OS_TYPE_GPT_PROTECTIVE) {
22207fe6a43SSeth Howell primary_partition = i;
22307fe6a43SSeth Howell ret = GPT_PROTECTIVE_MBR;
22407fe6a43SSeth Howell break;
22507fe6a43SSeth Howell }
22607fe6a43SSeth Howell }
22707fe6a43SSeth Howell
22807fe6a43SSeth Howell if (ret == GPT_PROTECTIVE_MBR) {
22907fe6a43SSeth Howell expected_start_lba = GPT_PRIMARY_PARTITION_TABLE_LBA;
23007fe6a43SSeth Howell if (from_le32(&mbr->partitions[primary_partition].start_lba) != expected_start_lba) {
2312172c432STomasz Zawadzki SPDK_DEBUGLOG(gpt_parse, "start lba mismatch, provided=%u, expected=%u\n",
23207fe6a43SSeth Howell from_le32(&mbr->partitions[primary_partition].start_lba),
23307fe6a43SSeth Howell expected_start_lba);
23407fe6a43SSeth Howell return -1;
23507fe6a43SSeth Howell }
23607fe6a43SSeth Howell
23707fe6a43SSeth Howell total_lba_size = from_le32(&mbr->partitions[primary_partition].size_lba);
23807fe6a43SSeth Howell if ((total_lba_size != ((uint32_t) gpt->total_sectors - 1)) &&
23907fe6a43SSeth Howell (total_lba_size != 0xFFFFFFFF)) {
2402172c432STomasz Zawadzki SPDK_DEBUGLOG(gpt_parse,
24107fe6a43SSeth Howell "GPT Primary MBR size does not equal: (record_size %u != actual_size %u)!\n",
24207fe6a43SSeth Howell total_lba_size, (uint32_t) gpt->total_sectors - 1);
24307fe6a43SSeth Howell return -1;
24407fe6a43SSeth Howell }
24507fe6a43SSeth Howell } else {
2462172c432STomasz Zawadzki SPDK_DEBUGLOG(gpt_parse, "Currently only support GPT Protective MBR format\n");
24707fe6a43SSeth Howell return -1;
24807fe6a43SSeth Howell }
24907fe6a43SSeth Howell
25007fe6a43SSeth Howell return 0;
25107fe6a43SSeth Howell }
25207fe6a43SSeth Howell
25307fe6a43SSeth Howell int
gpt_parse_mbr(struct spdk_gpt * gpt)2541d2faf6dSSeth Howell gpt_parse_mbr(struct spdk_gpt *gpt)
25507fe6a43SSeth Howell {
25607fe6a43SSeth Howell int rc;
25707fe6a43SSeth Howell
25807fe6a43SSeth Howell if (!gpt || !gpt->buf) {
25907fe6a43SSeth Howell SPDK_ERRLOG("Gpt and the related buffer should not be NULL\n");
26007fe6a43SSeth Howell return -1;
26107fe6a43SSeth Howell }
26207fe6a43SSeth Howell
2631d2faf6dSSeth Howell rc = gpt_check_mbr(gpt);
26407fe6a43SSeth Howell if (rc) {
2652172c432STomasz Zawadzki SPDK_DEBUGLOG(gpt_parse, "Failed to detect gpt in MBR\n");
26607fe6a43SSeth Howell return rc;
26707fe6a43SSeth Howell }
26807fe6a43SSeth Howell
26907fe6a43SSeth Howell return 0;
27007fe6a43SSeth Howell }
27107fe6a43SSeth Howell
27207fe6a43SSeth Howell int
gpt_parse_partition_table(struct spdk_gpt * gpt)2731d2faf6dSSeth Howell gpt_parse_partition_table(struct spdk_gpt *gpt)
27407fe6a43SSeth Howell {
27507fe6a43SSeth Howell int rc;
27607fe6a43SSeth Howell
2771d2faf6dSSeth Howell rc = gpt_read_header(gpt);
27807fe6a43SSeth Howell if (rc) {
27907fe6a43SSeth Howell SPDK_ERRLOG("Failed to read gpt header\n");
28007fe6a43SSeth Howell return rc;
28107fe6a43SSeth Howell }
28207fe6a43SSeth Howell
2831d2faf6dSSeth Howell rc = gpt_read_partitions(gpt);
28407fe6a43SSeth Howell if (rc) {
28507fe6a43SSeth Howell SPDK_ERRLOG("Failed to read gpt partitions\n");
28607fe6a43SSeth Howell return rc;
28707fe6a43SSeth Howell }
28807fe6a43SSeth Howell
28907fe6a43SSeth Howell return 0;
29007fe6a43SSeth Howell }
29107fe6a43SSeth Howell
2922172c432STomasz Zawadzki SPDK_LOG_REGISTER_COMPONENT(gpt_parse)
293