BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / partitioning_systems / gpt / Header.cpp
blob6099445c0b7f519fe4a8cc2154fe3db9a038911c
1 /*
2 * Copyright 2007-2013, Axel Dörfler, axeld@pinc-software.de.
3 * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. All rights reserved.
5 * Distributed under the terms of the MIT License.
6 */
9 #include "Header.h"
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <string.h>
15 #include <KernelExport.h>
17 #ifdef _KERNEL_MODE
18 # include <util/kernel_cpp.h>
19 #else
20 # include <new>
21 #endif
23 #include "crc32.h"
24 #include "utility.h"
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
38 namespace EFI {
41 Header::Header(int fd, uint64 lastBlock, uint32 blockSize)
43 fBlockSize(blockSize),
44 fStatus(B_NO_INIT),
45 fEntries(NULL)
47 // TODO: check the correctness of the protective MBR and warn if invalid
49 // Read and check the partition table header
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;
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();
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;
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;
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);
87 // allocate, read, and check partition entry array
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;
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;
106 if (!_ValidateEntriesCRC()) {
107 fStatus = B_BAD_DATA;
108 return;
112 // TODO: check overlapping or out of range partitions
114 #ifdef TRACE_EFI_GPT
115 _Dump(fHeader);
116 _Dump(fBackupHeader);
117 _DumpPartitions();
118 #endif
120 fStatus = B_OK;
124 #ifndef _BOOT_MODE
125 Header::Header(uint64 lastBlock, uint32 blockSize)
127 fBlockSize(blockSize),
128 fStatus(B_NO_INIT),
129 fEntries(NULL)
131 TRACE(("EFI::Header: Initialize GPT, block size %" B_PRIu32 "\n",
132 blockSize));
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(lastBlock);
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);
147 size_t arraySize = _EntryArraySize();
148 fEntries = new (std::nothrow) uint8[arraySize];
149 if (fEntries == NULL) {
150 fStatus = B_NO_MEMORY;
151 return;
154 memset(fEntries, 0, arraySize);
155 // TODO: initialize the entry guids
157 uint32 entryBlocks = (arraySize + fBlockSize - 1) / fBlockSize;
158 fHeader.SetFirstUsableBlock(EFI_PARTITION_ENTRIES_BLOCK + entryBlocks);
159 fHeader.SetLastUsableBlock(lastBlock - 1 - entryBlocks);
161 _SetBackupHeaderFromPrimary(lastBlock);
163 #ifdef TRACE_EFI_GPT
164 _Dump(fHeader);
165 _DumpPartitions();
166 #endif
168 fStatus = B_OK;
170 #endif // !_BOOT_MODE
173 Header::~Header()
175 delete[] fEntries;
179 status_t
180 Header::InitCheck() const
182 return fStatus;
186 #ifndef _BOOT_MODE
187 status_t
188 Header::WriteEntry(int fd, uint32 entryIndex)
190 // Determine block to write
191 off_t blockOffset =
192 + entryIndex * fHeader.EntrySize() / fBlockSize;
193 uint32 entryOffset = entryIndex * fHeader.EntrySize() % fBlockSize;
195 status_t status = _Write(fd,
196 (fHeader.EntriesBlock() + blockOffset) * fBlockSize,
197 fEntries + entryOffset, fBlockSize);
198 if (status != B_OK)
199 return status;
201 // Update header, too -- the entries CRC changed
202 status = _WriteHeader(fd);
204 // Write backup
205 status_t backupStatus = _Write(fd,
206 (fBackupHeader.EntriesBlock() + blockOffset) * fBlockSize,
207 fEntries + entryOffset, fBlockSize);
209 return status == B_OK ? backupStatus : status;
213 status_t
214 Header::Write(int fd)
216 status_t status = _Write(fd, fHeader.EntriesBlock() * fBlockSize, fEntries,
217 _EntryArraySize());
218 if (status != B_OK)
219 return status;
221 // First write the header, so that we have at least one completely correct
222 // data set
223 status = _WriteHeader(fd);
225 // Write backup entries
226 status_t backupStatus = _Write(fd,
227 fBackupHeader.EntriesBlock() * fBlockSize, fEntries, _EntryArraySize());
229 return status == B_OK ? backupStatus : status;
233 status_t
234 Header::_WriteHeader(int fd)
236 _UpdateCRC();
238 status_t status = _Write(fd, fHeader.AbsoluteBlock() * fBlockSize,
239 &fHeader, sizeof(efi_table_header));
240 if (status != B_OK)
241 return status;
243 return _Write(fd, fBackupHeader.AbsoluteBlock() * fBlockSize,
244 &fBackupHeader, sizeof(efi_table_header));
248 status_t
249 Header::_Write(int fd, off_t offset, const void* data, size_t size) const
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;
257 return B_OK;
261 void
262 Header::_UpdateCRC()
264 _UpdateCRC(fHeader);
265 _UpdateCRC(fBackupHeader);
269 void
270 Header::_UpdateCRC(efi_table_header& header)
272 header.SetEntriesCRC(crc32(fEntries, _EntryArraySize()));
273 header.SetHeaderCRC(0);
274 header.SetHeaderCRC(crc32((uint8*)&header, sizeof(efi_table_header)));
276 #endif // !_BOOT_MODE
279 status_t
280 Header::_Read(int fd, off_t offset, void* data, size_t size) const
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;
288 return B_OK;
292 bool
293 Header::_IsHeaderValid(efi_table_header& header, uint64 block)
295 return !memcmp(header.header, EFI_PARTITION_HEADER, sizeof(header.header))
296 && _ValidateHeaderCRC(header)
297 && header.AbsoluteBlock() == block;
301 bool
302 Header::_ValidateHeaderCRC(efi_table_header& header)
304 uint32 originalCRC = header.HeaderCRC();
305 header.SetHeaderCRC(0);
307 bool matches = originalCRC == crc32((const uint8*)&header,
308 sizeof(efi_table_header));
310 header.SetHeaderCRC(originalCRC);
311 return matches;
315 bool
316 Header::_ValidateEntriesCRC() const
318 return fHeader.EntriesCRC() == crc32(fEntries, _EntryArraySize());
322 void
323 Header::_SetBackupHeaderFromPrimary(uint64 lastBlock)
325 fBackupHeader = fHeader;
326 fBackupHeader.SetAbsoluteBlock(lastBlock);
327 fBackupHeader.SetEntriesBlock(
328 lastBlock - _EntryArraySize() / fBlockSize);
329 fBackupHeader.SetAlternateBlock(1);
333 #ifdef TRACE_EFI_GPT
334 const char *
335 Header::_PrintGUID(const guid_t &id)
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;
348 void
349 Header::_Dump(const efi_table_header& header)
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());
367 void
368 Header::_DumpPartitions()
370 for (uint32 i = 0; i < EntryCount(); i++) {
371 const efi_partition_entry &entry = EntryAt(i);
373 if (entry.partition_type == kEmptyGUID)
374 continue;
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());
385 char name[64];
386 to_utf8(entry.name, EFI_PARTITION_NAME_LENGTH, name, sizeof(name));
387 dprintf(" name: %s\n", name);
390 #endif // TRACE_EFI_GPT
393 } // namespace EFI