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>
24 #include "PartitionMap.h"
25 #include "PartitionMapWriter.h"
28 #if !defined(_BOOT_MODE) && !defined(_USER_MODE)
39 # define dprintf printf
41 # define TRACE(x) dprintf x
50 Header::Header(int fd
, uint64 lastBlock
, uint32 blockSize
)
52 fBlockSize(blockSize
),
57 // TODO: check the correctness of the protective MBR and warn if invalid
59 // Read and check the partition table header
61 fStatus
= _Read(fd
, (uint64
)EFI_HEADER_LOCATION
* blockSize
,
62 &fHeader
, sizeof(efi_table_header
));
63 if (fStatus
== B_OK
) {
64 if (!_IsHeaderValid(fHeader
, EFI_HEADER_LOCATION
))
68 if (fStatus
== B_OK
&& lastBlock
!= fHeader
.AlternateBlock()) {
69 dprintf("gpt: alternate header not in last block (%" B_PRIu64
" vs. %"
70 B_PRIu64
")\n", fHeader
.AlternateBlock(), lastBlock
);
71 lastBlock
= fHeader
.AlternateBlock();
74 // Read backup header, too
75 status_t status
= _Read(fd
, lastBlock
* blockSize
, &fBackupHeader
,
76 sizeof(efi_table_header
));
78 if (!_IsHeaderValid(fBackupHeader
, lastBlock
))
82 // If both headers are invalid, bail out -- this is probably not a GPT disk
83 if (status
!= B_OK
&& fStatus
!= B_OK
)
86 if (fStatus
!= B_OK
) {
87 // Recreate primary header from the backup
88 fHeader
= fBackupHeader
;
89 fHeader
.SetAbsoluteBlock(EFI_HEADER_LOCATION
);
90 fHeader
.SetEntriesBlock(EFI_PARTITION_ENTRIES_BLOCK
);
91 fHeader
.SetAlternateBlock(lastBlock
);
93 } else if (status
!= B_OK
) {
94 // Recreate backup header from primary
95 _SetBackupHeaderFromPrimary(lastBlock
);
98 // allocate, read, and check partition entry array
100 fEntries
= new (std::nothrow
) uint8
[_EntryArraySize()];
101 if (fEntries
== NULL
) {
102 // TODO: if there cannot be allocated enough (ie. the boot loader's
103 // heap is limited), try a smaller size before failing
104 fStatus
= B_NO_MEMORY
;
108 fStatus
= _Read(fd
, fHeader
.EntriesBlock() * blockSize
,
109 fEntries
, _EntryArraySize());
110 if (fStatus
!= B_OK
|| !_ValidateEntriesCRC()) {
111 // Read backup entries instead
112 fStatus
= _Read(fd
, fBackupHeader
.EntriesBlock() * blockSize
,
113 fEntries
, _EntryArraySize());
117 if (!_ValidateEntriesCRC()) {
118 fStatus
= B_BAD_DATA
;
123 // TODO: check overlapping or out of range partitions
127 _Dump(fBackupHeader
);
135 #if !defined(_BOOT_MODE) && !defined(_USER_MODE)
136 Header::Header(uint64 lastBlock
, uint32 blockSize
)
138 fBlockSize(blockSize
),
143 TRACE(("EFI::Header: Initialize GPT, block size %" B_PRIu32
"\n",
146 // Initialize to an empty header
147 memcpy(fHeader
.header
, EFI_PARTITION_HEADER
, sizeof(fHeader
.header
));
148 fHeader
.SetRevision(EFI_TABLE_REVISION
);
149 fHeader
.SetHeaderSize(sizeof(fHeader
));
150 fHeader
.SetHeaderCRC(0);
151 fHeader
.SetAbsoluteBlock(EFI_HEADER_LOCATION
);
152 fHeader
.SetAlternateBlock(lastBlock
);
154 uuid_generate_random(uuid
);
155 memcpy((uint8
*)&fHeader
.disk_guid
, uuid
, sizeof(guid_t
));
156 fHeader
.SetEntriesBlock(EFI_PARTITION_ENTRIES_BLOCK
);
157 fHeader
.SetEntryCount(EFI_PARTITION_ENTRY_COUNT
);
158 fHeader
.SetEntrySize(EFI_PARTITION_ENTRY_SIZE
);
159 fHeader
.SetEntriesCRC(0);
161 size_t arraySize
= _EntryArraySize();
162 fEntries
= new (std::nothrow
) uint8
[arraySize
];
163 if (fEntries
== NULL
) {
164 fStatus
= B_NO_MEMORY
;
168 memset(fEntries
, 0, arraySize
);
169 // TODO: initialize the entry guids
171 uint32 entryBlocks
= (arraySize
+ fBlockSize
- 1) / fBlockSize
;
172 fHeader
.SetFirstUsableBlock(EFI_PARTITION_ENTRIES_BLOCK
+ entryBlocks
);
173 fHeader
.SetLastUsableBlock(lastBlock
- 1 - entryBlocks
);
175 _SetBackupHeaderFromPrimary(lastBlock
);
184 #endif // !_BOOT_MODE && !_USER_MODE
194 Header::InitCheck() const
201 Header::IsDirty() const
209 Header::WriteEntry(int fd
, uint32 entryIndex
)
211 off_t entryOffset
= entryIndex
* fHeader
.EntrySize();
213 status_t status
= _Write(fd
,
214 fHeader
.EntriesBlock() * fBlockSize
+ entryOffset
,
215 fEntries
+ entryOffset
, fHeader
.EntrySize());
219 // Update header, too -- the entries CRC changed
220 status
= _WriteHeader(fd
);
223 status_t backupStatus
= _Write(fd
,
224 fBackupHeader
.EntriesBlock() * fBlockSize
+ entryOffset
,
225 fEntries
+ entryOffset
, fHeader
.EntrySize());
227 if (status
== B_OK
&& backupStatus
== B_OK
)
229 return status
== B_OK
? backupStatus
: status
;
234 Header::Write(int fd
)
236 // Try to write the protective MBR
237 PartitionMap partitionMap
;
238 PrimaryPartition
*partition
= NULL
;
240 while ((partition
= partitionMap
.PrimaryPartitionAt(index
)) != NULL
) {
242 uint64 deviceSize
= fHeader
.AlternateBlock() * fBlockSize
;
243 partition
->SetTo(fBlockSize
, deviceSize
, 0xEE, false, fBlockSize
);
248 PartitionMapWriter
writer(fd
, fBlockSize
);
249 writer
.WriteMBR(&partitionMap
, true);
250 // We also write the bootcode, so we can boot GPT disks from BIOS
252 status_t status
= _Write(fd
, fHeader
.EntriesBlock() * fBlockSize
, fEntries
,
257 // First write the header, so that we have at least one completely correct
259 status
= _WriteHeader(fd
);
262 // Write backup entries
263 status_t backupStatus
= _Write(fd
,
264 fBackupHeader
.EntriesBlock() * fBlockSize
, fEntries
, _EntryArraySize());
266 if (status
== B_OK
&& backupStatus
== B_OK
)
268 return status
== B_OK
? backupStatus
: status
;
273 Header::_WriteHeader(int fd
)
277 status_t status
= _Write(fd
, fHeader
.AbsoluteBlock() * fBlockSize
,
278 &fHeader
, sizeof(efi_table_header
));
282 return _Write(fd
, fBackupHeader
.AbsoluteBlock() * fBlockSize
,
283 &fBackupHeader
, sizeof(efi_table_header
));
288 Header::_Write(int fd
, off_t offset
, const void* data
, size_t size
) const
290 ssize_t bytesWritten
= write_pos(fd
, offset
, data
, size
);
291 if (bytesWritten
< 0)
293 if (bytesWritten
!= (ssize_t
)size
)
304 _UpdateCRC(fBackupHeader
);
309 Header::_UpdateCRC(efi_table_header
& header
)
311 header
.SetEntriesCRC(crc32(fEntries
, _EntryArraySize()));
312 header
.SetHeaderCRC(0);
313 header
.SetHeaderCRC(crc32((uint8
*)&header
, sizeof(efi_table_header
)));
315 #endif // !_BOOT_MODE
319 Header::_Read(int fd
, off_t offset
, void* data
, size_t size
) const
321 ssize_t bytesRead
= read_pos(fd
, offset
, data
, size
);
324 if (bytesRead
!= (ssize_t
)size
)
332 Header::_IsHeaderValid(efi_table_header
& header
, uint64 block
)
334 return !memcmp(header
.header
, EFI_PARTITION_HEADER
, sizeof(header
.header
))
335 && _ValidateHeaderCRC(header
)
336 && header
.AbsoluteBlock() == block
;
341 Header::_ValidateHeaderCRC(efi_table_header
& header
)
343 uint32 originalCRC
= header
.HeaderCRC();
344 header
.SetHeaderCRC(0);
346 bool matches
= originalCRC
== crc32((const uint8
*)&header
,
347 sizeof(efi_table_header
));
349 header
.SetHeaderCRC(originalCRC
);
355 Header::_ValidateEntriesCRC() const
357 return fHeader
.EntriesCRC() == crc32(fEntries
, _EntryArraySize());
362 Header::_SetBackupHeaderFromPrimary(uint64 lastBlock
)
364 fBackupHeader
= fHeader
;
365 fBackupHeader
.SetAbsoluteBlock(lastBlock
);
366 fBackupHeader
.SetEntriesBlock(
367 lastBlock
- _EntryArraySize() / fBlockSize
);
368 fBackupHeader
.SetAlternateBlock(1);
374 Header::_PrintGUID(const guid_t
&id
)
376 static char guid
[48];
377 snprintf(guid
, sizeof(guid
),
378 "%08" B_PRIx32
"-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
379 B_LENDIAN_TO_HOST_INT32(id
.data1
), B_LENDIAN_TO_HOST_INT16(id
.data2
),
380 B_LENDIAN_TO_HOST_INT16(id
.data3
), id
.data4
[0], id
.data4
[1],
381 id
.data4
[2], id
.data4
[3], id
.data4
[4], id
.data4
[5], id
.data4
[6],
388 Header::_Dump(const efi_table_header
& header
)
390 dprintf("EFI header: %.8s\n", header
.header
);
391 dprintf("EFI revision: %" B_PRIx32
"\n", header
.Revision());
392 dprintf("header size: %" B_PRId32
"\n", header
.HeaderSize());
393 dprintf("header CRC: %" B_PRIx32
"\n", header
.HeaderCRC());
394 dprintf("absolute block: %" B_PRIu64
"\n", header
.AbsoluteBlock());
395 dprintf("alternate block: %" B_PRIu64
"\n", header
.AlternateBlock());
396 dprintf("first usable block: %" B_PRIu64
"\n", header
.FirstUsableBlock());
397 dprintf("last usable block: %" B_PRIu64
"\n", header
.LastUsableBlock());
398 dprintf("disk GUID: %s\n", _PrintGUID(header
.disk_guid
));
399 dprintf("entries block: %" B_PRIu64
"\n", header
.EntriesBlock());
400 dprintf("entry size: %" B_PRIu32
"\n", header
.EntrySize());
401 dprintf("entry count: %" B_PRIu32
"\n", header
.EntryCount());
402 dprintf("entries CRC: %" B_PRIx32
"\n", header
.EntriesCRC());
407 Header::_DumpPartitions()
409 for (uint32 i
= 0; i
< EntryCount(); i
++) {
410 const efi_partition_entry
&entry
= EntryAt(i
);
412 if (entry
.partition_type
== kEmptyGUID
)
415 dprintf("[%3" B_PRIu32
"] partition type: %s\n", i
,
416 _PrintGUID(entry
.partition_type
));
417 dprintf(" unique id: %s\n", _PrintGUID(entry
.unique_guid
));
418 dprintf(" start block: %" B_PRIu64
"\n", entry
.StartBlock());
419 dprintf(" end block: %" B_PRIu64
"\n", entry
.EndBlock());
420 dprintf(" size: %g MB\n", (entry
.EndBlock() - entry
.StartBlock())
421 * 512 / 1024.0 / 1024.0);
422 dprintf(" attributes: %" B_PRIx64
"\n", entry
.Attributes());
425 to_utf8(entry
.name
, EFI_PARTITION_NAME_LENGTH
, name
, sizeof(name
));
426 dprintf(" name: %s\n", name
);
429 #endif // TRACE_EFI_GPT