xref: /aosp_15_r20/external/vboot_reference/firmware/lib/gpt_misc.c (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
1 /* Copyright 2013 The ChromiumOS Authors
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 "2common.h"
7 #include "2sysincludes.h"
8 #include "cgptlib.h"
9 #include "cgptlib_internal.h"
10 #include "crc32.h"
11 #include "gpt.h"
12 #include "vboot_api.h"
13 
14 /**
15  * Allocate and read GPT data from the drive.
16  *
17  * The sector_bytes and gpt_drive_sectors fields should be filled on input.  The
18  * primary and secondary header and entries are filled on output.
19  *
20  * Returns 0 if successful, 1 if error.
21  */
AllocAndReadGptData(vb2ex_disk_handle_t disk_handle,GptData * gptdata)22 int AllocAndReadGptData(vb2ex_disk_handle_t disk_handle, GptData *gptdata)
23 {
24 	int primary_valid = 0, secondary_valid = 0;
25 
26 	/* No data to be written yet */
27 	gptdata->modified = 0;
28 	/* This should get overwritten by GptInit() */
29 	gptdata->ignored = 0;
30 
31 	/* Allocate all buffers */
32 	gptdata->primary_header = (uint8_t *)malloc(gptdata->sector_bytes);
33 	gptdata->secondary_header =
34 		(uint8_t *)malloc(gptdata->sector_bytes);
35 	gptdata->primary_entries = (uint8_t *)malloc(GPT_ENTRIES_ALLOC_SIZE);
36 	gptdata->secondary_entries = (uint8_t *)malloc(GPT_ENTRIES_ALLOC_SIZE);
37 
38 	/* In some cases we try to validate header1 with entries2 or vice versa,
39 	   so make sure the entries buffers always got fully initialized. */
40 	memset(gptdata->primary_entries, 0, GPT_ENTRIES_ALLOC_SIZE);
41 	memset(gptdata->secondary_entries, 0, GPT_ENTRIES_ALLOC_SIZE);
42 
43 	if (gptdata->primary_header == NULL ||
44 	    gptdata->secondary_header == NULL ||
45 	    gptdata->primary_entries == NULL ||
46 	    gptdata->secondary_entries == NULL)
47 		return 1;
48 
49 	/* Read primary header from the drive, skipping the protective MBR */
50 	if (0 != VbExDiskRead(disk_handle, 1, 1, gptdata->primary_header)) {
51 		VB2_DEBUG("Read error in primary GPT header\n");
52 		memset(gptdata->primary_header, 0, gptdata->sector_bytes);
53 	}
54 
55 	/* Only read primary GPT if the primary header is valid */
56 	GptHeader* primary_header = (GptHeader*)gptdata->primary_header;
57 	if (0 == CheckHeader(primary_header, 0,
58 			gptdata->streaming_drive_sectors,
59 			gptdata->gpt_drive_sectors,
60 			gptdata->flags,
61 			gptdata->sector_bytes)) {
62 		primary_valid = 1;
63 		uint64_t entries_bytes =
64 				(uint64_t)primary_header->number_of_entries
65 				* primary_header->size_of_entry;
66 		uint64_t entries_sectors =
67 				(entries_bytes + gptdata->sector_bytes - 1)
68 				/ gptdata->sector_bytes;
69 		if (0 != VbExDiskRead(disk_handle,
70 				      primary_header->entries_lba,
71 				      entries_sectors,
72 				      gptdata->primary_entries)) {
73 			VB2_DEBUG("Read error in primary GPT entries\n");
74 			primary_valid = 0;
75 		}
76 	} else {
77 		VB2_DEBUG("Primary GPT header is %s\n",
78 			  memcmp(primary_header->signature,
79 				 GPT_HEADER_SIGNATURE_IGNORED,
80 				 GPT_HEADER_SIGNATURE_SIZE)
81 			  ? "invalid" : "being ignored");
82 	}
83 
84 	/* Read secondary header from the end of the drive */
85 	if (0 != VbExDiskRead(disk_handle, gptdata->gpt_drive_sectors - 1, 1,
86 			      gptdata->secondary_header)) {
87 		VB2_DEBUG("Read error in secondary GPT header\n");
88 		memset(gptdata->secondary_header, 0, gptdata->sector_bytes);
89 	}
90 
91 	/* Only read secondary GPT if the secondary header is valid */
92 	GptHeader* secondary_header = (GptHeader*)gptdata->secondary_header;
93 	if (0 == CheckHeader(secondary_header, 1,
94 			gptdata->streaming_drive_sectors,
95 			gptdata->gpt_drive_sectors,
96 			gptdata->flags,
97 			gptdata->sector_bytes)) {
98 		secondary_valid = 1;
99 		uint64_t entries_bytes =
100 				(uint64_t)secondary_header->number_of_entries
101 				* secondary_header->size_of_entry;
102 		uint64_t entries_sectors =
103 				(entries_bytes + gptdata->sector_bytes - 1)
104 				/ gptdata->sector_bytes;
105 		if (0 != VbExDiskRead(disk_handle,
106 				      secondary_header->entries_lba,
107 				      entries_sectors,
108 				      gptdata->secondary_entries)) {
109 			VB2_DEBUG("Read error in secondary GPT entries\n");
110 			secondary_valid = 0;
111 		}
112 	} else {
113 		VB2_DEBUG("Secondary GPT header is %s\n",
114 			  memcmp(secondary_header->signature,
115 				 GPT_HEADER_SIGNATURE_IGNORED,
116 				 GPT_HEADER_SIGNATURE_SIZE)
117 			  ? "invalid" : "being ignored");
118 	}
119 
120 	/* Return 0 if least one GPT header was valid */
121 	return (primary_valid || secondary_valid) ? 0 : 1;
122 }
123 
124 /**
125  * Write any changes for the GPT data back to the drive, then free the buffers.
126  *
127  * Returns 0 if successful, 1 if error.
128  */
WriteAndFreeGptData(vb2ex_disk_handle_t disk_handle,GptData * gptdata)129 int WriteAndFreeGptData(vb2ex_disk_handle_t disk_handle, GptData *gptdata)
130 {
131 	int skip_primary = 0;
132 	GptHeader *header;
133 	uint64_t entries_bytes, entries_sectors;
134 	int ret = 1;
135 
136 	header = (GptHeader *)gptdata->primary_header;
137 	if (!header)
138 		header = (GptHeader *)gptdata->secondary_header;
139 	if (!header)
140 		return 1;  /* No headers at all, so nothing to write */
141 
142 	entries_bytes = (uint64_t)header->number_of_entries
143 			* header->size_of_entry;
144 	entries_sectors = entries_bytes / gptdata->sector_bytes;
145 
146 	/*
147 	 * TODO(namnguyen): Preserve padding between primary GPT header and
148 	 * its entries.
149 	 */
150 	uint64_t entries_lba = GPT_PMBR_SECTORS + GPT_HEADER_SECTORS;
151 	if (gptdata->primary_header) {
152 		GptHeader *h = (GptHeader *)(gptdata->primary_header);
153 		entries_lba = h->entries_lba;
154 
155 		if (gptdata->ignored & MASK_PRIMARY) {
156 			VB2_DEBUG("Not updating primary GPT: "
157 				  "marked to be ignored.\n");
158 			skip_primary = 1;
159 		} else if (gptdata->modified & GPT_MODIFIED_HEADER1) {
160 			if (!memcmp(h->signature, GPT_HEADER_SIGNATURE2,
161 				    GPT_HEADER_SIGNATURE_SIZE)) {
162 				VB2_DEBUG("Not updating primary GPT: "
163 					  "legacy mode is enabled.\n");
164 				skip_primary = 1;
165 			} else {
166 				VB2_DEBUG("Updating GPT header 1\n");
167 				if (0 != VbExDiskWrite(disk_handle, 1, 1,
168 						       gptdata->primary_header))
169 					goto fail;
170 			}
171 		}
172 	}
173 
174 	if (gptdata->primary_entries && !skip_primary) {
175 		if (gptdata->modified & GPT_MODIFIED_ENTRIES1) {
176 			VB2_DEBUG("Updating GPT entries 1\n");
177 			if (0 != VbExDiskWrite(disk_handle, entries_lba,
178 					       entries_sectors,
179 					       gptdata->primary_entries))
180 				goto fail;
181 		}
182 	}
183 
184 	entries_lba = (gptdata->gpt_drive_sectors - entries_sectors -
185 		GPT_HEADER_SECTORS);
186 	if (gptdata->secondary_header && !(gptdata->ignored & MASK_SECONDARY)) {
187 		GptHeader *h = (GptHeader *)(gptdata->secondary_header);
188 		entries_lba = h->entries_lba;
189 		if (gptdata->modified & GPT_MODIFIED_HEADER2) {
190 			VB2_DEBUG("Updating GPT header 2\n");
191 			if (0 != VbExDiskWrite(disk_handle,
192 					       gptdata->gpt_drive_sectors - 1, 1,
193 					       gptdata->secondary_header))
194 				goto fail;
195 		}
196 	}
197 
198 	if (gptdata->secondary_entries && !(gptdata->ignored & MASK_SECONDARY)){
199 		if (gptdata->modified & GPT_MODIFIED_ENTRIES2) {
200 			VB2_DEBUG("Updating GPT entries 2\n");
201 			if (0 != VbExDiskWrite(disk_handle,
202 					       entries_lba, entries_sectors,
203 					       gptdata->secondary_entries))
204 				goto fail;
205 		}
206 	}
207 
208 	ret = 0;
209 
210  fail:
211 	/* Avoid leaking memory on disk write failure */
212 	if (gptdata->primary_header)
213 		free(gptdata->primary_header);
214 	if (gptdata->primary_entries)
215 		free(gptdata->primary_entries);
216 	if (gptdata->secondary_entries)
217 		free(gptdata->secondary_entries);
218 	if (gptdata->secondary_header)
219 		free(gptdata->secondary_header);
220 
221 	/* Success */
222 	return ret;
223 }
224 
IsUnusedEntry(const GptEntry * e)225 int IsUnusedEntry(const GptEntry *e)
226 {
227 	static Guid zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}};
228 	return !memcmp(&zero, (const uint8_t*)(&e->type), sizeof(zero));
229 }
230 
231 /*
232  * Func: GptGetEntrySize
233  * Desc: This function returns size(in lba) of a partition represented by
234  * given GPT entry.
235  */
GptGetEntrySizeLba(const GptEntry * e)236 uint64_t GptGetEntrySizeLba(const GptEntry *e)
237 {
238 	return (e->ending_lba - e->starting_lba + 1);
239 }
240 
241 /*
242  * Func: GptGetEntrySize
243  * Desc: This function returns size(in bytes) of a partition represented by
244  * given GPT entry.
245  */
GptGetEntrySizeBytes(const GptData * gpt,const GptEntry * e)246 uint64_t GptGetEntrySizeBytes(const GptData *gpt, const GptEntry *e)
247 {
248 	return GptGetEntrySizeLba(e) * gpt->sector_bytes;
249 }
250