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 */
24int 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 */
103int 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
184fail:
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
199int 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 */
210size_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 */
220size_t GptGetEntrySizeBytes(const GptData *gpt, const GptEntry *e)
221{
222	return GptGetEntrySizeLba(e) * gpt->sector_bytes;
223}
224