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 */
AllocAndReadGptData(VbExDiskHandle_t disk_handle,GptData * gptdata)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 */
WriteAndFreeGptData(VbExDiskHandle_t disk_handle,GptData * gptdata)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
IsUnusedEntry(const GptEntry * e)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 */
GptGetEntrySizeLba(const GptEntry * e)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 */
GptGetEntrySizeBytes(const GptData * gpt,const GptEntry * e)220 size_t GptGetEntrySizeBytes(const GptData *gpt, const GptEntry *e)
221 {
222 return GptGetEntrySizeLba(e) * gpt->sector_bytes;
223 }
224