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