1 /* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 2 * Use of this source code is governed by a BSD-style license that can be 3 * found in the LICENSE file. 4 */ 5 6 #include "sysincludes.h" 7 8 #include "cgptlib.h" 9 #include "cgptlib_internal.h" 10 #include "crc32.h" 11 #include "gpt.h" 12 #include "utility.h" 13 #include "vboot_api.h" 14 15 16 /** 17 * Allocate and read GPT data from the drive. 18 * 19 * The sector_bytes and gpt_drive_sectors fields should be filled on input. The 20 * primary and secondary header and entries are filled on output. 21 * 22 * Returns 0 if successful, 1 if error. 23 */ 24 int AllocAndReadGptData(VbExDiskHandle_t disk_handle, GptData *gptdata) 25 { 26 uint64_t max_entries_bytes = MAX_NUMBER_OF_ENTRIES * sizeof(GptEntry); 27 int primary_valid = 0, secondary_valid = 0; 28 29 /* No data to be written yet */ 30 gptdata->modified = 0; 31 32 /* Allocate all buffers */ 33 gptdata->primary_header = (uint8_t *)VbExMalloc(gptdata->sector_bytes); 34 gptdata->secondary_header = 35 (uint8_t *)VbExMalloc(gptdata->sector_bytes); 36 gptdata->primary_entries = (uint8_t *)VbExMalloc(max_entries_bytes); 37 gptdata->secondary_entries = (uint8_t *)VbExMalloc(max_entries_bytes); 38 39 if (gptdata->primary_header == NULL || 40 gptdata->secondary_header == NULL || 41 gptdata->primary_entries == NULL || 42 gptdata->secondary_entries == NULL) 43 return 1; 44 45 /* Read primary header from the drive, skipping the protective MBR */ 46 if (0 != VbExDiskRead(disk_handle, 1, 1, gptdata->primary_header)) 47 return 1; 48 49 /* Only read primary GPT if the primary header is valid */ 50 GptHeader* primary_header = (GptHeader*)gptdata->primary_header; 51 if (0 == CheckHeader(primary_header, 0, 52 gptdata->streaming_drive_sectors, 53 gptdata->gpt_drive_sectors, 54 gptdata->flags)) { 55 primary_valid = 1; 56 uint64_t entries_bytes = primary_header->number_of_entries 57 * primary_header->size_of_entry; 58 uint64_t entries_sectors = entries_bytes 59 / gptdata->sector_bytes; 60 if (0 != VbExDiskRead(disk_handle, 61 primary_header->entries_lba, 62 entries_sectors, 63 gptdata->primary_entries)) 64 return 1; 65 } else { 66 VBDEBUG(("Primary GPT header invalid!\n")); 67 } 68 69 /* Read secondary header from the end of the drive */ 70 if (0 != VbExDiskRead(disk_handle, gptdata->gpt_drive_sectors - 1, 1, 71 gptdata->secondary_header)) 72 return 1; 73 74 /* Only read secondary GPT if the secondary header is valid */ 75 GptHeader* secondary_header = (GptHeader*)gptdata->secondary_header; 76 if (0 == CheckHeader(secondary_header, 1, 77 gptdata->streaming_drive_sectors, 78 gptdata->gpt_drive_sectors, 79 gptdata->flags)) { 80 secondary_valid = 1; 81 uint64_t entries_bytes = secondary_header->number_of_entries 82 * secondary_header->size_of_entry; 83 uint64_t entries_sectors = entries_bytes 84 / gptdata->sector_bytes; 85 if (0 != VbExDiskRead(disk_handle, 86 secondary_header->entries_lba, 87 entries_sectors, 88 gptdata->secondary_entries)) 89 return 1; 90 } else { 91 VBDEBUG(("Secondary GPT header invalid!\n")); 92 } 93 94 /* Return 0 if least one GPT header was valid */ 95 return (primary_valid || secondary_valid) ? 0 : 1; 96 } 97 98 /** 99 * Write any changes for the GPT data back to the drive, then free the buffers. 100 * 101 * Returns 0 if successful, 1 if error. 102 */ 103 int WriteAndFreeGptData(VbExDiskHandle_t disk_handle, GptData *gptdata) 104 { 105 int legacy = 0; 106 GptHeader *header = (GptHeader *)gptdata->primary_header; 107 uint64_t entries_bytes = header->number_of_entries 108 * header->size_of_entry; 109 uint64_t entries_sectors = entries_bytes / gptdata->sector_bytes; 110 int ret = 1; 111 112 /* 113 * TODO(namnguyen): Preserve padding between primary GPT header and 114 * its entries. 115 */ 116 uint64_t entries_lba = GPT_PMBR_SECTORS + GPT_HEADER_SECTORS; 117 if (gptdata->primary_header) { 118 GptHeader *h = (GptHeader *)(gptdata->primary_header); 119 entries_lba = h->entries_lba; 120 121 /* 122 * Avoid even looking at this data if we don't need to. We 123 * may in fact not have read it from disk if the read failed, 124 * and this avoids a valgrind complaint. 125 */ 126 if (gptdata->modified) { 127 legacy = !Memcmp(h->signature, GPT_HEADER_SIGNATURE2, 128 GPT_HEADER_SIGNATURE_SIZE); 129 } 130 if (gptdata->modified & GPT_MODIFIED_HEADER1) { 131 if (legacy) { 132 VBDEBUG(("Not updating GPT header 1: " 133 "legacy mode is enabled.\n")); 134 } else { 135 VBDEBUG(("Updating GPT header 1\n")); 136 if (0 != VbExDiskWrite(disk_handle, 1, 1, 137 gptdata->primary_header)) 138 goto fail; 139 } 140 } 141 } 142 143 if (gptdata->primary_entries) { 144 if (gptdata->modified & GPT_MODIFIED_ENTRIES1) { 145 if (legacy) { 146 VBDEBUG(("Not updating GPT entries 1: " 147 "legacy mode is enabled.\n")); 148 } else { 149 VBDEBUG(("Updating GPT entries 1\n")); 150 if (0 != VbExDiskWrite(disk_handle, entries_lba, 151 entries_sectors, 152 gptdata->primary_entries)) 153 goto fail; 154 } 155 } 156 } 157 158 entries_lba = (gptdata->gpt_drive_sectors - entries_sectors - 159 GPT_HEADER_SECTORS); 160 if (gptdata->secondary_header) { 161 GptHeader *h = (GptHeader *)(gptdata->secondary_header); 162 entries_lba = h->entries_lba; 163 if (gptdata->modified & GPT_MODIFIED_HEADER2) { 164 VBDEBUG(("Updating GPT entries 2\n")); 165 if (0 != VbExDiskWrite(disk_handle, 166 gptdata->gpt_drive_sectors - 1, 1, 167 gptdata->secondary_header)) 168 goto fail; 169 } 170 } 171 172 if (gptdata->secondary_entries) { 173 if (gptdata->modified & GPT_MODIFIED_ENTRIES2) { 174 VBDEBUG(("Updating GPT header 2\n")); 175 if (0 != VbExDiskWrite(disk_handle, 176 entries_lba, entries_sectors, 177 gptdata->secondary_entries)) 178 goto fail; 179 } 180 } 181 182 ret = 0; 183 184 fail: 185 /* Avoid leaking memory on disk write failure */ 186 if (gptdata->primary_header) 187 VbExFree(gptdata->primary_header); 188 if (gptdata->primary_entries) 189 VbExFree(gptdata->primary_entries); 190 if (gptdata->secondary_entries) 191 VbExFree(gptdata->secondary_entries); 192 if (gptdata->secondary_header) 193 VbExFree(gptdata->secondary_header); 194 195 /* Success */ 196 return ret; 197 } 198 199 int IsUnusedEntry(const GptEntry *e) 200 { 201 static Guid zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}}; 202 return !Memcmp(&zero, (const uint8_t*)(&e->type), sizeof(zero)); 203 } 204 205 /* 206 * Func: GptGetEntrySize 207 * Desc: This function returns size(in lba) of a partition represented by 208 * given GPT entry. 209 */ 210 size_t GptGetEntrySizeLba(const GptEntry *e) 211 { 212 return (e->ending_lba - e->starting_lba + 1); 213 } 214 215 /* 216 * Func: GptGetEntrySize 217 * Desc: This function returns size(in bytes) of a partition represented by 218 * given GPT entry. 219 */ 220 size_t GptGetEntrySizeBytes(const GptData *gpt, const GptEntry *e) 221 { 222 return GptGetEntrySizeLba(e) * gpt->sector_bytes; 223 } 224