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.
15 #include <KernelExport.h>
18 # include <util/kernel_cpp.h>
30 # define dprintf printf
32 # define TRACE(x) dprintf x
41 Header::Header(int fd
, uint64 lastBlock
, uint32 blockSize
)
43 fBlockSize(blockSize
),
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
))
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
));
68 if (!_IsHeaderValid(fBackupHeader
, lastBlock
))
72 // If both headers are invalid, bail out -- this is probably not a GPT disk
73 if (status
!= B_OK
&& fStatus
!= B_OK
)
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
;
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());
106 if (!_ValidateEntriesCRC()) {
107 fStatus
= B_BAD_DATA
;
112 // TODO: check overlapping or out of range partitions
116 _Dump(fBackupHeader
);
125 Header::Header(uint64 lastBlock
, uint32 blockSize
)
127 fBlockSize(blockSize
),
131 TRACE(("EFI::Header: Initialize GPT, block size %" B_PRIu32
"\n",
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
;
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
);
170 #endif // !_BOOT_MODE
180 Header::InitCheck() const
188 Header::WriteEntry(int fd
, uint32 entryIndex
)
190 // Determine block to write
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
);
201 // Update header, too -- the entries CRC changed
202 status
= _WriteHeader(fd
);
205 status_t backupStatus
= _Write(fd
,
206 (fBackupHeader
.EntriesBlock() + blockOffset
) * fBlockSize
,
207 fEntries
+ entryOffset
, fBlockSize
);
209 return status
== B_OK
? backupStatus
: status
;
214 Header::Write(int fd
)
216 status_t status
= _Write(fd
, fHeader
.EntriesBlock() * fBlockSize
, fEntries
,
221 // First write the header, so that we have at least one completely correct
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
;
234 Header::_WriteHeader(int fd
)
238 status_t status
= _Write(fd
, fHeader
.AbsoluteBlock() * fBlockSize
,
239 &fHeader
, sizeof(efi_table_header
));
243 return _Write(fd
, fBackupHeader
.AbsoluteBlock() * fBlockSize
,
244 &fBackupHeader
, sizeof(efi_table_header
));
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)
254 if (bytesWritten
!= (ssize_t
)size
)
265 _UpdateCRC(fBackupHeader
);
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
280 Header::_Read(int fd
, off_t offset
, void* data
, size_t size
) const
282 ssize_t bytesRead
= read_pos(fd
, offset
, data
, size
);
285 if (bytesRead
!= (ssize_t
)size
)
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
;
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
);
316 Header::_ValidateEntriesCRC() const
318 return fHeader
.EntriesCRC() == crc32(fEntries
, _EntryArraySize());
323 Header::_SetBackupHeaderFromPrimary(uint64 lastBlock
)
325 fBackupHeader
= fHeader
;
326 fBackupHeader
.SetAbsoluteBlock(lastBlock
);
327 fBackupHeader
.SetEntriesBlock(
328 lastBlock
- _EntryArraySize() / fBlockSize
);
329 fBackupHeader
.SetAlternateBlock(1);
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],
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());
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
)
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());
386 to_utf8(entry
.name
, EFI_PARTITION_NAME_LENGTH
, name
, sizeof(name
));
387 dprintf(" name: %s\n", name
);
390 #endif // TRACE_EFI_GPT