xref: /haiku/src/add-ons/kernel/partitioning_systems/gpt/Header.cpp (revision a09c983cc63b6d50c46a0f2a872fb1adbffcc363)
1 /*
2  * Copyright 2007-2013, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. All rights reserved.
4  *
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 #include "Header.h"
10 
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <string.h>
14 
15 #include <KernelExport.h>
16 
17 #ifdef _KERNEL_MODE
18 #	include <util/kernel_cpp.h>
19 #else
20 #	include <new>
21 #endif
22 
23 #include "crc32.h"
24 #include "utility.h"
25 
26 
27 #define TRACE_EFI_GPT
28 #ifdef TRACE_EFI_GPT
29 #	ifndef _KERNEL_MODE
30 #		define dprintf printf
31 #	endif
32 #	define TRACE(x) dprintf x
33 #else
34 #	define TRACE(x) ;
35 #endif
36 
37 
38 namespace EFI {
39 
40 
41 Header::Header(int fd, uint64 lastBlock, uint32 blockSize)
42 	:
43 	fBlockSize(blockSize),
44 	fStatus(B_NO_INIT),
45 	fEntries(NULL)
46 {
47 	// TODO: check the correctness of the protective MBR and warn if invalid
48 
49 	// Read and check the partition table header
50 
51 	fStatus = _Read(fd, (uint64)EFI_HEADER_LOCATION * blockSize,
52 		&fHeader, sizeof(efi_table_header));
53 	if (fStatus == B_OK) {
54 		if (!_IsHeaderValid(fHeader, EFI_HEADER_LOCATION))
55 			fStatus = B_BAD_DATA;
56 	}
57 
58 	if (fStatus == B_OK && lastBlock != fHeader.AlternateBlock()) {
59 		dprintf("gpt: alternate header not in last block (%" B_PRIu64 " vs. %"
60 			B_PRIu64 ")\n", fHeader.AlternateBlock(), lastBlock);
61 		lastBlock = fHeader.AlternateBlock();
62 	}
63 
64 	// Read backup header, too
65 	status_t status = _Read(fd, lastBlock * blockSize, &fBackupHeader,
66 		sizeof(efi_table_header));
67 	if (status == B_OK) {
68 		if (!_IsHeaderValid(fBackupHeader, lastBlock))
69 			status = B_BAD_DATA;
70 	}
71 
72 	// If both headers are invalid, bail out -- this is probably not a GPT disk
73 	if (status != B_OK && fStatus != B_OK)
74 		return;
75 
76 	if (fStatus != B_OK) {
77 		// Recreate primary header from the backup
78 		fHeader = fBackupHeader;
79 		fHeader.SetAbsoluteBlock(EFI_HEADER_LOCATION);
80 		fHeader.SetEntriesBlock(EFI_PARTITION_ENTRIES_BLOCK);
81 		fHeader.SetAlternateBlock(lastBlock);
82 	} else if (status != B_OK) {
83 		// Recreate backup header from primary
84 		_SetBackupHeaderFromPrimary(lastBlock);
85 	}
86 
87 	// allocate, read, and check partition entry array
88 
89 	fEntries = new (std::nothrow) uint8[_EntryArraySize()];
90 	if (fEntries == NULL) {
91 		// TODO: if there cannot be allocated enough (ie. the boot loader's
92 		//	heap is limited), try a smaller size before failing
93 		fStatus = B_NO_MEMORY;
94 		return;
95 	}
96 
97 	fStatus = _Read(fd, fHeader.EntriesBlock() * blockSize,
98 		fEntries, _EntryArraySize());
99 	if (fStatus != B_OK || !_ValidateEntriesCRC()) {
100 		// Read backup entries instead
101 		fStatus = _Read(fd, fBackupHeader.EntriesBlock() * blockSize,
102 			fEntries, _EntryArraySize());
103 		if (fStatus != B_OK)
104 			return;
105 
106 		if (!_ValidateEntriesCRC()) {
107 			fStatus = B_BAD_DATA;
108 			return;
109 		}
110 	}
111 
112 	// TODO: check overlapping or out of range partitions
113 
114 #ifdef TRACE_EFI_GPT
115 	_Dump(fHeader);
116 	_Dump(fBackupHeader);
117 	_DumpPartitions();
118 #endif
119 
120 	fStatus = B_OK;
121 }
122 
123 
124 #ifndef _BOOT_MODE
125 Header::Header(uint64 lastBlock, uint32 blockSize)
126 	:
127 	fBlockSize(blockSize),
128 	fStatus(B_NO_INIT),
129 	fEntries(NULL)
130 {
131 	TRACE(("EFI::Header: Initialize GPT, block size %" B_PRIu32 "\n",
132 		blockSize));
133 
134 	// Initialize to an empty header
135 	memcpy(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header));
136 	fHeader.SetRevision(EFI_TABLE_REVISION);
137 	fHeader.SetHeaderSize(sizeof(fHeader));
138 	fHeader.SetHeaderCRC(0);
139 	fHeader.SetAbsoluteBlock(EFI_HEADER_LOCATION);
140 	fHeader.SetAlternateBlock(0); // TODO
141 	// TODO: set disk guid
142 	fHeader.SetEntriesBlock(EFI_PARTITION_ENTRIES_BLOCK);
143 	fHeader.SetEntryCount(EFI_PARTITION_ENTRY_COUNT);
144 	fHeader.SetEntrySize(EFI_PARTITION_ENTRY_SIZE);
145 	fHeader.SetEntriesCRC(0);
146 
147 	size_t arraySize = _EntryArraySize();
148 	fEntries = new (std::nothrow) uint8[arraySize];
149 	if (fEntries == NULL) {
150 		fStatus = B_NO_MEMORY;
151 		return;
152 	}
153 
154 	memset(fEntries, 0, arraySize);
155 		// TODO: initialize the entry guids
156 
157 	uint32 entryBlocks = (arraySize + fBlockSize - 1) / fBlockSize;
158 	fHeader.SetFirstUsableBlock(EFI_PARTITION_ENTRIES_BLOCK + entryBlocks);
159 	fHeader.SetLastUsableBlock(lastBlock - 1 - entryBlocks);
160 
161 	_SetBackupHeaderFromPrimary(lastBlock);
162 
163 #ifdef TRACE_EFI_GPT
164 	_Dump(fHeader);
165 	_DumpPartitions();
166 #endif
167 
168 	fStatus = B_OK;
169 }
170 #endif // !_BOOT_MODE
171 
172 
173 Header::~Header()
174 {
175 	delete[] fEntries;
176 }
177 
178 
179 status_t
180 Header::InitCheck() const
181 {
182 	return fStatus;
183 }
184 
185 
186 #ifndef _BOOT_MODE
187 status_t
188 Header::WriteEntry(int fd, uint32 entryIndex)
189 {
190 	// Determine block to write
191 	off_t blockOffset =
192 		+ entryIndex * fHeader.EntrySize() / fBlockSize;
193 	uint32 entryOffset = entryIndex * fHeader.EntrySize() % fBlockSize;
194 
195 	status_t status = _Write(fd,
196 		(fHeader.EntriesBlock() + blockOffset) * fBlockSize,
197 		fEntries + entryOffset, fBlockSize);
198 	if (status != B_OK)
199 		return status;
200 
201 	// Update header, too -- the entries CRC changed
202 	status = _WriteHeader(fd);
203 
204 	// Write backup
205 	status_t backupStatus = _Write(fd,
206 		(fBackupHeader.EntriesBlock() + blockOffset) * fBlockSize,
207 		fEntries + entryOffset, fBlockSize);
208 
209 	return status == B_OK ? backupStatus : status;
210 }
211 
212 
213 status_t
214 Header::Write(int fd)
215 {
216 	status_t status = _Write(fd, fHeader.EntriesBlock() * fBlockSize, fEntries,
217 		_EntryArraySize());
218 	if (status != B_OK)
219 		return status;
220 
221 	// First write the header, so that we have at least one completely correct
222 	// data set
223 	status = _WriteHeader(fd);
224 
225 	// Write backup entries
226 	status_t backupStatus = _Write(fd,
227 		fBackupHeader.EntriesBlock() * fBlockSize, fEntries, _EntryArraySize());
228 
229 	return status == B_OK ? backupStatus : status;
230 }
231 
232 
233 status_t
234 Header::_WriteHeader(int fd)
235 {
236 	_UpdateCRC();
237 
238 	status_t status = _Write(fd, fHeader.AbsoluteBlock() * fBlockSize,
239 		&fHeader, sizeof(efi_table_header));
240 	if (status != B_OK)
241 		return status;
242 
243 	return _Write(fd, fBackupHeader.AbsoluteBlock() * fBlockSize,
244 		&fBackupHeader, sizeof(efi_table_header));
245 }
246 
247 
248 status_t
249 Header::_Write(int fd, off_t offset, const void* data, size_t size) const
250 {
251 	ssize_t bytesWritten = write_pos(fd, offset, data, size);
252 	if (bytesWritten < 0)
253 		return bytesWritten;
254 	if (bytesWritten != (ssize_t)size)
255 		return B_IO_ERROR;
256 
257 	return B_OK;
258 }
259 
260 
261 void
262 Header::_UpdateCRC()
263 {
264 	_UpdateCRC(fHeader);
265 	_UpdateCRC(fBackupHeader);
266 }
267 
268 
269 void
270 Header::_UpdateCRC(efi_table_header& header)
271 {
272 	header.SetEntriesCRC(crc32(fEntries, _EntryArraySize()));
273 	header.SetHeaderCRC(0);
274 	header.SetHeaderCRC(crc32((uint8*)&header, sizeof(efi_table_header)));
275 }
276 #endif // !_BOOT_MODE
277 
278 
279 status_t
280 Header::_Read(int fd, off_t offset, void* data, size_t size) const
281 {
282 	ssize_t bytesRead = read_pos(fd, offset, data, size);
283 	if (bytesRead < 0)
284 		return bytesRead;
285 	if (bytesRead != (ssize_t)size)
286 		return B_IO_ERROR;
287 
288 	return B_OK;
289 }
290 
291 
292 bool
293 Header::_IsHeaderValid(const efi_table_header& header, uint64 block)
294 {
295 	return !memcmp(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header))
296 		&& _ValidateHeaderCRC()
297 		&& fHeader.AbsoluteBlock() == block;
298 }
299 
300 
301 bool
302 Header::_ValidateHeaderCRC()
303 {
304 	uint32 originalCRC = fHeader.HeaderCRC();
305 	fHeader.SetHeaderCRC(0);
306 
307 	bool matches = originalCRC == crc32((const uint8*)&fHeader,
308 		sizeof(efi_table_header));
309 
310 	fHeader.SetHeaderCRC(originalCRC);
311 	return matches;
312 }
313 
314 
315 bool
316 Header::_ValidateEntriesCRC() const
317 {
318 	return fHeader.EntriesCRC() == crc32(fEntries, _EntryArraySize());
319 }
320 
321 
322 void
323 Header::_SetBackupHeaderFromPrimary(uint64 lastBlock)
324 {
325 	fBackupHeader = fHeader;
326 	fBackupHeader.SetAbsoluteBlock(lastBlock);
327 	fBackupHeader.SetEntriesBlock(
328 		lastBlock - _EntryArraySize() / fBlockSize);
329 	fBackupHeader.SetAlternateBlock(1);
330 }
331 
332 
333 #ifdef TRACE_EFI_GPT
334 const char *
335 Header::_PrintGUID(const guid_t &id)
336 {
337 	static char guid[48];
338 	snprintf(guid, sizeof(guid),
339 		"%08" B_PRIx32 "-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
340 		B_LENDIAN_TO_HOST_INT32(id.data1), B_LENDIAN_TO_HOST_INT16(id.data2),
341 		B_LENDIAN_TO_HOST_INT16(id.data3), id.data4[0], id.data4[1],
342 		id.data4[2], id.data4[3], id.data4[4], id.data4[5], id.data4[6],
343 		id.data4[7]);
344 	return guid;
345 }
346 
347 
348 void
349 Header::_Dump(const efi_table_header& header)
350 {
351 	dprintf("EFI header: %.8s\n", header.header);
352 	dprintf("EFI revision: %" B_PRIx32 "\n", header.Revision());
353 	dprintf("header size: %" B_PRId32 "\n", header.HeaderSize());
354 	dprintf("header CRC: %" B_PRId32 "\n", header.HeaderCRC());
355 	dprintf("absolute block: %" B_PRIu64 "\n", header.AbsoluteBlock());
356 	dprintf("alternate block: %" B_PRIu64 "\n", header.AlternateBlock());
357 	dprintf("first usable block: %" B_PRIu64 "\n", header.FirstUsableBlock());
358 	dprintf("last usable block: %" B_PRIu64 "\n", header.LastUsableBlock());
359 	dprintf("disk GUID: %s\n", _PrintGUID(header.disk_guid));
360 	dprintf("entries block: %" B_PRIu64 "\n", header.EntriesBlock());
361 	dprintf("entry size:  %" B_PRIu32 "\n", header.EntrySize());
362 	dprintf("entry count: %" B_PRIu32 "\n", header.EntryCount());
363 	dprintf("entries CRC: %" B_PRIu32 "\n", header.EntriesCRC());
364 }
365 
366 
367 void
368 Header::_DumpPartitions()
369 {
370 	for (uint32 i = 0; i < EntryCount(); i++) {
371 		const efi_partition_entry &entry = EntryAt(i);
372 
373 		if (entry.partition_type == kEmptyGUID)
374 			continue;
375 
376 		dprintf("[%3" B_PRIu32 "] partition type: %s\n", i,
377 			_PrintGUID(entry.partition_type));
378 		dprintf("      unique id: %s\n", _PrintGUID(entry.unique_guid));
379 		dprintf("      start block: %" B_PRIu64 "\n", entry.StartBlock());
380 		dprintf("      end block: %" B_PRIu64 "\n", entry.EndBlock());
381 		dprintf("      size: %g MB\n", (entry.EndBlock() - entry.StartBlock())
382 			* 512 / 1024.0 / 1024.0);
383 		dprintf("      attributes: %" B_PRIx64 "\n", entry.Attributes());
384 
385 		char name[64];
386 		to_utf8(entry.name, EFI_PARTITION_NAME_LENGTH, name, sizeof(name));
387 		dprintf("      name: %s\n", name);
388 	}
389 }
390 #endif	// TRACE_EFI_GPT
391 
392 
393 }	// namespace EFI
394